Skip to content

feat: ship type annotations with py.typed - #20

Open
Hoeze wants to merge 4 commits into
feat/transforms-cleanupfrom
build/mypy-strict
Open

Hoeze wants to merge 4 commits into
feat/transforms-cleanupfrom
build/mypy-strict

Conversation

@Hoeze

@Hoeze Hoeze commented Oct 16, 2026 •

Copy link
Copy Markdown
Member

tl;dr: kipoiseq2 ships a py.typed marker, so type checkers read its annotations. mypy checks the package in strict mode, so every function needs annotations.

The wheel and the sdist contain kipoiseq2/py.typed. kipoiseq2/__init__.py lists Interval, Variant, Alphabet, AMINO_ACIDS, DNA, RNA, Encoder, Tokenizer, extractors and transforms in __all__. Without __all__, strict mypy rejects from kipoiseq2 import Interval, Variant, the import in README.md, because strict mode does not re-export imported names. from kipoiseq2 import * now binds only these ten names.

pyproject.toml sets strict = true for mypy. Before, mypy skipped the bodies of functions without annotations, and ignore_missing_imports = true hid every package without types. Now an override ignores only the missing types of pyfaidx, polars-bio and Biopython. The typecheck tox env still runs mypy src/kipoiseq2.

Strict mode needed few changes. _import_polars and _check_names in kipoiseq2.extractors get annotations. mypy sees the results of polars_bio.scan_vcf and polars_bio.overlap as Any, so they get the annotation LazyFrame.

Some annotations are more precise:

  • The constructors of Interval and Variant take the positions as typing.SupportsIndex, so strict mypy accepts numpy integers, as the runtime did already.
  • The private _resize_bounds takes the anchor as Literal["start", "center", "end"], and VariantSeqExtractor._split takes keep as Literal["left", "right", "both"].

mypy does not check the tests. About 60 test calls pass wrong types on purpose, to test the runtime checks, e.g. a float as Interval.shift(offset) or a positional use_strand. Each of them would need a # type: ignore.

@Hoeze
Hoeze added this pull request to stack #16 October 16, 2026 23:52
@Hoeze
Hoeze force-pushed the build/mypy-strict branch from 8213851 to 161706c Compare October 17, 2026 00:19
@Hoeze
Hoeze force-pushed the build/mypy-strict branch 2 times, most recently from 8bde764 to cf34749 Compare October 17, 2026 16:22
Hoeze added 4 commits October 17, 2026 18:36
mypy skipped the bodies of functions without annotations, and
ignore_missing_imports hid every package without types. Strict mode
needs annotations everywhere. pyfaidx, polars-bio and Biopython ship no
types, so an override ignores only their imports.

mypy sees the results of polars_bio.scan_vcf and polars_bio.overlap as
Any, so they get the annotation LazyFrame.
_resize_bounds gets the anchor after Interval.resize, pad or trim
checked it. VariantSeqExtractor._split takes keep as "left", "right" or
"both".
Type checkers read the annotations of kipoiseq2. __all__ lists the
names that kipoiseq2 re-exports: Interval, Variant, Alphabet, the
presets AMINO_ACIDS, DNA and RNA, Encoder and Tokenizer.
Strict mypy rejects `from kipoiseq2 import Encoder` otherwise. So
`from kipoiseq2 import *` binds only these names, extractors and
transforms.
Interval takes start and end as typing.SupportsIndex instead of int, and
Variant takes pos as SupportsIndex. So strict mypy accepts numpy
integers there, as the runtime and test_interval_numpy_integers already
did. The fields stay int. A bool still raises a TypeError at runtime,
although SupportsIndex accepts it. The method parameters stay int.
@Hoeze
Hoeze force-pushed the build/mypy-strict branch from cf34749 to 9e43fba Compare October 17, 2026 17:19

This branch has not been deployed

No deployments
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