suite.selection¶
Suite-less test selection — resolve --tests / -m against collected tests.
This module holds the pure selection-resolution logic that otto test
carried inline in otto.cli.test before it moved here (library-extraction
Phase A): matching --tests names against every repo’s collected tests
(resolve_selection()) and narrowing repos by a marker expression alone
(repos_with_marker_matches()). Extracting it here lets a suite-less
selection be resolved as a plain library call, independent of Typer.
This module never imports typer — the library raises library exceptions
(UnknownSelectionError for a genuinely unknown test name); the CLI
adapter in otto.cli.test owns the translation to typer.BadParameter.
- class otto.suite.selection.SelectionMatch(repo: Repo, targets: list[str])¶
Bases:
objectOne repo’s resolved selection: the repo plus its matched pytest targets.
targetsare absolute nodeids (fromresolve_selection()) or absolute test-directory paths (from the-m-alone branch ofotto.suite.run.run_selection()) — always non-empty, since callers only construct aSelectionMatchfor a repo that matched.
-
exception otto.suite.selection.UnknownSelectionError(message: str, *, param_hint: str =
'--tests')¶ Bases:
ValueErrorA requested test name matched nothing despite a non-empty test universe.
Raised by
resolve_selection()when at least one requested name went unmatched while there were collected tests to search — i.e. a genuine typo, not an empty selection. The message carries the same did-you-mean suggestions the CLI has always shown;param_hintnames the CLI flag the selection came from so the CLI adapter can re-raise astyper.BadParameterwith an identical rendering.Subclasses
ValueError, so callers that catch a genericValueErrorfor “nothing matched” must catch this first to distinguish the typo case.
- otto.suite.selection.resolve_selection(repos: list[Repo], names: list[str], markers: str) list[SelectionMatch]¶
Resolve –tests names to exact nodeids, one entry per matching repo.
A bare name matches every collected test with that function name (all parametrizations);
Class::namerestricts to one suite. Unknown names raiseUnknownSelectionErrorwith did-you-mean suggestions — never a silent empty run — but only when there was an actual test universe to search (at least one collected test across every searched repo). With no repos, or repos that collected nothing, there is nothing to suggest, so this returns an empty list instead: the generic “no tests matched the selection” failure (raised by callers such asotto.suite.run.run_selection()) is the more honest message than a did-you-mean hint with no hints to offer.
- otto.suite.selection.repos_with_marker_matches(repos: list[Repo], markers: str) list[Repo]¶
Filter to repos whose collection has >=1 item matching
markers.Used by the
-m-alone branch ofrun_selectionso a repo whose suites don’t carry the given marker never gets a pytest session of its own — such a session would collect nothing and exit 5 (NO_TESTS_COLLECTED), which previously failed the whole multi-repo run viaworst = max(worst, rc)even when every other repo matched fine.