From ee4c69129f7f9b12f7dc3307c51d25e31ffdc996 Mon Sep 17 00:00:00 2001 From: Esteban Zimanyi Date: Mon, 31 Aug 2026 15:47:57 +0200 Subject: [PATCH] Resolve the source root the provisioning names MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The directory a MobilityDB checkout sits in belongs to whoever made it: the provisioning action checks it out as `_mobilitydb_src` and hands the parse `MDB_SRC_ROOT`, while the resolver's literal probe names `_mobilitydb`. The resolver consults `MDB_SRC_ROOT`, so it answers over the checkout the provisioning provides whatever that checkout is called, and still answers None where no tree exists — the honest signal a caller degrades on. Reading no source is silent in the output and expensive in it. The object model reports `errors: source-unavailable` and carries no raised code, and `attach_type_relations` locates no catalog and attaches nothing, so a consumer receives a catalog with no `typeRelations` and cannot tell it from a complete one. Against a provisioned checkout the same parse reports `errors: scanned` with 685 functions carrying raised codes and 22 base types carrying their set, span, span set and temporal relations, and the drift gate runs rather than skipping. --- parser/object_model.py | 18 ++++++++++---- tests/test_object_model.py | 48 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 62 insertions(+), 4 deletions(-) diff --git a/parser/object_model.py b/parser/object_model.py index 59efd88..ff1d3da 100644 --- a/parser/object_model.py +++ b/parser/object_model.py @@ -31,15 +31,25 @@ class is invented; a function with no prefix match is recorded honestly as def find_mobilitydb_src(headers_dir: Path | None = None) -> Path | None: """Resolve the MobilityDB C source root for the error scan / drift gate. - First existing of: $MOBILITYDB_SRC, the sparse-checkout - ``_mobilitydb/meos/src``, or the ``src`` sibling of the headers dir. - Returns None when no source tree is available — callers must degrade to - an honest signal, never fabricate. + First existing of: $MOBILITYDB_SRC, the checkout $MDB_SRC_ROOT names, the + sparse-checkout ``_mobilitydb/meos/src``, or the ``src`` sibling of the + headers dir. Returns None when no source tree is available — callers must + degrade to an honest signal, never fabricate. + + ``MDB_SRC_ROOT`` is the checkout the provisioning hands the parse, and it is + consulted because the directory name is the provisioner's to choose: the CI + action checks MobilityDB out as ``_mobilitydb_src`` while the probe below + names ``_mobilitydb``, so a resolver that knows only the literal name reports + no source over a tree that is present, and every catalog it derives silently + loses what the source carries. """ candidates = [] env = os.environ.get("MOBILITYDB_SRC") if env: candidates.append(Path(env)) + root = os.environ.get("MDB_SRC_ROOT") + if root: + candidates.append(Path(root) / "meos" / "src") candidates.append(Path("_mobilitydb") / "meos" / "src") if headers_dir is not None: candidates.append(Path(headers_dir).parent / "src") diff --git a/tests/test_object_model.py b/tests/test_object_model.py index 75c6e39..9de7b9c 100644 --- a/tests/test_object_model.py +++ b/tests/test_object_model.py @@ -9,6 +9,7 @@ """ import json +import os import re import sys import tempfile @@ -252,6 +253,53 @@ def _enum_block(text: str, end_marker: str) -> dict: re.findall(r"\b([A-Z][A-Z0-9_]+)\s*=\s*(\d+)", block)} +class SourceRootResolutionTest(unittest.TestCase): + """The resolver answers over the checkout the provisioning names. + + The directory name belongs to the provisioner: the CI action checks MobilityDB + out as ``_mobilitydb_src`` and hands the parse ``MDB_SRC_ROOT``, while the + literal probe names ``_mobilitydb``. A resolver reading only the literal name + reports no source over a tree that is present, and the catalog then carries + neither the object model nor the type relations the source states. + """ + + def _tree(self, d): + src = Path(d) / "meos" / "src" / "temporal" + src.mkdir(parents=True) + (src / "meos_catalog.c").write_text("/* catalog */\n") + return Path(d) + + def test_provisioned_root_resolves_whatever_the_directory_is_called(self): + with tempfile.TemporaryDirectory() as d: + root = self._tree(Path(d) / "_mobilitydb_src") + saved = {k: os.environ.pop(k, None) for k in ("MOBILITYDB_SRC", "MDB_SRC_ROOT")} + try: + os.environ["MDB_SRC_ROOT"] = str(root) + self.assertEqual(find_mobilitydb_src(), root / "meos" / "src") + finally: + for k, v in saved.items(): + if v is None: + os.environ.pop(k, None) + else: + os.environ[k] = v + + def test_no_source_still_answers_none(self): + # From a directory holding no checkout, so the relative `_mobilitydb` + # probe cannot answer and the result is the resolver's own. + saved = {k: os.environ.pop(k, None) for k in ("MOBILITYDB_SRC", "MDB_SRC_ROOT")} + cwd = os.getcwd() + try: + os.environ["MDB_SRC_ROOT"] = "/no/such/checkout" + with tempfile.TemporaryDirectory() as empty: + os.chdir(empty) + self.assertIsNone(find_mobilitydb_src()) + finally: + os.chdir(cwd) + for k, v in saved.items(): + if v is not None: + os.environ[k] = v + + _SRC = find_mobilitydb_src(ROOT / "meos" / "include") _CAT_C = (_SRC / "temporal" / "meos_catalog.c") if _SRC else None