CQ010: NoMagicNumbers
Overview
| Property | Value |
|---|---|
| ID | CQ010 |
| Name | NoMagicNumbers |
| Group | code-quality |
| Severity | NOTE |
Description
Flags unexplained numeric constants (magic numbers) in code that should be replaced with named constants.
Magic numbers are numeric literals that:
- Lack context: The meaning of
86400is unclear without context - Reduce maintainability: Changes require finding and updating every occurrence
- Obscure intent: Named constants like
SECONDS_PER_DAY = 86400are self-documenting - Create subtle bugs: Typos in repeated magic numbers are hard to spot
This check uses AST parsing to find numeric literals that should be named constants.
What it checks
The check scans all Python files in the package (excluding test files, hidden directories, and __pycache__), identifies numeric constants, and reports those that aren’t in the allowed list:
- PASSED: No unexplained magic numbers found
- FAILED: Magic numbers found in code
- NOT_APPLICABLE: Package directory not found
Default allowed numbers
The following values are allowed by default:
- Common values:
0,1,-1,2 - Powers of 2:
4,8,16,32,64,128,256,512,1024,2048,4096 - Common bases:
10,100,1000
Ignored contexts
Magic numbers are NOT flagged when used in:
- Type hints and annotations
- Default function arguments
- List/array indexes
# These are flagged as magic numbers
timeout = 3600 # What does 3600 mean?
buffer_size = 65536 # Magic number
# These are NOT flagged (in allowed list or ignored context)
count = 0 # 0 is allowed
items = data[5] # Index context is ignored
def process(retries: int = 3): # Default arg is ignored
passHow to fix
Replace magic numbers with named constants
# Before
def calculate_delay(attempts):
return attempts * 1.5 * 3600
# After
BACKOFF_MULTIPLIER = 1.5
SECONDS_PER_HOUR = 3600
def calculate_delay(attempts):
return attempts * BACKOFF_MULTIPLIER * SECONDS_PER_HOURUse module-level constants
# Before
def validate_input(text):
if len(text) > 255:
raise ValueError("Too long")
if len(text) < 3:
raise ValueError("Too short")
# After
MAX_INPUT_LENGTH = 255
MIN_INPUT_LENGTH = 3
def validate_input(text):
if len(text) > MAX_INPUT_LENGTH:
raise ValueError("Too long")
if len(text) < MIN_INPUT_LENGTH:
raise ValueError("Too short")Configuration
Add project-specific allowed numbers
[tool.pycmdcheck.checks.CQ010]
allowed_numbers = [42, 60, 3600, 86400] # Add to defaultsSkip this check
[tool.pycmdcheck]
skip = ["CQ010"]CLI
pycmdcheck --skip CQ010