Skip to content

TypedPython gate: rebinding and mixed-element containers - #33

Merged
b-re-w merged 6 commits into
developfrom
work/typedpython-rebind
Oct 3, 2026
Merged

b-re-w merged 6 commits into
developfrom
work/typedpython-rebind

Conversation

@b-re-w

@b-re-w b-re-w commented Oct 2, 2026

Copy link
Copy Markdown
Member

Closes #22

Stacked on #19. This branch contains #19's commits. Merge #19 first; this PR's own change is the last commit, 38b5118.

Feature added

  • forbidden/rebinding: flags an unannotated name that is rebound to a value of a different type, including through augmented assignment (total = 0; total += 0.5). Covers function, class and module scope.
  • forbidden/mixed-container: flags a list, set or dict display whose inferred element type is a union ([1, "a"] → list[int | str]).
  • Both follow the forbidden-list severity: a warning under checked, an error under compiled.
  • Not flagged: a declared union (x: int | str), an annotated parameter, the same type assigned again, and tuples (they are typed per slot).

How it works: Pyrefly records types for name loads, not for assignment targets. So the gate makes a copy of the file and inserts ; x after each candidate assignment on the same line, which keeps line numbers unchanged. It runs Pyrefly a second time on that copy and reads the types at the inserted positions. That second run happens only for files that assign a name twice or contain a container display.

Test added

test_rebinding.py, 14 tests:

  • Red (skeleton only): the 7 tests that expect a finding failed with NotImplementedError: rebinding.probe. The 7 that expect nothing passed trivially, as expected.
  • Green: all 42 tests in the module pass.
  • Mutation: with the probe pass disabled, 6 rebinding tests fail. With it restored, they pass.

Documentation corrected

SPEC N-6 now lists the two rules and what they don't cover. Design §6.1 records the method and its cost.

Known cost / gaps

  • The gate is now about 15× a plain pyrefly check on the synthetic module (232 ms → 3.6 s, load 41). The synthetic total += ... triggers the probe pass. Reducing this is TypedPython gate: cut the type-report overhead #21.
  • for targets and comprehension variables are not probed yet.

b-re-w added 6 commits October 3, 2026 07:04
Step 1 of docs/design/typedpython.md: python -m typedpython check runs Pyrefly
with its Any-leak kinds raised to errors, reports user-code expressions of type
Any outside cast() (any-flow), and reports the forbidden list as warnings in
checked mode and errors in compiled mode. Lives in python-multiplatform-ksp.

INTENT gains TypedPython as purpose 4 (maintainer statements quoted); SPEC
gains N-6 (partial: not yet run by the build).

Refs #16
toolchain installs the gate with uv at a pinned version (1b, #20), so the
package needs a build backend and a console script. Wheel built and smoke-
tested: installed from --find-links, typedpython check exits 0 on clean code
and 1 on an unannotated function.

Refs #16
Pyrefly 1.3.2 reports neither and records no types for assignment targets,
but it does record the type of every name load. The gate now writes a probed
copy in which each candidate assignment is followed on the same line by a
load of its targets (line numbers unchanged), runs Pyrefly on it, and judges
the types at the probe positions. The second run happens only when a file
assigns a name twice or has a container display.

Refs #22
The gate's Pyrefly config lives in a temporary directory, which Pyrefly took
as the import root, so relative and absolute self-imports of user packages did
not resolve and every value from them failed as any-flow. Each checked file's
import root (above its outermost package) now goes on the search path.

Red before the fix: 3 failed in test_imports.py.

Refs #16
A probed copy written alone into a temporary directory lost its package, so
relative imports in it did not resolve, their values were Unknown, and
rebinding went unreported in exactly the code that lives in packages. The
probe pass now copies each file's package tree with the same layout.

Red before the fix: test_rebinding_inside_a_package_with_relative_imports.

Refs #22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant