-
Notifications
You must be signed in to change notification settings - Fork 2k
Expand file tree
/
Copy pathexpressions_test.py
More file actions
340 lines (266 loc) · 7.13 KB
/
expressions_test.py
File metadata and controls
340 lines (266 loc) · 7.13 KB
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
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
#encoding: utf-8
def dup_key():
return { 1: -1,
1: -2,
u'a' : u'A',
u'a' : u'B'
}
def simple_func(*args, **kwrgs): pass
#Unnecessary lambdas
lambda arg0, arg1: simple_func(arg0, arg1)
lambda arg0, *arg1: simple_func(arg0, *arg1)
lambda arg0, **arg1: simple_func(arg0, **arg1)
# these lambdas are_ necessary
lambda arg0, arg1=1: simple_func(arg0, arg1)
lambda arg0, arg1: simple_func(arg0, *arg1)
lambda arg0, arg1: simple_func(arg0, **arg1)
lambda arg0, *arg1: simple_func(arg0, arg1)
lambda arg0, **arg1: simple_func(arg0, arg1)
#Non-callable called
class NonCallable(object):
pass
class MaybeCallable(Unknown, object):
pass
def call_non_callable(arg):
non = NonCallable()
non(arg)
()()
[]()
dont_know = MaybeCallable()
dont_know() # Not a violation
#Explicit call to __del__
x.__del__()
#Unhashable object
def func():
mapping = dict(); unhash = list()
return mapping[unhash]
#Using 'is' when should be using '=='
s = "Hello " + "World"
if "Hello World" is s:
print ("OK")
#This is OK in CPython, but may not be portable
s = str(7)
if "7" is s:
print ("OK")
#And some data flow
CONSTANT = 20
if x is CONSTANT:
print ("OK")
#This is OK
x = object()
y = object()
if x is y:
print ("Very surprising!")
#This is also OK
if s is None:
print ("Also surprising")
#Portable is comparisons
def f(arg):
arg is ()
arg is 0
arg is ''
#Non-container
class XIter(object):
#Support both 2 and 3 form of next, but omit __iter__ method
def __next__(self):
pass
def next(self):
pass
def non_container():
seq = XIter()
if 1 in seq:
pass
if 1 not in seq:
pass
#Container inheriting from builtin
class MyDict(dict):
pass
class MySequence(UnresolvablebaseClass):
pass
def is_container():
mapping = MyDict()
if 1 in mapping:
pass
seq = MySequence()
if 1 in seq:
pass
seq = None
if seq is not None and 1 in seq:
pass
#Equals none
def x(arg):
return arg == None
class NotMyDict(object):
def f(self):
super(MyDict, self).f()
# class defining __del__
class Test(object):
def __del__(self):
pass
# subclass
class SubTest(Test):
def __del__(self):
# This is permitted and required.
Test.__del__(self)
# This is a violation.
self.__del__()
# This is an alternate syntax for the super() call, and hence OK.
super(SubTest, self).__del__()
# This is the Python 3 spelling of the same.
super().__del__()
#Some more lambdas
#Unnecessary lambdas
lambda arg0: len(arg0)
lambda arg0: XIter.next(arg0)
class UL(object):
def f(self, x):
pass
def g(self):
return lambda x: self.f(x)
# these lambdas are necessary
lambda arg0: XIter.next(arg0, arg1)
lambda arg0: func()(arg0)
lambda arg0, arg1: arg0.meth(arg0, arg1)
#Cannot flag lists as unhashable if the object
#we are subscripting may be a numpy array
def func(maybe_numpy):
unhash = list()
return maybe_numpy[unhash]
#Guarded non-callable called
def guarded_non_callable(cond):
if cond:
val = []
else:
val = func
if hasattr(val, "__call__"):
x(1)
#ODASA-4056
def format_string(s, formatter='minimal'):
"""Format the given string using the given formatter."""
if not callable(formatter):
formatter = get_formatter_for_name(formatter)
if formatter is None:
output = s
else:
output = formatter(s)
return output
#ODASA-4614
def f(x):
d = {}
d['one'] = {}
d['two'] = 0
return x[d['two']]
#ODASA-4055
class C:
def _internal(arg):
# arg is not a C
def wrapper(args):
return arg(args)
return wrapper
@_internal
def method(self, *args):
pass
#ODASA-4689
class StrangeIndex:
def __getitem__(self,index):
return 1
x = StrangeIndex();
print(x[{'a': 'b'}])
def not_dup_key():
return { u'a' : 0,
b'a' : 0,
u"😄" : 1,
u"😅" : 2,
u"😆" : 3
}
# Lookup of unhashable object triggers TypeError, but the
# exception is caught, so it's not a bug. This used to be
# a false positive of the HashedButNoHash query.
def func():
unhash = list()
try:
hash(unhash)
except TypeError:
return 1
return 0
def func():
mapping = dict(); unhash = list()
try:
mapping[unhash]
except TypeError:
return 1
return 0
# False positive for py/member-test-non-container
# Container wrapped in MappingProxyType
from types import MappingProxyType
def mpt_arg(d=MappingProxyType({})):
return 1 in d
#### UseofApply.ql
# Use of the builtin function `apply` is generally considered bad now that the
# ability to destructure lists of arguments is possible, but we should not flag
# cases where the function is merely named `apply` rather than being the actual
# builtin `apply` function.
def useofapply():
def foo():
pass
# Positive Cases
# This use of `apply` is a reference to the builtin function and so SHOULD be
# caught by the query.
apply(foo, [1])
# Negative Cases
# This use of `apply` is a reference to the locally defined function inside of
# `local`, and so SHOULD NOT be caught by the query.
def local():
def apply(f):
pass
apply(foo)([1])
# Class used as a decorator: the runtime value at attribute access is the
# function's return value, not the decorator class instance.
class cached_property(object):
def __init__(self, func):
self.func = func
def __get__(self, obj, cls):
val = self.func(obj)
setattr(obj, self.func.__name__, val)
return val
class MyForm(object):
@cached_property
def changed_data(self):
return [1, 2, 3]
def test_decorator_class(form):
f = MyForm()
# OK: cached_property is a descriptor; the actual runtime value is a list.
if "name" in f.changed_data:
pass
# Class with dynamically added methods via setattr: we cannot statically
# determine its full interface, so we should not flag it.
class DynamicProxy(object):
def __init__(self, args):
self._args = args
for method_name in ["__contains__", "__iter__", "__len__"]:
def wrapper(self, *args, __method_name=method_name):
pass
setattr(DynamicProxy, method_name, wrapper)
def test_dynamic_methods():
proxy = DynamicProxy(())
# OK: __contains__ is added dynamically via setattr.
if "name" in proxy:
pass
# isinstance guard should suppress non-container warning
def guarded_contains(x):
obj = XIter()
if isinstance(obj, dict):
if x in obj: # OK: guarded by isinstance
pass
def guarded_contains_tuple(x):
obj = XIter()
if isinstance(obj, (list, dict, set)):
if x in obj: # OK: guarded by isinstance with tuple of types
pass
# Negated isinstance guard: early return when NOT a container
def guarded_contains_negated(x):
obj = XIter()
if not isinstance(obj, dict):
return
if x in obj: # OK: guarded by negated isinstance + early return
pass