Skip to content
0

miscased-constants

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.

completes the pair opened, one policing each half of the casing contract. It surfaces a single-name assignment whose value is inert and whose name has more than one character, carries no leading underscore, and is not already 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.

Configuration

KeyTypeDefaultMeaning
enabledbooltrueToggle the rule on or off
allow-patternregex""Constant names exempted from the lint

The allow-pattern regex is empty by default, exempting nothing beyond the structural carve-outs. A project carrying old-style PascalCase aliases (Vector = list[float]) sets it to spare them, whereas a TypeAlias-annotated target skips the gate without any configuration.

The Canonical Case

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.

python
max_retries = 5

More Examples

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.

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.

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 project carrying old-style PascalCase aliases sets allow-pattern = "^[A-Z][a-z]", so Vector = list[float] passes while the unmatched timeout still draws the TIMEOUT rename. The pattern is empty by default, exempting nothing.

No Change

max_retries: int declares a type without a value, so there is no constant to classify and the lint stays silent.

No Change

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.

No Change

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 reassigned-constants, which flags a SCREAMING_CASE name that a later write contradicts.

No Change

MAX_RETRIES already reads as a constant, so the lint leaves it alone. This is the shape every flagged case suggests renaming to.

No Change

Bindings under if TYPE_CHECKING: carry type-only semantics distinct from runtime configuration, so json_type never flags.

No Change

A TypeAlias annotation marks the target a type rather than a constant, so Vector skips the gate structurally. An old-style bare alias without the annotation is spared through the allow-pattern instead.

No Change

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.

No Change

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.

No Change

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.

No Change

A leading underscore marks deliberate module-private state, so _cache and the dunder __version__ both drop out ahead of the casing gate.

For per-line opt-outs, the Suppression chapter covers the # prose: ignore[miscased-constants] directive.