Python Coding Violations
Not In Loop
Description: Break or continue keywords are used outside a loop
def check_value(value):
if value > 10:
continue
print(value)
for i in range(3):
check_value(i)
for i in range(3):
if i == 2:
continue
print(i)
Function Redefined
Description: A function / class / method is redefined
def greet():
print('Hello')
def greet():
print('Hi again')
def say_hello():
print('Hello')
def say_goodbye():
print('Goodbye')
Continue In Finally
Description: Emitted when the continue
keyword is found inside a finally
clause, which is a SyntaxError
while running:
try:
pass
finally:
continue # [continue-in-finally]
while running:
try:
pass
except KeyError:
pass
else:
continue
Abstract Class Instantiated
Description: An abstract class with abc.ABCMeta
or abc.ABC
as metaclass has abstract methods and is instantiated
import abc
class Vehicle(abc.ABC):
@abc.abstractmethod
def start_engine(self):
pass
car = Vehicle() # [abstract-class-instantiated]
import abc
class Vehicle(abc.ABC):
@abc.abstractmethod
def start_engine(self):
pass
class Car(Vehicle):
def start_engine(self):
print("Vroom")
car = Car()
Star Needs Assignment Target
Description: Can use starred expression only in assignment target. Emitted when a star expression is not used in an assignment target
planets = *["Mercury", "Venus", "Earth"] # [star-needs-assignment-target]
mercury, *rest_of_planets = ["Mercury", "Venus", "Earth"]
Duplicate Argument Name
Description: Duplicate argument names in function definitions are syntax errors
def get_animals(dog, cat, dog): # [duplicate-argument-name]
pass
def get_animals(dog, cat, bird):
pass
Return In Init
Description: Explicit return in __init__
Used when the special class method __init__
has an explicit return value. __init__
magic method is a constructor of an instance, so it should construct it but not return anything.
class Multiply:
def __init__(self, x, y): # [return-in-init]
return x * y
class Multiply:
def __init__(self, x, y) -> None:
self.product = x * y
Too Many Star Expressions
Description: More than one starred expression in assignment. Emitted when there are more than one starred expressions (*x
) in an assignment. This is a SyntaxError
*dogs, *cats = ["Labrador", "Poodle", "Sphynx"] # [too-many-star-expressions]
*labrador_and_poodle, sphynx = ["Labrador", "Poodle", "Sphynx"]
Nonlocal And Global
Description: Emitted when a name is both nonlocal
and global
VALUE = 100
def update_value(new_value): # [nonlocal-and-global]
global VALUE
nonlocal VALUE
VALUE = new_value
print(f"Updated global value: {VALUE}")
update_value(200)
VALUE = 100
def update_value(new_value):
global VALUE
VALUE = new_value
print(f"Updated global value: {VALUE}")
update_value(200)
Used Prior Global Declaration
Description: Emitted when a name is used prior a global
declaration, which results in an error since Python 3.6. It can't be emitted when using Python < 3.6
FRUIT = "apple"
def update_fruit():
print(FRUIT) # [used-prior-global-declaration]
global FRUIT
FRUIT = "orange"
FRUIT = "apple"
def update_fruit():
global FRUIT
FRUIT = "banana"
Assignment Outside Function
Description: An assignment is found outside a function or method
x = 5 # [assignment-outside-function]
def assign_value():
x = 5
Return Arg In Generator
Description: A return
statement with an argument is found in a generator function or method. It's allowed in Python >= 3.3 but wasn't allowed earlier.
def yield_items():
for item in ['a', 'b', 'c']:
yield item
return "Finished!" # This was not allowed in Python 3.3 and earlier.
Invalid Star Assignment Target
Description: Starred assignment target must be in a list
or tuple
. Error occurs when using a star expression in invalid contexts.
*numbers = [1, 2, 3] # [invalid-star-assignment-target]
numbers = [1, 2, 3]
Bad Reversed Sequence
Description: The argument passed to reversed()
is not a sequence. This error occurs when a non-iterable type is passed to reversed()
.
reversed(1234) # [bad-reversed-sequence]
reversed([1, 2, 3, 4])
Nonexistent Operator
Description: Attempting to use C-style increment or decrement operators (++
or --
), which are not valid in Python.
x = 0
while x < 5:
print(x)
++x # [nonexistent-operator]
x = 0
while x < 5:
print(x)
x += 1
Yield Outside Function
Description: A yield
statement is found outside a function or method, which is invalid.
for i in range(5):
yield i # [yield-outside-function]
def range_yield():
for i in range(5):
yield i
Init Is Generator
Description: The special method __init__
is turned into a generator, which is incorrect. __init__
cannot contain yield
.
class Animal:
def __init__(self, names): # [init-is-generator]
yield from names
cat = Animal(["Tom", "Jerry"])
class Animal:
def __init__(self, names):
self.names = names
def get_names(self):
yield from self.names
cat = Animal(["Tom", "Jerry"])
for name in cat.get_names():
pass
Misplaced Format Function
Description: The format()
function is not called on a string object directly, causing an error.
print('Hello {}').format('World') # [misplaced-format-function]
print('Hello {}'.format('World'))
Nonlocal Without Binding
Description: Emitted when a nonlocal
variable does not have an attached name somewhere in the parent scopes
class Animal:
def get_sound(self):
nonlocal sounds # [nonlocal-without-binding]
class Animal:
sounds = ["bark", "meow"]
def get_sound(self):
nonlocal sounds
Lost Exception
Description: A break or a return statement is found inside the finally clause of a try
...finally
block, the exceptions raised in the try
clause will be silently swallowed instead of being re-raised
class InfinityError(ArithmeticError):
def __init__(self):
uper().__init__("You can't reach infinity!")
def calculate_division(dividend: float, divisor: float) -> float:
try:
return dividend / divisor
except ArithmeticError as e:
raise InfinityError() from e
finally:
return float('inf') # [lost-exception]
class InfinityError(ArithmeticError):
def __init__(self):
super().__init__("You can't reach infinity!")
def calculate_division(dividend: float, divisor: float) -> float:
try:
return dividend / divisor
except ArithmeticError as e:
raise InfinityError() from e
Assert On Tuple
Description: A call of assert
on a tuple
will always evaluate to true if the tuple
is not empty, and will always evaluate to false if it is
assert (42, None) # [assert-on-tuple]
val1, val2 = (42, None)
assert val1
assert val2
Assert On String Literal
Description: When an assert statement has a string literal as its first argument, which will cause the assert to always pass
def test_multiplication():
a = 4 * 5
assert "No MultiplicationError were raised" # [assert-on-string-literal]
def test_multiplication():
a = 4 * 5
assert a == 20
Self Assigning Variable
Description: Emitted when we detect that a variable is assigned to itself
temperature = 100
temperature = temperature # [self-assigning-variable]
temperature = 100
Comparison With Callable
Description: This message is emitted when pylint detects that a comparison with a callable was made, which might suggest that some parenthesis were omitted, resulting in potential unwanted behavior
def function_returning_a_number() -> int:
return 42
def is_forty_two(num: int = 21):
# 21 == <function function_returning_a_number at 0x7f343ff0a1f0>
return num == function_returning_a_number # [comparison-with-callable]
def function_returning_a_number() -> int:
return 42
def is_forty_two(num: int = 21):
# 21 == 42
return num == function_returning_a_number()
Dangerous Default Value
Description: A mutable value as list or dictionary is detected in a default value for an argument
def whats_in_the_bag(items=[]): # [dangerous-default-value]
items.append("book")
return items
def whats_in_the_bag(items=None):
if items is None:
items = []
items.append("book")
return items
Duplicate Key
Description: A dictionary expression binds the same key multiple times
exam_scores = {"English": 80, "Physics": 85, "English": 90} # [duplicate-key]
exam_scores = {"English": 80, "Physics": 85, "Chemistry": 90}
Useless Else On Loop
Description: Loops should only have an else clause if they can exit early with a break statement, otherwise the statements under else should be on the same scope as the loop itself
def find_positive_number(numbers):
for x in numbers:
if x > 0:
return x
else: # [useless-else-on-loop]
print("Did not find a positive number")
def find_positive_number(numbers):
for x in numbers:
if x > 0:
return x
print("Did not find a positive number")
Expression Not Assigned
Description: An expression that is not a function call is assigned to nothing. Probably something else was intended
float(3.14) == "3.14" # [expression-not-assigned]
is_equal: bool = float(3.14) == "3.14"
Confusing With Statement
Description: Emitted when a with
statement component returns multiple values and uses name binding with as
only for a part of those values, as in with ctx()
as a, b
. This can be misleading, since it's not clear if the context manager returns a tuple or if the node without a name binding is another context manager
with open("data.txt", "r") as f1, f2: # [confusing-with-statement]
pass
with open("data.txt", "r", encoding="utf8") as f1:
with open("log.txt", "w", encoding="utf8") as f2:
pass
Unnecessary Lambda
Description: The body of a lambda expression is a function call on the same argument list as the lambda itself. Such lambda expressions are in all but a few cases replaceable with the function being called in the body of the lambda
df.map(lambda x: x.upper()) # [unnecessary-lambda]
df.map(str.upper)
Assign To New Keyword
Description: async
and await
are reserved Python keywords in Python >= 3.6 versions
Redeclared Assigned Name
Description: Emitted when we detect that a variable was redeclared in the same assignment
ITEM, ITEM = ('apple', 'banana') # [redeclared-assigned-name]
ITEM1, ITEM2 = ('apple', 'banana')
Pointless Statement
Description: A statement doesn't have (or at least seems to) any effect
[4, 5, 6] # [pointless-statement]
NUMS = [4, 5, 6]
print(NUMS)
Pointless String Statement
Description: A string is used as a statement (which of course has no effect). This is a particular case of W0104 with its own message so you can easily disable it if you're using those strings as documentation, instead of comments
"""Function to calculate sum"""
"""Another pointless string statement""" # [pointless-string-statement]
"""Function to calculate sum"""
# A comment explaining the logic used in summing.
Unnecessary Pass
Description: A pass
statement that can be avoided is encountered
class ValidationError(Exception):
"""This exception is raised for invalid inputs."""
pass # [unnecessary-pass]
class ValidationError(Exception):
"""This exception is raised for invalid inputs."""
Unreachable
Description: There is some code behind a return
or raise
statement, which will never be accessed
def greet_user():
return True
print("Welcome!") # [unreachable]
def greet_user():
print("Welcome!")
return True
Eval Used
Description: You use the eval
function, to discourage its usage. Consider using ast.literal_eval
for safely evaluating strings containing Python expressions from untrusted sources
eval("{1: 'a', 2: 'b'}") # [eval-used]
from ast import literal_eval
literal_eval("{1: 'a', 2: 'b'}")
Exec Used
Description: You use the exec
statement (function for Python 3), to discourage its usage. That doesn't mean you cannot use it! It's dangerous to use this function for a user input
user_input = "John"
code = f"""eval(input('Execute this code, {user_input}: '))"""
result = exec(code) # [exec-used]
exec(result) # [exec-used]
def get_user_input(name):
return input(f"Enter code for execution, {name}: ")
user_input = "John"
allowed_globals = {"__builtins__": None}
allowed_locals = {"print": print}
# pylint: disable-next=exec-used
exec(get_user_input(user_input), allowed_globals, allowed_locals)
Using Constant Test
Description: Emitted when a conditional statement (If or ternary if) uses a constant value for its test
if False: # [using-constant-test]
print("Will never run.")
if True: # [using-constant-test]
print("Will always run.")
print("This statement is executed.")
Missing Parentheses For Call In Test
Description: Emitted when a conditional statement (If or ternary if) seems to wrongly call a function due to missing parentheses
import random
def is_sunny():
return random.choice([True, False])
if is_sunny: # [missing-parentheses-for-call-in-test]
print("It is sunny!")
import random
def is_sunny():
return random.choice([True, False])
if is_sunny():
print("It is sunny!")
Literal Comparison
Description: Comparing an object to a literal, which is usually what you do not want to do, since you can compare to a different literal than what was expected altogether
def is_a_banana(fruit):
return fruit is "banana" # [literal-comparison]
def is_a_banana(fruit):
return fruit == "banana"
Comparison With Itself
Description: Something is compared against itself
def is_a_banana(fruit):
a_banana = "banana"
return fruit == fruit # [comparison-with-itself]
def is_a_banana(fruit):
a_banana = "banana"
return a_banana == fruit
Non Ascii Name
Description: Name contains at least one non-ASCII unicode character
Invalid Name
Description: The name doesn't conform to naming rules associated to its type (constant, variable, class...)
class dog: # [invalid-name]
def Bark(self, NUMBER_OF_BARK): # [invalid-name, invalid-name]
print("Bark" * NUMBER_OF_BARK)
return NUMBER_OF_BARK
Dog = dog().Bark(10) # [invalid-name]
class Dog:
def bark(self, number_of_bark):
print("Bark" * number_of_bark)
return number_of_bark
DOG = Dog().bark(10)
Blacklisted Name
Description: The name is listed in the black list (unauthorized names)
Singleton Comparison
Description: An expression is compared to singleton values like True, False or None
lights_on = False
if lights_on == False: # [singleton-comparison]
print("Lights are off.")
lights_on = False
if not lights_on:
print("Lights are off.")
Misplaced Comparison Constant
Description: The constant is placed on the left side of a comparison. It is usually clearer in intent to place it in the right hand side of the comparison
Empty Docstring
Description: A module, function, class or method has an empty docstring (it would be too easy)
def bar(): # [empty-docstring]
""""""
def bar():
"""A simple function."""
Missing Class Docstring
Description: A class has no docstring. Even an empty class must have a docstring
class Car: # [missing-class-docstring]
def __init__(self, make, model):
self.make = make
self.model = model
class Car:
"""Class representing a car"""
def __init__(self, make, model):
self.make = make
self.model = model
Missing Function Docstring
Description: Missing function or method docstring used when a function or method has no docstring. Some special methods like __init__
, protected, private functions, setters and deleters do not require a docstring. It's a good practice to describe what a function does for other programmers
import os
def list_files(): # [missing-function-docstring]
print(os.listdir())
import os
def list_files():
"""Function listing files in the current directory."""
print(os.listdir())
Missing Module Docstring
Description: A module has no docstring. Empty modules do not require a docstring
import json # [missing-module-docstring]
def parse_json(data):
return json.loads(data)
"""Module providing a function for parsing JSON data."""
import json
def parse_json(data):
return json.loads(data)
Unidiomatic Typecheck
Description: Using type()
instead of isinstance()
for a type check. The idiomatic way to perform an explicit type check in Python is to use isinstance(x, y)
rather than type(x) == Y
, type(x) is Y
. Though there are unusual situations where these give different results
data = [1, 2, 3]
if type(data) is list: # [unidiomatic-typecheck]
pass
data = [1, 2, 3]
if isinstance(data, list):
pass
Access Member Before Definition
Description: An instance member is accessed before it's actually assigned
class Robot:
def __init__(self, power_level):
if self.power_level > 100: # [access-member-before-definition]
print("Power Overload!")
self.power_level = power_level
class Robot:
def __init__(self, power_level):
self.power_level = power_level
if self.power_level > 100:
print("Power Overload!")
Method Hidden
Description: A class defines a method which is hidden by an instance attribute from an ancestor class or set by some client code
class Plant:
def __init__(self, nutrients):
self.nutrients = nutrients
def nutrients(self): # [method-hidden]
pass
class Plant:
def __init__(self, nutrients):
self.nutrients = nutrients
def water_supply(self):
pass
Assigning Non Slot
Description: Assigning to an attribute not defined in the class slots
class Worker:
__slots__ = ("name")
def __init__(self, name, position):
self.name = name
self.position = position # [assigning-non-slot]
self.initialize()
class Worker:
__slots__ = ("name", "position")
def __init__(self, name, position):
self.name = name
self.position = position
self.initialize()
Duplicate Bases
Description: A class has duplicate bases
class Vehicle:
pass
class Bike(Vehicle, Vehicle): # [duplicate-bases]
pass
class Vehicle:
pass
class Bike(Vehicle):
pass
class Truck(Vehicle):
pass
Inconsistent Mro
Description: A class has an inconsistent method resolution order
class X:
pass
class Y(X):
pass
class Z(X, Y): # [inconsistent-mro]
pass
class X:
pass
class Y(X):
pass
class Z(Y): # or 'Y, X' but not 'X, Y'
pass
Inherit Non Class
Description: A class inherits from something which is not a class
class Animal(str): # [inherit-non-class]
pass
class Animal:
def __str__(self):
pass
Invalid Slots
Description: An invalid __slots__
is found in class. Only a string, an iterable or a sequence is permitted
class Vehicle: # [invalid-slots]
__slots__ = True
class Vehicle:
__slots__ = ("make", "model")
Invalid Slots Object
Description: An invalid (non-string) object occurs in __slots__
class Animal:
__slots__ = ("species", 5) # [invalid-slots-object]
class Animal:
__slots__ = ("species", "breed")
No Method Argument
Description: A method which should have the bound instance as first argument has no argument defined
class Dog:
def bark(): # [no-method-argument]
print("woof")
class Dog:
def bark(self):
print("woof")
No Self Argument
Description: A method has an attribute different the "self" as first argument. This is considered as an error since this is a so common convention that you shouldn't break it!
class Book:
def __init__(this, title): # [no-self-argument]
this.title = title
class Book:
def __init__(self, title):
self.title = title
Unexpected Special Method Signature
Description: Emitted when a special method was defined with an invalid number of parameters. If it has too few or too many, it might not work at all
class FileHandler:
def __enter__(self, filepath): # [unexpected-special-method-signature]
pass
def __exit__(self, exception): # [unexpected-special-method-signature]
pass
class FileHandler:
def __enter__(self):
pass
def __exit__(self, exc_type, exc_value, traceback):
pass
Class Variable Slots Conflict
Description: A value in slots conflicts with a class variable, property or method
class Robot:
# +1: [class-variable-slots-conflict]
__slots__ = ("model", "year", "shutdown")
model = None
def __init__(self, model, year):
self.model = model
self.year = year
@property
def year(self):
return self.year
def shutdown(self):
print("Shutting down...")
class Robot:
__slots__ = ("_year", "model")
def __init__(self, model, year):
self._year = year
self.model = model
@property
def year(self):
return self._year
def shutdown(self):
print("Shutting down...")
Invalid Bool Returned
Description: bool does not return bool Used when a bool method returns something which is not a bool
class LightSwitch:
"""__bool__ returns a string"""
def __bool__(self): # [invalid-bool-returned]
return "on"
Invalid Bytes Returned
Description: bytes does not return bytes Used when a bytes method returns something which is not bytes
class DataPacket:
"""__bytes__ returns <type 'list'>"""
def __bytes__(self): # [invalid-bytes-returned]
return [1, 2, 3]
class DataPacket:
"""__bytes__ returns <type 'bytes'>"""
def __bytes__(self):
return b"data"
Invalid Format Returned
Description: format does not return str Used when a format method returns something which is not a string
class Temperature:
"""__format__ returns <type 'float'>"""
def __format__(self, format_spec): # [invalid-format-returned]
return 98.6
class Temperature:
"""__format__ returns <type 'str'>"""
def __format__(self, format_spec):
return "98.6°F"
Invalid Getnewargs Returned
Description: getnewargs does not return a tuple. Used when a getnewargs method returns something which is not a tuple
class CustomGetNewArgs:
"""__getnewargs__ returns a string"""
def __getnewargs__(self): # [invalid-getnewargs-returned]
return 'abc'
class CustomGetNewArgs:
"""__getnewargs__ returns <type 'tuple'>"""
def __getnewargs__(self):
return ('abc', 'def')
Invalid Getnewargs Ex Returned
Description: getnewargs_ex does not return a tuple containing (tuple, dict). Used when a getnewargs_ex method returns something which is not of the form tuple(tuple, dict)
class CustomGetNewArgsEx:
"""__getnewargs_ex__ returns tuple with incorrect argument types"""
def __getnewargs_ex__(self): # [invalid-getnewargs-ex-returned]
return (list('x'), set())
class CustomGetNewArgsEx:
"""__getnewargs_ex__ returns <type 'tuple'>"""
def __getnewargs_ex__(self):
return ((1, 2), {'x': 'y'})
Invalid Hash Returned
Description: hash does not return an integer. Used when a hash method returns something which is not an integer
class CustomHash:
"""__hash__ returns a list"""
def __hash__(self): # [invalid-hash-returned]
return [1, 2, 3]
class CustomHash:
"""__hash__ returns an int"""
def __hash__(self):
return 123
Invalid Index Returned
Description: index does not return an integer. Used when an index method returns something which is not an integer
class CustomIndex:
"""__index__ returns a float"""
def __index__(self): # [invalid-index-returned]
return 3.14
class CustomIndex:
"""__index__ returns an int"""
def __index__(self):
return 42
Non Iterator Returned
Description: An __iter__
method returns something which is not an iterable (i.e., lacks a __next__
method)
import random
class RandomItems:
def __init__(self, items):
self.items = items
def __iter__(self): # [non-iterator-returned]
self.idx = 0
return self
LIST_ITEMS = ['Apple', 'Banana', 'Grape']
for item in RandomItems(LIST_ITEMS):
print(item)
import random
class RandomItems:
def __init__(self, items):
self.items = items
def __iter__(self):
self.idx = 0
return self
def __next__(self):
if self.idx == len(self.items):
raise StopIteration
self.idx += 1
return self.items[self.idx - 1]
LIST_ITEMS = ['Apple', 'Banana', 'Grape']
for item in RandomItems(LIST_ITEMS):
print(item)
Invalid Length Returned
Description: A __len__
method returns something that is not a non-negative integer
class CustomSet:
def __init__(self, elements):
self.elements = {'a', 'b', 'c'}
def __len__(self): # [invalid-length-returned]
return -1
class CustomSet:
def __init__(self, elements):
self.elements = {'a', 'b', 'c'}
def __len__(self):
return len(self.elements)
Invalid Length Hint Returned
Description: length_hint does not return a non-negative integer. Used when a length_hint method returns something that is not a non-negative integer
class CustomLengthHint:
"""__length_hint__ returns a negative int"""
def __length_hint__(self): # [invalid-length-hint-returned]
return -5
class CustomLengthHint:
"""__length_hint__ returns a non-negative int"""
def __length_hint__(self):
return 20
Invalid Repr Returned
Description: repr does not return str Used when a repr method returns something which is not a string
class CustomRepr:
"""__repr__ returns <type 'float'>"""
def __repr__(self): # [invalid-repr-returned]
return 3.14
class CustomRepr:
"""__repr__ returns <type 'str'>"""
def __repr__(self):
return "pi"
Invalid Str Returned
Description: str does not return str Used when a str method returns something which is not a string
class CustomStr:
"""__str__ returns list"""
def __str__(self): # [invalid-str-returned]
return [1, 2, 3]
class CustomStr:
"""__str__ returns <type 'str'>"""
def __str__(self):
return "apple pie"
Protected Access
Description: A protected member (i.e. class member with a name beginning with an underscore) is accessed outside the class or a descendant of the class where it's defined
class Bird:
def __fly(self):
pass
jane = Bird()
jane.__fly() # [protected-access]
class Bird:
def __fly(self):
pass
def soar(self):
return self.__fly()
jane = Bird()
jane.soar()
Attribute Defined Outside Init
Description: An instance attribute is defined outside the __init__
method
class Employee:
def promote(self):
self.is_promoted = True # [attribute-defined-outside-init]
class Employee:
def __init__(self):
self.is_promoted = False
def promote(self):
self.is_promoted = True
No Init
Description: A class has no __init__
method, neither its parent classes
class Car:
def drive(self):
print('driving')
class Car:
def __init__(self):
self.speed = 0
def drive(self):
print('driving')
Abstract Method
Description: An abstract method (i.e. raise NotImplementedError) is not overridden in concrete class
import abc
class Vehicle:
@abc.abstractmethod
def start_engine(self):
pass
class Bike(Vehicle): # [abstract-method]
pass
import abc
class Vehicle:
@abc.abstractmethod
def start_engine(self):
pass
class Bike(Vehicle):
def start_engine(self):
print("Vroom")
Invalid Overridden Method
Description: We detect that a method was overridden in a way that does not match its base class which could result in potential bugs at runtime
class Tree:
async def grow(self, soil):
soil.enrich(self)
class Oak(Tree):
def grow(self, soil): # [invalid-overridden-method]
soil.enrich(self)
class Tree:
async def grow(self, soil):
soil.enrich(self)
class Oak(Tree):
async def grow(self, soil):
soil.enrich(self)
Arguments Differ
Description: A method has a different number of arguments than in the implemented interface or in an overridden method
class Sandwich:
def make(self, bread, filling):
return f'{bread} and {filling}'
class BLTSandwich(Sandwich):
def make(self, bread, filling, bacon): # [arguments-differ]
return f'{bread}, {filling}, and {bacon}'
Signature Differs
Description: A method signature is different than in the implemented interface or in an overridden method
class Vehicle:
def start(self, fuel=100):
print(f"Started with {fuel} units of fuel!")
class Car(Vehicle):
def start(self, fuel): # [signature-differs]
super(Vehicle, self).start(fuel)
print("Engine is now running!")
class Vehicle:
def start(self, fuel=100):
print(f"Started with {fuel} units of fuel!")
class Car(Vehicle):
def start(self, fuel=100):
super(Vehicle, self).start(fuel)
print("Engine is now running!")
Bad Staticmethod Argument
Description: A static method has self
or a value specified in valid-classmethod-first-arg option or valid-metaclass-classmethod-first-arg option as the first argument
class Eagle:
@staticmethod
def fly(self): # [bad-staticmethod-argument]
pass
class Eagle:
@staticmethod
def fly(height):
pass
Useless Super Delegation
Description: Used whenever we can detect that an overridden method is useless, relying on super()
delegation to do the same thing as another method from the MRO
class Bird:
def fly(self):
print("Flying")
class Sparrow(Bird):
def fly(self): # [useless-super-delegation]
super().fly()
print("Flying again")
class Bird:
def fly(self):
print("Flying")
class Sparrow(Bird):
def fly(self):
print("Sparrow in flight")
Non Parent Init Called
Description: An __init__
method is called on a class which is not in the direct ancestors for the analysed class
class Insect:
def __init__(self):
self.has_wings = True
class Butterfly(Insect):
def __init__(self):
super().__init__()
self.is_beautiful = True
class MonarchButterfly(Butterfly):
def __init__(self):
Insect.__init__(self) # [non-parent-init-called]
self.is_migratory = True
class Insect:
def __init__(self):
self.has_wings = True
class Butterfly(Insect):
def __init__(self):
super().__init__()
self.is_beautiful = True
class MonarchButterfly(Butterfly):
def __init__(self):
super().__init__()
self.is_migratory = True
Super Init Not Called
Description: An ancestor class method has an __init__
method which is not called by a derived class
class Tree:
def __init__(self, species="Tree"):
self.species = species
print(f"Planting a {self.species}")
class Oak(Tree):
def __init__(self): # [super-init-not-called]
print("Growing an oak tree")
class Tree:
def __init__(self, species="Tree"):
self.species = species
print(f"Planting a {self.species}")
class Oak(Tree):
def __init__(self):
super().__init__("Oak")
Property With Parameters
Description: Detected that a property also has parameters, which are useless, given that properties cannot be called with additional arguments
class Drill:
@property
def depth(self, value): # [property-with-parameters]
pass
class Drill:
@property
def depth(self):
"""Property accessed with '.depth'."""
pass
def set_depth(value):
"""Function called with .set_depth(value)."""
pass
Useless Object Inheritance
Description: A class inherits from object
, which under Python 3 is implicit, hence can be safely removed from bases
class Plane(object): # [useless-object-inheritance]
...
class Plane: ...
No Classmethod Decorator
Description: A class method is defined without using the decorator syntax
class Flower:
COLORS = []
def __init__(self, color):
self.color = color
def set_colors(cls, *args):
"""classmethod to set flower colors"""
cls.COLORS = args
set_colors = classmethod(set_colors) # [no-classmethod-decorator]
class Flower:
COLORS = []
def __init__(self, color):
self.color = color
@classmethod
def set_colors(cls, *args):
"""classmethod to set flower colors"""
cls.COLORS = args
No Staticmethod Decorator
Description: A static method is defined without using the decorator syntax
class Mouse:
def run(self):
pass
run = staticmethod(run) # [no-staticmethod-decorator]
class Mouse:
@staticmethod
def run(self):
pass
No Self Use
Description: A method doesn't use its bound instance, and so could be written as a function
class Car:
def start_engine(self): # [no-self-use]
print('Engine started')
def start_engine():
print('Engine started')
Single String Used For Slots
Description: A class __slots__
is a simple string, rather than an iterable
class Tree: # [single-string-used-for-slots]
__slots__ = 'species'
def __init__(self, species):
self.species = species
class Tree:
__slots__ = ('species',)
def __init__(self, species):
self.species = species
Bad Classmethod Argument
Description: A class method has a first argument named differently than the value specified in valid-classmethod-first-arg option (default to "cls"), recommended to easily differentiate them from regular instance methods
class Vehicle:
@classmethod
def create(cls): # [bad-classmethod-argument]
return cls()
class Vehicle:
@classmethod
def create(cls):
return cls()
Bad Mcs Classmethod Argument
Description: A metaclass class method has a first argument named differently than the value specified in valid-metaclass-classmethod-first-arg option (default to mcs
), recommended to easily differentiate them from regular instance methods
class Factory(type):
@classmethod
def make_product(thing): # [bad-mcs-classmethod-argument]
pass
class Factory(type):
@classmethod
def make_product(mcs):
pass
Bad Mcs Method Argument
Description: A metaclass method has a first argument named differently than the value specified in valid-classmethod-first-arg option (default to cls
), recommended to easily differentiate them from regular instance methods
class Manager(type):
def assign_task(task): # [bad-mcs-method-argument]
pass
class Manager(type):
def assign_task(cls):
pass
Method Check Failed
Description: Pylint has been unable to check methods signature compatibility for an unexpected reason. Please report this kind if you don't make sense of it
class Device:
def start_device(self, config):
# [method-check-failed]
pass
class Device:
def start_device(self, config):
pass
Too Few Public Methods
Description: Class has too few public methods, so be sure it's really worth it
class Bird:
def __init__(self, species: str):
self.species = species
def fly(self):
print(f"The {self.species} is flying.")
import dataclasses
@dataclasses.dataclass
class Bird:
species: str
def fly(bird: Bird):
print(f"The {bird.species} is flying.")
Too Many Ancestors
Description: Class has too many parent classes, try to reduce this to get a simpler (and so easier to use) class
class Mammal: ...
class FlyingAnimal(Mammal): ...
class SwimmingAnimal(Mammal): ...
class EndangeredSpecies(Mammal): ...
# max of 7 by default, can be configured
class Bat( # [too-many-ancestors]
FlyingAnimal,
SwimmingAnimal,
EndangeredSpecies,
):
pass
class Animal:
can_fly: bool
can_swim: bool
is_endangered: bool
class Mammal(Animal):
can_fly = False
can_swim = False
is_endangered = False
class Bat(Mammal):
can_fly = True
is_endangered = True
Too Many Arguments
Description: A function or method takes too many arguments
def process_sensor_data( # [too-many-arguments]
accelerometer,
gyroscope,
magnetometer,
barometer,
proximity_sensor,
light_sensor,
current_time,
temperature_sensor,
):
pass
from dataclasses import dataclass
@dataclass
class Sensor:
accelerometer: float
gyroscope: float
magnetometer: float
barometer: float
@dataclass
class EnvironmentSensor:
proximity: float
light: float
temperature: float
def process_sensor_data(
motion: Sensor,
environment: EnvironmentSensor,
current_time,
):
pass
Too Many Boolean Expressions
Description: An if statement contains too many boolean expressions
def check_valid_triangle(a, b, c):
# +1: [too-many-boolean-expressions]
if (a > 0 and b > 0 and c > 0) and (a + b > c and a + c > b and b + c > a):
pass
def check_valid_triangle(a, b, c):
if all(x > 0 for x in [a, b, c]) and (a + b > c and a + c > b and b + c > a):
pass
Too Many Branches
Description: A function or method has too many branches, making it hard to follow
def map_day_to_word(day): # [too-many-branches]
if day == 1:
return 'Monday'
elif day == 2:
return 'Tuesday'
elif day == 3:
return 'Wednesday'
elif day == 4:
return 'Thursday'
elif day == 5:
return 'Friday'
elif day == 6:
return 'Saturday'
elif day == 7:
return 'Sunday'
else:
return None
def map_day_to_word(day):
return {
1: 'Monday',
2: 'Tuesday',
3: 'Wednesday',
4: 'Thursday',
5: 'Friday',
6: 'Saturday',
7: 'Sunday',
}.get(day)
Too Many Instance Attributes
Description: Class has too many instance attributes, try to reduce this to get a simpler (and so easier to use) class
class Car: # [too-many-instance-attributes]
def __init__(self):
self.make = 'Toyota'
self.model = 'Camry'
self.year = 2022
self.engine = 'V6'
self.transmission = 'Automatic'
self.color = 'Blue'
self.fuel_type = 'Gasoline'
self.seating_capacity = 5
self.mileage = 30000
import dataclasses
@dataclasses.dataclass
class Engine:
engine_type: str
fuel_type: str
@dataclasses.dataclass
class Car:
make: str
model: str
year: int
engine: Engine
seating_capacity: int
mileage: int
car = Car(
make='Toyota', model='Camry', year=2022, engine=Engine('V6', 'Gasoline'), seating_capacity=5, mileage=30000
)
Too Many Locals
Description: A function or method has too many local variables
def process_purchase_info(purchases): # [too-many-locals]
item_list = []
purchase_total = 0
discount = 0.1
shipping_cost = 5.99
tax = 0.07
final_total = 0
reward_points = 0
loyalty_bonus = 0.05
payment_method = 'Credit Card'
for item in purchases:
pass
from typing import NamedTuple
class PurchaseDetails(NamedTuple):
purchase_total: float
discount: float
shipping_cost: float
tax: float
def process_purchase_info(purchases):
purchase_details = PurchaseDetails(100, 0.1, 5.99, 0.07)
final_total = _calculate_final_total(purchase_details)
_handle_rewards_and_loyalty(purchases, final_total)
def _calculate_final_total(details: PurchaseDetails):
# logic to calculate final total
pass
def _handle_rewards_and_loyalty(purchases, final_total):
# handle reward logic
pass
Too Many Public Methods
Description: Class has too many public methods, try to reduce this to get a simpler (and so easier to use) class
class Game: # [too-many-public-methods]
def start(self): pass
def stop(self): pass
def reset(self): pass
def save(self): pass
def load(self): pass
def pause(self): pass
def resume(self): pass
def quit(self): pass
def restart(self): pass
def autosave(self): pass
class GameController:
def __init__(self):
self.state = 'Stopped'
def start(self):
self.state = 'Running'
def stop(self):
self.state = 'Stopped'
def pause(self):
self.state = 'Paused'
class SaveSystem:
def save(self): pass
def load(self): pass
class Game:
def __init__(self):
self.controller = GameController()
self.save_system = SaveSystem()
Too Many Return Statements
Description: Function contains too many return statements
def classify_grade(score):
if score >= 90:
return 'A'
elif score >= 80:
return 'B'
elif score >= 70:
return 'C'
elif score >= 60:
return 'D'
else:
return 'F'
def classify_grade(score):
return next((grade for score_threshold, grade in [(90, 'A'), (80, 'B'), (70, 'C'), (60, 'D')] if score >= score_threshold), 'F')
Too Many Statements
Description: A function or method contains too many statements
def initialize_game(): # [too-many-statements]
player = Player()
game = Game()
game.setup_board()
game.assign_pieces()
player.set_position(0, 0)
player.set_health(100)
player.set_inventory([])
enemy = Enemy()
enemy.set_position(10, 10)
enemy.set_health(100)
weapon = Weapon('sword')
armor = Armor('shield')
player.equip(weapon)
player.equip(armor)
game.start()
def initialize_game():
player = _initialize_player()
enemy = _initialize_enemy()
game = _initialize_game_environment(player, enemy)
game.start()
def _initialize_player():
player = Player()
player.set_position(0, 0)
player.set_health(100)
player.set_inventory([])
player.equip(Weapon('sword'))
player.equip(Armor('shield'))
return player
def _initialize_enemy():
enemy = Enemy()
enemy.set_position(10, 10)
enemy.set_health(100)
return enemy
def _initialize_game_environment(player, enemy):
game = Game()
game.setup_board()
game.assign_pieces()
return game
Bad Except Order
Description: Except clauses are not in the correct order (from the more specific to the more generic). If you don't fix the order, some exceptions may not be caught by the most specific handler.
try:
result = int(input('Enter a number: '))
except Exception:
print('An error occurred')
except ValueError: # [bad-except-order]
print('Invalid input')
try:
result = int(input('Enter a number: '))
except ValueError:
print('Invalid input')
except Exception:
print('An error occurred')
Catching Non Exception
Description: A class which doesn't inherit from Exception
is used as an exception in an except clause
class BarError:
pass
try:
'abc' + 123
except BarError: # [catching-non-exception]
pass
class BarError(Exception):
pass
try:
'abc' + 123
except BarError:
pass
Bad Exception Context
Description: Using the syntax raise ... from ...
, where the exception context is not an exception, nor None. This message belongs to the exceptions checker
try:
raise ValueError('Issue encountered') from 'Not an exception' # [bad-exception-context]
except ValueError:
pass
try:
raise ValueError('Issue encountered') from KeyError('Wrong key')
except ValueError:
pass
Notimplemented Raised
Description: NotImplemented
is raised instead of NotImplementedError
class Bird:
def fly(self):
raise NotImplemented # [notimplemented-raised]
class Bird:
def fly(self):
raise NotImplementedError
Raising Bad Type
Description: Something which is neither a class, an instance or a string is raised (i.e. a TypeError
will be raised)
class ImpossibleCalculationError(OverflowError):
def __init__(self):
super().__init__("Calculation exceeded the limits!")
def calculate_area(radius: float) -> float:
try:
return 3.14 * radius ** 2
except OverflowError as e:
raise None # [raising-bad-type]
class ImpossibleCalculationError(OverflowError):
def __init__(self):
super().__init__("Calculation exceeded the limits!")
def calculate_area(radius: float) -> float:
try:
return 3.14 * radius ** 2
except OverflowError as e:
raise ImpossibleCalculationError() from e
Raising Non Exception
Description: A new style class which doesn't inherit from BaseException
is raised
raise list # [raising-non-exception]
raise ValueError("Invalid operation!")
Misplaced Bare Raise
Description: A bare raise is not used inside an except clause. This generates an error, since there are no active exceptions to be reraised. An exception to this rule is represented by a bare raise inside a finally clause, which might work, as long as an exception is raised inside the try block, but it is nevertheless a code smell that must not be relied upon
def validate_input(x):
if x == '':
raise # [misplaced-bare-raise]
def validate_input(x):
if x == '':
raise ValueError(f"Input cannot be empty: {x}")
Duplicate Except
Description: An except catches a type that was already caught by a previous handler
try:
1 + 'a'
except TypeError:
pass
except TypeError: # [duplicate-except]
pass
try:
1 + 'a'
except TypeError:
pass
Broad Except
Description: An except catches a too general exception, possibly burying unrelated errors
try:
1 / 0
except Exception: # [broad-except]
pass
try:
1 / 0
except ZeroDivisionError: # handle specific error
pass
Raise Missing From
Description: Consider explicitly re-raising using the 'from' keyword. Python 3's exception chaining ensures the traceback shows both the current exception and the original one. Not using 'raise from' leads to an inaccurate traceback that might obscure the true source of the error.
try:
open('non_existent_file.txt')
except FileNotFoundError as e:
raise IOError("File cannot be opened") # [raise-missing-from]
try:
open('non_existent_file.txt')
except FileNotFoundError as e:
raise IOError("File cannot be opened") from e
Raising Format Tuple
Description: Passing multiple arguments to an exception constructor, with one of them being a tuple for string formatting, leads to incorrect behavior. Correct the formatting by using '%' operator inside the string.
raise TypeError("Unsupported operand type(s) %s %s", ("int", "str")) # [raising-format-tuple]
raise TypeError("Unsupported operand type(s) %s %s" % ("int", "str"))
Binary Op Exception
Description: Avoid using except A or B
for catching exceptions. To catch multiple exceptions, use the tuple syntax: except (A, B)
.
try:
int('abc')
except ValueError or TypeError: # [binary-op-exception]
pass
try:
int('abc')
except (ValueError, TypeError):
pass
Wrong Exception Operation
Description: Binary operations between exceptions like ValueError + TypeError
are invalid. Use a tuple (ValueError, TypeError)
to catch multiple exceptions.
try:
float('abc')
except ValueError + TypeError: # [wrong-exception-operation]
pass
try:
float('abc')
except (ValueError, TypeError):
pass
Bare Except
Description: Avoid using bare except
clauses, as they catch all exceptions and obscure the specific errors you're trying to handle.
try:
open('non_existent_file.txt')
except: # [bare-except]
file = None
try:
open('non_existent_file.txt')
except FileNotFoundError:
file = None
Try Except Raise
Description: Raising the same exception immediately after catching it (raise
by itself) is redundant. Either raise a more detailed exception or remove the try-except block entirely.
try:
int('invalid')
except ValueError as e: # [try-except-raise]
raise
# Raise a more descriptive exception:
try:
int('invalid')
except ValueError as e:
raise TypeError("Input must be a valid integer") from e
Bad Indentation
Description: Improper indentation in Python is problematic, as it can lead to runtime errors. Ensure consistent indentation.
if input():
print('yes') # [bad-indentation]
if input():
print('yes')
Missing Final Newline
Description: The last line in a file is missing a newline
open("file.txt") # CRLF
close("file.txt") # End-of-file (EOF)
# [missing-final-newline]
open("file.txt")
close("file.txt")
# End-of-file (EOF)
Line Too Long
Description: A line is longer than a given number of characters
# +1: [line-too-long]
PLANETS = ["mercury", "venus", "earth", "mars", "jupiter", "saturn", "uranus", "neptune"]
PLANETS = [
"mercury",
"venus",
"earth",
"mars",
"jupiter",
"saturn",
"uranus",
"neptune",
]
Mixed Line Endings
Description: There are mixed (LF and CRLF) newline signs in a file
read_file("file.txt") # CRLF
write_file("file.txt") # LF
# [mixed-line-endings]
read_file("file.txt") # CRLF
write_file("file.txt") # CRLF
Multiple Statements
Description: More than one statement is found on the same line
animals = ["cat", "dog", "parrot"]
if "cat" in animals: pass # [multiple-statements]
else:
print("no cats!")
animals = ["cat", "dog", "parrot"]
if "cat" in animals:
pass
else:
print("no cats!")
Too Many Lines
Description: A module has too many lines, reducing its readability
def calculate_sum(lst): # [too-many-lines]
sum = 0
for num in lst:
sum += num
return sum
def main():
print(calculate_sum([1, 2, 3]))
print(calculate_sum([10, 20, 30]))
Trailing Newlines
Description: There are trailing blank lines in a file
print("banana")
# The file ends with 2 empty lines # +1: [trailing-newlines]
print("banana")
Trailing Whitespace
Description: There is whitespace between the end of a line and the newline
print("Goodbye")
# [trailing-whitespace] #
print("Goodbye")
Unexpected Line Ending Format
Description: There is a different newline than expected
print("I'm eating breakfast!") # CRLF
print("I'm eating lunch!") # CRLF
# [unexpected-line-ending-format]
print("I'm eating breakfast!") # LF
print("I'm eating lunch!") # LF
Superfluous Parens
Description: A single item in parentheses follows an if
, for
, or other keyword
name = input()
age = input()
if (name == age): # [superfluous-parens]
pass
name = input()
age = input()
if name == age:
pass
Relative Beyond Top Level
Description: A relative import tries to access too many levels in the current package
from ................galaxy import Star # [relative-beyond-top-level]
from universe.galaxy import Star
Import Self
Description: A module is importing itself
from my_module import function_name # [import-self]
def function_name():
pass
Preferred Module
Description: A module imported has a preferred replacement module. They are configured in .pylintrc
or through CLI arguments
import xmlrpc.client # [preferred-module]
import http.client
Reimported
Description: A module is reimported multiple times
import json
import json # [reimported]
import json
Deprecated Module
Description: Used a module marked as deprecated is imported
import imp # [deprecated-module]
import importlib
Wildcard Import
Description: This is a bad practice because it clutters namespace with unneeded modules, packages, variables, etc. Moreover, it takes time to load them too
from os import * # [wildcard-import]
import os
from os.path import join, dirname
Misplaced Future
Description: Python 2.5 and greater require future import to be the first non-docstring statement in the module. This message belongs to the imports checker
import time
from __future__ import division # [misplaced-future]
from __future__ import division
import time
Cyclic Import
Description: A cyclic import between two or more modules is detected
def add_numbers():
from .helper import add_two_numbers
return add_two_numbers() + 1
def add_two_numbers():
return 2
def add_three_numbers():
return add_two_numbers() + 1
Wrong Import Order
Description: PEP8 import order is not respected (standard imports first, then third-party libraries, then local imports)
import os
from . import tools
import sys # [wrong-import-order]
import os
import sys
from . import tools
Wrong Import Position
Description: Code and imports are mixed
import os
user_dir = os.getenv('USER')
import sys # [wrong-import-position]
import os
import sys
user_dir = os.getenv('USER')
Useless Import Alias
Description: An import alias is the same as the original package (e.g., using import numpy as numpy
instead of import numpy as np
)
import numpy as numpy # [useless-import-alias]
import numpy as np
Import Outside Toplevel
Description: An import statement is used anywhere other than the module top-level. Move this import to the top of the file
def get_version():
import platform
return platform.python_version()
import platform
def get_version():
return platform.python_version()
Ungrouped Imports
Description: Imports are not grouped by packages
import os
import sys
import json
import logging.config # [ungrouped-imports]
import os
import sys
import logging.config
import json
Multiple Imports
Description: Import statement importing multiple modules is detected
import os, sys # [multiple-imports]
import os
import sys
Logging Format Truncated
Description: A logging statement format string terminates before the end of a conversion specifier
import logging
logging.warning("Incorrect version: %", sys.version) # [logging-format-truncated]
import logging
logging.warning("Python version: %s", sys.version)
Logging Too Few Args
Description: A logging format string is given too few arguments
import logging
try:
function()
except Exception as e:
logging.error("%s error: %s", e) # [logging-too-few-args]
import logging
try:
function()
except Exception as e:
logging.error("%s error: %s", type(e), e)
Logging Too Many Args
Description: A logging format string is given too many arguments
import logging
try:
function()
except Exception as e:
logging.error("Error: %s", type(e), e) # [logging-too-many-args]
import logging
try:
function()
except Exception as e:
logging.error("%s error: %s", type(e), e)
Logging Unsupported Format
Description: An unsupported format character is used in a logging statement format string
import logging
logging.info("%s %y", "Hello", "World") # [logging-unsupported-format]
import logging
logging.info("%s %s", "Hello", "World")
Logging Format Interpolation
Description: A logging statement uses a call form of logging.<logging method>(format_string.format(...))
. It’s better to pass the parameters directly to the logging function instead
logging.error("Version: {}".format(sys.version)) # [logging-format-interpolation]
logging.error("Version: %s", sys.version)
Logging Fstring Interpolation
Description: A logging statement has a call form of logging.<logging method>(f"...")
. Use another type of string formatting instead. You can use different formatting but leave interpolation to the logging function by passing the parameters as arguments. If logging-format-interpolation is disabled then you can use str.format. If logging-not-lazy is disabled then you can use certain formatting as normal
import logging
import os
logging.warning(f"Current directory: {os.getcwd()}") # [logging-fstring-interpolation]
import logging
import os
logging.warning("Current directory: %s", os.getcwd())
Logging Not Lazy
Description: A logging statement has a call form of logging.<logging method>(format_string % (format_args...))
. Use another type of string formatting instead. You can use different formatting but leave interpolation to the logging function by passing the parameters as arguments. If logging-fstring-interpolation is disabled then you can use fstring formatting. If logging-format-interpolation is disabled then you can use str.format
import logging
try:
connect_to_database()
except DatabaseError as e:
logging.error("Database connection failed: %s" % e) # [logging-not-lazy]
raise
import logging
try:
connect_to_database()
except DatabaseError as e:
logging.error("Database connection failed: %s", e)
raise
Unpacking In Except
Description: Implicit unpacking of exceptions is not supported in Python 3 Python3 will not allow implicit unpacking of exceptions in except clauses. See https://www.python.org/dev/peps/pep-3110/
try:
raise ValueError("An error occurred")
except ValueError, e: # Implicit unpacking (invalid in Python 3)
print(e)
try:
raise ValueError("An error occurred")
except ValueError as e: # Explicit unpacking (valid in Python 3)
print(e)
Import Star Module Level
Description: Import * only allowed at module level Used when the import star syntax is used somewhere else than the module level. This message can't be emitted when using Python >= 3.0
def my_function():
from math import * # Importing * inside a function (invalid)
from math import * # Importing * at the module level (valid)
Non Ascii Bytes Literal
Description: Non-ascii bytes literals not supported in 3.x Used when non-ascii bytes literals are found in a program. They are no longer supported in Python 3. This message can't be emitted when using Python >= 3.0
my_bytes = b"non-ASCII character: ü" # Non-ASCII byte literal (invalid in Python 3)
my_string = "non-ASCII character: ü" # Use string literals for non-ASCII text
Parameter Unpacking
Description: Parameter unpacking specified Used when parameter unpacking is specified for a function(Python 3 doesn't allow it)
exec 'print("Hello, world!")' # Python 2 style exec statement (invalid in Python 3)
exec('print("Hello, world!")') # Use exec() function in Python 3
Long Suffix
Description: Use of long suffix Used when "l" or "L" is used to mark a long integer. This will not work in Python 3, since int and long types have merged. This message can't be emitted when using Python >= 3.0
my_number = long(100) # long is not available in Python 3 (invalid)
my_number = int(100) # Use int in Python 3
Old Octal Literal
Description: Use of old octal literal Used when encountering the old octal syntax, removed in Python 3. To use the new syntax, prepend 0o on the number. This message can't be emitted when using Python >= 3.0
class MyClass: # Old-style class definition (Python 2)
pass
class MyClass(object): # New-style class definition (Python 3)
pass
Old Ne Operator
Description: Use of the <>
operator Used when the deprecated <>
operator is used instead of "!=". This is removed in Python 3. This message can't be emitted when using Python >= 3.0
if isinstance(my_var, basestring): # basestring is not available in Python 3
print("It's a string")
if isinstance(my_var, str): # Use str instead in Python 3
print("It's a string")
Backtick
Description: Use of the operator Used when the deprecated "
" (backtick) operator is used instead of the str() function
if x <> y: # Python 2 style not-equal operator (invalid in Python 3)
print("x is not equal to y")
if x != y: # Use != operator in Python 3
print("x is not equal to y")
Old Raise Syntax
Description: Use raise ErrorClass(args) instead of raise ErrorClass, args. Used when the alternate raise syntax 'raise foo, bar' is used instead of 'raise foo(bar)'
print "my_var" # Backticks used for repr (invalid in Python 3)
print(repr(my_var)) # Use repr() function instead
Print Statement
Description: print statement used Used when a print statement is used (print is a function in Python 3)
print "Hello, world!" # Python 2 style print statement (invalid in Python 3)
print("Hello, world!") # Python 3 print function
Deprecated Types Field
Description: Accessing a deprecated fields on the types module Used when accessing a field on types that has been removed in Python 3
import types
my_type = types.SomeDeprecatedField # Deprecated field (invalid in Python 3)
# Use an alternative valid type or method in Python 3
Deprecated Itertools Function
Description: Accessing a deprecated function on the itertools module Used when accessing a function on itertools that has been removed in Python 3
import string # Let's assume "string" module is deprecated (hypothetical)
import string_new # Import updated or alternative module
Deprecated String Function
Description: Accessing a deprecated function on the string module Used when accessing a string function that has been deprecated in Python 3
import string
print(string.uppercase) # Deprecated in Python 3
import string
print(string.ascii_uppercase) # Use ascii_uppercase in Python 3
Deprecated Operator Function
Description: Accessing a removed attribute on the operator module Used when accessing a field on operator module that has been removed in Python 3
my_list = [1, 2, 3]
my_list.sort(lambda x: -x) # Deprecated way of sorting
my_list = sorted(my_list, reverse=True) # Use sorted() with reverse
Deprecated Sys Function
Description: Accessing a removed attribute on the sys module Used when accessing a field on sys module that has been removed in Python 3
my_iter = iter([1, 2, 3])
value = my_iter.next() # Use of ".next()" method (invalid in Python 3)
value = next(my_iter) # Use "next()" function instead
Deprecated Urllib Function
Description: Accessing a removed attribute on the urllib module Used when accessing a field on urllib module that has been removed or moved in Python 3
try:
risky_code()
except Exception, e: # Old-style exception handling (invalid in Python 3)
print(e)
try:
risky_code()
except Exception as e: # Use Python 3 style exception handling
print(e)
Xreadlines Attribute
Description: Accessing a removed xreadlines attribute Used when accessing the xreadlines() function on a file stream, removed in Python 3
import MyModule # Deprecated import
from mymodule import new_functionality # Use updated import
Metaclass Assignment
Description: Assigning to a class's metaclass attribute Used when a metaclass is specified by assigning to metaclass (Python 3 specifies the metaclass as a class statement argument)
my_list = [1, 2, 3]
print(my_list.has_key(2)) # Example: has_key() method is deprecated (invalid in Python 3)
print(2 in my_list) # Use "in" operator instead of has_key()
Next Method Called
Description: Called a next() method on an object Used when an object's next() method is called (Python 3 uses the next() built-in function)
Dict Iter Method
Description: Calling a dict.iter*() method Used for calls to dict.iterkeys(), itervalues() or iteritems() (Python 3 lacks these methods)
my_dict = {"a": 1, "b": 2}
for key in my_dict.iterkeys(): # iterkeys() is removed in Python 3
print(key)
my_dict = {"a": 1, "b": 2}
for key in my_dict.keys(): # Use keys() instead
print(key)
Dict View Method
Description: Calling a dict.view*() method Used for calls to dict.viewkeys(), viewvalues() or viewitems() (Python 3 lacks these methods)
my_dict = {"a": 1, "b": 2}
print(my_dict.viewkeys()) # viewkeys() is removed in Python 3
my_dict = {"a": 1, "b": 2}
print(my_dict.keys()) # Use keys() instead
Exception Message Attribute
Description: Exception.message removed in Python 3 Used when the message attribute is accessed on an Exception. Use str(exception) instead
try:
raise Exception("An error occurred")
except Exception as e:
print(e.message) # Exception.message is removed in Python 3
try:
raise Exception("An error occurred")
except Exception as e:
print(str(e)) # Use str() to access the error message
Eq Without Hash
Description: Implementing eq without also implementing hash Used when a class implements eq but not hash. In Python 2, objects get object.hash as the default implementation, in Python 3 objects get None as their default hash implementation if they also implement eq
class Fruit: # [eq-without-hash]
def __init__(self) -> None:
self.name = "apple"
def __eq__(self, other: object) -> bool:
return isinstance(other, Fruit) and other.name == self.name
class Fruit:
def __init__(self) -> None:
self.name = "apple"
def __eq__(self, other: object) -> bool:
return isinstance(other, Fruit) and other.name == self.name
def __hash__(self) -> int:
return hash(self.name)
Indexing Exception
Description: Indexing exceptions will not work on Python 3 Indexing exceptions will not work on Python 3. Use exception.args[index] instead
Bad Python3 Import
Description: Module moved in Python 3 Used when importing a module that no longer exists in Python 3
Raising String
Description: Raising a string exception Used when a string exception is raised. This will not work on Python 3
Standarderror Builtin
Description: StandardError built-in referenced Used when the StandardError built-in function is referenced (missing from Python 3)
try:
raise StandardError("An error occurred") # StandardError is removed in Python 3
except StandardError as e:
print(e)
try:
raise Exception("An error occurred") # Use Exception instead
except Exception as e:
print(e)
Comprehension Escape
Description: Using a variable that was bound inside a comprehension Emitted when using a variable, that was bound in a comprehension handler, outside of the comprehension itself. On Python 3 these variables will be deleted outside of the comprehension
Exception Escape
Description: Using an exception object that was bound by an except handler Emitted when using an exception, that was bound in an except handler, outside of the except handler. On Python 3 these exceptions will be deleted once they get out of the except handler
Deprecated Str Translate Call
Description: Using str.translate with deprecated deletechars parameters Used when using the deprecated deletechars parameters from str.translate. Use re.sub to remove the desired characters
Using Cmp Argument
Description: Using the cmp argument for list.sort / sorted Using the cmp argument for list.sort or the sorted builtin should be avoided, since it was removed in Python 3. Using either key or functools.cmp_to_key should be preferred
my_list = [3, 1, 2]
my_list.sort(cmp=lambda x, y: x - y) # cmp argument removed in Python 3
from functools import cmp_to_key
my_list = [3, 1, 2]
my_list.sort(key=cmp_to_key(lambda x, y: x - y)) # Use cmp_to_key in Python 3
Cmp Method
Description: cmp method defined Used when a cmp method is defined (method is not used by Python 3)
class MyClass:
def __cmp__(self, other): # __cmp__ is removed in Python 3
return cmp(self.value, other.value)
class MyClass:
def __lt__(self, other): # Define rich comparison methods instead
return self.value < other.value
Coerce Method
Description: coerce method defined Used when a coerce method is defined (method is not used by Python 3)
class MyClass:
def __coerce__(self, other):
return self.value, other.value # __coerce__ is not used in Python 3
class MyClass:
def __add__(self, other):
return self.value + other.value # Use other rich comparison methods
Delslice Method
Description: delslice method defined Used when a delslice method is defined (method is not used by Python 3)
class MyList(list):
def __delslice__(self, i, j): # __delslice__ is not used in Python 3
del self[i:j]
class MyList(list):
def __delitem__(self, key): # Use __delitem__ instead
del self[key]
Div Method
Description: div method defined Used when a div method is defined. Using truediv and settingdiv = truediv should be preferred.(method is not used by Python 3)
class MyClass:
def __div__(self, other): # __div__ is removed in Python 3
return self.value / other.value
class MyClass:
def __truediv__(self, other): # Use __truediv__ instead
return self.value / other.value
Getslice Method
Description: getslice method defined Used when a getslice method is defined (method is not used by Python 3)
class MyList(list):
def __getslice__(self, i, j): # __getslice__ is not used in Python 3
return self[i:j]
class MyList(list):
def __getitem__(self, key): # Use __getitem__ instead
return self[key]
Hex Method
Description: hex method defined Used when a hex method is defined (method is not used by Python 3)
class MyClass:
def __hex__(self): # __hex__ is not used in Python 3
return hex(self.value)
class MyClass:
def __repr__(self): # Use __repr__ or custom method instead
return f'{self.value:#x}'
Idiv Method
Description: idiv method defined Used when an idiv method is defined. Using itruediv and settingidiv = itruediv should be preferred.(method is not used by Python 3)
class MyClass:
def __idiv__(self, other): # __idiv__ is removed in Python 3
self.value /= other.value
class MyClass:
def __itruediv__(self, other): # Use __itruediv__ instead
self.value /= other.value
Nonzero Method
Description: nonzero method defined Used when a nonzero method is defined (method is not used by Python 3)
class MyClass:
def __nonzero__(self): # __nonzero__ is removed in Python 3
return bool(self.value)
class MyClass:
def __bool__(self): # Use __bool__ instead
return bool(self.value)
Oct Method
Description: oct method defined Used when an oct method is defined (method is not used by Python 3)
class MyClass:
def __oct__(self): # __oct__ is not used in Python 3
return oct(self.value)
class MyClass:
def __repr__(self): # Use __repr__ or custom method instead
return f'{self.value:#o}'
Rdiv Method
Description: rdiv method defined Used when a rdiv method is defined. Using rtruediv and settingrdiv = rtruediv should be preferred.(method is not used by Python 3)
class MyClass:
def __rdiv__(self, other): # __rdiv__ is removed in Python 3
return other.value / self.value
class MyClass:
def __rtruediv__(self, other): # Use __rtruediv__ instead
return other.value / self.value
Setslice Method
Description: setslice method defined Used when a setslice method is defined (method is not used by Python 3)
class MyList(list):
def __setslice__(self, i, j, sequence): # __setslice__ is removed in Python 3
self[i:j] = sequence
class MyList(list):
def __setitem__(self, key, value): # Use __setitem__ instead
self[key] = value
Apply Builtin
Description: apply built-in referenced Used when the apply built-in function is referenced (missing from Python 3)
apply(func, (arg1, arg2)) # apply is removed in Python 3
func(arg1, arg2) # Direct function call is valid in Python 3
Basestring Builtin
Description: basestring built-in referenced Used when the basestring built-in function is referenced (missing from Python 3)
if isinstance(my_var, basestring): # basestring is removed in Python 3
print("It's a string")
if isinstance(my_var, str): # Use str in Python 3
print("It's a string")
Buffer Builtin
Description: buffer built-in referenced Used when the buffer built-in function is referenced (missing from Python 3)
my_buf = buffer(my_var) # buffer is removed in Python 3
# buffer functionality is handled differently in Python 3, use memoryview or bytearray
my_buf = memoryview(my_var)
Cmp Builtin
Description: cmp built-in referenced Used when the cmp built-in function is referenced (missing from Python 3)
result = cmp(a, b) # cmp is removed in Python 3
result = (a > b) - (a < b) # Use comparison operators in Python 3
Coerce Builtin
Description: coerce built-in referenced Used when the coerce built-in function is referenced (missing from Python 3)
result = coerce(a, b) # coerce is removed in Python 3
Dict Items Not Iterating
Description: dict.items referenced when not iterating Used when dict.items is referenced in a non-iterating context (returns an iterator in Python 3)
my_items = my_dict.items() # This returns a view object in Python 3
my_items = list(my_dict.items()) # Convert to list for Python 3 behavior
Dict Keys Not Iterating
Description: dict.keys referenced when not iterating Used when dict.keys is referenced in a non-iterating context (returns an iterator in Python 3)
my_keys = my_dict.keys() # This returns a view object in Python 3
my_keys = list(my_dict.keys()) # Convert to list for Python 3 behavior
Dict Values Not Iterating
Description: dict.values referenced when not iterating Used when dict.values is referenced in a non-iterating context (returns an iterator in Python 3)
my_values = my_dict.values() # This returns a view object in Python 3
my_values = list(my_dict.values()) # Convert to list for Python 3 behavior
Old Division
Description: division w/o future statement Used for non-floor division w/o a float literal or from future import division (Python 3 returns a float for int division unconditionally)
result = 5 / 2 # Returns integer in Python 2 (invalid in Python 3)
Execfile Builtin
Description: execfile built-in referenced Used when the execfile built-in function is referenced (missing from Python 3)
execfile('script.py') # execfile is removed in Python 3
with open('script.py') as file:
exec(file.read()) # Use open and exec in Python 3
File Builtin
Description: file built-in referenced Used when the file built-in function is referenced (missing from Python 3)
file_object = file('example.txt', 'r') # file is removed in Python 3
file_object = open('example.txt', 'r') # Use open in Python 3
Filter Builtin Not Iterating
Description: filter built-in referenced when not iterating Used when the filter built-in is referenced in a non-iterating context (returns an iterator in Python 3)
result = filter(lambda x: x > 0, my_list) # Returns iterator in Python 3, no list (invalid)
result = list(filter(lambda x: x > 0, my_list)) # Convert to list for Python 3
No Absolute Import
Description: import missing from __future__ import absolute_import
Used when an import is not accompanied by from future import absolute_import (default behaviour in Python 3)
import mymodule # Without future import in Python 2
from __future__ import absolute_import # Use absolute imports in Python 3
import mymodule
Input Builtin
Description: input built-in referenced Used when the input built-in is referenced (backwards-incompatible semantics in Python 3)
user_input = input("Enter something: ") # Works differently in Python 2
Intern Builtin
Description: intern built-in referenced Used when the intern built-in is referenced (Moved to sys.intern in Python 3)
my_interned = intern("example") # intern is moved in Python 3
import sys
my_interned = sys.intern("example") # Use sys.intern() in Python 3
Long Builtin
Description: long built-in referenced Used when the long built-in function is referenced (missing from Python 3)
my_num = long(123456789) # long is removed in Python 3
my_num = int(123456789) # Use int in Python 3
Map Builtin Not Iterating
Description: map built-in referenced when not iterating Used when the map built-in is referenced in a non-iterating context (returns an iterator in Python 3)
result = map(lambda x: x * 2, my_list) # Returns an iterator in Python 3
result = list(map(lambda x: x * 2, my_list)) # Convert to list in Python 3
Next Method Defined
Description: next method defined Used when a next method is defined that would be an iterator in Python 2 but is treated as a normal function in Python 3
class MyClass:
def next(self): # Treated differently in Python 3
return 42
class MyClass:
def __next__(self): # Use __next__ in Python 3
return 42
Invalid Str Codec
Description: non-text encoding used in str.decode Used when using str.encode or str.decode with a non-text encoding. Use codecs module to handle arbitrary codecs
encoded_str = "hello".encode("hex") # Non-text codec is removed in Python 3
import codecs
encoded_str = codecs.encode("hello", "hex") # Use codecs module in Python 3
Range Builtin Not Iterating
Description: range built-in referenced when not iterating Used when the range built-in is referenced in a non-iterating context (returns a range in Python 3)
result = range(10) # Returns an iterator in Python 3
result = list(range(10)) # Convert to list in Python 3
Raw_input Builtin
Description: raw_input built-in referenced Used when the raw_input built-in function is referenced (missing from Python 3)
user_input = raw_input("Enter something: ") # raw_input is removed in Python 3
user_input = input("Enter something: ") # Use input in Python 3
Reduce Builtin
Description: reduce built-in referenced Used when the reduce built-in function is referenced (missing from Python 3)
result = reduce(lambda x, y: x + y, my_list) # reduce is removed from built-ins in Python 3
from functools import reduce # Import from functools in Python 3
result = reduce(lambda x, y: x + y, my_list)
Reload Builtin
Description: reload built-in referenced Used when the reload built-in function is referenced (missing from Python 3). You can use instead imp.reload or importlib.reload
reload(mymodule) # reload is removed in Python 3
from importlib import reload # Use importlib.reload in Python 3
reload(mymodule)
Round Builtin
Description: round built-in referenced Used when the round built-in is referenced (backwards-incompatible semantics in Python 3)
rounded = round(5.675, 2) # Different behavior in Python 3 for rounding
Sys Max Int
Description: sys.maxint removed in Python 3 Used when accessing sys.maxint. Use sys.maxsize instead
import sys
print(sys.maxint) # maxint is removed in Python 3
import sys
print(sys.maxsize) # Use sys.maxsize in Python 3
Unichr Builtin
Description: unichr built-in referenced Used when the unichr built-in is referenced (Use chr in Python 3)
char = unichr(97) # unichr is removed in Python 3
char = chr(97) # Use chr in Python 3
Unicode Builtin
Description: unicode built-in referenced Used when the unicode built-in function is referenced (missing from Python 3)
my_str = unicode("example") # unicode is removed in Python 3
my_str = str("example") # Use str in Python 3
Xrange Builtin
Description: xrange built-in referenced Used when the xrange built-in function is referenced (missing from Python 3)
for i in xrange(10): # xrange is removed in Python 3
print(i)
for i in range(10): # Use range in Python 3
print(i)
Zip Builtin Not Iterating
Description: zip built-in referenced when not iterating Used when the zip built-in is referenced in a non-iterating context (returns an iterator in Python 3)
result = zip(list1, list2) # Returns an iterator in Python 3
result = list(zip(list1, list2)) # Convert to list in Python 3
Simplifiable Condition
Description: A boolean condition is able to be simplified
def has_bananas(bananas) -> bool:
return bool(bananas or False) # [simplifiable-condition]
def has_bananas(bananas) -> bool:
return bool(bananas)
Condition Evals To Constant
Description: A boolean condition can be simplified to a constant value
def is_a_vegetable(vegetable):
return bool(vegetable in {"carrot", "broccoli"} or True) # [condition-evals-to-constant]
def is_a_vegetable(vegetable):
return vegetable in {"carrot", "broccoli"}
Simplify Boolean Expression
Description: Emitted when redundant pre-python 2.5 ternary syntax is used
def has_grapes(grapes, oranges=None) -> bool:
return oranges and False or grapes # [simplify-boolean-expression]
def has_grapes(grapes, oranges=None) -> bool:
return grapes
Consider Using In
Description: To check if a variable is equal to one of many values, combine the values into a tuple and check if the variable is contained in
it instead of checking for equality against each of the values. This is faster and less verbose
def fruit_is_yellow(fruit):
# +1: [consider-using-in]
return fruit == "banana" or fruit == "lemon" or fruit == "pineapple"
def fruit_is_yellow(fruit):
return fruit in {"banana", "lemon", "pineapple"}
Consider Merging Isinstance
Description: Multiple consecutive isinstance
calls can be merged into one
from typing import Any
def is_string(value: Any) -> bool:
# +1: [consider-merging-isinstance]
return isinstance(value, str) or isinstance(value, bytes)
from typing import Any
def is_string(value: Any) -> bool:
return isinstance(value, (str, bytes))
Super With Arguments
Description: Consider using Python 3 style super() without arguments. Emitted when calling the super() builtin with the current class and instance. On Python 3 these arguments are the default and they can be omitted
class Fruit:
pass
class Banana(Fruit):
def __init__(self):
super(Banana, self).__init__() # [super-with-arguments]
class Fruit:
pass
class Banana(Fruit):
def __init__(self):
super().__init__()
Consider Using Dict Comprehension
Description: Emitted when detect the creation of a dictionary using the dict()
callable and a transient list. Although there is nothing syntactically wrong with this code, it is hard to read and can be simplified to a dict comprehension. Also it is faster since you don't need to create another transient list
fruits = ["apple", "pear", "banana"]
# +1: [consider-using-dict-comprehension]
FRUIT_LENGTHS = dict([(fruit, len(fruit)) for fruit in fruits])
fruits = ["apple", "pear", "banana"]
FRUIT_LENGTHS = {fruit: len(fruit) for fruit in fruits}
Consider Using Set Comprehension
Description: Although there is nothing syntactically wrong with this code, it is hard to read and can be simplified to a set comprehension. Also, it is faster since you don't need to create another transient list
fruits = ["apple", "banana", "banana", "pear", "kiwi", "kiwi"]
# +1: [consider-using-set-comprehension]
UNIQUE_FRUITS = set([fruit for fruit in fruits if fruit.startswith('k')])
fruits = ["apple", "banana", "banana", "pear", "kiwi", "kiwi"]
UNIQUE_FRUITS = {fruit for fruit in fruits if fruit.startswith('k')}
Consider Using Get
Description: Using the builtin dict.get
for key lookups is preferred to avoid KeyError
exceptions
knights = {"Arthur": "the brave", "Lancelot": "the noble"}
if "Arthur" in knights: # [consider-using-get]
DESCRIPTION = knights["Arthur"]
else:
DESCRIPTION = ""
knights = {"Arthur": "the brave", "Lancelot": "the noble"}
description = knights.get("Arthur", "")
Consider Using Join
Description: Using str.join(sequence)
is faster, uses less memory and increases readability compared to for-loop iteration
def numbers_to_string(numbers):
formatted_number = ''
for number in numbers:
formatted_number += str(number) # [consider-using-join]
return formatted_number
print(numbers_to_string([1, 2, 3]))
print(''.join(map(str, [1, 2, 3])))
Consider Using Sys Exit
Description: Instead of using exit()
or quit()
, consider using the sys.exit()
if __name__ == '__main__':
age = input('Enter your age: ')
print(f'Your age is {age}')
exit(0) # [consider-using-sys-exit]
import sys
if __name__ == '__main__':
age = input('Enter your age: ')
print(f'Your age is {age}')
sys.exit(0)
Consider Using Ternary
Description: One of known pre-python 2.5 ternary syntax is used
a, b = 5, 10
result = a > b and a or b # [consider-using-ternary]
a, b = 5, 10
result = a if a > b else b
Consider Swap Variables
Description: You do not have to use a temporary variable in order to swap variables. Using tuple unpacking
to directly swap variables makes the intention more clear
x = 3
y = 5
temp = x # [consider-swap-variables]
x = y
y = temp
x = 3
y = 5
x, y = y, x
Trailing Comma Tuple
Description: In Python, a tuple is created by the comma symbol, not by parentheses. Using a trailing comma can create unwanted tuples, so always use parentheses for clarity.
DIRECTIONS = 'left', 'right', 'up', 'down', # [trailing-comma-tuple]
DIRECTIONS = ('left', 'right', 'up', 'down')
Stop Iteration Return
Description: According to PEP479, raising StopIteration
in a generator can lead to bugs. It's better to rely on the natural termination of a generator.
def color_generator():
for color in ['red', 'blue']:
yield color
raise StopIteration # [stop-iteration-return]
def color_generator():
"""No need for an explicit return in this simple case."""
for color in ['red', 'blue']:
yield color
Inconsistent Return Statements
Description: All return
statements in a function should be consistent. If some return a value, others should explicitly return None
.
def find_maximum(value: int) -> int | None: # [inconsistent-return-statements]
if value > 0:
return value
def find_maximum(value: int) -> int | None:
if value > 0:
return value
return None
Redefined Argument From Local
Description: A local name is redefining an argument, which might suggest a potential error. This is relevant for iterations and similar constructs.
def calculate(radius=5.5):
# +1: [redefined-argument-from-local]
for radius, area in [(3, 28.27), (5, 78.54)]:
print(radius, area)
def calculate(radius=5.5):
for r, area in [(3, 28.27), (5, 78.54)]:
print(radius, r, area)
Consider Using In
Description: When testing for membership in a collection inside a loop, it is preferable to structure your loop to avoid deeply nested blocks. Using continue
statements can lead to complex control flows, which is harder to read and maintain.
for fruit in fruits:
if fruit in UNIQUE_FRUITS: # [consider-using-in]
print(fruit)
continue
print(fruit)
for fruit in fruits:
if fruit in UNIQUE_FRUITS:
print(fruit)
continue
else:
print(fruit)
Simplifiable If Expression
Description: An if expression can be replaced with bool(test)
VEHICLES = ["car", "boat", "rocket", "this example"]
def is_vehicle(an_object):
return True if an_object in VEHICLES else False # [simplifiable-if-expression]
def is_not_vehicle(an_object):
return False if an_object in VEHICLES else True # [simplifiable-if-expression]
VEHICLES = ["car", "boat", "rocket", "this example"]
def is_vehicle(an_object):
return an_object in VEHICLES
def is_not_vehicle(an_object):
return an_object not in VEHICLES
Simplifiable If Statement
Description: An if statement can be replaced with bool(test)
WILD_ANIMALS = ["lion", "tiger", "elephant", "this example"]
def is_wild_animal(an_object):
# +1: [simplifiable-if-statement]
if isinstance(an_object, Animal) and an_object in WILD_ANIMALS:
is_wild = True
else:
is_wild = False
return is_wild
WILD_ANIMALS = ["lion", "tiger", "elephant", "this example"]
def is_wild_animal(an_object):
is_wild = isinstance(an_object, Animal) and an_object.name in WILD_ANIMALS
return is_wild
Too Many Nested Blocks
Description: A function or a method has too many nested blocks. This makes the code less understandable and maintainable. Maximum number of nested blocks for function / method body is 5 by default
def validate_colors(colors):
if len(colors) > 2: # [too-many-nested-blocks]
if "red" in colors:
if "blue" in colors:
count = colors["blue"]
if count % 2:
if "green" in colors:
if count == 2:
return True
return False
def validate_colors(colors):
if len(colors) > 2 and "red" in colors and "blue" in colors:
count = colors["blue"]
if count % 2 and "green" in colors and count == 2:
return True
return False
No Else Break
Description: Used in order to highlight an unnecessary block of code following an if
containing a break
statement. As such, it will warn when it encounters an else
following a chain of if
s, all of them containing a break
statement
def next_ten_numbers(iterator):
for i, item in enumerate(iterator):
if i == 10: # [no-else-break]
break
else:
yield item
def next_ten_numbers(iterator):
for i, item in enumerate(iterator):
if i == 10:
break
yield item
No Else Continue
Description: Used in order to highlight an unnecessary block of code following an if
containing a continue
statement. As such, it will warn when it encounters an else
following a chain of if
s, all of them containing a continue
statement
def odd_number_under(n: int):
for i in range(n):
if i % 2 == 0: # [no-else-continue]
continue
else:
yield i
def odd_number_under(n: int):
for i in range(n):
if i % 2 == 0:
continue
yield i
No Else Raise
Description: Used in order to highlight an unnecessary block of code following an if
containing a raise
statement. As such, it will warn when it encounters an else
following a chain of if
s, all of them containing a raise
statement
def float_sum(a: float, b: float) -> float:
if not (isinstance(a, float) and isinstance(b, float)): # [no-else-raise]
raise ValueError("Function supports only float parameters.")
else:
return a + b
def float_sum(a: float, b: float) -> float:
if not (isinstance(a, float) and isinstance(b, float)):
raise ValueError("Function supports only float parameters.")
return a + b
No Else Return
Description: Unnecessary else
after return
. Used in order to highlight an unnecessary block of code following an if containing a return
statement. As such, it will warn when it encounters an else
following a chain of ifs, all of them containing a return
statement
def compare_strings(a: str, b: str) -> int:
if a == b: # [no-else-return]
return 0
elif a < b:
return -1
else:
return 1
def compare_strings(a: str, b: str) -> int:
if a == b:
return 0
if a < b:
return -1
return 1
Unnecessary Comprehension
Description: Instead of using an identity comprehension, consider using the list
, dict
or set
constructor. It is faster and simpler
ANIMALS = ["cat", "dog", "fish", "this example"]
UNIQUE_ANIMALS = {animal for animal in ANIMALS} # [unnecessary-comprehension]
ANIMALS = ["cat", "dog", "fish", "this example"]
UNIQUE_ANIMALS = set(ANIMALS)
Useless Return
Description: A function is returning a value that is unnecessary. This could be useful to identify functions that should simply be called for their side effects
import os
def show_current_directory():
return os.getcwd() # [useless-return]
import os
def show_current_directory():
print(os.getcwd())
Unneeded Not
Description: A boolean expression contains an unnecessary negation. Removing it increases readability and avoids potential confusion.
if not (x == y): # [unneeded-not]
if x != y:
Consider Iterating Dictionary
Description: When iterating over a dictionary, there's no need to explicitly use .keys()
. Just iterate through the dictionary itself for simplicity.
CARS = {'Toyota': 3, 'Honda': 5, 'Ford': 2}
for car in CARS.keys(): # [consider-iterating-dictionary]
print(car)
CARS = {'Toyota': 3, 'Honda': 5, 'Ford': 2}
for car in CARS:
print(car)
Fuzzy Comparison
Description: Fuzzy comparisons can produce unexpected results. Use a mathematical equivalence instead.
def is_square(n):
return n == n ** 0.5 # [fuzzy-comparison]
def is_cube(n):
return n == n ** (1/3) # [fuzzy-comparison]
def is_square(n):
return n ** 2 == n
def is_cube(n):
return n ** (1/3) == n
Len As Condition
Description: Pylint detects that len(sequence)
is being used without explicit comparison inside a condition to determine if a sequence is empty. Instead of coercing the length to a boolean, either rely on the fact that empty sequences are false or compare the length against a scalar.
if len(sequence) == 0: # [unnecessary-length-check]
if not sequence: # More Pythonic way to check for an empty sequence
Invalid Envvar Value
Description: Env manipulation functions support only string type arguments.
import os
os.getenv(True) # [invalid-envvar-value]
import os
os.getenv("TRUE")
Bad Open Mode
Description: Python supports r
, w
, a[, x]
modes with b,
+,
and U
(only with r)
options.
def open_and_get_content(file_path):
with open(file_path, "abc") as file: # [bad-open-mode]
return file.read()
def open_and_get_content(file_path):
with open(file_path, "r") as file:
return file.read()
Invalid Envvar Default
Description: Env manipulation functions return None
or str
values. Supplying anything different as a default may cause bugs. See https://docs.python.org/3/library/os.html#os.getenv for more details.
import os
env = os.getenv("API_URL", []) # [invalid-envvar-default]
import os
env = os.getenv("API_URL", "default_value")
Redundant Unittest Assert
Description: The first argument of assertTrue
and assertFalse
is a condition. If a constant is passed as a parameter, that condition will always be true. In this case, a warning should be emitted.
import unittest
class DummyTestCase(unittest.TestCase):
def test_dummy(self):
self.assertTrue(1) # [redundant-unittest-assert]
import unittest
class DummyTestCase(unittest.TestCase):
def test_dummy(self):
actual = "result"
self.assertEqual(actual, "expected")
Shallow Copy Environ
Description: os.environ
is not a dict
object but a proxy object, so a deep copy may still affect the original object. See https://bugs.python.org/issue15373 for reference.
import copy
import os
copied_env = copy.deepcopy(os.environ) # [deep-copy-environ]
import os
copied_env = os.environ.copy()
Boolean Datetime
Description: Using datetime.date
in a boolean context can hide subtle bugs when the date they represent is a specific value like the epoch. This behavior was fixed in Python 3.5. See http://bugs.python.org/issue13936 for reference. It can't be emitted when using Python >= 3.5.
import datetime
if not datetime.date(2000, 1, 1): # [boolean-datetime]
print("Date is valid.")
import datetime
today = datetime.date.today()
if today >= datetime.date(2000, 1, 1):
print("Date is valid.")
Deprecated Method
Description: The method is marked as deprecated and will be removed in a future version of Python. Consider looking for an alternative in the documentation.
old_function() # [deprecated-function]
new_function()
Subprocess Popen Preexec Fn
Description: The preexec_fn
parameter is not safe to use in the presence of threads in your application. The child process could deadlock before exec
is called. If you must use it, keep it trivial! Minimize the number of libraries you call into.
import subprocess
def do_nothing():
pass
subprocess.Popen(preexec_fn=do_nothing) # [subprocess-popen-preexec-fn]
import subprocess
subprocess.Popen()
Subprocess Run Check
Description: The check parameter should always be used with explicitly set check
keyword to make clear what the error-handling behavior is.
import subprocess
proc = subprocess.run(["echo", "Hello World"]) # [subprocess-run-check]
import subprocess
proc = subprocess.run(["echo", "Hello World"], check=False)
Bad Thread Instantiation
Description: The warning is emitted when a threading.Thread
class is instantiated without the target function being passed. By default, the first parameter is the group
param, not the target
param.
import threading
def thread_function():
print("Thread running")
thread = threading.Thread(target=thread_function, args=None) # [bad-thread-instantiation]
thread.start()
import threading
def thread_function(n):
print(n)
thread = threading.Thread(target=thread_function, args=(5,))
thread.start()
Bad String Format Type
Description: A type required by the format string is not suitable for the actual argument type.
print("%s" % 10) # [bad-string-format-type]
print("%s" % "10")
Format Needs Mapping
Description: A format string that uses named conversion specifiers is used with an argument that is not a mapping
print("%(a)s %(b)s" % ["foo", "bar"]) # [format-needs-mapping]
print("%(a)s %(b)s" % {"a": "foo", "b": "bar"})
Truncated Format String
Description: A format string terminates before the end of a conversion specifier
NUM_1 = 5
print("value %2" % NUM_1) # [truncated-format-string]
NUM_1 = 5
print(f"value {NUM_1}")
Missing Format String Key
Description: A format string that uses named conversion specifiers is used with a dictionary that doesn't contain all the keys required by the format string
# +1: [missing-format-string-key]
vegetable_prices = """
Carrot: %(carrot_price)d ¤
Potato: %(potato_price)d ¤
""" % {
"carrot_price": 30
}
vegetable_prices = """
Carrot: %(carrot_price)d ¤
Potato: %(potato_price)d ¤
""" % {
"carrot_price": 30,
"potato_price": 45,
}
Mixed Format String
Description: A format string contains both named and unnamed conversion specifiers. This is also used when a named conversion specifier contains * for the minimum field width and/or precision
print("a=%(a)d, b=%d" % (2, 3)) # [mixed-format-string]
print("a=%(a)d, b=%(b)d" % {"a": 2, "b": 3})
Too Few Format Args
Description: A format string that uses unnamed conversion specifiers is given too few arguments
print("The weather is {0}, and tomorrow will be {1}".format("sunny")) # [too-few-format-args]
print("The weather is {0}, and tomorrow will be {1}".format("sunny", "cloudy"))
Bad Str Strip Call
Description: The argument to a str.{l,r,}strip
call contains a duplicate character
"Python Programming".strip("Pyt") # [bad-str-strip-call]
# >>> 'hon Programming'
"Python Programming".strip("Pytho")
# >>> 'n Programming'
Too Many Format Args
Description: A format string that uses unnamed conversion specifiers is given too many arguments
# +1: [too-many-format-args]
print("This year is {0}, next year will be {1}".format("2023", "2024", "2025"))
print("This year is {0}, next year will be {1}".format("2023", "2024"))
Bad Format Character
Description: An unsupported format character is used in a format string
print("%d %q" % (10, 20)) # [bad-format-character]
print("%d %d" % (10, 20))
Anomalous Unicode Escape In String
Description: An escape like \u is encountered in a byte string where it has no effect
print(b"%𐍈" % b"data") # [anomalous-unicode-escape-in-string]
print(b"\x50\u2341" % b"data")
Anomalous Backslash In String
Description: A backslash is in a literal string but not as an escape
path = "c:\folder" # [syntax-error]
path = "c:\\folder"
Duplicate String Formatting Argument
Description: We detect that a string formatting is repeating an argument instead of using named string arguments
# pylint: disable=missing-docstring, consider-using-f-string
SKY = "sky ☁️"
STAR = "star ✨"
# +1: [duplicate-string-formatting-argument,duplicate-string-formatting-argument]
CONST = """
Twinkle twinkle little {}, {}
How I wonder what you {}, {}
Up above the world so {}, {}
Like a diamond in the {}, {}!
""".format(
STAR,
STAR,
SKY,
SKY,
SKY,
STAR,
STAR
)
# pylint: disable=missing-docstring, consider-using-f-string
SKY = "sky ☁️"
STAR = "star ✨"
CONST = """
Twinkle twinkle little {star}, {star}
How I wonder what you {sky}, {sky}
Up above the world so {sky}, {sky}
Like a diamond in the {star}, {star}!
""".format(
star=STAR, sky=SKY
)
Format Combined Specification
Description: A PEP 3101 format string contains both automatic field numbering (e.g. {}
) and manual field specification (e.g. {0}
)
print("{} {2}".format("apple", "banana")) # [format-combined-specification]
print("{0} {1}".format("apple", "banana"))
Bad Format String Key
Description: A format string that uses named conversion specifiers is used with a dictionary whose keys are not all strings
print("%(key1)s" % {"key1": "value", 100: "num"}) # [bad-format-string-key]
print("%(key1)s, %(key2)s" % {"key1": "value", "key2": "num"})
Implicit Str Concat
Description: Implicit string concatenation found. String literals are implicitly concatenated in a literal iterable definition: maybe a comma is missing?
with open("file.txt" "w") as f: # [implicit-str-concat]
f.write("data")
with open("file.txt", "w") as f:
f.write("data")
Bad Format String
Description: A PEP 3101 format string is invalid
print("{b[0] * b[1]}".format(b=[2, 3])) # [bad-format-string]
print("{b[0]} * {b[1]}".format(b=[2, 3]))
Missing Format Attribute
Description: A PEP 3101 format string uses an attribute specifier ({0.real}), but the argument passed for formatting doesn't have that attribute
print("{0.imag}".format(5)) # [missing-format-attribute]
print("{0.imag}".format(5j))
Missing Format Argument Key
Description: A PEP 3101 format string that uses named fields doesn't receive one or more required keywords
print("My pet is a {type} named {name}".format(type="dog")) # [missing-format-argument-key]
print("My pet is a {type} named {name}".format(type="dog", name="Buddy"))
Inconsistent Quotes
Description: Quote delimiter is inconsistent with the rest of the file. Quote delimiters are not used consistently throughout a module (with allowances made for avoiding unnecessary escaping)
import time
print('Current time: ', time.strftime("%H:%M:%S")) # [inconsistent-quotes]
import time
print("Current time: ", time.strftime("%H:%M:%S"))
Unused Format String Argument
Description: A PEP 3101 format string that uses named fields is used with an argument that is not required by the format string
print("{a} {b}".format(a=4, b=5, c=6)) # [unused-format-string-argument]
print("{a} {b} {c}".format(a=4, b=5, c=6))
Unused Format String Key
Description: A format string that uses named conversion specifiers is used with a dictionary that contains keys not required by the format string
"The lazy %(animal)s sleeps all day." % {
"animal": "cat",
"activity": "jumps",
}
# -4: [unused-format-string-key]
"The lazy %(animal)s %(activity)s all day." % {
"animal": "cat",
"activity": "jumps",
}
F String Without Interpolation
Description: Using an f-string that does not have any interpolated variables. This occurs when an f-string is used, but it can be a normal string or a potential bug in the code
x = 3
y = 4
print(f"x multiplied by y equals x * y") # [f-string-without-interpolation]
x = 3
y = 4
print(f"{x} multiplied by {y} equals {x * y}")
Invalid Format Index
Description: A PEP 3101 format string uses a lookup specifier ({a[2]}
), but the argument passed for formatting doesn't contain or doesn't have that key as an attribute
fruits = ["mango"]
print('The third fruit is {fruits[2]}'.format(fruits=fruits)) # [invalid-format-index]
fruits = ["mango", "apple", "banana"]
print("The third fruit is {fruits[2]}".format(fruits=fruits))
Unsupported Assignment Operation
Description: Emitted when an object does not support item assignment (i.e., doesn't define the __setitem__
method)
def get_colors(colors):
for color in colors:
print(color)
get_colors(["blue"])[0] = "red" # [unsupported-assignment-operation]
def get_colors(colors):
for color in colors:
print(color)
return []
get_colors(["blue"])[0] = "red"
Unsupported Delete Operation
Description: Emitted when an object does not support item deletion (i.e. doesn't define __delitem__
method)
VEGETABLES = ("carrot", "tomato", "pepper")
del VEGETABLES[1] # [unsupported-delete-operation]
VEGETABLES = ["carrot", "tomato", "pepper"]
del VEGETABLES[1]
Invalid Unary Operand Type
Description: Emitted when a unary operand is used on an object which does not support this type of operation
cookies = 5
missing_cookies = str
cookies = -missing_cookies # [invalid-unary-operand-type]
cookies = 5
missing_cookies = 2
cookies -= missing_cookies
Unsupported Binary Operation
Description: Emitted when a binary arithmetic operation between two operands is not supported
color = "blue" & None # [unsupported-binary-operation]
shape = {} | None # [unsupported-binary-operation]
masked = 0b110101 & 0b101001
result = 0xABCD | 0x1234
No Member
Description: A variable is accessed for a non-existent member
from os import path
location = path(".").drives # [no-member]
class Dog:
def bark(self):
print("Bark")
Dog().run() # [no-member]
from os import path
location = path.abspath(".")
class Dog:
def bark(self):
print("Bark")
Dog().bark()
Not Callable
Description: An object being called has been inferred to a non callable object
AGE = 25
print(AGE()) # [not-callable]
AGE = 25
print(AGE)
Redundant Keyword Arg
Description: A function call would result in assigning multiple values to a function parameter, one value from a positional argument and one from a keyword argument
def cube(x):
return x * x * x
cube(3, x=2) # [redundant-keyword-arg]
def cube(x):
return x * x * x
cube(3)
Assignment From No Return
Description: An assignment is done on a function call but the inferred function doesn't return anything
def subtract(x, y):
print(x - y)
result = subtract(7, 3) # [assignment-from-no-return]
def subtract(x, y):
return x - y
result = subtract(7, 3)
Assignment From None
Description: An assignment is done on a function call but the inferred function returns nothing but None
def compute():
return None
result = compute() # [assignment-from-none]
def compute():
return None
result = compute() if compute() else 42
Not Context Manager
Description: An instance in a with statement doesn't implement the context manager protocol (__enter__
/__exit__
)
class MyFileHandler:
def __enter__(self):
print('Opening file')
with MyFileHandler() as f: # [not-context-manager]
print('Processing file')
class MyFileHandler:
def __enter__(self):
print('Opening file')
def __exit__(self, *exc):
print('Closing file')
with MyFileHandler() as f:
print('Processing file')
Unhashable Dict Key
Description: Emitted when a dict key is not hashable (i.e. doesn't define hash method)
d = {[1, 2, 3]: 'numbers'} # [unhashable-dict-key]
d = {(1, 2, 3): 'tuple-key'}
Repeated Keyword
Description: Emitted when a function call got multiple values for a keyword
def greet(name, msg='Hello'):
return f'{msg}, {name}'
greet('John', msg='Hi', **{'msg': 'Welcome'}) # [repeated-keyword]
def greet(name, msg='Hello'):
return f'{msg}, {name}'
greet('John', msg='Hi')
Invalid Metaclass
Description: Emitted whenever we can detect that a class is using, as a metaclass, something which might be invalid for using as a metaclass
class Car(metaclass=str): # [invalid-metaclass]
pass
class Vehicle:
pass
class Car(Vehicle):
pass
Missing Kwoa
Description: A function call does not pass a mandatory keyword-only argument
def divide(dividend, *, divisor):
return dividend / divisor
def calculate(*args, **kwargs):
divide(*args) # [missing-kwoa]
def divide(dividend, *, divisor):
return dividend / divisor
def calculate(*args, **kwargs):
divide(*args, **kwargs)
No Value For Parameter
Description: A function call passes too few arguments
def multiply(x, y):
return x * y
multiply(4) # [no-value-for-parameter]
def multiply(x, y):
return x * y
multiply(4, 5)
Not An Iterable
Description: A non-iterable value is used in place where an iterable is expected
for char in 100: # [not-an-iterable]
pass
for char in '100':
pass
Not A Mapping
Description: A non-mapping value is used in place where mapping is expected
def print_animals(**animals):
print(animals)
print_animals(**list('cat', 'dog')) # [not-a-mapping]
def print_animals(**animals):
print(animals)
print_animals(**dict(cat=1, dog=2))
Invalid Sequence Index
Description: A sequence type is indexed with an invalid type. Valid types are ints, slices, and objects with an __index__
method
colors = ['red', 'green', 'blue']
print(colors['green']) # [invalid-sequence-index]
colors = ['red', 'green', 'blue']
print(colors[1])
Invalid Slice Index
Description: A slice index is not an integer, None
, or an object
with an __index__
method
NUMBERS = [1, 2, 3, 4]
FIRST_TWO = NUMBERS[:"2"] # [invalid-slice-index]
NUMBERS = [1, 2, 3, 4]
FIRST_TWO = NUMBERS[:2]
Too Many Function Args
Description: A function call passes too many positional arguments. Maximum number of arguments for function/method is 5 by default
class Vehicle:
def __init__(self, type):
self.type = type
car = Vehicle("sedan", "car", "2024") # [too-many-function-args]
class Vehicle:
def __init__(self, type, model):
self.type = type
self.model = model
car = Vehicle("sedan", "2024")
Unexpected Keyword Arg
Description: A function call passes a keyword argument that doesn't correspond to one of the function's parameter names
def greet(name="John", age=25):
print(f"Name: {name}, Age: {age}")
greet(name="Alice", age=30, gender="female") # [unexpected-keyword-arg]
def greet(name="John", age=25):
print(f"Name: {name}, Age: {age}")
greet(name="Alice", age=30)
Dict Iter Missing Items
Description: Emitted when trying to iterate through a dict without calling .items()
capitals = {"France": "Paris", "Japan": "Tokyo", "USA": "Washington D.C."}
for country, capital in capitals: # [dict-iter-missing-items]
print(f"{country} has capital {capital}.")
capitals = {"France": "Paris", "Japan": "Tokyo", "USA": "Washington D.C."}
for country, capital in capitals.items():
print(f"{country} has capital {capital}.")
Unsupported Membership Test
Description: Emitted when an instance in membership test expression doesn't implement membership protocol (__contains__
/__iter__
/__getitem__
)
class Animal:
pass
dog = "dog" in Animal() # [unsupported-membership-test]
class Animal:
ANIMALS = ["dog", "cat"]
def __contains__(self, name):
return name in self.ANIMALS
dog = "dog" in Animal()
Unsubscriptable Object
Description: Emitted when a subscripted value doesn't support subscription (i.e. doesn't define __getitem__
method or __class_getitem__
for a class)
class Car:
pass
Car()[0] # [unsubscriptable-object]
class Car:
def __init__(self):
self.models = ["Sedan", "SUV", "Truck"]
def __getitem__(self, idx):
return self.models[idx]
Car()[0]
Keyword Arg Before Vararg
Description: When defining a keyword argument before variable positional arguments, one can end up in having multiple values passed for the aforementioned parameter in case the method is called with keyword arguments
def sum_values(x=None, *nums): # [keyword-arg-before-vararg]
return sum(nums) + (x if x else 0)
def sum_values(*nums, x=None):
return sum(nums) + (x if x else 0)
Non Str Assignment To Dunder Name
Description: Non-string value assigned to name Emitted when a non-string value is assigned to name
class Car:
pass
Car.__name__ = 100 # [non-str-assignment-to-dunder-name]
class Car:
pass
Car.__name__ = "Sedan"
Arguments Out Of Order
Description: Emitted when the caller's argument names fully match the parameter names in the function signature but do not have the same order
def process_args(first, second, third):
return first, second, third
def call_with_wrong_order():
first = 1
second = 2
third = 3
process_args( # [arguments-out-of-order]
first, third, second
)
def process_args(first, second, third):
return first, second, third
def call_with_correct_order():
first = 1
second = 2
third = 3
process_args(first, second, third)
Isinstance Second Argument Not Valid Type
Description: Second argument of isinstance is not a type Emitted when the second argument of an isinstance call is not a type
isinstance([1, 2, 3], dict) # [isinstance-second-argument-not-valid-type]
isinstance([1, 2, 3], list)
C Extension No Member
Description: A variable is accessed for non-existent member of C extension. Due to unavailability of source static analysis is impossible, but it may be performed by introspecting living objects in run-time
Unpacking Non Sequence
Description: Something which is not a sequence is used in an unpack assignment
a, b, c = 5 # [unpacking-non-sequence]
a, b, c = 1, 2, 3
Invalid All Object
Description: An invalid (non-string) object occurs in __all__
__all__ = (
None, # [invalid-all-object]
Car,
Bike,
)
class Car:
pass
class Bike:
pass
__all__ = ["Car", "Bike"]
class Car:
pass
class Bike:
pass
No Name In Module
Description: A name cannot be found in a module
from sys import sandwich # [no-name-in-module]
from sys import version
Undefined Variable
Description: An undefined variable is accessed
print(speed + 5) # [undefined-variable]
speed = 60
print(speed + 5)
Undefined All Variable
Description: An undefined variable name is referenced in all
__all__ = ["calculate_area"] # [undefined-all-variable]
def calc_area():
pass
__all__ = ["calc_area"]
def calc_area():
pass
Used Before Assignment
Description: A local variable is accessed before its assignment
print(greeting) # [used-before-assignment]
greeting = "Hi there!"
greeting = "Hi there!"
print(greeting)
Cell Var From Loop
Description: A variable used in a closure is defined in a loop. This will result in all closures using the same value for the closed-over variable
def parent_greeting(people):
messages = []
for person in people:
def say_hi():
# do something
print(f"Hi, {person}!") # [cell-var-from-loop]
if person.isalpha():
messages.append(say_hi)
for say_hi in messages:
# the "person" variable is evaluated when the function is called here,
# which is the last value it had in the loop - "Unknown"
say_hi()
parent_greeting(["John", "Paul", "George", "Ringo"])
# "Hi, Ringo!"
# "Hi, Ringo!"
# "Hi, Ringo!"
# "Hi, Ringo!"
Global Variable Undefined
Description: A variable is defined through the global
statement but the variable is not defined in the module scope
def update_vegetable():
global VEGETABLE # [global-variable-undefined]
VEGETABLE = "potato"
VEGETABLE = "carrot"
def update_vegetable():
global VEGETABLE
VEGETABLE = "potato"
Self Cls Assignment
Description: Invalid assignment to self or cls in instance or class method respectively
class Vehicle:
@classmethod
def list_vehicles(cls):
cls = "car" # [self-cls-assignment]
def print_speed(self, *speeds):
self = "fast" # [self-cls-assignment]
speed = speeds[0]
print(speed)
class Vehicle:
@classmethod
def list_vehicles(cls):
vehicle = "car"
print(vehicle)
def print_speed(self, *speeds):
speed = speeds[0]
print(speed)
Unbalanced Tuple Unpacking
Description: There is an unbalanced tuple unpacking in assignment
colors = ("red", "green", "blue", "yellow")
red, green = colors # [unbalanced-tuple-unpacking]
colors = ("red", "green", "blue", "yellow")
red, green, *others = colors
Possibly Unused Variable
Description: A variable is defined but might not be used. The possibility comes from the fact that locals()
might be used, which could consume or not the said variable
def choose_color(colors):
print(colors)
hue = "blue" # [possibly-unused-variable]
return locals()
def choose_color(colors):
current_locals = locals()
print(colors)
hue = "blue"
print(hue)
return current_locals
Redefined Builtin
Description: A variable or function overrides a built-in
def list(): # [redefined-builtin]
pass
def list_items():
pass
Redefine In Handler
Description: An exception handler assigns the exception to an existing name
try:
1/0
except ZeroDivisionError as e:
e = 'Error occurred' # [redefine-in-handler]
try:
1/0
except ZeroDivisionError as err:
err = 'Error occurred'
Redefined Outer Name
Description: A variable's name hides a name defined in the outer scope
counter = 20
def count_down(counter): # [redefined-outer-name]
for i in range(counter, 0, -1):
print(i)
counter = 20
def count_down(limit):
for i in range(limit, 0, -1):
print(i)
Unused Import
Description: An imported module or variable is not used
from os import getenv
from datetime import datetime # [unused-import]
API_KEY = getenv('API_KEY')
from os import getenv
API_KEY = getenv('API_KEY')
Unused Argument
Description: A function or method argument is not used
def area(length, width): # [unused-argument]
return length * length
def area(length, width):
return length * width
Unused Wildcard Import
Description: An imported module or variable is not used from a 'from X import *'
style import
from collections import * # [unused-wildcard-import]
Counter(['apple', 'orange', 'banana'])
from collections import Counter
Counter(['apple', 'orange', 'banana'])
Unused Variable
Description: A variable is defined but not used
def greet():
first_name = "John"
last_name = "Doe" # [unused-variable]
print(f"Hello {first_name}")
def greet():
first_name = "John"
last_name = "Doe"
print(f"Hello {first_name} {last_name}")
Global Variable Not Assigned
Description: A variable is defined through the global
statement but no assignment to this variable is done
FRUIT = "apple"
def update_fruit():
global FRUIT # [global-variable-not-assigned]
print(FRUIT)
FRUIT = "apple"
def update_fruit():
global FRUIT
FRUIT = "banana"
Undefined Loop Variable
Description: A loop variable (i.e. defined by a for loop or a list comprehension or a generator expression) is used outside the loop
def find_odd_number(numbers):
for x in numbers:
if x % 2 != 0:
break
return x # [undefined-loop-variable]
def find_odd_number(numbers):
for x in numbers:
if x % 2 != 0:
return x
return None
Global Statement
Description: You use the global
statement to update a global variable. Pylint discourages this usage, but it doesn't mean you cannot use it.
num = 5
def update_num():
global num # [global-statement]
num = 15
print(num)
update_num()
print(num)
num = 5
def update_num():
print(num)
return 15
num = update_num()
print(num)
Global At Module Level
Description: You use the global
statement at the module level since it has no effect
count = 10
global count # [global-at-module-level]
count = 10