Skip to content

AssertionError: Cannot find component 'SSL' for 'OpenSSL.SSL.Context depdendt on SCCs ordering in incremental checks #20655

@cburroughs

Description

@cburroughs

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; master at and branched from 67b9c81dd0fa7f3193d6d3448704a01808620158
  • 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

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions