test(python): a top-level __go(locals()) keeps the package namespace

sqlalchemy's sql/__init__.py ends with __go(locals()). The callee is a same-module function that never touches its parameter or the module's globals; only the submodules its body imports become package attributes. The extraction test pins the exact shape (the namespace survives, the imported submodules are shadowed at the call) and its controls: the body uses the parameter, global (also nested), globals(), sys.modules[__name__], the own package name, a same-module helper; an imported, rebound or not yet defined callee; an argument with an effect; a module-level locals. The pipeline fixture adds the same shape end to end, with a re-export the body's submodule import replaces and a __go that writes its parameter.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
This commit is contained in:
Martin Vogel
2026-10-19 05:55:21 +02:00
parent bf56fcfd14
commit 74b115e929
2 changed files with 140 additions and 1 deletions
+111
View File
@@ -12408,6 +12408,116 @@ TEST(python_namespace_facts_string_statements_and_future_imports) {
PASS();
}
/* A top-level call of a same-module function is a namespace effect, unless the
* callee provably cannot rebind the caller's names: sqlalchemy's
* `sql/__init__.py` ends with `__go(locals())`. Every submodule its body
* imports becomes a package attribute, so those names are shadowed at the
* call; everything else of the namespace survives. */
static bool py_facts_has_effect(const CBMFileResult *r) {
for (int i = 0; r && i < r->py_namespace.count; i++) {
if (r->py_namespace.items[i].kind == CBM_PY_NS_UNKNOWN &&
r->py_namespace.items[i].reason == CBM_PY_NS_REASON_EFFECT)
return true;
}
return false;
}
static int py_facts_count_kind(const CBMFileResult *r, CBMPyNamespaceFactKind kind) {
int n = 0;
for (int i = 0; r && i < r->py_namespace.count; i++)
n += r->py_namespace.items[i].kind == kind;
return n;
}
static bool py_facts_valid_complete(const CBMFileResult *r) {
return r && !r->has_error && r->py_namespace.status == CBM_PY_NS_COMPLETE &&
cbm_py_namespace_facts_valid(&r->py_namespace);
}
TEST(python_namespace_facts_init_call_allowlist) {
static const char go[] = "from typing import TYPE_CHECKING, Any\n"
"from .impl import base\n"
"from .elements import cast\n"
"def __go(lcls: Any) -> None:\n"
" from .. import util as _sa_util\n"
" from . import base\n"
" from . import coercions, elements as _el\n"
" if not TYPE_CHECKING:\n"
" base.coercions = _el.coercions = coercions\n"
" from .annotation import _prepare_annotations\n"
" _prepare_annotations(cast, cast)\n"
" _sa_util.preloaded.import_prefix(\"sqlalchemy.sql\")\n"
"\n"
"__go(locals())\n";
CBMFileResult *r = extract(go, CBM_LANG_PYTHON, "t", "sql/__init__.py");
bool ok = py_facts_valid_complete(r) && !py_facts_has_effect(r) && r->py_namespace.count == 9;
if (ok) {
const CBMPyNamespaceFact *f = r->py_namespace.items;
static const char *const shadowed[] = {"base", "coercions", "elements", "annotation"};
ok = f[2].kind == CBM_PY_NS_IMPORT_NAME && strcmp(f[2].local_name, "base") == 0 &&
f[4].kind == CBM_PY_NS_OWN_DEF && strcmp(f[4].local_name, "__go") == 0;
for (int i = 0; ok && i < 4; i++) {
ok = f[5 + i].kind == CBM_PY_NS_SHADOW && f[5 + i].local_name &&
strcmp(f[5 + i].local_name, shadowed[i]) == 0;
}
}
if (!ok && r) {
for (int i = 0; i < r->py_namespace.count; i++) {
const CBMPyNamespaceFact *f = &r->py_namespace.items[i];
printf(" __go fact %d: kind %d reason %d name %s\n", i, (int)f->kind, (int)f->reason,
f->local_name ? f->local_name : "-");
}
}
if (r)
cbm_free_result(r);
ASSERT_TRUE(ok); /* RED while every top-level call withdraws the namespace */
/* A plain module is no package: the body's imports set attributes of its
* parent package, never of the module itself. */
r = extract("def __go(lcls):\n from . import base\n\n__go(locals())\n", CBM_LANG_PYTHON,
"t", "sql/mod.py");
ok = py_facts_valid_complete(r) && !py_facts_has_effect(r) &&
py_facts_count_kind(r, CBM_PY_NS_SHADOW) == 0;
if (r)
cbm_free_result(r);
ASSERT_TRUE(ok);
/* Each of these can rebind a name of the caller's namespace (or is not
* provably a same-module function): the call stays a whole-namespace
* effect. */
static const char *const effects[][2] = {
{"param", "def __go(lcls):\n lcls['base'] = 1\n\n__go(locals())\n"},
{"global", "def __go(lcls):\n global base\n base = 1\n\n__go(locals())\n"},
{"nested global", "def __go(lcls):\n def inner():\n global base\n"
" base = 1\n inner()\n\n__go(locals())\n"},
{"globals()", "def __go(lcls):\n globals()['base'] = 1\n\n__go(locals())\n"},
{"sys.modules", "def __go(lcls):\n import sys\n sys.modules[__name__].base = 1\n"
"\n__go(locals())\n"},
{"imported callee", "from .setup import __go\n__go(locals())\n"},
{"own package name", "def __go(lcls):\n from .. import sql\n sql.base = 1\n"
"\n__go(locals())\n"},
{"same-module helper", "def helper():\n pass\n\ndef __go(lcls):\n helper()\n"
"\n__go(locals())\n"},
{"rebound callee", "def __go(lcls):\n pass\n\n__go = other\n__go(locals())\n"},
{"call before def", "__go(locals())\n\ndef __go(lcls):\n pass\n"},
{"argument effect", "def __go(lcls):\n pass\n\n__go(make())\n"},
{"rebound locals", "def locals():\n return {}\n\ndef __go(lcls):\n pass\n"
"\n__go(locals())\n"},
};
for (size_t i = 0; i < sizeof(effects) / sizeof(effects[0]); i++) {
char src[512];
snprintf(src, sizeof(src), "from .impl import base\n%s", effects[i][1]);
r = extract(src, CBM_LANG_PYTHON, "t", "sql/__init__.py");
ok = py_facts_valid_complete(r) && py_facts_has_effect(r);
if (!ok)
printf(" control '%s' is no namespace effect\n", effects[i][0]);
if (r)
cbm_free_result(r);
ASSERT_TRUE(ok);
}
PASS();
}
TEST(python_namespace_facts_distinguish_all_operations_and_unknowns) {
static const char source[] = "__all__ = ['old']; __all__ = []\n"
"__all__ = dynamic\n"
@@ -12796,6 +12906,7 @@ SUITE(extraction) {
RUN_TEST(python_namespace_facts_parse_errors_are_complete_unknown);
RUN_TEST(python_namespace_facts_type_checking_alternatives);
RUN_TEST(python_namespace_facts_string_statements_and_future_imports);
RUN_TEST(python_namespace_facts_init_call_allowlist);
RUN_TEST(python_namespace_facts_capture_and_copy_fail_atomically);
RUN_TEST(python_namespace_facts_validation_rejects_partial_and_unknown_schema);
RUN_TEST(python_namespace_facts_compact_spill_and_copy_keep_owned_payload);
+29 -1
View File
@@ -14423,16 +14423,20 @@ static const char *const py_reexport_calls[][2] = {
{"ctl_from_fn", "direct.impl.direct_fn"},
{"lazy_tc", "lz.lazy_mod.Lazy"},
{"doc_reexport", "docp.impl.Doc"},
{"go_reexport", "gop.impl.Thing"},
};
enum { PY_REEXPORT_N_CALLS = sizeof(py_reexport_calls) / sizeof(py_reexport_calls[0]) };
/* Calls that must NOT reach their target by the LSP: an `__all__`-unlisted
* name, a private name under a star, a re-export the module shadows later,
* and one made before a whole-namespace barrier (a top-level `try:`). */
* one made before a whole-namespace barrier (a top-level `try:`), a
* re-export a submodule import of `__go` replaces, and one behind a `__go`
* that writes its parameter. */
static const char *const py_reexport_excluded[][2] = {
{"excl_unlisted", "pkg.all_mod.Unlisted"}, {"excl_private", "pkg.star_mod._private_fn"},
{"excl_shadowed", "shad.impl.Gone"}, {"excl_barrier", "barr.impl.Before"},
{"excl_compat", "cmp.types_only.Shim"}, {"excl_fstring", "fsp.impl.Kept"},
{"excl_go_submodule", "gop.impl.base"}, {"excl_go_param", "gox.impl.Kept"},
};
enum { PY_REEXPORT_N_EXCLUDED = sizeof(py_reexport_excluded) / sizeof(py_reexport_excluded[0]) };
@@ -14505,6 +14509,25 @@ static int py_reexport_run(bool parallel, bool incremental, bool *lsp, bool *exc
"from .impl import Kept\n"
"f\"{Kept}\"\n");
write_temp_file(tmp, "app/fsp/impl.py", "class Kept:\n pass\n");
/* sqlalchemy's sql/__init__.py shape: a top-level `__go(locals())`. */
write_temp_file(tmp, "app/gop/__init__.py",
"from typing import Any\n"
"from .impl import Thing\n"
"from .impl import base\n\n\n"
"def __go(lcls: Any) -> None:\n"
" from . import base\n"
" from .annotation import prepare\n"
" prepare(Thing)\n\n\n"
"__go(locals())\n");
write_temp_file(tmp, "app/gop/impl.py",
"class Thing:\n pass\n\n\ndef base():\n return 1\n");
write_temp_file(tmp, "app/gop/base.py", "def helper():\n return 1\n");
write_temp_file(tmp, "app/gop/annotation.py", "def prepare(cls):\n return cls\n");
write_temp_file(tmp, "app/gox/__init__.py",
"from .impl import Kept\n\n\n"
"def __go(lcls):\n lcls[\"Kept\"] = None\n\n\n"
"__go(locals())\n");
write_temp_file(tmp, "app/gox/impl.py", "class Kept:\n pass\n");
write_temp_file(tmp, "app/main.py",
"from app import pkg\n"
"from app import cyc\n"
@@ -14514,6 +14537,8 @@ static int py_reexport_run(bool parallel, bool incremental, bool *lsp, bool *exc
"from app import cmp\n"
"from app import docp\n"
"from app import fsp\n"
"from app import gop\n"
"from app import gox\n"
"from app.db import models\n"
"from app.pkg import Explicit, Listed, Deep, star_fn, Renamed\n"
"from app.direct.impl import Direct, direct_fn\n\n\n"
@@ -14541,6 +14566,9 @@ static int py_reexport_run(bool parallel, bool incremental, bool *lsp, bool *exc
"def lazy_tc():\n lz.Lazy()\n\n\n"
"def doc_reexport():\n docp.Doc()\n\n\n"
"def excl_fstring():\n fsp.Kept()\n\n\n"
"def go_reexport():\n gop.Thing()\n\n\n"
"def excl_go_submodule():\n gop.base()\n\n\n"
"def excl_go_param():\n gox.Kept()\n\n\n"
"def ctl_from_cls():\n Direct()\n\n\n"
"def ctl_from_fn():\n direct_fn()\n");
for (int i = 0; parallel && i < 52; i++) {