Skip to content

test: enable mypy typing checks for test/components/preprocessors - #12504

Merged
sjrl merged 1 commit into
deepset-ai:mainfrom
ShousenZHANG:mypy-preprocessors
Sep 1, 2026
Merged

test: enable mypy typing checks for test/components/preprocessors#12504
sjrl merged 1 commit into
deepset-ai:mainfrom
ShousenZHANG:mypy-preprocessors

Conversation

@ShousenZHANG

@ShousenZHANG ShousenZHANG commented Aug 29, 2026

Copy link
Copy Markdown
Contributor

Related Issues

Claimed in this comment.

Proposed Changes:

test/components/preprocessors/ was not in the repository mypy target, so its 13 modules were not checked by hatch run test:types. This adds the path and fixes the errors that surfaced.

The baseline was 265 errors, but 206 of them were in test_recursive_splitter.py alone, cascading from one pattern repeated seven times where the dict returned by run() was rebound to the list it contains:

doc_chunks = splitter.run([doc])
doc_chunks = doc_chunks["documents"]   # dict -> list, same name

Naming the two values separately drops that file from 206 errors to 26, and the directory from 265 to 85.

The remaining 85 are mostly Document.content being str | None. AGENTS.md asks to avoid type: ignore, casts and assertions where possible and to explain them where they are necessary, so:

  • Narrowing assert ... is not None before first use, where the value was already being dereferenced. cast(str, ...) would type-check equally well but keeps the old behaviour of failing with a TypeError from inside a comprehension; the assert names the expectation at the point it is made. cast also appears only twice in all of test/.
  • Comprehensions and generators over .content expanded into loops. A narrowing in an enclosing block does not reach a comprehension's own scope, even when the comprehension reuses the same name, so the assert has to live inside the iteration. The alternative already merged in this campaign is test: enable mypy typing checks for test/components/samplers/ #12433's in-comprehension filter plus a compensating assert len(filtered) == len(docs). That is two lines instead of four, but it is only equivalent as long as every one of the 14 sites remembers the length guard; dropping it silently skips elements. I went with the loop because it cannot be got wrong site-by-site — happy to convert to the filter idiom if you prefer the smaller diff.
  • any(...) kept as any(...) over the collected list rather than expanded into per-element asserts, since turning an existential check into a universal one would change what the test claims.

Two smaller groups:

  • Six # type: ignore comments on read_csv(...) in test_csv_document_splitter.py are stale. warn_unused_ignores flags them as soon as the directory enters the target, so removing them is required rather than optional.
  • Eight tests pass a wrong type on purpose to assert the raised error. Those keep the value and carry a specific ignore code ([arg-type] / [list-item]), never a bare one.

test_document_preprocessor.py needed a different fix: Pipeline.get_component() returns the Component protocol, so the attribute reads failed. An isinstance narrowing resolves it, the same way #12343 and #12319 did.

No test assertion, expected value, or input datum changed. Assert counts per file only go up or stay flat.

How did you test it?

  • hatch run test:types -> Success: no issues found in 481 source files (468 before).
  • hatch run test:unit test/components/preprocessors/ -> 361 passed, 19 deselected — identical to the counts on main before the change.
  • hatch run test:integration test/components/preprocessors/ -> 9 passed, 10 skipped. The 10 skipped are the OPENAI_API_KEY-gated embedding-splitter tests; six of the edits land in those bodies, so they are covered by mypy and collection but were not executed. To check those six anyway I ran their pre- and post-change versions side by side against a stubbed embedder over a range of mutated outputs, and the two behave identically.
  • hatch run fmt-check -> clean across the repo (6787 files).

Notes for the reviewer

This and my #12481 both add a directory to the same types = line, so whichever lands second will conflict there and I will rebase it — no need to sequence them on my account.

Nine open PRs add tests to these files (#12284, #12346, #12356, #12373, #12424, #12431, #12434, #12468, #12478). All are pure additions and a three-way merge against each is clean in both orders, so this does not block them. Two of them (#12284, #12478) add a Document.content dereference that mypy will start checking once this lands; I will leave them a note so their CI does not go red unexpectedly.

No release note: CONTRIBUTING.md scopes the requirement to user-facing changes and notes that tests-only changes can carry ignore-for-release-notes — happy for you to add the label if you would rather have it explicit.

This PR was fully generated with an AI assistant. I have reviewed the changes and run the relevant tests.

Checklist

  • I have read the contributors guidelines and the code of conduct.
  • I have updated the related issue with new insights and changes.
  • I have added unit tests and updated the docstrings - n/a, test-only typing change with no behavior or public API change.
  • I've used one of the conventional commit types for my PR title.
  • I have documented my code.
  • I have added a release note file - not needed here, see above.
  • I have run pre-commit hooks and fixed any issue.

Add test/components/preprocessors/ to the mypy target in pyproject.toml and fix
the type errors that surfaced.

The bulk of the baseline came from one pattern repeated seven times in
test_recursive_splitter.py, where the dict returned by run() was rebound to the
list it contains:

    doc_chunks = splitter.run([doc])
    doc_chunks = doc_chunks["documents"]

Giving the two values separate names drops that file from 206 errors to 26, and
the directory from 265 to 85.

The rest is Document.content being str | None: narrowing asserts before use,
which check strictly more than before. Generators and comprehensions over
.content are expanded into loops because a narrowing in an enclosing block does
not reach a comprehension's own scope. Six stale type: ignore comments on
read_csv calls in test_csv_document_splitter.py are removed, since
warn_unused_ignores flags them once the directory enters the target. Tests that
pass a wrong type on purpose keep the value and carry a specific ignore code.

No test assertion, expected value, or input datum changed.
@ShousenZHANG
ShousenZHANG requested a review from a team as a code owner August 29, 2026 07:12
@ShousenZHANG
ShousenZHANG requested review from sjrl and a lite review from Copilot and removed request for a team August 29, 2026 07:12
@vercel

vercel Bot commented Aug 29, 2026

Copy link
Copy Markdown

@ShousenZHANG is attempting to deploy a commit to the deepset Team on Vercel.

A member of the Team first needs to authorize it.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@github-actions

Copy link
Copy Markdown
Contributor

Coverage report

Click to see where and how coverage changed

FileStatementsMissingCoverageCoverage
(new stmts)
Lines missing
  haystack/core/pipeline
  pipeline.py
Project Total  

This report was generated by python-coverage-comment-action

@sjrl sjrl self-assigned this Sep 1, 2026

@sjrl sjrl left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks!

@sjrl
sjrl merged commit 774e8e9 into deepset-ai:main Sep 1, 2026
24 of 25 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants