mirror of
https://github.com/openglow-org/forgefirm.git
synced 2026-10-19 02:51:42 -07:00
forgetest: the implementation hash walks a sibling imported as from . import x too
_imported walked a relative import only when it names a module (`from .x import y`): a level-1 ImportFrom with no module, `from . import x`, was passed over, so the suite module x joined no hash through it. The guard on the suite's package imports reads levels 2 and 0 alone, so it passed that spelling, and its own message sends an absolute import there: "import it relatively (from .. or from .)". So the guard and the walk did not read the same spellings: on a scratch copy of the catalog, forgectrl.auth's import of setup.py written `from . import setup` passed the guard, and an edit to setup.py's decode(), which the test calls, left forgectrl.auth's implementation hash where it was. homeoff.py imports cloud.py so (`from . import cloud as logs`); its `from .cloud import log_lines_since, log_size` on the next line keeps cloud.py in the walk, so no hash misses a module. _imported now walks every level-1 relative import: one with no module walks each name it imports as <suite>/<name>.py, the name and not its alias (a name that is no module of the suite is passed over, as a module was). The docstrings of _imported and sibling_imports, and the comment of the guard's case, name both spellings. Proof: forgetest-ci "Unit tests" (`python -B -m unittest discover -s tests -v` in forgetest): test_manifest.py runs 39 tests, OK, on Windows with python 3.14 and on Ubuntu 24.04 with python 3.12. test_a_sibling_imported_inside_a_function_moves_the_tests_that_run_it gains a leg in which m.two imports judge.py as `from . import judge as j`: an edit to judge.py moves m.two and not m.one. Negative controls on scratch copies: catalog.py as it was fails that leg (the two hashes are equal), and so does the walk that reads the alias (j) in place of the name. On a scratch copy of the catalog with forgectrl.auth's import written `from . import setup`, an edit to setup.py's decode() now moves forgectrl.auth's hash. The coverage lint on the tree manifest from the recipe pins reports 0 uncovered paths, 0 host-tested row problems, 43 tests. pyflakes reports no undefined name. Acceptance: none. The source_sha of 43 of 43 tests is unchanged: the one module-less sibling import outside the registry (suite/__init__.py, which has no tests) is homeoff.py's, and cloud.py is in its walk already. catalog.py is the registry and no shared judge, forgetest is in no fingerprint, and tests/ are in no fingerprint.
This commit is contained in:
@@ -246,9 +246,9 @@ def module_parts(path):
|
||||
|
||||
def sibling_imports(path, seen=None):
|
||||
"""The suite modules a module's shared text imports with `from .x
|
||||
import ...`, as paths, transitively, in a stable order. A helper a
|
||||
test calls from a sibling module is part of the judge, so its text
|
||||
belongs in the test's fingerprint."""
|
||||
import ...` or `from . import x`, as paths, transitively, in a stable
|
||||
order. A helper a test calls from a sibling module is part of the
|
||||
judge, so its text belongs in the test's fingerprint."""
|
||||
if seen is None:
|
||||
seen = []
|
||||
for sib in _direct_siblings(path):
|
||||
@@ -260,15 +260,17 @@ def sibling_imports(path, seen=None):
|
||||
|
||||
def _imported(path, nodes):
|
||||
"""The suite modules the statements `nodes` import with `from .x
|
||||
import`, at any depth (inside a function or a block too), in the
|
||||
order they are written."""
|
||||
import` or `from . import x` (each name a module of the suite), at any
|
||||
depth (inside a function or a block too), in the order they are
|
||||
written."""
|
||||
here = os.path.dirname(path)
|
||||
found = sorted((n for top in nodes for n in ast.walk(top)
|
||||
if isinstance(n, ast.ImportFrom) and n.level == 1 and n.module),
|
||||
if isinstance(n, ast.ImportFrom) and n.level == 1),
|
||||
key=lambda n: (n.lineno, n.col_offset))
|
||||
out = []
|
||||
for node in found:
|
||||
sib = os.path.join(here, node.module.replace(".", os.sep) + ".py")
|
||||
for name in ([node.module] if node.module else [a.name for a in node.names]):
|
||||
sib = os.path.join(here, name.replace(".", os.sep) + ".py")
|
||||
if os.path.isfile(sib) and sib not in out:
|
||||
out.append(sib)
|
||||
return out
|
||||
|
||||
@@ -536,8 +536,9 @@ class CatalogTests(unittest.TestCase):
|
||||
# a suite module does import, at its top level or inside a function
|
||||
# or a block, has to be on it (or be the registry, which judges
|
||||
# nothing), or that judge would be in no fingerprint. The sibling
|
||||
# walk follows the relative spelling alone, so a suite module
|
||||
# imports the package by no absolute name.
|
||||
# walk follows the relative spellings alone (`from .x import` and
|
||||
# `from . import x`), so a suite module imports the package by no
|
||||
# absolute name.
|
||||
import ast
|
||||
import glob
|
||||
paths = catalog.shared_judge_paths()
|
||||
@@ -724,6 +725,15 @@ def two(ctx):
|
||||
a4, b4 = self.shas()
|
||||
self.assertNotEqual(a3, a4)
|
||||
self.assertNotEqual(b3, b4)
|
||||
# the spelling with no module (`from . import x`, as homeoff.py imports cloud.py) is
|
||||
# walked the same
|
||||
self.write(self.MODULE.replace("def two(ctx):\n",
|
||||
"def two(ctx):\n from . import judge as j\n j.judge(2)\n"))
|
||||
a8, b8 = self.shas()
|
||||
write_judge("def judge(x):\n return x > 4\n")
|
||||
a9, b9 = self.shas()
|
||||
self.assertEqual(a8, a9)
|
||||
self.assertNotEqual(b8, b9)
|
||||
|
||||
def test_line_endings_do_not_count(self):
|
||||
a1, b1 = self.shas()
|
||||
|
||||
Reference in New Issue
Block a user