• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

source-academy / py-slang / 31160943792
86%

Build:
DEFAULT BRANCH: main
Ran 07 Aug 2026 08:34AM UTC
Jobs 1
Files 138
Run time 1min
Badge
Embed ▾
README BADGES
x

If you need to use a raster PNG badge, change the '.svg' to '.png' in the link

Markdown

Textile

RDoc

HTML

Rst

07 Aug 2026 08:15AM UTC coverage: 86.222% (+0.005%) from 86.217%
31160943792

push

github

web-flow
Stepper: defer import substitution to the import statement's own step (#418)

* Stepper: reject a def/assignment that redeclares an import

py-slang#413: `from rune import red` followed by a local `def
red(x): return x` and then `print(red(4))` crashed the substitution
stepper with a conductor-level "Invalid opaque identifier: 4" instead
of being rejected outright.

Both the import and the def live at the program's top level.
Reassignment isn't allowed in Python §1/§2 -- deliberately: those
chapters are designed so substitution alone is a sufficient
explanation model, with no environment/mutation reasoning required --
and Python §3/§4, where reassignment is allowed, don't have a
stepper. So this program should never have been accepted in the first
place, the same as `x = 1` followed by `x = 2` already isn't.

createNoReassignmentValidator (wired in for chapters 1-2 only) only
ever inspected Assign/AnnAssign/FunctionDef targets to decide what's
already declared in a scope. `from rune import red` was invisible to
it, so the later `def red` sailed through preprocessing undetected,
and the program only failed later -- confusingly -- once the
substitution stepper actually tried to route a call through the
(never-actually-shadowed) real imported `red`, which expects an
opaque Rune handle, not the raw Python int `4`.

createNoReassignmentValidator now also registers each `from X import
Y` binding, so a later def/assignment of the same name is rejected
exactly like any other reassignment. An import is never itself
rejected for colliding with an *earlier* import of the same name,
though: two imports binding the same name (even from different
modules) is real, separately-tested, intentional "last one wins"
behaviour (src/tests/py2js-from-import.test.ts), and the grammar
itself (python.ne) guarantees every import precedes every other
statement, so an import can never be the *later* half of a collision
with a def/assign -- only ever the earlier half, which is sti... (continued)

4802 of 6007 branches covered (79.94%)

Branch coverage included in aggregate %.

28 of 29 new or added lines in 4 files covered. (96.55%)

10724 of 12000 relevant lines covered (89.37%)

164576.32 hits per line

Uncovered Changes

Lines Coverage ∆ File
1
92.01
-0.5% src/conductor/stepper/ast.ts
Jobs
ID Job ID Ran Files Coverage
1 31160943792.1 07 Aug 2026 08:34AM UTC 138
86.22
GitHub Action Run
Source Files on build 31160943792
  • Tree
  • List 138
  • Changed 5
  • Source Changed 5
  • Coverage Changed 5
Coverage ∆ File Lines Relevant Covered Missed Hits/Line Branch Hits Branch Misses
  • Back to Repo
  • Github Actions Build #31160943792
  • 53e689aa on github
  • Prev Build on main (#31155376990)
  • Next Build on main (#31163103731)
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc