version: 1
rules:
- id: no-tabs
pattern: '(?m)^\t+'
message: "PEP 8: spaces, not tabs, for indentation"
severity: warning
tests:
- name: tab indentation is flagged
text: "\tindented"
violations: 1
- name: clean lines pass
text: "indented"
violations: 0
- id: no-semicolons
pattern: '(?m)^\s*[^#\s].*;\s*$'
message: "PEP 8: avoid semicolons to separate statements"
severity: warning
- id: no-wildcard-imports
pattern: '(?m)^\s*from\s+[\w.]+\s+import\s+\*'
message: "wildcard imports make names ambiguous"
severity: warning
- id: bare-except
pattern: '(?m)^\s*except\s*:'
message: "bare except catches everything — name the exception"
severity: warning
- id: compare-to-none
pattern: '==\s*None\b'
message: "use 'is None', not '== None'"
severity: warning
- id: assignment-in-condition
pattern: '(?m)^\s*(?:if|elif|while)\s+[^#=]*[^=!<>]=[^=]'
message: "single '=' assigns — did you mean '=='?"
severity: error
- id: mutable-default
pattern: '(?m)^\s*def\s+(?<function>\w+)\s*\([^)]*=[ \t]*(?<default>\[\]|\{\})(?<rest>[^\n]*)'
severity: error
callback: |
return function(c)
if c.captures.rest:find("declint:allow") then
return nil
end
return { message = "default " .. c.captures["default"] .. " in '" ..
c.captures["function"] .. "' is shared across calls" }
end
tests:
- name: shared default is flagged
text: "def f(a = []):"
violations: 1
messages: ["default [] in 'f' is shared across calls"]
- name: allow marker vetoes
text: "def f(a = []): # declint:allow"
violations: 0
scopes:
- id: function
start: '^(?:async\s+)?def\s'
end: '^\S'
rules:
- id: print-in-function
pattern: '\bprint\('
message: "leftover print() — remove or use logging"
severity: warning
tests:
- name: print inside a function is flagged
text: "def f():\n print(1)\n"
violations: 1
- name: top-level print is allowed
text: "print(1)\n"
violations: 0