-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
/
unnecessary_dunder_call.py
130 lines (101 loc) · 3.69 KB
/
unnecessary_dunder_call.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
"""Checks for unnecessary-dunder-call."""
# pylint: disable=too-few-public-methods, undefined-variable
# pylint: disable=missing-class-docstring, missing-function-docstring
from collections import OrderedDict
from typing import Any
# Test includelisted dunder methods raise lint when manually called.
num_str = some_num.__str__() # [unnecessary-dunder-call]
num_repr = some_num.__add__(2) # [unnecessary-dunder-call]
my_repr = my_module.my_object.__repr__() # [unnecessary-dunder-call]
MY_CONTAINS_BAD = {1, 2, 3}.__contains__(1) # [unnecessary-dunder-call]
MY_CONTAINS_GOOD = 1 in {1, 2, 3}
# Just instantiate like a normal person please
my_list_bad = []
my_list_bad.__init__({1, 2, 3}) # [unnecessary-dunder-call]
my_list_good = list({1, 2, 3})
# Test unknown/user-defined dunder methods don't raise lint.
my_woohoo = my_object.__woohoo__()
# Test lint raised within function.
def is_bigger_than_two(val):
return val.__gt__(2) # [unnecessary-dunder-call]
# Test dunder methods don't raise lint
# if within a dunder method definition.
class Foo1:
def __init__(self):
object.__init__(self)
class Foo2:
def __init__(self):
super().__init__(self)
class Bar1:
def __new__(cls):
object.__new__(cls)
class Bar2:
def __new__(cls):
super().__new__(cls)
class CustomRegistry(dict):
def __init__(self) -> None:
super().__init__()
self._entry_ids = {}
def __setitem__(self, key, entry) -> None:
super().__setitem__(key, entry)
self._entry_ids.__setitem__(entry.id, entry)
self._entry_ids.__delitem__(entry.id)
def __delitem__(self, key: str) -> None:
entry = self[key]
self._entry_ids.__delitem__(entry.id)
super().__delitem__(key)
class CustomState:
def __init__(self, state):
self._state = state
def __eq__(self, other: Any) -> bool:
return self._state.__eq__(other)
class CustomDict(OrderedDict):
def __init__(self, *args, **kwds):
OrderedDict.__init__(self, *args, **kwds)
def __setitem__(self, key, value):
OrderedDict.__setitem__(self, key, value)
class MyClass(list):
def __contains__(self, item):
print("do some special checks")
return super().__contains__(item)
class PluginBase:
subclasses = []
def __init_subclass__(cls, **kwargs):
super().__init_subclass__(**kwargs)
cls.subclasses.append(cls)
# Validate that dunder call is allowed
# at any depth within dunder definition
class SomeClass:
def __init__(self):
self.my_attr = object()
def __setattr__(self, name, value):
def nested_function():
self.my_attr.__setattr__(name, value)
nested_function()
# Allow use of dunder methods that don't
# have an alternate method of being called
class Base:
@classmethod
def get_first_subclass(cls):
for subklass in cls.__subclasses__():
return subklass
return object
# Test no lint raised for attributes.
my_instance_name = x.__class__.__name__
my_pkg_version = pkg.__version__
# Allow use of dunder methods on non instantiated classes
MANUAL_SELF = int.__add__(1, 1)
MY_DICT = {"a": 1, "b": 2}
dict.__setitem__(MY_DICT, "key", "value")
# Still flag instantiated classes
INSTANTIATED_SELF = int("1").__add__(1) # [unnecessary-dunder-call]
{"a": 1, "b": 2}.__setitem__("key", "value") # [unnecessary-dunder-call]
# We also exclude dunder methods called on super
# since we can't apply alternate operators/functions here.
a = [1, 2, 3]
assert super(type(a), a).__str__() == "[1, 2, 3]"
class MyString(str):
"""Custom str implementation"""
def rjust(self, width, fillchar= ' '):
"""Acceptable call to __index__"""
width = width.__index__()