Skip to content

Consider using stubtest to test the stubs #1056

Closed

Description

@sobolevn

It is a default tool from mypy (we already have, since it is a part of mypy).
It used to test that runtime and stubs match: https://github.com/typeddjango/django-stubs/blob/master/scripts/stubtest.sh

It can find missing type definition or ones that we forgot to remove for some reason.
Extra / missing func parameters / names / etc.

It is a really useful tool.

Activity

  1. Siyet commented on Jul 4, 2026

    @Siyet
    Contributor

    Agreed this is worth adding. I ran stubtest against current main (mypy 2.1.0) to see where we stand:

    $ python -m mypy.stubtest msgspec
    Found 130 errors (checked 12 modules)
    

    Breakdown:

    • ~110 (85%) are one structural false positive: stubtest can't see through the C-level __init__/__new__ signatures synthesized for Struct subclasses and for the Meta/Ext/UnsetType C types (70 come from the Struct subclasses in inspect.py alone). These want an allowlist, not a fix.
    • The remaining ~20 are actionable:
      • msgspec.structs.__all__ is not present in the stub.
      • msgspec.StructMeta is a disjoint base at runtime but isn't marked @disjoint_base in the stub (same class of drift Annotate types without Py_TPFLAGS_BASETYPE as @final #1043 fixed by hand).
      • 11 public json/msgpack callables (decode, encode, decode_lines, encode_lines, json.format, and the Decoder/Encoder methods) mark their first arg positional-only (/) in the stub, but the runtime accepts it as a keyword. Either the stub drops / or the runtime is made positional-only, needs a call.
      • 7 private msgspec._utils helpers leak into the check (no .pyi); simplest is to exclude _utils.

    So the real signal is ~13 items behind a roughly 10-line allowlist, with 85% of the raw output being one repeated false positive.

    This is exactly the stub/runtime drift the spring stub PRs fixed manually (#1043 @final/@disjoint_base, #1062 missing __class_getitem__), so wiring it into CI should stop that recurring. mypy is already in the test-typing group, so no new dependency.

    Happy to open a PR adding a stubtest step to the test-typing job with an allowlist plus the fixes above. @sobolevn does that split sound right, and any preference on the positional-only mismatch (relax the stubs vs make the runtime positional-only)?

  2. sobolevn commented on Jul 4, 2026

    @sobolevn
    MemberAuthor

    Please, go ahead with the PR :)
    We must always prefer the real function over the typing one.

    Because some people might not use typing and use the keyword form for the call, if we replace it with posonly parameter, it will be a breaking change for them. But, allowing pos-or-keyword calls in the existing functions / methods is a new feature.

  3. added a commit that references this issue on Sep 5, 2026
    7b18791
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions