band-constants
OrderingHoists module-level constants into a leading band below the imports and a trailing band beneath the definitions.
PEP 8 writes a module constant in SCREAMING_CASE, so a public module-level name that binds a fixed value yet reads as max_retries tells every caller "mutable state" when nothing ever writes it again.
SCREAMING_CASE, when nothing in the module reassigns it.The condition reads inertness through the same classifier the notebook banding gate consumes, which is what keeps the lint quiet where pylint's equivalent drowns in noise. A call-produced global (logger = get_logger(__name__), app = build()) is effectful, so it never flags, and a leading underscore marks deliberate module-private state (_cache = {}), the dunder __version__ riding the same exemption. A single-character name is spared too, its lone-capital SCREAMING form reading as a matrix by linear-algebra convention and its lowercase form usually a mathematical scalar. What remains is public data with no reassignment anywhere in the module, exactly the shape the SCREAMING_CASE convention exists for. The lint reports without an auto-fix and names the SCREAMING_CASE form in its help line, because renaming a module constant breaks importers outside the file. Notebooks are skipped whole, a cell's top-level assignments being working variables rather than module constants.
A type alias is never renamed, because SCREAMING_CASE is the one casing an alias must not take, PascalCase being what an alias is written in. The rule tells an alias from a constant by looking at the value rather than at the name. A value that points at something already built (Pen = Turtle, open = TarFile.open, Interval = Union[int, float]) is an alias, and PEP 604's Interval = int | float reads the same way, recursing into both sides so that 1 | 2 stays a constant. A value that builds something new (a literal, an f-string, a collection display, an arithmetic expression) is a constant and still draws the rename, which is what keeps the dispatch tables and derived strings the lint exists to catch. A lambda binds a callable, so it is spared too.
Looking at the value is not always enough, since SETTINGS["db"] and Literal["read"] are the same shape. Two further checks run, and none of the three can ever turn a constant into an alias, only an alias into a constant, so a value that none of them pins down keeps its name and draws no warning. That is deliberate, in that a missed warning costs one line of output whereas renaming a real alias breaks every module that imports it.
The first checks the slice against what a type is allowed to contain. A type never holds a slice or a dunder, which makes path_separators[1:] and sys.modules[__name__] lookups into data, and it never holds an integer, signed or not, a bool, or bytes outside Literal, which makes LEVELS[1] and items[-1] indexes while Literal[1, 2, 3] and Literal[-1] stay types. PEP 593 lets every Annotated argument after the first be anything, so the call in Annotated[int, Field(gt=0)] is metadata rather than a call in a type position. The second looks the base up in the module's own bindings, so SETTINGS["db"] is a dict lookup once SETTINGS is found assigned a {...} literal in the same file, whereas an imported NDArray[float] and a locally declared Box[int] are both types. The third reads how the module uses the name, so a name it truth-tests, order-compares, or does arithmetic on holds data (if path_sep:), whereas a name used in an annotation is a type whatever else happens to it.
What the third check ignores matters as much as what it counts, because a class does most of what data does. An Enum is iterable, so for color in Colors: says nothing about Colors. A class compares by equality, so type(value) == Kind says nothing about Kind, and typing-aware code compares type objects with is (if base is Generic:) and reads a class alias for its methods (int_.from_bytes). What counts as data is the short list of operations a class raises TypeError on.
The same caution sets the rule's limit. When the base cannot be resolved in the file, because it was imported or bound by a call, the subscript stays a type, so database = SETTINGS["db"] goes unflagged where SETTINGS comes from elsewhere. Settling that would take a cross-file import map, which is exactly the machinery this rule does without.
The value read is what
consumes for its alias sub-band, so one answer settles both the rename and the band. The read contexts stay with the lint, a name being a type wherever it is used whereas the band sorts on the value alone.| Key | Type | Default | Meaning |
|---|---|---|---|
enabled | bool | true | Toggles the rule on or off. |
allow-pattern | regex | "" | Constant names exempted from the lint, such as old-style bare aliases. |
The allow-pattern regex is empty by default, exempting nothing beyond the structural carve-outs. A project holding a name out of SCREAMING_CASE on purpose sets it to spare that name, which is a different job from never flagging a type alias, and only the second happens without configuration.
max_retries binds an inert literal nothing in the module reassigns, so it reads as a constant whose casing never says so. The lint reports it without rewriting and names the MAX_RETRIES form in its help line, because renaming a module constant breaks importers outside the file.
max_retries = 5
Word-boundary case folding turns maxRetries into MAX_RETRIES for the help line, where a naive uppercasing would produce MAXRETRIES. The same fold covers a PascalCase MaxRetries.
SETTINGS["db"] and Literal["read"] are the same shape, so the rule looks the base up in the file. SETTINGS is assigned a dict literal right above, which makes database a lookup and draws the DATABASE rename, whereas nothing here binds Literal and Mode stays a type.
The scan descends into a top-level if, so default_encoding behind a platform guard draws the DEFAULT_ENCODING rename. Only a def, a class, or an if TYPE_CHECKING: block ends the descent.
The value settles the rename rather than the target's casing, so a PascalCase name whose value constructs new data (a collection display, a literal, an f-string, an arithmetic expression) stays a constant and draws the SCREAMING_CASE rename. This is the converse of the bare-alias carve-out, and it is what keeps the dispatch tables and derived strings the lint exists to catch.
A | union reads as an alias only when both sides name existing objects, so int | float is spared while the literal-valued 0o644 | 0o111 still draws the PERMISSIONS rename.
An annotated assignment carrying a value reads the same as a bare one, so timeout: int = 30 draws the TIMEOUT rename. A bare annotation with no value binds nothing and stays silent.
A type never contains a slice, a dunder, or a bare integer, signed or not, so each one marks its subscript an index into data and draws the rename. The check reads only the code in front of it, no import map and no type inference, and dict[str, int], Literal["read", "write"], and the signed Literal[-1] come through as the types they are.
path_sep = os.sep and opener = tarfile.TarFile.open are the same shape, and nothing in either value says which one holds data. How the module uses them does. It writes if path_sep:, and truth-testing a class is pointless, so path_sep draws the PATH_SEP rename, whereas opener is only ever called and keeps its name. Vec and Crate are types on the shape of their values alone, one parametrizing an imported generic and the other a class declared here.
A project keeping a name out of SCREAMING_CASE on purpose sets allow-pattern = "^[A-Z][a-z]", so the constructed Defaults dict passes while the unmatched timeout still draws the TIMEOUT rename. The pattern is empty by default, exempting nothing.
An integer, a bool, and a bytes string are three things no type contains, so LEVELS[1], OPTIONS[True], and CODECS[b"gzip"] all index data and draw the rename. Literal is the one construct that does contain them, which keeps Literal[1, 2, 3], Literal[True], Literal[-1], and Literal[b"gzip"] types. PEP 593 lets every Annotated argument after the first be anything, so the Field(gt=0) call is metadata rather than a call in a type position, and the parameter list of a Callable, the empty tuple[()], and the ... of Callable[..., int] all stand as written.
max_retries: int declares a type without a value, so there is no constant to classify and the lint stays silent.
Four uses that look like handling data are things a class does too, so none of them draws a rename. An Enum is iterable, which keeps for color in Colors: off Colors. A class compares by equality, so type(value) == Kind says nothing about Kind. Typing-aware code compares type objects with is, as base is Node does, and reads a class alias for its methods, as int_.from_bytes reads int. Counting any of the four would rename a real type.
The scan does not descend into a def, so a local max_retries is a working variable rather than a module constant and stays silent.
counter is written twice, so it is mutable state rather than a constant, and the lint leaves it to its lowercase name. This is the mirror of
SCREAMING_CASE name that a later write contradicts.A recursive alias forward-references itself as a string, because Tree is unbound while its own value evaluates. The string constructs data, so the value-shape read alone would draw a rename, leaving the TypeAlias annotation the only thing that spares it.
MAX_RETRIES already reads as a constant, so the lint leaves it alone. This is the shape every flagged case suggests renaming to.
Bindings under if TYPE_CHECKING: carry type-only semantics distinct from runtime configuration, so json_type never flags.
A TypeAlias annotation marks the target a type rather than a constant, so Vector skips the gate on the annotation alone, ahead of the value-shape read that would spare it anyway.
A bare name, an attribute, and a subscript each name an object that already exists, which is the shape a PEP 484 type alias takes, so none of them draws a SCREAMING_CASE rename. The rule reads the value rather than the name, leaving the lowercase opener spared alongside the PascalCase Pen.
logger and app bind the result of a call, so evaluating them runs code rather than reading a fixed value. The shared inert-value classifier keeps the lint quiet on these effectful globals, exactly the split that spares pylint's noise.
Chained assignment (first = second = 1), tuple unpacking (first, second = 1, 2), and attribute targets (config.timeout = 30) all fall outside the single-name shape the rule reads, so each line passes silently whatever the casing of its names.
A lone letter at module scope usually names a mathematical scalar, and its SCREAMING form is a single capital, which linear algebra reads as a matrix. So x and n stay silent, where the suggestion would mislead more than help.
A leading underscore marks deliberate module-private state, so _cache and the dunder __version__ both drop out ahead of the casing gate.
Hoists module-level constants into a leading band below the imports and a trailing band beneath the definitions.
Surfaces a module-level constant reassigned despite its UPPER_SNAKE_CASE casing.
Surfaces single-use local bindings that could inline cleanly.
For per-line opt-outs, the Suppression chapter covers the # prose: ignore[miscased-constants] directive.