mypy icon indicating copy to clipboard operation
mypy copied to clipboard

Crash for TypedDict with non-literal keys

Open gsakkis opened this issue 2 years ago • 3 comments

Crash Report

Creating a TypedDict with non-literal keys gives TypedDict() expects a dictionary literal as the second argument. Attempting to ignore this with # type: ignore causes an AssertionError if (and only if):

  • any error (unrelated to TypedDict) happens later in a different module that imports the one with the TypedDict definition
  • and .mypy_cache exists

Traceback

✗ mypy --show-traceback main.py
main.py:3: error: Unsupported operand types for + ("int" and "str")  [operator]
Found 1 error in 1 file (checked 1 source file)

✗ mypy --show-traceback main.py
Traceback (most recent call last):
  (...)
  File "mypy/main.py", line 95, in main
  File "mypy/main.py", line 174, in run_build
  File "mypy/build.py", line 197, in build
  File "mypy/build.py", line 270, in _build
  File "mypy/build.py", line 2927, in dispatch
  File "mypy/build.py", line 3318, in process_graph
  File "mypy/build.py", line 3399, in process_fresh_modules
  File "mypy/build.py", line 2114, in fix_cross_refs
  File "mypy/fixup.py", line 53, in fixup_module
  File "mypy/fixup.py", line 126, in visit_symbol_table
AssertionError: ('counts.Counts', 'counts.TypedDict')

To Reproduce

# counts.py

from typing import TypedDict

Counts = TypedDict("Counts", {k: int for k in "abc"})  # type: ignore
# main.py

from counts import Counts

print(1 + "a")

Your Environment

  • Mypy version used: 1.4.1
  • Python version used: 3.10

gsakkis avatar Jun 30 '23 13:06 gsakkis

I can reproduce this, but importantly, this doesn't crash the first time you run mypy -- only the second time you run mypy. (This indicates that it's probably something to do with mypy's cache.)

AlexWaygood avatar Jun 30 '23 13:06 AlexWaygood

Just noticed that in my original code I get different traceback from the MCVE above:

  File "mypy/main.py", line 95, in main
  File "mypy/main.py", line 174, in run_build
  File "mypy/build.py", line 197, in build
  File "mypy/build.py", line 270, in _build
  File "mypy/build.py", line 2927, in dispatch
  File "mypy/build.py", line 3318, in process_graph
  File "mypy/build.py", line 3399, in process_fresh_modules
  File "mypy/build.py", line 2114, in fix_cross_refs
  File "mypy/fixup.py", line 53, in fixup_module
  File "mypy/fixup.py", line 136, in visit_symbol_table
  File "mypy/fixup.py", line 86, in visit_type_info
  File "mypy/types.py", line 2382, in accept
  File "mypy/fixup.py", line 287, in visit_typeddict_type
  File "mypy/types.py", line 391, in accept
  File "mypy/fixup.py", line 231, in visit_type_alias_type
  File "mypy/fixup.py", line 386, in lookup_fully_qualified_alias
AssertionError: Should never get here in normal mode, got NoneType: instead of TypeAlias

gsakkis avatar Jun 30 '23 13:06 gsakkis

I can reproduce this, but importantly, this doesn't crash the first time you run mypy -- only the second time you run mypy. (This indicates that it's probably something to do with mypy's cache.)

Here's the traceback I get using mypy master on Python 3.11 with the original repro:

>mypy main.py --show-traceback
Traceback (most recent call last):
  File "<frozen runpy>", line 198, in _run_module_as_main
  File "<frozen runpy>", line 88, in _run_code
  File "C:\Users\alexw\coding\mypy\venv\Scripts\mypy.exe\__main__.py", line 7, in <module>
  File "C:\Users\alexw\coding\mypy\mypy\__main__.py", line 15, in console_entry
    main()
  File "C:\Users\alexw\coding\mypy\mypy\main.py", line 95, in main
    res, messages, blockers = run_build(sources, options, fscache, t0, stdout, stderr)
                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\alexw\coding\mypy\mypy\main.py", line 174, in run_build
    res = build.build(sources, options, None, flush_errors, fscache, stdout, stderr)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\alexw\coding\mypy\mypy\build.py", line 194, in build
    result = _build(
             ^^^^^^^
  File "C:\Users\alexw\coding\mypy\mypy\build.py", line 267, in _build
    graph = dispatch(sources, manager, stdout)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\alexw\coding\mypy\mypy\build.py", line 2926, in dispatch
    process_graph(graph, manager)
  File "C:\Users\alexw\coding\mypy\mypy\build.py", line 3317, in process_graph
    process_fresh_modules(graph, prev_scc, manager)
  File "C:\Users\alexw\coding\mypy\mypy\build.py", line 3398, in process_fresh_modules
    graph[id].fix_cross_refs()
  File "C:\Users\alexw\coding\mypy\mypy\build.py", line 2113, in fix_cross_refs
    fixup_module(self.tree, self.manager.modules, self.options.use_fine_grained_cache)
  File "C:\Users\alexw\coding\mypy\mypy\fixup.py", line 53, in fixup_module
    node_fixer.visit_symbol_table(tree.names, tree.fullname)
  File "C:\Users\alexw\coding\mypy\mypy\fixup.py", line 126, in visit_symbol_table
    assert stnode.node is not None, (table_fullname + "." + key, cross_ref)
           ^^^^^^^^^^^^^^^^^^^^^^^
AssertionError: ('counts.Counts', 'counts.TypedDict')

AlexWaygood avatar Jun 30 '23 13:06 AlexWaygood