Skip to content

pytest_plugin(refactor[types]): Type improvements#521

Merged
tony merged 9 commits intomasterfrom
type-extraction
Apr 6, 2026
Merged

pytest_plugin(refactor[types]): Type improvements#521
tony merged 9 commits intomasterfrom
type-extraction

Conversation

@tony
Copy link
Copy Markdown
Member

@tony tony commented Apr 6, 2026

Cleans up the public type surface of libvcs.pytest_plugin so that downstream projects can import stable, named types and so the Sphinx docs render cross-reference links rather than expanded type strings.

  • New TypeAliases: Env (public alias for the subprocess env mapping, replacing the private _ENV in all env: parameters) and GitCommitEnvVars (dict[str, str], the type of the git_commit_envvars fixture return value)
  • Rename: CreateRepoPytestFixtureFnCreateRepoFn; the "PytestFixture" infix was redundant inside pytest_plugin.py and inconsistent with the Fn-suffix pattern used by CreateRepoPostInitFn
  • Docs: add autoclass entries for CreateRepoFn and CreateRepoPostInitFn in docs/api/pytest-plugin.md so sphinx_autodoc_pytest_fixtures resolves them as hyperlinks in the fixture summary table (previously rendered as plain text because the classes were absent from Sphinx's Python domain index)

Breaking change

Replace any from libvcs.pytest_plugin import CreateRepoPytestFixtureFn with CreateRepoFn.

Test plan

  • uv run ruff check . --fix --show-fixes; uv run ruff format .; uv run mypy; uv run py.test --reruns 0
  • just build-docs — verify CreateRepoFn renders as a hyperlink in the fixture summary table

tony added 3 commits April 5, 2026 19:57
why: `_ENV` from `_internal/run.py` leaked into public API signatures,
causing Sphinx to render its full expanded form
(`Mapping[bytes, ...] | Mapping[str, ...]`) in the docs instead of a
linkable name. `dict[str, str]` was also used ad-hoc in test
annotations without a shared name.
what:
- Add `Env: t.TypeAlias = _ENV` as public alias for the subprocess env
  type; replace `env: _ENV | None` in all public signatures
- Add `GitCommitEnvVars: t.TypeAlias = dict[str, str]` for the
  narrower git-commit env type; replace `git_commit_envvars: _ENV`
  in fixture parameters
- Update test TYPE_CHECKING imports to use `GitCommitEnvVars`
…reateRepoFn

why: "PytestFixture" is redundant context inside pytest_plugin.py;
the shorter name aligns with the existing Fn-suffix convention used
by CreateRepoPostInitFn.
what:
- Rename Protocol class in src/libvcs/pytest_plugin.py
- Update all import sites and type annotations across tests and README.md
…nitFn for cross-referencing

why: The fixture summary table rendered return types like `CreateRepoFn`
as plain text instead of hyperlinks. sphinx_autodoc_pytest_fixtures
emits :class:`~libvcs.pytest_plugin.CreateRepoFn` for every fixture
returning that type, but Sphinx silently degrades to plain text when
the name is absent from the Python domain's object index — which it
was, since the Protocol classes had no autoclass directive anywhere.
what:
- Add a "Types" section to docs/api/pytest-plugin.md with autoclass
  directives for CreateRepoFn and CreateRepoPostInitFn
- Use :special-members: __call__ to expose the callback contract
- Use :exclude-members: __init__, _abc_impl, _is_protocol to suppress
  Protocol internals surfaced by gp-sphinx's global autodoc defaults
  (private-members: True, autodoc_class_signature: "separated")
@codecov
Copy link
Copy Markdown

codecov bot commented Apr 6, 2026

Codecov Report

❌ Patch coverage is 0% with 5 lines in your changes missing coverage. Please review.
✅ Project coverage is 57.05%. Comparing base (ae3c53f) to head (4260c59).
⚠️ Report is 10 commits behind head on master.

Files with missing lines Patch % Lines
src/libvcs/pytest_plugin.py 0.00% 5 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master     #521      +/-   ##
==========================================
- Coverage   57.07%   57.05%   -0.03%     
==========================================
  Files          41       41              
  Lines        6216     6219       +3     
  Branches     1074     1074              
==========================================
  Hits         3548     3548              
- Misses       2144     2147       +3     
  Partials      524      524              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@tony tony force-pushed the type-extraction branch from f5ba6fd to 9be0ac2 Compare April 6, 2026 11:27
tony added 5 commits April 6, 2026 06:35
…ars TypeAlias

why: sphinx's automodule :members: skips module-level TypeAliases whose
value has a __module__ that differs from the documented module (e.g.
dict[str, str] has __module__ = 'builtins').  Static source analysis via
ModuleAnalyzer.attr_docs only picks up the member when a docstring is
present.  Without it, GitCommitEnvVars never enters the py:data inventory
and cannot be cross-referenced from the fixture summary table.

what:
- Add one-line docstring to GitCommitEnvVars: TypeAlias so sphinx's
  static analyser includes it in automodule :members: output, registering
  it as a py:data entry in the Sphinx inventory
…encing

why: The fixture summary table rendered by sphinx-autodoc-pytest-fixtures
emits :class: cross-references for every fixture's return type.  For
GitCommitEnvVars (now a py:data entry after gaining a docstring) to
resolve as a clickable link in that table, it must appear in the
autodata directive that populates the ## Types section — the same
pattern used for CreateRepoFn and CreateRepoPostInitFn.

what:
- Add .. autodata:: libvcs.pytest_plugin.GitCommitEnvVars at the top of
  the ## Types block so the type is both documented and reachable as an
  anchor from the fixture table link
…s: links

why: sphinx-autodoc-pytest-fixtures wraps every fixture return-type
identifier in a :class: cross-reference so that Sphinx and intersphinx
can resolve it.  Sphinx 8.x's Python domain only searches py:class and
py:exception entries for :class: references; TypeAliases land in the
inventory as py:data instead.  The result is a broken (unlinked) type
name in the fixture summary table for any TypeAlias return type.

The same handler was added to unihan-etl's conf.py for the same reason;
this brings libvcs in line with that approach.

what:
- Import typing as t at the top of conf.py
- Guard docutils / sphinx type imports under TYPE_CHECKING so mypy gets
  accurate types without a runtime dependency on types-docutils being
  present in every environment
- Add _on_missing_class_reference(): when a :class: reference cannot be
  resolved, fall back to searching all Python-domain objects so that
  py:data entries (TypeAliases) are found and linked
- Add setup(app) to wire the handler into Sphinx's missing-reference
  event
why: env.get_domain() returns Domain, the base class, which does not
declare find_obj().  mypy correctly flags this as an attr-defined error
because find_obj is defined only on PythonDomain.

what:
- Import PythonDomain from sphinx.domains.python under TYPE_CHECKING
- Annotate py_domain as PythonDomain with a type: ignore[assignment]
  comment to satisfy mypy while keeping the cast lightweight at runtime
@tony tony merged commit ec9bee2 into master Apr 6, 2026
7 checks passed
@tony tony deleted the type-extraction branch April 6, 2026 11:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant