chore(typecheck): clean datasets/eval/export/inference; enforce mypy on 12 packages#834
chore(typecheck): clean datasets/eval/export/inference; enforce mypy on 12 packages#834xieofxie wants to merge 5 commits into
Conversation
| # Generate config(s). The ``module: str | None`` overload of | ||
| # generate_hf_build_config returns WinMLBuildConfig | list[...], | ||
| # which isinstance(result, list) narrows for the branches below. | ||
| result = generate_hf_build_config( |
There was a problem hiding this comment.
| # overload, which returns WinMLBuildConfig | list[WinMLBuildConfig] — matching | ||
| # this dispatcher's implementation return type. The dispatcher's own | ||
| # narrowing overloads above still tighten the return type for its callers. | ||
| return generate_hf_build_config( |
…on 12 packages - Clear all mypy errors across the 4 folders (189 → 0). Notable source-level fixes that cleared cascades: - core/time_utils.format_timestamp_iso: added @overload variants so float→str and None→None propagate to all 4 export call sites. - export/htp/base_writer.step: replaced `-> Any` decorator factory with `Callable[[F], F]` and dropped the no-op @wraps wrapper — one source fix cleared 18 [untyped-decorator] errors across console_writer / markdown_report_writer / metadata_writer. - export/htp/exporter: typed the `None`-init core fields with proper Optional class annotations and a single non-None assert in `_apply_hierarchy_tags` cleared the union-attr chain. - export/htp/hierarchy.trace_model_execution: widened the example_inputs param to tuple|dict (callee already handled both via isinstance). - Added missing third-party stubs / ignores in pyproject.toml: - dev dep: scipy-stubs, types-psutil. - ignore_missing_imports: soundfile, sklearn.*, evaluate(.*). - Extended .github/workflows/lint.yml required mypy loop from 8 to 12 packages. New errors in any of the listed folders now block PRs. - Silenced two unguarded `winml.modelkit.inference.onnx_config.*` imports in export/htp/config_generator.py with `# type: ignore[import-not-found]` referencing #859, which tracks whether to restore or delete the dead integration.
DingmaomaoBJTU
left a comment
There was a problem hiding this comment.
Type-checking PR review
Overall: Clean, well-scoped PR. The approach of progressively moving packages into the required mypy gate is sound. Found one structural correctness issue, two warnings about suppressions masking real problems, and a few low-priority notes.
Bug - dead runtime guard in tensor_similarity_evaluator.py
mypy proves the isinstance(model, WinMLCompositeModel) branch is unreachable given the function's declared parameter type. The two # type: ignore[unreachable] suppressions silence the warning and make the guard invisible to callers. Either widen the parameter type to WinMLPreTrainedModel | WinMLCompositeModel so the check is live, or remove it and document that composite models are excluded at the call site.
Warning - cast admits subtype incompatibility (evaluate.py)
cast("str | dict[str, str | Path]", config.model_path) with a comment admitting it is "subtype-incompatible-but-runtime-compatible" suppresses a genuine type mismatch. The right fix is to widen WinMLEvaluationConfig.model_path's declared type to match what WinMLAutoModel.from_onnx actually accepts, rather than casting at each call site.
Warning - dead try-branches in config_generator.py
Neither inference.onnx_config.input_generator nor inference.onnx_config.patterns exists at runtime. Both try-blocks always fall through to their fallback paths. The # type: ignore[import-not-found] suppresses mypy but the code inside the try-blocks is dead. Consider removing the unreachable import bodies entirely (leaving only the fallback logic) until issue #859 resolves the module's fate.
Warning - duplicate if TYPE_CHECKING blocks (pipeline.py)
Two separate if TYPE_CHECKING: blocks appear at lines 26 and 30. They should be merged into one block. As-is it reads as if the second was added without noticing the first.
Info - class-level empty-string sentinel (datasets/image.py)
Replacing None with "" as the pre-detection sentinel is fine since all checks use if not self._image_col: consistently. Worth noting: a subclass that accidentally skips _detect_columns() will inherit the class-level "" and fail with KeyError: '' rather than a more debuggable AttributeError. Not a blocker.
Info - @step decorator TypeVar bound (base_writer.py)
F = TypeVar("F", bound="Callable[..., int]") is stricter than the old wrapper. Any @step-decorated handler that does not return int will now get a mypy error. Confirm all existing handlers annotate -> int; if any return None, they'll surface as new failures once dependent packages are gated.
|
Heads-up on the These shallow-copy the returned |
Fixed and one issue created for you #859 |
Summary
Extend the strict-mypy required-CI gate from 8 → 12 packages by cleaning
datasets/,eval/,export/, andinference/(189 errors → 0). Also restructures the dispatcher pattern inconfig/build.pyto fix a regression timenick flagged on #825 (kwarg type-checking lost behind**dict[str, Any]).What's in this PR
Dispatcher / overload cleanup (
config/build.py,commands/config.py)generate_hf_build_config: added a third@overloadacceptingmodule: str | Noneand returning the union of the two narrow returns. Lets thegenerate_build_configdispatcher collapse to a single call (no kwarg duplication, noAnywidening, nocast).commands.configcommand: ripped out the_shared_kwargs: dict[str, Any] = {...}workaround and replaced theif module:discriminator withisinstance(result, list)narrowing. Restores full call-site type checking against the callee's signature.Cleaning 4 new folders (189 → 0)
Notable source-level fixes that cascaded:
core/time_utils.format_timestamp_iso— added@overloads sofloat → strandNone → Nonepropagate; cleared 3 downstream[arg-type]chains.export/htp/base_writer.stepdecorator — replaced-> Anyfactory withCallable[[F], F], dropped no-op@wrapswrapper. One source fix cleared 18 cascading[untyped-decorator]errors across writers.export/htp/exporter— typed theNone-init core fields with properOptionalannotations + single non-Noneassertin_apply_hierarchy_tags; cleared the union-attr chain.datasets/*— common pattern: class-level_image_col: str = ""default instead of runtimeNone-init in_detect_columns; eliminatesAny | Noneproperty-return errors across image/segmentation/detection/depth datasets.eval/*—BaseImageProcessor | Noneguards across evaluators;pipeline()call-overload suppressions for runtime task strings that can't be statically matched against transformers' 60+ Literal overloads.Configuration
pyproject.tomlignore_missing_imports: addedsoundfile,sklearn.*,evaluate(.*).scipy-stubs,types-psutil..github/workflows/lint.yml: switched dependency install touv sync --all-extras --all-groupsso type stubs in[dependency-groups] devare present in CI. Extended the required mypy loop from 8 to 12 packages.Tracked follow-ups
winml.modelkit.inference.onnx_config.*imports inexport/htp/config_generator.pyreference a subpackage that doesn't exist in the repo. Silenced with# type: ignore[import-not-found]referencing the issue; Remove dead reference to inference.onnx_config.* subpackage in HTP config generator #859 tracks delete-vs-restore decision.🤖 Generated with Claude Code