Skip to content

Fix Ruff's typing-only import, annotation and in-function import findings (TC, ANN, PLC) - #65

Merged
anth-volk merged 7 commits into
mainfrom
claude/ruff-typing
Nov 3, 2026
Merged

anth-volk merged 7 commits into
mainfrom
claude/ruff-typing

Conversation

@anth-volk

@anth-volk anth-volk commented Oct 30, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #64

Part of #53.

What this does

The third of the pull requests stacked on #55 that fix the Ruff findings #55 leaves ignored: the 180 findings in TC, ANN and PLC, after which pyproject.toml no longer ignores those three families. Stacked on #63. Behaviour is unchanged.

  • TC001, TC002, TC003 (65) and TC006 (15): typing-only imports. Ruff's fixes move the imports used only in annotations into each module's if TYPE_CHECKING: block, and quote the types passed to cast(). Ruff reports these only where annotations are not evaluated at runtime, and Select every Ruff rule, configured as the simulation API and the Public API configure it #55's runtime-evaluated-base-classes keeps the SQLAlchemy models' imports at runtime. I checked that a moved name is never imported from the module that moved it, that no test patches one, and that every name Microcosm imports from the emitter (orrery.client, orrery.contracts, telemetry.client, telemetry.constants, the lifecycle and launcher modules) still resolves.
  • PLC0414 (11): redundant aliases. Seven modules re-export interface names with import X as X, the form that marks a re-export for type checkers. Both packages ship py.typed, so dropping the alias alone would hide those names from type checkers of code that imports them, such as Microcosm's imports of PublicationFileRole, PublicationInventory and PublicationReceipt from orrery.contracts. Instead, each module lists all its public names in __all__, as huggingface/service/queue.py already does, and imports plainly. Neither repository uses a star import of these modules.
  • PLC0415 (10): in-function imports.
    • The root conftest.py moves to tests/conftest.py. Its four in-function imports fall under the tests' exemptions there, as in both services, which have no root conftest.py; the rules tool allows no exception at the root. testpaths is tests, so the autouse fixture still applies to every test (pytest --fixtures lists it from tests/conftest.py). emitter.tests.no-ambient-credentials-or-hosted-collector names the new path.
    • banned-module-level-imports gains microcosm_emitter.host.__main__, which only microcosm-emitter host imports, inside main.
    • core/auth.py imports threading at module level.
    • Three # noqa: PLC0415, each with its reason above it, in cli.py: the two imports of core.collector_origin and the import of core.auth stay inside their commands. At module level they would load the collector and authentication code, and through it the Hugging Face Hub, in every microcosm-emitter host process as it starts; today the host loads that code only when it loads a module, after reading its configuration. They also would bind the functions once, while the telemetry-token tests patch core.auth's functions and rely on the command looking them up when it runs. Neither module can join the banned list, because the services import both at module level. A module that only the CLI imports lazily would bind the names once too, so it would break those tests. TELEMETRY_SCOPE, a constant, is now imported at module level.
  • ANN (79): annotations.
    • Parameters that accept any value and only inspect it are typed object (ANN401). Some callers pass something narrower: _wait_seconds (a float), publication_id_of (an inventory), receipt_matches (a mapping) and the **details of six telemetry methods.
    • JSON values use a new type alias, JsonValue, in the interface's serialization module. wire_value returns it, and three callers narrow it with cast("dict[str, Any]", ...).
    • Hugging Face Hub objects are typed with the Hub's own classes (HfApi, CommitOperationAdd, LastCommitInfo), imported only under TYPE_CHECKING; the functions still import them at runtime only when called. LastCommitInfo was checked against the locked huggingface_hub only.
    • The other parameter and return types are filled in, among them the standard library's signature for redirect_request, SQLAlchemy's Dialect, and -> None on __init__ methods.
  • Ruff no longer ignores TC, ANN and PLC.

JsonValue stays public (round 1): it is the return type of the public wire_value, and three emitter modules import it. A separate added fragment records it, so merging this pull request is a minor release.

A helper agent wrote the annotations from a brief I gave it. I checked the result: with annotations, TYPE_CHECKING blocks, typing imports, docstrings and cast calls removed, the syntax tree of every module changed in that commit matches the base.

Test-first record

There is no red step, since nothing changes behaviour.

Commit Result
4f5ef91 TC and PLC0414 ruff check . and ruff format --check . pass. The import checks above pass.
d3d3f9a PLC0415 ruff check ., ruff format --check . and the guards pass. pytest --fixtures finds the fixture in tests/conftest.py.
829912c ANN, and the three families leave the list ruff check ., ruff format --check . and all five guards pass with GITHUB_BASE_REF=main. The 25 changed modules' syntax trees match once the annotations are removed.
2474eb6, 82ce473 Merges of #63 ruff check . and ruff format --check . pass.

Review round 1:

Commit Result
78bd37b Test the seven modules' __all__ tests/test_public_names.py pins each re-exporting module's __all__ to its current names, and checks each name exists. Red, before committing: with PublicationReceipt dropped from orrery.contracts' __all__, 1 failed and 6 passed. Green: 7 passed.
06bf28a The cli comments and the added fragment The comments say when the code loads: microcosm-emitter host loads it only when it loads a module, after reading its configuration. tools/bump-version.py --check now gives 0.12.0. ruff check . and the guards pass.

