-
-
Notifications
You must be signed in to change notification settings - Fork 3.1k
Description
Crash Report
I'm running mypy through the Pants build system on an internal Python monorepo. You can think of Pants as roughly in the vein of Bazel, or Buck2 if you are more familiar with those systems. Notably Pants runs typechecking (along with most other operations) in an ephemeral sandbox with the minimum dependency set needed. So if I have src/{foo,bar,bax} and I check src/foo only the transitive dependencies of src/foo are included (and not bar or bax). While it is common to typecheck the whole monorepo, it is also common to just check the little library in the corner you are working on, or dynamically check what has changed since trunk. This means that the packages that mypy can see in the environment can vary significantly on each run. This is historically a source of anecdotal reports of "spooky" issues with the mypy cache pantsbuild/pants#18519 -- some of wich have been fixed; thank you!
(An alternative design would be to use a separate cache per target combination, but that would produce poor hit rates and an explosion of storage).
This issue attempts to describe one such issue that leads to an AssertionError when type checking code that uses pyopenssl. I think this is likely the same issue as #19477. I've attempted fixes that suggest overlap with #16214, but I think that is related as opposed to identical.
Traceback
Traceback (most recent call last):
File "/workspace/build/CI/MY_REPO/pants.d.cache/pex_root/venvs/3/00d861817b8c511dac47c6904fb3ad1319d7fd53/a94ba71a28dab5f2849156666f783a089f13d400/pex", line 380, in <module>
boot(
File "/workspace/build/CI/MY_REPO/pants.d.cache/pex_root/venvs/3/00d861817b8c511dac47c6904fb3ad1319d7fd53/a94ba71a28dab5f2849156666f783a089f13d400/pex", line 368, in boot
sys.exit(func())
^^^^^^
File "/workspace/build/CI/MY_REPO/.cache/pants/named_caches/pex_root/venvs/3/s/77d10232/venv/lib/python3.12/site-packages/mypy/__main__.py", line 15, in console_entry
main()
File "/workspace/build/CI/MY_REPO/.cache/pants/named_caches/pex_root/venvs/3/s/77d10232/venv/lib/python3.12/site-packages/mypy/main.py", line 135, in main
res, messages, blockers = run_build(sources, options, fscache, t0, stdout, stderr)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/workspace/build/CI/MY_REPO/.cache/pants/named_caches/pex_root/venvs/3/s/77d10232/venv/lib/python3.12/site-packages/mypy/main.py", line 219, in run_build
res = build.build(sources, options, None, flush_errors, fscache, stdout, stderr)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/workspace/build/CI/MY_REPO/.cache/pants/named_caches/pex_root/venvs/3/s/77d10232/venv/lib/python3.12/site-packages/mypy/build.py", line 351, in build
result = build_inner(
^^^^^^^^^^^^
File "/workspace/build/CI/MY_REPO/.cache/pants/named_caches/pex_root/venvs/3/s/77d10232/venv/lib/python3.12/site-packages/mypy/build.py", line 448, in build_inner
graph = dispatch(sources, manager, stdout)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/workspace/build/CI/MY_REPO/.cache/pants/named_caches/pex_root/venvs/3/s/77d10232/venv/lib/python3.12/site-packages/mypy/build.py", line 3214, in dispatch
process_graph(graph, manager)
File "/workspace/build/CI/MY_REPO/.cache/pants/named_caches/pex_root/venvs/3/s/77d10232/venv/lib/python3.12/site-packages/mypy/build.py", line 3625, in process_graph
done, still_working, results = manager.wait_for_done(graph)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/workspace/build/CI/MY_REPO/.cache/pants/named_caches/pex_root/venvs/3/s/77d10232/venv/lib/python3.12/site-packages/mypy/build.py", line 1101, in wait_for_done
process_stale_scc(graph, next_scc, self)
File "/workspace/build/CI/MY_REPO/.cache/pants/named_caches/pex_root/venvs/3/s/77d10232/venv/lib/python3.12/site-packages/mypy/build.py", line 3755, in process_stale_scc
process_fresh_modules(graph, sorted(prev_scc.mod_ids), manager)
File "/workspace/build/CI/MY_REPO/.cache/pants/named_caches/pex_root/venvs/3/s/77d10232/venv/lib/python3.12/site-packages/mypy/build.py", line 3703, in process_fresh_modules
graph[id].fix_cross_refs()
File "/workspace/build/CI/MY_REPO/.cache/pants/named_caches/pex_root/venvs/3/s/77d10232/venv/lib/python3.12/site-packages/mypy/build.py", line 2364, in fix_cross_refs
fixup_module(self.tree, self.manager.modules, self.options.use_fine_grained_cache)
File "/workspace/build/CI/MY_REPO/.cache/pants/named_caches/pex_root/venvs/3/s/77d10232/venv/lib/python3.12/site-packages/mypy/fixup.py", line 53, in fixup_module
node_fixer.visit_symbol_table(tree.names, tree.fullname)
File "/workspace/build/CI/MY_REPO/.cache/pants/named_caches/pex_root/venvs/3/s/77d10232/venv/lib/python3.12/site-packages/mypy/fixup.py", line 160, in visit_symbol_table
self.visit_type_info(value.node)
File "/workspace/build/CI/MY_REPO/.cache/pants/named_caches/pex_root/venvs/3/s/77d10232/venv/lib/python3.12/site-packages/mypy/fixup.py", line 73, in visit_type_info
self.visit_symbol_table(info.names, info.fullname)
File "/workspace/build/CI/MY_REPO/.cache/pants/named_caches/pex_root/venvs/3/s/77d10232/venv/lib/python3.12/site-packages/mypy/fixup.py", line 162, in visit_symbol_table
value.node.accept(self)
File "/workspace/build/CI/MY_REPO/.cache/pants/named_caches/pex_root/venvs/3/s/77d10232/venv/lib/python3.12/site-packages/mypy/nodes.py", line 1164, in accept
return visitor.visit_decorator(self)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/workspace/build/CI/MY_REPO/.cache/pants/named_caches/pex_root/venvs/3/s/77d10232/venv/lib/python3.12/site-packages/mypy/fixup.py", line 192, in visit_decorator
d.func.accept(self)
File "/workspace/build/CI/MY_REPO/.cache/pants/named_caches/pex_root/venvs/3/s/77d10232/venv/lib/python3.12/site-packages/mypy/nodes.py", line 1004, in accept
return visitor.visit_func_def(self)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/workspace/build/CI/MY_REPO/.cache/pants/named_caches/pex_root/venvs/3/s/77d10232/venv/lib/python3.12/site-packages/mypy/fixup.py", line 170, in visit_func_def
func.type.accept(self.type_fixer)
File "/workspace/build/CI/MY_REPO/.cache/pants/named_caches/pex_root/venvs/3/s/77d10232/venv/lib/python3.12/site-packages/mypy/types.py", line 2337, in accept
return visitor.visit_callable_type(self)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/workspace/build/CI/MY_REPO/.cache/pants/named_caches/pex_root/venvs/3/s/77d10232/venv/lib/python3.12/site-packages/mypy/fixup.py", line 281, in visit_callable_type
argt.accept(self)
File "/workspace/build/CI/MY_REPO/.cache/pants/named_caches/pex_root/venvs/3/s/77d10232/venv/lib/python3.12/site-packages/mypy/types.py", line 3394, in accept
return visitor.visit_union_type(self)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/workspace/build/CI/MY_REPO/.cache/pants/named_caches/pex_root/venvs/3/s/77d10232/venv/lib/python3.12/site-packages/mypy/fixup.py", line 374, in visit_union_type
it.accept(self)
File "/workspace/build/CI/MY_REPO/.cache/pants/named_caches/pex_root/venvs/3/s/77d10232/venv/lib/python3.12/site-packages/mypy/types.py", line 1686, in accept
return visitor.visit_instance(self)
File "/workspace/build/CI/MY_REPO/.cache/pants/named_caches/pex_root/venvs/3/s/77d10232/venv/lib/python3.12/site-packages/mypy/fixup.py", line 245, in visit_instance
inst.type = lookup_fully_qualified_typeinfo(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/workspace/build/CI/MY_REPO/.cache/pants/named_caches/pex_root/venvs/3/s/77d10232/venv/lib/python3.12/site-packages/mypy/fixup.py", line 383, in lookup_fully_qualified_typeinfo
stnode = lookup_fully_qualified(name, modules, raise_on_missing=not allow_missing)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/workspace/build/CI/MY_REPO/.cache/pants/named_caches/pex_root/venvs/3/s/77d10232/venv/lib/python3.12/site-packages/mypy/lookup.py", line 53, in lookup_fully_qualified
assert key in names, f"Cannot find component {key!r} for {name!r}"
^^^^^^^^^^^^
AssertionError: Cannot find component 'SSL' for 'OpenSSL.SSL.Context'To Reproduce
Alas! I have been unable so far to create a deterministic self contained reproduction. I'm going to instead give a qualitative explanation that I hope explains what is going on from post-hoc cache inspection, trace logs, and smashing LLMs against them. In my monorepo I've rigged a setup to generate each combination of available targets (so src/*, src/foo, src/bar, src/foo & src/bar, etc in my example above). I then run this in N shards and see what pops up. This deterministically produces the error (shard 107 is always one of the ones that hits it), but not in a way I've been able to isolate and extract stand alone. I've then downloaded the trace logs and caches for local analysis.
The trace that doesn't hit this error looks something like:
Line 14669: TRACE: Found fresh SCC (OpenSSL)
Line 14711: TRACE: Found fresh SCC (OpenSSL.crypto)
Line 14751: TRACE: Found fresh SCC (OpenSSL.SSL)
...
Line 18530: LOG: Processing 131 fresh SCCs
Line 18563: TRACE: Load tree OpenSSL
Line 18682: TRACE: Load tree OpenSSL.crypto
Line 18685: TRACE: Load tree OpenSSL.SSL ← All three loaded in same batch
While an error trace is along the lines of:
Line 14305: TRACE: Found fresh SCC (OpenSSL)
Line 15297: TRACE: Found fresh SCC (OpenSSL.crypto)
Line 15340: TRACE: Found fresh SCC (OpenSSL.SSL)
...
Line 16649: LOG: Processing 63 fresh SCCs
Line 16668: TRACE: Load tree OpenSSL ← Only parent loaded
Line 16764: TRACE: Load tree redis.client ← Has OpenSSL.SSL.Context type ref
Line 16870: AssertionError ← fix_cross_refs fails
LLM "Why OpenSSL.SSL Wasn't Loaded" ascii art
The dependency chain is:
my_src.mcp_server.prompts (NEW, no cache)
└── imports → my_src.mcp_server.config (interface changed)
└── imports → my_src.mcp_server.metrics (stale)
└── imports → ... → redis.client (fresh, from cache)
└── imports → OpenSSL (parent package)
└── type_ref → "OpenSSL.SSL.Context" (NOT an import!)
Since `redis.client` only **imports** `OpenSSL` (the parent), not `OpenSSL.SSL`, the SCC dependency graph doesn't include `OpenSSL.SSL`. The type reference `"OpenSSL.SSL.Context"` is stored as a string in the cache and isn't tracked as a dependency.
redis/client.py for reference.
LLM "When the Bug Triggers" Summary
The bug triggers when:
1. **New source files** are added that have no cache
2. Those files transitively depend on `redis.client` (through some import chain)
3. `redis.client` is in a fresh SCC that gets loaded
4. `OpenSSL.SSL` is in a different fresh SCC that is NOT in the current batch
5. During `fix_cross_refs` for `redis.client`, the lookup for `OpenSSL.SSL.Context` fails
I'm not an expert at the mypy codebase and I've tried to be clear where I'm leaning on LLMs. I'm happy to run any PRs through my internal setup. For transparency -- and not because any human is obliged to read it -- here is a consolidated LLM generated summary: https://gist.github.com/cburroughs/a255831b2780aafe128c500ea43bb4e8
Your Environment
- Mypy version used: 1.19.1;
masterat and branched from67b9c81dd0fa7f3193d6d3448704a01808620158 - Mypy command-line flags:
--explicit-package-bases -vv --sqlite-cache - Mypy configuration options from
mypy.ini(and other config files): Edited sample: https://gist.github.com/cburroughs/6435cc46d75f0fff7ec151a5f580ddc6 - Python version used: 3.12.12
- Operating system and version: Linux 6.12.58