mirror of
https://github.com/colbymchenry/codegraph.git
synced 2026-10-02 01:37:32 +08:00
fix(python): a module member follows the package's re-exports too (#2248)
resolvePythonModuleMember (`mod.member` after `import mod` / `from pkg import mod`) looks the member up with pythonModuleSymbol, so a package's __init__.py re-exports lead to the definition. Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
6d8802cef8
commit
34ede4de88
@@ -14,6 +14,7 @@ and adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
### Fixes
|
||||
|
||||
- A Python module member used through the imported module, like `generic.ObjectEditView` after `from netbox.views import generic`, `serializers.ValidationError` or `utils.assert_deserialize(…)`, now also follows the package's re-exports to its definition. Before, netbox's view classes lost their `generic.*` base classes, and mealie's test helpers called through `utils.` went unresolved.
|
||||
- A Python name imported from a package now follows the package's re-exports to where it's defined. That covers both `from .users import *` and `from .tokens import Token` in its `__init__.py`. Before, httpx's own tests' `httpx.Client(…)` and `httpx.URL(…)`, pytest's `pytest.raises(…)` and `pytest.param(…)`, and netbox's `from users.models import User` went unresolved or to a same-named class elsewhere, such as a test's `User` or an Alembic migration's copy of a model.
|
||||
- In NestJS projects, the provider naming convention (`…Service`, `…Controller`, …) now picks a class only from the reference's own file. Another file's provider is reached through its import, or by ordinary name matching that prefers the nearest declaration. Before, the convention's preference for `*.service.ts` files sent Nest's own specs' `TransientService` and `TestService` to same-named classes in unrelated sample apps.
|
||||
- In Astro projects, the component-name convention now applies only inside `.astro` files, where components are used. Before, a TypeScript type with a component's name was linked to that component: astrowind's `image?: Image` and `callToAction?: CallToAction` in its type declarations, and starlight's Playwright `page: Page` in its tests.
|
||||
|
||||
@@ -32,6 +32,22 @@ from .users import *
|
||||
`,
|
||||
'netbox/alembic/versions/0001_tokens.py': `class Token:
|
||||
pass
|
||||
`,
|
||||
'netbox/netbox/__init__.py': '',
|
||||
'netbox/netbox/views/__init__.py': '',
|
||||
'netbox/netbox/views/generic/__init__.py': `from .object_views import *
|
||||
`,
|
||||
'netbox/netbox/views/generic/object_views.py': `class ObjectEditView:
|
||||
pass
|
||||
`,
|
||||
'netbox/dcim/views.py': `from netbox.views import generic
|
||||
|
||||
|
||||
class DeviceEditView(generic.ObjectEditView):
|
||||
pass
|
||||
`,
|
||||
'netbox/extras/views.py': `class ObjectEditView:
|
||||
pass
|
||||
`,
|
||||
'netbox/core/views.py': `from users.models import Token, User
|
||||
|
||||
@@ -53,6 +69,12 @@ afterAll(() => {
|
||||
});
|
||||
|
||||
describe('Python package re-exports', () => {
|
||||
it('lead a module member to its definition', () => {
|
||||
const ids = cg.getNodesInFile('netbox/dcim/views.py').map((n) => n.id);
|
||||
const bases = cg.getOutgoingEdgesFrom(ids).filter((e) => e.kind === 'extends').map((e) => cg.getNode(e.target)!.filePath);
|
||||
expect(bases).toEqual(['netbox/netbox/views/generic/object_views.py']);
|
||||
});
|
||||
|
||||
it('lead an imported name to its definition, through star and named re-exports', () => {
|
||||
const ids = cg.getNodesInFile('netbox/core/views.py').map((n) => n.id);
|
||||
const targets = cg.getOutgoingEdgesFrom(ids).filter((e) => e.kind === 'instantiates').map((e) => cg.getNode(e.target)!.filePath).sort();
|
||||
|
||||
@@ -1897,16 +1897,10 @@ function resolvePythonModuleMember(
|
||||
}
|
||||
if (!resolvedPath || resolvedPath === ref.filePath) continue;
|
||||
|
||||
// Find the member as a top-level definition in the module file. Exclude
|
||||
// `method` so `mod.foo` never lands on a same-named class method.
|
||||
const target = context.getNodesInFile(resolvedPath).find(
|
||||
(n) =>
|
||||
n.name === member &&
|
||||
(n.kind === 'function' ||
|
||||
n.kind === 'class' ||
|
||||
n.kind === 'variable' ||
|
||||
n.kind === 'constant')
|
||||
);
|
||||
// Find the member as a top-level definition in the module file, or one it
|
||||
// re-exports (a package's `__init__.py`). Exclude `method` so `mod.foo`
|
||||
// never lands on a same-named class method.
|
||||
const target = pythonModuleSymbol(resolvedPath, member, context, 0);
|
||||
if (target) {
|
||||
return { original: ref, targetNodeId: target.id, confidence: 0.85, resolvedBy: 'import' };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user