Only the new test was run, by node ID (its red and green runs above). Before it, this branch changed no test function and only moved conftest.py, unchanged. CI runs the other tests.

CI

No workflow, trigger or script changes, and no environment variable or secret is read differently.

Release

Two fragments: changelog.d/ruff-typing.changed.md for the fixes and changelog.d/json-value.added.md for JsonValue. The added fragment makes the bump minor: merging releases 0.12.0 of both distributions.

🤖 Generated with Claude Code

@anth-volk
anth-volk added this pull request to stack #46 October 30, 2026 20:24
@anth-volk anth-volk changed the title claude/ruff typing Fix Ruff's typing-only import, annotation and in-function import findings (TC, ANN, PLC) Oct 30, 2026
@anth-volk
anth-volk removed this pull request from stack #46 November 2, 2026 14:03
@anth-volk
anth-volk added this pull request to stack #72 November 2, 2026 14:04
@anth-volk
anth-volk force-pushed the claude/ruff-typing branch 3 times, most recently from 828e463 to 452d39e Compare November 2, 2026 16:49
Base automatically changed from claude/ruff-docstrings to main November 2, 2026 17:47
anth-volk and others added 6 commits November 2, 2026 21:47
…_all__

- TC001, TC002, TC003: Ruff's fixes move the 65 imports used only in
  annotations into each module's TYPE_CHECKING block, and TC006 quotes
  the 15 types passed to cast(). No module imports a moved name from
  the module that moved it, no test patches one, and every name
  Microcosm imports from the emitter still resolves.
- PLC0414: the seven modules that re-export interface names with
  "import X as X" list every public name in __all__ instead, so type
  checkers of this py.typed package still treat them as exported.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- The root conftest.py moves to tests/conftest.py, where the test
  exemptions apply, as in both services, which have no root conftest.
  testpaths is tests, so its autouse fixture still applies to every test
  (pytest --fixtures lists it from tests/conftest.py). The isolation rule
  names the new path.
- banned-module-level-imports lists microcosm_emitter.host.__main__,
  which only `microcosm-emitter host` imports, inside main.
- auth.py imports threading at module level.
- cli.py keeps three in-function imports with `# noqa: PLC0415` and the
  reason: the collector-origin and authentication modules load only when
  their command runs, so `microcosm-emitter host` never loads them, and
  the telemetry-token tests patch core.auth's functions, which the
  command must look up when it runs. TELEMETRY_SCOPE, a constant, is
  imported at module level.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- ANN401: parameters that accept any value and only inspect it are
  typed object. JSON values use the interface package's new JsonValue
  alias, which wire_value now returns; three callers narrow it with
  cast. Hugging Face Hub objects are typed with the Hub's own classes,
  imported only under TYPE_CHECKING, since the functions still import
  them at runtime only when called.
- ANN001, ANN002, ANN003, ANN201, ANN202, ANN204: the missing parameter
  and return types, including the standard library's signature for
  redirect_request, sqlalchemy's Dialect, and -> None on __init__.
- With annotations, TYPE_CHECKING blocks, typing imports and casts
  removed, the syntax tree of every changed module matches the base.
- Ruff no longer ignores ANN, PLC or TC.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Each module's __all__ must equal the listed names, and every listed name
must exist, so a public name can't be dropped or added unnoticed. With
PublicationReceipt dropped from orrery.contracts' __all__, the test
failed for that module (1 failed, 6 passed); restored, all 7 pass.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…onValue

- The cli.py comments say exactly when the collector-origin and
  authentication code loads: `microcosm-emitter host` loads it only when
  it loads a module, after reading its configuration.
- changelog.d/json-value.added.md records the interface's new public
  JsonValue alias, so the release is a minor one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
anth-volk added a commit that referenced this pull request Nov 3, 2026
#65 to #69 were rebased onto main after #63 merged, so this merge
conflicted in the 13 files this branch had changed on top of #69. Each
keeps this branch's side, plus the release's version bump in
pyproject.toml; python -m scripts.rules render left the rules views as
they were. The tree differs from 746e71e exactly by main's release
(213b093..ed06bc2).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@anth-volk
anth-volk marked this pull request as ready for review November 3, 2026 14:21
@anth-volk
anth-volk merged commit 589cd0a into main Nov 3, 2026
12 checks passed
@anth-volk
anth-volk deleted the claude/ruff-typing branch November 3, 2026 14:22
anth-volk added a commit that referenced this pull request Nov 3, 2026
#67 and #69 were rebased onto main after #65 merged, so this merge
conflicted in the 9 files this branch had changed on top of #69. Each
keeps this branch's side, plus the release's version bump in
pyproject.toml; python -m scripts.rules render left the rules views as
they were. The tree differs from 54f2464 exactly by main's release
(e233d97..d79aa4d).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.

Fix Ruff's typing-only import, annotation and in-function import findings (TC, ANN, PLC)

1 participant