step-narration
LintProse surfaces comments that narrate the next line.
A SCREAMING_CASE name promises a constant, so a module-level one that is reassigned contradicts its own casing.
SCREAMING_CASE binding only when it is reassigned (an assignment_count above one or an augmented assignment recorded against the name), leaving a write-once constant silent whatever its value. The fix renames the variable to lowercase or stops reassigning it, work the lint leaves to a future migration pass that picks up its output.The rule weighs module-level SCREAMING_CASE assignments and annotated assignments, firing only on the reassigned ones. Names on the configurable allow list stay quiet. Dunder-style names (__version__, __all__) fall outside SCREAMING_CASE because they lead with an underscore. Typing constructs from the standard library (TypeVar, ParamSpec, NewType, TypeAliasType) and any binding declared inside an if TYPE_CHECKING: block also stay quiet, since both carry their own semantics distinct from runtime configuration. In-place mutation through a method call or a subscript store stays out of scope, since the binding table records those as reads. The lint is non-rewriting, so the diagnostic surfaces without touching the source.
| Key | Type | Default | Meaning |
|---|---|---|---|
enabled | bool | true | Toggle the rule on or off |
allow | list of names | [] | Module-level names exempted from the lint |
The allow list holds bare names. An entry never produces a lint, even when its shape would otherwise match.
A SCREAMING_CASE name reassigned at
RETRIES = 3
RETRIES = 5
A reassigned SCREAMING_CASE annotated assignment draws the FLAG: bool and the valued MAX: int = 1 shapes both feed the annotated-assignment path ahead of the reassignment gate.
A name listed in [tool.prose.rules.reassigned-constants].allow passes through silently even when reassigned, so a blessed LOG_LEVEL publishes at the module root where an unlisted CACHE_DIR reassignment still draws the
A # fmt: off block carries the same
A clean file with only an allowed __version__
Chained assignment (A = B = 1), tuple unpacking (A, B = 1, 2), and attribute targets (FOO.bar = 1) all fall outside the single-target name shape the rule flags, so each line passes through silently.
The attribute-qualified typing.TYPE_CHECKING guard skips its block exactly as the bare TYPE_CHECKING name does, so a constant inside draws no
Dunder names such as __version__, __all__, and __author__ are
A SCREAMING_CASE name assigned exactly once is a genuine constant, so the rule stays silent whatever its value shape.
Prose surfaces comments that narrate the next line.
Prose surfaces single-use local
For per-line opt-outs, the Suppression chapter covers the # prose: ignore[reassigned-constants] directive.