mirror of
https://github.com/colbymchenry/codegraph.git
synced 2026-11-15 00:01:39 +08:00
fix(python): a base's type argument is no supertype, so Repo.save() is not User.save
The Python branch of declaredSupertypes read every word of a class's base list, so class Repo(Base[User]) declared both Base and User. A call on the class or on a receiver named after it, Repo.save() or repo.save(), walks those supertypes for the method (classStrategyMethod to inheritedClassMethod), and when Base declares no save it reached User.save at 0.85 or 0.8. In class Mixed(Base[Optional[User]], Mixin) the type argument also came ahead of Mixin, so Mixed.save() reached User.save instead of Mixin.save. Bracketed type arguments, nested ones and quoted forward references too, are now dropped before the names are read. The base itself is still read and searched. Other languages are untouched. Validation: - The new test file: red on main (the type-argument and later-base cases fail, the case that a base's own method is still reached passes), green here. We added the Mixed and quoted forward-reference cases to the contributor's test. - A/B against main on 7 Python repos, 670,226 edges on main: requests, flask, litestar, mypy, polar's server, django unchanged, sqlalchemy 1 edge removed, 0 added, 0 retargeted. That edge is a.references(b) in relationships.py. Its local a named a test's class a(TypedColumns), and TypedColumns' type argument "Column[Any]" reached Column.references: the right method by chance, by the path this fixes. Its twin b.references(a) two lines down has no edge in either arm. - Controls express and gin: full dumps byte-identical. - Kernel parity on flask and requests: 43/43 in both arms (the kernel is not touched). Index cost on sqlalchemy, median of 3 interleaved: 29.5 s on main, 25.7 s here, so nothing measurable. Original PR #3000 by @JosefAschauer. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
committed by
Colby Mchenry
co-authored by
Claude Opus 5.5
parent
3f66df358e
commit
cd04b21a31
@@ -21514,8 +21514,12 @@ function readDeclaredSupertypes(cls: Node, context: ResolutionContext): string[]
|
||||
// A base built by a class's factory — Django's `class Manager(
|
||||
// BaseManager.from_queryset(QuerySet))` — subclasses that class: the
|
||||
// qualifier is the supertype, not the method called on it.
|
||||
case 'python': return names(/\bclass\s+\w+\s*\(([^)]*)\)/.exec(head)?.[1]?.replace(/\w+\s*=\s*[\w.]+/g, '')
|
||||
.replace(/([A-Za-z_][\w.]*)\.\w+\s*\(/g, '$1 ('));
|
||||
// A parameterized base, `Base[User]`, is `Base`: its type arguments are no supertypes.
|
||||
case 'python': {
|
||||
let bases = /\bclass\s+\w+\s*\(([^)]*)\)/.exec(head)?.[1];
|
||||
while (bases && /\[[^[\]]*\]/.test(bases)) bases = bases.replace(/\[[^[\]]*\]/g, '');
|
||||
return names(bases?.replace(/\w+\s*=\s*[\w.]+/g, '').replace(/([A-Za-z_][\w.]*)\.\w+\s*\(/g, '$1 ('));
|
||||
}
|
||||
case 'ruby': return names(/\bclass\s+[\w:]+\s*<\s*([\w:]+)/.exec(head)?.[1]);
|
||||
case 'php': return names(/\bextends\s+([\w.\\]+)/.exec(head)?.[1]);
|
||||
case 'typescript': case 'tsx': case 'javascript': case 'jsx':
|
||||
|
||||
Reference in New Issue
Block a user