Repository navigation
fix(python): bind a nested class base to an earlier sibling of its class body - #2695
Merged
Merged
Conversation
…ss body The sqlalchemy asyncpg adapter re-declares the DBAPI exception hierarchy as classes nested in one class body, each based on an earlier sibling spelled bare (class IntegrityError(DatabaseError)). Python evaluates the bases in the enclosing class body, so the sibling wins over same-named module classes and over another adapter's nested namesakes; a sibling defined later is not bound yet. Sequential and parallel pipeline, each followed by an incremental re-index. Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
sqlalchemy's asyncpg adapter imports its exc module; once that import resolves, name guesses prefer exc's same-named classes over the earlier sibling of the class body. The fixture now imports exc as a module too. Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
…ass body Python evaluates a class statement's bases in the body that encloses it, so a bare base of a class nested in a class names an earlier sibling of that body (sqlalchemy's asyncpg adapter: class IntegrityError(DatabaseError)) before any same-named module or imported class. The base resolvers ran the registry's name strategies instead, which prefer an import-reachable candidate, so the sibling lost as soon as the adapter's exc import resolved. cbm_registry_resolve_python_class_body_base looks the sibling up exactly (<enclosing class>.<base>, both type-like) and requires it to be defined earlier in the same file, from the graph-buffer node lines: a later sibling is not bound yet when the base is evaluated. The sequential and incremental semantic pass, the parallel resolver and the cross-LSP base resolver ask it first; a sibling needs no external-base gate. Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
thugrock7
pushed a commit
to thugrock7/codebase-memory-mcp
that referenced
this pull request
Oct 19, 2026
…a#2680, DeusData#2690, DeusData#2695, DeusData#2696) py_lsp.c: the module-member lookup of this branch and the C3 super() lookup of DeusData#2696 were added at the same place; both are kept. Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Python evaluates a class statement's bases in the body that encloses it. In a class body, a bare base
therefore names an earlier sibling of that body before any module-level or imported class of the same
name:
The base resolvers sent such a base through the registry's name strategies, which prefer an
import-reachable candidate. So the sibling lost as soon as the module's
excimport resolved, andsqlalchemy's whole asyncpg exception hierarchy hung on
sqlalchemy.exc. Today that only happens by luck:sqlalchemy reaches
excthrough a relative import, which #2680 makes resolve. Merged with #2680, 16INHERITS edges go wrong without this change.
registry.c:cbm_registry_resolve_python_class_body_baselooks up<enclosing class>.<base>exactly, requiring both the enclosing class and the sibling to be type-like. It binds the sibling only
when it is defined earlier in the same file, using the graph-buffer node lines. A later sibling is not
bound yet when the base is evaluated:
class Early(Later)beforeclass Laterkeeps the module'sLater.cross-LSP base resolver ask it first. A sibling needs no external-base gate, because the class body
shadows module imports. Other languages and module-level classes are unchanged.
No index-format change.
Evidence
Measured where the regression lives: main 1346ac4 merged with #2680 (99dafc1), without and with this
change; the two trees differ by this change only. 26 Python repos (manifest v1), production builds, macOS
arm64,
--repeat 2, every repeat byte-identical.Every other edge type is identical (2 CALLS keep their target and change attributes only). All 21
INHERITS changes are retargets, and every one is a correction:
AsyncAdapt_asyncpg_dbapi.IntegrityError,.InternalError,.DataError,.OperationalErrorand the rest now inherit their sibling nested class, notsqlalchemy.exc.*._FancyAxislineStyle.FilledArrowinherits its sibling_FancyAxislineStyle.SimpleArrow, notAxislineStyle.SimpleArrow.TestGetAllSubclasses.BranchI/BranchIIinheritTestGetAllSubclasses.Base, notansible.playbook.base.Base.TestSmartUnionWithSubclass.ModelBinheritsTestSmartUnionWithSubclass.ModelA, notTestModelClass.ModelA.FilledArrow.__init__overrides_FancyAxislineStyle.SimpleArrow.__init__, andInvalidCachedStatementError.__init__overridesAsyncAdapt_asyncpg_dbapi.Error.__init__.Tests
pipeline_python_class_body_sibling_base{,_parallel}: an asyncpg-shaped nested exception hierarchywhose module imports
excboth as a module and by name, plus a second adapter with the same nestednames. Each nested base binds its earlier sibling, a non-sibling base keeps its import, and a later
sibling is never bound. Both venues, each followed by an incremental re-index. RED without the fix
(
ASSERT(ie_sib[run])).pipeline_semantic_manifest_repro, cross_repo, py_lsp, py_lsp_stress, py_lsp_scale, the go / c / php / cs
/ ts / java / kotlin / rust LSP suites, lsp_resolution_probe, edge_structural, edge_types_probe,
convergence_probe, matrix_known_classes, parallel, incremental, integration) pass.