Skip to content

Add type hints, centralize tool config, split Sort.launch - #66

Merged
curfew-marathon merged 2 commits into
mainfrom
refactor/type-hints-and-tool-config
Oct 25, 2026
Merged

curfew-marathon merged 2 commits into
mainfrom
refactor/type-hints-and-tool-config

Conversation

@curfew-marathon

@curfew-marathon curfew-marathon commented Oct 25, 2026 •

Copy link
Copy Markdown
Owner

Summary

  • Added type hints to every function/method in src/importrr (previously none existed anywhere in the package, which made mypy in CI effectively a no-op given mypy.ini had no strictness flags).
  • Added a per-module mypy override (disallow_untyped_defs) as each module was typed, and moved ruff/mypy/pytest/coverage config out of mypy.ini and bare CI flags into a new pyproject.toml. CI's actual command line is unchanged.
  • Extracted Sort.launch (previously ~105 lines mixing path validation, staging, organizing, metrics, manifest writing, and archiving) into _validate_import_dir, _organize_batch, and _archive_batch. Call order and the write-ahead-log / cleanup-only-on-full-success guarantee are unchanged; verified by a line-by-line diff review plus the full existing test suite.
  • Removed two unused test mock packages (tests/mocks/exiftool, tests/mocks/ffmpy) left over from before pyexiftool and ffmpy were adopted as real dependencies.
  • Normalized test_config.py off unittest.TestCase onto plain pytest functions, and added tests/conftest.py for the mock_directories fixture that was only defined in one test file.
  • Closed two coverage gaps: launch.py's if __name__ == "__main__": guard (extracted into a testable _run(), with new tests) and sort.py's except IndexError: branch in sort_media, which is actually unreachable (Python string slicing never raises IndexError) and is now marked # pragma: no cover with a comment explaining why, instead of being left as a silent gap.

No runtime behavior changes anywhere in this PR. The one open item identified during this work (launch.py's signal handler calling sys.exit(0) after scheduler.shutdown(), which could in principle skip Sort.launch's cleanup if a signal lands mid-batch) is deliberately not touched here since fixing it would be a behavior change; flagging it as a follow-up for a separate PR if wanted.

Test plan

  • ruff check . and ruff format --check . pass
  • mypy . passes with no errors across all 18 source files
  • PYTHONPATH=src pytest --cov=src --cov-report=term-missing --cov-fail-under=80 passes: 128 tests (3 new), coverage 99.85% (up from 98.76% baseline), floor unchanged at 80%
  • Manually diffed git diff -U20 src/importrr/sort.py to confirm every statement in the original Sort.launch body appears exactly once, same relative order, after the extraction
  • Not run against a real ExifTool/ffmpeg/filesystem end to end; this PR is a pure refactor and the existing unit test suite (which mocks the exiftool/ffmpy boundary, as it always has) is the verification surface

Summary by CodeRabbit

  • Maintenance
    • Improved consistency and validation across the application’s internal processing and configuration.
    • Added project-wide checks for code quality, type safety, and test coverage.
  • Tests
    • Expanded checks for metrics startup behavior and added reusable test setup.
    • Updated configuration tests to use the project’s standard test framework.
  • User Impact
    • No significant user-visible behavior changes are reported.

…unch

mypy had no strictness and nothing in src/importrr was annotated, so it was
effectively a no-op in CI. Added honest type hints to every module and
enabled disallow_untyped_defs per module as each one was typed, then moved
ruff/mypy/pytest/coverage config out of mypy.ini and bare CI flags into a
single pyproject.toml.

Sort.launch had grown into one 105-line method mixing path validation,
staging, organizing, metrics, manifest writing, and archiving. Extracted it
into _validate_import_dir, _organize_batch, and _archive_batch with no change
to call order or the write-ahead-log/cleanup-only-on-success guarantee.

Also removed two unused test mock packages left over from before pyexiftool
and ffmpy were adopted, normalized test_config.py off unittest.TestCase,
added a conftest.py for the previously-duplicated mock_directories fixture,
and closed two coverage gaps: launch.py's __main__ guard (extracted into a
testable _run()) and sort.py's unreachable IndexError branch (marked
pragma: no cover, since string slicing cannot raise it).

No runtime behavior changes. Full suite (128 tests) passes, coverage moved
from 98.76% to 99.85%, ruff and mypy are clean.
@coderabbitai

coderabbitai Bot commented Oct 25, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

No actionable comments were generated in the recent review.

Recent review info
Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 3edb2004-a512-42c3-b958-ede2e0b39eb9
Commits

Reviewing files that changed from the base of the PR and between 689e4a6 and bbe2426.

