Skip to content

Fix crash when singledispatch is shadowed by a user function - #21848

Open
TheMuffinMan1320 wants to merge 1 commit into
python:masterfrom
TheMuffinMan1320:fix-20339-singledispatch
Open

Fix crash when singledispatch is shadowed by a user function#21848
TheMuffinMan1320 wants to merge 1 commit into
python:masterfrom
TheMuffinMan1320:fix-20339-singledispatch

Conversation

@TheMuffinMan1320

Copy link
Copy Markdown

Fixes #20339

Root cause

The plugin hook for functools.singledispatch (create_singledispatch_function_callback in mypy/plugins/singledispatch.py) is registered in mypy/plugins/default.py purely by matching the callee's fully qualified name against the string "functools.singledispatch".

If a user's own code produces a function whose fullname happens to be exactly "functools.singledispatch" — e.g. a top-level module literally named functools.py that defines its own def singledispatch(func): ... — the plugin hook still fires for calls to that unrelated function, since the match is name-based rather than tied to the real typeshed definition.

In that case ctx.default_return_type is whatever mypy inferred for the user's own function (not an Instance of functools._SingleDispatchCallable, which is what the real typeshed stub for singledispatch returns), so:

singledispatch_obj = get_proper_type(ctx.default_return_type)
assert isinstance(singledispatch_obj, Instance)

fails its assertion and crashes mypy with an AssertionError / INTERNAL ERROR.

Fix

Following the reproduction and suggestion from @hauntsaninja in the issue thread, when ctx.default_return_type doesn't have the shape the plugin expects, back off and return ctx.default_return_type unchanged instead of asserting — the same pattern already used by the two earlier guard clauses in this function. This lets normal type checking continue rather than crashing.

Testing

Added a regression test in test-data/unit/check-singledispatch.test (testDontCrashWhenSingledispatchIsShadowedByUserFunction) that defines a module named functools.py with its own singledispatch function, matching the issue's exact repro. Verified this test:

  • crashes with the pre-fix code (reproduces the reported AssertionError)
  • passes cleanly with the fix

Ran the targeted suite: pytest mypy/test/testcheck.py -k singledispatch → 16 passed, 2 xfailed (pre-existing, unrelated).

Also manually verified the original repro script from the issue no longer crashes and reports Success: no issues found in 1 source file.

…20339)

The functools.singledispatch plugin hook is matched purely by fully
qualified name. If a user's own module happens to produce a function
whose fullname is exactly "functools.singledispatch" (e.g. a
top-level module literally named functools.py defining its own
singledispatch function), the hook fires even though the return type
is not an Instance of functools._SingleDispatchCallable, tripping an
assert and crashing mypy. Bail out gracefully instead.
@github-actions

Copy link
Copy Markdown
Contributor

According to mypy_primer, this change doesn't affect type check results on a corpus of open source code. ✅

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.

Assertion failure crash with shadowed functools module: assert isinstance(singledispatch_obj, Instance)

1 participant