Make every example import the core sitting next to it - #89
Merged
Conversation
`python ch4_rf_point_positioning/example_toa_positioning.py` puts the *chapter*
directory on `sys.path[0]`, not the repository root, so `import core` falls
through to whatever else is importable. On a fresh clone that is
ModuleNotFoundError. On a machine that has ever installed this package it is
worse and quieter: measured in this worktree, a probe run as a script from the
chapter directory resolved `core` to `C:/Users/qmohs/IPIN-Examples/core` -- a
different checkout -- and would have run to completion against it. No error;
only the answer changes.
All 38 examples now insert the repository root before their first `core`
import. This is not a new idiom: `ch5_fingerprinting/example_classification`
has carried the same line for as long as it has existed, and nine of the twelve
generators in `scripts/` do the same. It had simply never been brought to the
other 37. (The three `scripts/` files without it import no `core` at all, so
they need none.)
`tests/test_examples_import_this_checkout.py` holds it, and checks *order*
rather than presence, because a bootstrap below the import changes nothing.
Both mutations fire: removing the line, and moving it after the import.
Three bugs in the sweep script, all found by pyflakes rather than by reading,
each a plausible way to locate an insertion point:
- `ast.walk` to test whether `sys` was imported counts an `import sys` inside
a *function*, so four files got a module-level `sys.path.insert` with no
`sys` bound.
- Locating the stdlib group by `line.startswith(("import ", "from "))`
matched a line of module *docstring prose* beginning "from range
measurements ...", writing `import sys` inside the docstring.
- Accepting any module-level import of a name ignores position: ch7's
pose-graph example imports `pathlib.Path` three lines *below* its first
`core` import, so the name existed but not yet where the bootstrap runs.
All three compile. `test_the_check_reads_the_shapes_it_has_to_distinguish` pins
the last two against the guard's own parser, since that is the half most likely
to agree with itself.
Lint moved the right way. Inserting `import sys` at the head of a stdlib group
is unsorted, so I001 went 113 -> 147; `ruff --select I001 --fix` over the
chapter directories cleared 63 of them, 29 of which predated this change,
leaving 84. Baseline lowered accordingly. E402 does not fire at all -- ruff
exempts imports following a `sys.path` manipulation, which is what makes the
idiom viable.
Verified by capturing `--help` for all 38 examples before the change and
diffing after: byte-identical, twice -- once after the insertions and again
after ruff reordered the imports. `--help` exits during argument parsing but
only after every module-level import has run, so it exercises exactly what this
touches.
The README's paragraph contrasting the two invocation forms said the script
form only works once the package is installed. That is no longer true, and it
now says what the line is for instead.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…trap-syspath # Conflicts: # CLAUDE.md
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.
Closes #86.
python ch4_rf_point_positioning/example_toa_positioning.pyputs the chapter directory onsys.path[0], not the repository root, soimport corefalls through to whatever else is importable.Reproduced, and the quiet failure is the real one
A probe run as a script from the chapter directory, in this worktree:
versus module form, which resolves
coreinside the worktree. No error either way — only the answer changes. On a fresh clone with no install it isModuleNotFoundErrorinstead.One correction to the issue, and one gap it did not name
The issue's premise, "running an example the way the README shows it", is out of date: the READMEs show
python -mthroughout (6 occurrences in the top-level README, 16 in ch4's), held bytests/docs/test_documented_commands_use_module_form.py.But that guard's regex is anchored to
ch\d+_chapter directories, sopython scripts/generate_*.pysits outside it and is still documented in script form in several dataset READMEs. I checked those: the threescripts/files lacking a bootstrap import nocoreat all, so they need none. (My earlier read that they were an inconsistency was wrong.)The fix was already the repo's own idiom
ch5_fingerprinting/example_classificationhas carriedsys.path.insert(0, ...)for as long as it has existed, and nine of twelvescripts/generators do the same. It had simply never reached the other 37 examples. This is not a new pattern to debate — it is an existing one applied to one directory and not the other.tests/test_examples_import_this_checkout.pyholds it and checks order, not presence: a bootstrap below the import changes nothing. Both mutations fire — removing the line, and moving it after the import.Three bugs in the sweep, all caught by pyflakes rather than review
Each was a plausible way to find an insertion point, and each compiles:
ast.walkto test whethersyswas imported counts animport sysinside a function → four files got a module-levelsys.path.insertwith nosysbound.line.startswith(("import ", "from "))matched a line of module docstring prose beginning "from range measurements ..." →import syswritten inside the docstring.pathlib.Paththree lines below its firstcoreimport.test_the_check_reads_the_shapes_it_has_to_distinguishpins the last two against the guard's own parser, since that is the half most likely to agree with itself.Lint moved the right way
Inserting
import sysat the head of a stdlib group is unsorted, so I001 first went to 147;ruff --select I001 --fixover the chapter directories cleared 63, 29 of which predated this change. Baseline lowered. E402 never fires — ruff exempts imports following asys.pathmanipulation, which is what makes this idiom viable at all.Verification
--helpcaptured for all 38 examples before the change and diffed after: byte-identical, twice — once after the insertions, again after ruff reordered imports.--helpexits during argument parsing but only after every module-level import has run, so it exercises exactly what this touches.590 passed across repo conventions, the
--helpsweep, console encoding, the new guard and the lint ratchet. pyflakes clean.The README paragraph contrasting the two invocation forms claimed the script form only works once installed. That is no longer true, and it now says what the line is for instead.
🤖 Generated with Claude Code