Files selected for processing: 16
  • mypy.ini
  • pyproject.toml
  • src/importrr/archive.py
  • src/importrr/config.py
  • src/importrr/exifhelper.py
  • src/importrr/launch.py
  • src/importrr/metrics.py
  • src/importrr/sort.py
  • src/importrr/transcode.py
  • tests/conftest.py
  • tests/mocks/exiftool/__init__.py
  • tests/mocks/exiftool/exceptions.py
  • tests/mocks/ffmpy/__init__.py
  • tests/test_config.py
  • tests/test_launch.py
  • tests/test_sort_security.py
Files with no reviewable changes: 5
  • mypy.ini
  • tests/mocks/exiftool/exceptions.py
  • tests/mocks/ffmpy/init.py
  • tests/mocks/exiftool/init.py
  • tests/test_sort_security.py

Included review availability: This review used your included allowance. Your plan provides up to 1 included review per hour; 0 remain after this review.

Walkthrough

The pull request adds mypy, Ruff, pytest, and coverage settings; annotates application functions; and extracts sorting and archiving steps into methods. It also moves startup logic into _run() and updates tests and fixtures.

Changes: Typing, sorting, and test updates

Layer / File(s) Summary
Project tooling configuration
mypy.ini, pyproject.toml
Moves mypy settings into pyproject.toml, adds per-module typing requirements and Ruff rules, and removes mypy.ini.
Application type annotations
src/importrr/archive.py, src/importrr/config.py, src/importrr/exifhelper.py, src/importrr/metrics.py, src/importrr/transcode.py
Adds parameter, return, and local variable annotations. The summaries report no executable behavior changes in these modules.
Sorting pipeline organization and archiving
src/importrr/sort.py
Adds annotations and separates import-path validation, batch organization, and archiving into methods used by launch. Archive errors are counted and re-raised.
Startup entry point and tests
src/importrr/launch.py, tests/test_launch.py
Moves startup statements into _run(). Adds tests for metrics startup with default, configured, and disabled settings; each test expects the scheduler to start.
Pytest configuration and fixtures
pyproject.toml, tests/conftest.py, tests/mocks/*, tests/test_config.py, tests/test_sort_security.py
Configures pytest and coverage, adds a shared directory fixture, converts configuration checks to pytest functions, and removes test mocks and the local security-test fixture definition.

Priority: Low

Estimated code review effort: 3 (Moderate) | ~25 minutes

Change: Refactor

Possibly related PRs

  • curfew-marathon/importrr#50: The sorting refactor relocates metric updates and archive-error handling added in that PR.
  • curfew-marathon/importrr#55: The sorting refactor relocates unprocessed-file classification and manifest writing added in that PR.
  • curfew-marathon/importrr#65: This pull request annotates the previously noted unreachable sorting branch and moves the main-guard startup logic into the tested _run() entry point.
Merge Risk: Minimal · up to bbe24

No actionable regression is identified; the change appears mergeable after normal checks.

Pre-merge checks: Passed 4 · Failed 1

Failed Failed checks: 1 warning

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 39.58% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 48 functions across 10 files. (1 skipped:… Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed Passed checks: 4 passed
Check name Status Explanation
Description Check Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check Passed The title clearly summarizes the main changes: adding type hints, centralizing tool configuration, and splitting Sort.launch.
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
Full details: Docstring Coverage

Explanation

Docstring coverage is 39.58% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 48 functions across 10 files. (1 skipped: 1 unsupported.)

Resolution

Write docstrings for the functions missing them to satisfy the coverage threshold.

Finishing Touches: 💡 1
📝 Generate docstrings: 💡
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The missing mypy source-package base prevents the new strictness overrides from applying in CI and weakens cross-module checks.

1 open issue
What changed in this PR

This PR adds type annotations and centralized tooling configuration to importrr, while splitting batch processing into smaller helpers intended to preserve existing behavior.

Changes:

  • Annotates source functions and adds module-specific mypy strictness settings.
  • Extracts sorting phases and scheduler startup into focused helpers.
  • Consolidates test fixtures, adds startup tests, and removes unused mocks.
File Description
tests/​test_sort_security.py Uses the shared directory fixture.
tests/​test_launch.py Tests metrics configuration and scheduler startup.
tests/​test_config.py Converts unittest methods to pytest functions.
tests/​mocks/​ffmpy/​__init__.py Removes unused FFmpeg mocks.
tests/​mocks/​exiftool/​exceptions.py Removes an unused exception mock.
tests/​mocks/​exiftool/​__init__.py Removes unused ExifTool mocks.
tests/​conftest.py Provides the shared directory fixture.
src/​importrr/​transcode.py Adds annotations and logging type suppressions.
src/​importrr/​sort.py Adds annotations and extracts batch-processing helpers.
src/​importrr/​metrics.py Annotates metrics startup and documents invalid-port handling.
src/​importrr/​launch.py Adds annotations and extracts testable startup logic.
src/​importrr/​exifhelper.py Annotates ExifTool helpers and makes a fallback return explicit.
src/​importrr/​config.py Annotates configuration methods and stored data.
src/​importrr/​archive.py Adds annotations and clarifies error-handling comments.
pyproject.toml Centralizes mypy, Ruff, pytest, and coverage settings.
mypy.ini Removes configuration migrated to pyproject.toml.

Review effort: Balanced

Comment thread pyproject.toml
CI runs mypy . from the repo root, which resolved every src/importrr module
as src.importrr.X instead of importrr.X (confirmed via mypy --verbose).
That meant every [[tool.mypy.overrides]] module = "importrr.X" block added
in the prior refactor matched nothing, and internal "from importrr import
..." statements were falling back to ignore_missing_imports, silently
stubbing cross-module calls as Any.

Adding mypy_path = "src" fixes the module resolution and makes mypy
actually check cross-module calls, which surfaced two real (if currently
harmless) gaps in launch.py's main_process: Config.get_data() entries are
typed dict[str, Any], so d.get(...) is Optional even for keys Config always
populates. Suppressed with scoped type: ignore comments rather than
switching to dict indexing, since that would change the exception type
logged in the hypothetical case of a missing key.

Addresses the Copilot review comment on PR #66.
@curfew-marathon
curfew-marathon merged commit 7f29cae into main Oct 25, 2026
5 checks passed
@curfew-marathon
curfew-marathon deleted the refactor/type-hints-and-tool-config branch October 25, 2026 13:42
curfew-marathon added a commit that referenced this pull request Oct 25, 2026
* Add type hints across importrr, centralize tool config, split Sort.launch

mypy had no strictness and nothing in src/importrr was annotated, so it was
effectively a no-op in CI. Added honest type hints to every module and
enabled disallow_untyped_defs per module as each one was typed, then moved
ruff/mypy/pytest/coverage config out of mypy.ini and bare CI flags into a
single pyproject.toml.

Sort.launch had grown into one 105-line method mixing path validation,
staging, organizing, metrics, manifest writing, and archiving. Extracted it
into _validate_import_dir, _organize_batch, and _archive_batch with no change
to call order or the write-ahead-log/cleanup-only-on-success guarantee.

Also removed two unused test mock packages left over from before pyexiftool
and ffmpy were adopted, normalized test_config.py off unittest.TestCase,
added a conftest.py for the previously-duplicated mock_directories fixture,
and closed two coverage gaps: launch.py's __main__ guard (extracted into a
testable _run()) and sort.py's unreachable IndexError branch (marked
pragma: no cover, since string slicing cannot raise it).

No runtime behavior changes. Full suite (128 tests) passes, coverage moved
from 98.76% to 99.85%, ruff and mypy are clean.

* Fix mypy_path so the per-module strictness overrides actually apply

CI runs mypy . from the repo root, which resolved every src/importrr module
as src.importrr.X instead of importrr.X (confirmed via mypy --verbose).
That meant every [[tool.mypy.overrides]] module = "importrr.X" block added
in the prior refactor matched nothing, and internal "from importrr import
..." statements were falling back to ignore_missing_imports, silently
stubbing cross-module calls as Any.

Adding mypy_path = "src" fixes the module resolution and makes mypy
actually check cross-module calls, which surfaced two real (if currently
harmless) gaps in launch.py's main_process: Config.get_data() entries are
typed dict[str, Any], so d.get(...) is Optional even for keys Config always
populates. Suppressed with scoped type: ignore comments rather than
switching to dict indexing, since that would change the exception type
logged in the hypothetical case of a missing key.

Addresses the Copilot review comment on PR #66.

* Pin Docker base image and align Python target to 3.15

The Dockerfile used FROM python:alpine with no tag, which silently floats
to whatever Python release is current. A digest comparison showed it
already resolved to Python 3.15, three minors ahead of the 3.11 that CI,
mypy, and the project docs all claimed. Pin the base image to what it
already runs today (preserves current behavior, no downgrade), and bring
CI's setup-python, mypy's python_version, ruff's target-version, and
CLAUDE.md in line with that same version so they stop silently drifting
apart. Also raise the enforced coverage floor to 99%, matching actual
coverage, since it had been left at a stale 80%.
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.

2 participants