Observation
1.4.1's per-module base resolution (#181) emits PY_EXTENDS edges to :PyExternal ghosts when a base class is not declared in the application — 142 such ghosts on odoo-slim-19. 10 of them carry a relative-import spelling in the id:
can://python/odoo-slim-19/@external/./Command
The . is the written import form (from . import Command), not a module. Such an id names nothing resolvable, cannot be joined to any real module, and two different from . import Command statements in different packages collapse onto the same ghost.
Why it matters
- A consumer grouping externals by module segment gets a module named
..
- Two distinct bases alias onto one ghost, so inheritance queries over-merge.
- Small population today (10 of 142), but the shape is wrong, not merely rare.
Suggestion
Resolve the relative import against the declaring module's package before minting the ghost (from . import Command in odoo/orm/__init__.py → odoo.orm.Command); fall back to an external ghost only when that also fails, spelled with the resolved absolute path, never with ./...
Definition of done
- No
:PyExternal id contains a /./ or /../ segment.
- Two relative imports of the same name from different packages do not share a ghost.
Found while verifying python-sdk#327; the 142 new ghosts are otherwise the expected effect of #181.
Observation
1.4.1's per-module base resolution (#181) emits
PY_EXTENDSedges to:PyExternalghosts when a base class is not declared in the application — 142 such ghosts on odoo-slim-19. 10 of them carry a relative-import spelling in the id:The
.is the written import form (from . import Command), not a module. Such an id names nothing resolvable, cannot be joined to any real module, and two differentfrom . import Commandstatements in different packages collapse onto the same ghost.Why it matters
..Suggestion
Resolve the relative import against the declaring module's package before minting the ghost (
from . import Commandinodoo/orm/__init__.py→odoo.orm.Command); fall back to an external ghost only when that also fails, spelled with the resolved absolute path, never with./...Definition of done
:PyExternalid contains a/./or/../segment.Found while verifying python-sdk#327; the 142 new ghosts are otherwise the expected effect of #181.