Repository navigation
fix(python): from . import x names the importing package, not its parent - #2680
Merged
Merged
Conversation
A package-relative from-import (`from . import x`, `from .. import x`,
`from . import x as y`) has a module made only of dots. The extractor joined
it to the imported name with another '.', recording "..x" for `from . import x`:
the parent package. The import resolver then missed the real binding and fell
to the symbol-name guess (the lexicographically first same-named definition in
the project), so the import map, and the Python LSP binding that consumes it,
sent every call to an unrelated function at import_map / lsp_callable_alias
confidence. On home-assistant, 7,175 caller/callee pairs bound to another
integration's function (webhook's `async_register` to accuweather's).
Join without the extra separator when the module already ends in '.'.
`from .m import f` (".m.f") and absolute imports are unchanged.
Tests: python_package_relative_import_paths (extraction: the recorded paths);
pipeline_python_package_relative_imports{,_parallel} (all three forms resolve
to the own / parent package, never to the namesake that sorts first, without a
name guess, on both resolvers); pipeline_python_from_import_bare_call_issue1237
{,_parallel} (#1237 guard: a plain from-import and bare calls bind the imported
def and keep it across a re-index that adds a file).
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
…art CI selection fix #2684) Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
yermakoffivan
pushed a commit
to yermakoffivan/codebase-memory-mcp
that referenced
this pull request
Oct 18, 2026
…ONAL on every selection path A runner suite registered only under a preprocessor condition (the four coverage_* suites under `#if defined(CBM_TEST_COVERAGE) && !defined(_WIN32)` in tests/test_main.c) is absent from builds without that condition. The selection marked such a suite CONDITIONAL only on the per-registration path, and only when a registration of its own was conditional and contributed a reason. Every suite-level path (setup unknown or hit, suite-body, changed and rule triggers, inventory, rejected artifact) made the suite whole without it, and the runner registration's own condition was never recorded. PR CI on the x86 Linux gcc leg then failed with "the test selection names suites this build does not register: coverage_setup_success coverage_setup_uncertain" (DeusData#2679, DeusData#2680): the suite-body trigger and an incomplete setup row selected both suites whole without CONDITIONAL, so selection.py left them out of optional-suites.txt. The model now records whether a runner registration sits under a condition (conditional only when every site of the suite is), and the selection marks any selected conditionally registered suite whole with CONDITIONAL after every whole-suite path. Unconditionally registered suites keep the strict contract. Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
This was referenced Oct 18, 2026
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
A package-relative from-import (
from . import x,from .. import x,from . import x as y) has amodule spelled only with dots.
extract_imports.cjoined that module to the imported name with another., sofrom . import xwas recorded as..x, which names the PARENT package. The import resolvermissed the real binding and fell to the symbol-name fallback in
pass_pkgmap.c, which picks thelexicographically first same-named definition anywhere in the project. That node became the import-map
value, and the Python LSP's alias binding consumes the same value. Every call through such an import
therefore became a confident
import_map/lsp_callable_aliasedge into an unrelated function.Example (home-assistant):
homeassistant/components/webhook/trigger.pydoesfrom . import async_registerand calls it. The edge went tohomeassistant.components.accuweather.system_health .async_register, the first of 28 functions with that name. Test packages were hit hardest:tests/components/<integration>/test_*.pyimportingsetup_integration/init_integrationfrom theirown package bound to
tests.components.acaia.*.The fix joins without the extra separator when the module already ends in
..from .m import f(recorded
.m.f) and absolute imports are unchanged.Evidence
Base origin/main 9f85f10 vs this head, 26 Python repos (manifest v1), production builds, macOS arm64,
--repeat 2, every repeat byte-identical.8 of 26 repos are identical; home-assistant carries most of the change (CALLS +7,402/-7,316).
Every changed stratum was sampled (12 edges each) and read:
lsp_callable_alias+7,113/-5,829 andimport_map+226/-1,394: every sampled removal pointed atthe alphabetically first namesake (
tests.components.acaia.*,ads.binary_sensor.setup_platform, amethod of an unrelated class); the additions point at the importing package's own definition
(11-12 of 12 correct; one plausible module alias).
same_module-19: module-attribute calls throughfrom . import X as aliasbound to a same-namedfunction of the caller's module; replaced by the correct
import_mapedges.from . import util as orm_utilin sqlalchemy's ormbound to the parent package's
util); additions name the right package member or module.production function of another integration.
receivers are resolved by name guesses ranked by import reachability; with sqlalchemy's relative imports
now resolving, those guesses re-rank. Neither side is a reliable target.
astover every corpus file, 26 repos; each expected caller/callee pair checkedagainst the graph): bare calls through relative from-imports 23,217 / 30,405 correct (76.4%, 7,179
wrong) -> 30,396 / 30,405 (99.97%, 0 wrong); aliased relative 89 / 93 -> 93 / 93; module-attribute calls
+115 correct. Absolute from-imports, re-exports and same-file calls are unchanged (the fix touches only
the relative forms).
Exposed regressions (17 edges plus derived OVERRIDE)
These are pre-existing approximations that the corrected imports now reach:
AsyncAdapt_asyncpg_dbapi(class OperationalError(DatabaseError)names the SIBLING nested class) nowresolve to
lib.sqlalchemy.exc.DatabaseError. A bare base name is resolved through the registry's namestrategies, which prefer an import-reachable candidate; the sibling won only while
excwas notreachable. Closed by fix(python): bind a nested class base to an earlier sibling of its class body #2695, which resolves a nested class's bare base in its enclosing class body first
(exact). Measured with this change: all 16 come back.
super()call (home-assistant radarr):RadarrCalendarEntity(RadarrEntity, CalendarEntity)'ssuper()._async_write_ha_state()moved fromCalendarEntity(C3 order) toEntity: thesuper()lookup walks each base's ancestry depth-first; resolving
RadarrEntity's ancestry exposed it. Closed byfix(python-lsp): resolve super() through the C3 method resolution order #2696, which walks the C3 MRO.
@testing.combinationsandsql.cast/sql.select/sql.and_now land on asame-named method inside the correctly resolved package instead of the re-exported function (the
package
__init__re-exports it). Closed by the re-export resolution work in a follow-up PR, which lists theseas required cases.
Re-measured on current main (clean builds)
The numbers above were measured against main 9f85f10. This change merged with main ea5c1f7 was rebuilt
from a clean build directory and compared with a clean ea5c1f7 build on sqlalchemy and matplotlib
(
--repeat 2, noise 0):USAGE, WRITES and TESTS are identical too.
WRITES +284 / -280 (was +281 / -277), TESTS +15 / -14 (was +15 / -15). matplotlib CALLS is +63 / -24
(was +62 / -24).
calls now start at the nested def instead of the file node (
collections._dict_decorators.setdefaultinstead of
collections.py), and the newly extracted nested defs add a few more calls through thecorrected imports. The targets are unchanged.
Tests
python_package_relative_import_paths(extraction): the recorded paths for all three forms, plus.m.fand absolute controls.pipeline_python_package_relative_importsand..._parallel: each form resolves to the own or parentpackage, never to the same-named function that sorts first, and not through a name guess; both
resolvers. RED with the extractor change reverted (
"..async_register" != ".async_register";own == 0), GREEN here.pipeline_python_from_import_bare_call_issue1237and..._parallel(guard for trace_path/callers returns empty for plain "from module import func" + bare call across files (Python) #1237): a plainfrom pkg.db import get_row_or_noneplus bare calls from a function, a method and the same file bindthe imported def, never a same-named def elsewhere; a re-index that only adds a file leaves CALLS
unchanged. Main already resolves this shape, so this guard has no RED on revert.
lsp_resolution_probe, incremental): 1,323 passed on the tree merged with main.
Notes
files are extracted again.