pi-lens
Version:
Real-time code feedback for pi — LSP, linters, formatters, type-checking, structural analysis & booboo
74 lines (61 loc) • 2.12 kB
YAML
# Slotted Class Attribute Assignment
# Detects `self.X = ...` inside a class with `__slots__` when X is NOT in the slots list.
id: slots-assignment-mismatch
name: Slotted Class Attribute Assignment
severity: warning
category: reliability
defect_class: convention
inline_tier: blocking
language: python
message: "Attribute '{{name}}' assigned in class with __slots__ but not declared"
description: |
Python `__slots__` declares a fixed set of allowed attributes for
instances of a class. Assigning to an attribute NOT in the slots
list will raise `AttributeError` at runtime (unless the parent
class has `__dict__`, in which case it's allowed but defeats the
purpose of __slots__).
Matches: a `self.X = ...` assignment inside any method of a class
whose body contains `__slots__ = ...`. Post-filter:
1. Parse the `__slots__` assignment (single string, tuple, or list)
2. If the assigned attribute X is in the slots list → suppress
3. Otherwise → fire
Caveat: If the parent class has its own `__dict__` (e.g. generated
ANTLR `ParserRuleContext` classes), the child's `__slots__` is
effectively a no-op for the purpose of preventing attribute
creation. SonarCloud S8494 still flags these as a code-smell
(misleading intent), but no runtime error will occur.
✅ FIX: Add the attribute to `__slots__`, or remove `__slots__`.
query: |
(function_definition
body: (block
(expression_statement
(assignment
left: (attribute
object: (identifier) @SELF
attribute: (identifier) @ATTR)
right: (_) @VAL)))) @METHOD
metavars:
- SELF
- ATTR
- VAL
- METHOD
post_filter: slots_attribute_mismatch
has_fix: false
tags:
- reliability
- python-specific
- slots
- attribute-assignment
examples:
bad: |
class Foo:
__slots__ = ("x",)
def __init__(self):
self.x = 1 # OK - in slots
self.y = 2 # BAD - raises AttributeError
good: |
class Foo:
__slots__ = ("x", "y")
def __init__(self):
self.x = 1
self.y = 2