fix(python): prefer member Function on absolute from-import IMPORTS

resolve_module often lands on the Module for pkg.mod.symbol paths.
When the member Function/Class exists, IMPORTS must target it so
\`from M import f as g\` + g() can CALLS→f (Yui bridge_execute shape).

Co-authored-by: Cursor <cursoragent@cursor.com>
Signed-off-by: 涂名恩 <125283161+Joseph-MingEn@users.noreply.github.com>
This commit is contained in:
涂名恩
2026-07-30 21:39:23 -07:00
co-authored by Cursor
parent 7fda9f7115
commit 164d074d52
2 changed files with 77 additions and 2 deletions
+25
View File
@@ -1520,6 +1520,31 @@ const cbm_gbuf_node_t *cbm_pipeline_resolve_import_node(const cbm_pipeline_ctx_t
const cbm_gbuf_node_t *target = target_qn ? cbm_gbuf_find_by_qn(ctx->gbuf, target_qn) : NULL;
free(target_qn);
if (target) {
/* Python/TS from-import of a member: module_path is often
* "pkg.mod.symbol" while resolve_module lands on the Module node
* "pkg.mod". Prefer the member Function/Class when it exists —
* required for `from M import f as g` so CALLS can import_map to f
* (local_name g ≠ f). */
if (target->label &&
(strcmp(target->label, "Module") == 0 || strcmp(target->label, "File") == 0)) {
char symbuf[256];
const char *sym = import_candidate_symbol(imp->module_path, symbuf, sizeof(symbuf));
if (sym && sym[0] && strcmp(sym, "*") != 0) {
const char *mod_tail = import_last_segment(target->qualified_name
? target->qualified_name
: "");
if (!mod_tail || strcmp(mod_tail, sym) != 0) {
char member_qn[CBM_SZ_512];
snprintf(member_qn, sizeof(member_qn), "%s.%s", target->qualified_name, sym);
const cbm_gbuf_node_t *member = cbm_gbuf_find_by_qn(ctx->gbuf, member_qn);
if (member && import_targetable_label(member->label) &&
strcmp(member->label, "Module") != 0 &&
strcmp(member->label, "File") != 0) {
return member;
}
}
}
}
return target;
}
+52 -2
View File
@@ -456,8 +456,7 @@ TEST(contract_python_relative_import) {
PASS();
}
/* Python: aliased from-import must CALLS the real def (execute), not a ghost
* named after the local alias (bridge_execute). Mirrors Yui G1. */
/* Python: relative aliased from-import must CALLS the real def (execute). */
TEST(contract_python_aliased_from_import_calls) {
static const LangFile f[] = {
{"gate.py", "def execute(x):\n return x\n"},
@@ -504,6 +503,56 @@ TEST(contract_python_aliased_from_import_calls) {
PASS();
}
/* Python: absolute aliased from-import must CALLS the real def (Yui router shape). */
TEST(contract_python_absolute_aliased_from_import_calls) {
static const LangFile f[] = {
{"services/satori_bridge/__init__.py", ""},
{"services/satori_bridge/gate.py", "def execute(x):\n return x\n"},
{"services/yui_core/__init__.py", ""},
{"services/yui_core/router.py",
"from services.satori_bridge.gate import execute as bridge_execute\n\n\n"
"def submit_task(y):\n return bridge_execute(y)\n"},
{"services/__init__.py", ""}};
LangProj lp;
cbm_store_t *store = lang_index_files(&lp, f, 5);
ASSERT_TRUE(store != NULL);
cbm_node_t *nodes = NULL;
int ncount = 0;
ASSERT_EQ(cbm_store_find_nodes_by_name(store, lp.project, "submit_task", &nodes, &ncount),
CBM_STORE_OK);
ASSERT_TRUE(ncount >= 1);
char **callers = NULL;
char **callees = NULL;
int n_callers = 0;
int n_callees = 0;
ASSERT_EQ(cbm_store_node_neighbor_names(store, nodes[0].id, 32, &callers, &n_callers, &callees,
&n_callees),
0);
int saw_execute = 0;
int saw_alias_ghost = 0;
for (int i = 0; i < n_callees; i++) {
if (callees[i] && strcmp(callees[i], "execute") == 0) {
saw_execute = 1;
}
if (callees[i] && strcmp(callees[i], "bridge_execute") == 0) {
saw_alias_ghost = 1;
}
}
for (int i = 0; i < n_callers; i++) {
free(callers[i]);
}
for (int i = 0; i < n_callees; i++) {
free(callees[i]);
}
free(callers);
free(callees);
cbm_store_free_nodes(nodes, ncount);
lang_cleanup(&lp, store);
ASSERT_TRUE(saw_execute);
ASSERT_FALSE(saw_alias_ghost);
PASS();
}
/* TypeScript: a relative import resolves to a sibling module → IMPORTS edge. */
TEST(contract_typescript_relative_import) {
static const LangFile f[] = {
@@ -1674,6 +1723,7 @@ SUITE(lang_contract) {
RUN_TEST(contract_kotlin_methods);
RUN_TEST(contract_python_relative_import);
RUN_TEST(contract_python_aliased_from_import_calls);
RUN_TEST(contract_python_absolute_aliased_from_import_calls);
RUN_TEST(contract_typescript_relative_import);
/* Graph-level breadth across all grammars (P4). */