Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(3)

Side by Side Diff: third_party/pylint/checkers/python3.py

Issue 753543006: pylint: upgrade to 1.4.0 (Closed) Base URL: https://chromium.googlesource.com/chromium/tools/depot_tools.git@master
Patch Set: Created 6 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « third_party/pylint/checkers/newstyle.py ('k') | third_party/pylint/checkers/similar.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 # Copyright 2014 Google Inc.
2 # This program is free software; you can redistribute it and/or modify it under
3 # the terms of the GNU General Public License as published by the Free Software
4 # Foundation; either version 2 of the License, or (at your option) any later
5 # version.
6 #
7 # This program is distributed in the hope that it will be useful, but WITHOUT
8 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
9 # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
10 #
11 # You should have received a copy of the GNU General Public License along with
12 # this program; if not, write to the Free Software Foundation, Inc.,
13 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
14 """Check Python 2 code for Python 2/3 source-compatible issues."""
15 from __future__ import absolute_import
16
17 import re
18 import tokenize
19
20 import astroid
21 from pylint import checkers, interfaces
22 from pylint.utils import WarningScope
23 from pylint.checkers import utils
24
25
26 _ZERO = re.compile("^0+$")
27
28 def _is_old_octal(literal):
29 if _ZERO.match(literal):
30 return False
31 if re.match('0\d+', literal):
32 try:
33 int(literal, 8)
34 except ValueError:
35 return False
36 return True
37
38 def _check_dict_node(node):
39 inferred_types = set()
40 try:
41 inferred = node.infer()
42 for inferred_node in inferred:
43 inferred_types.add(inferred_node)
44 except (astroid.InferenceError, astroid.UnresolvableName):
45 pass
46 return (not inferred_types
47 or any(isinstance(x, astroid.Dict) for x in inferred_types))
48
49
50 class Python3Checker(checkers.BaseChecker):
51
52 __implements__ = interfaces.IAstroidChecker
53 enabled = False
54 name = 'python3'
55
56 msgs = {
57 # Errors for what will syntactically break in Python 3, warnings for
58 # everything else.
59 'E1601': ('print statement used',
60 'print-statement',
61 'Used when a print statement is used '
62 '(`print` is a function in Python 3)',
63 {'maxversion': (3, 0)}),
64 'E1602': ('Parameter unpacking specified',
65 'parameter-unpacking',
66 'Used when parameter unpacking is specified for a function'
67 "(Python 3 doesn't allow it)",
68 {'maxversion': (3, 0)}),
69 'E1603': ('Implicit unpacking of exceptions is not supported '
70 'in Python 3',
71 'unpacking-in-except',
72 'Python3 will not allow implicit unpacking of '
73 'exceptions in except clauses. '
74 'See http://www.python.org/dev/peps/pep-3110/',
75 {'maxversion': (3, 0),
76 'old_names': [('W0712', 'unpacking-in-except')]}),
77 'E1604': ('Use raise ErrorClass(args) instead of '
78 'raise ErrorClass, args.',
79 'old-raise-syntax',
80 "Used when the alternate raise syntax "
81 "'raise foo, bar' is used "
82 "instead of 'raise foo(bar)'.",
83 {'maxversion': (3, 0),
84 'old_names': [('W0121', 'old-raise-syntax')]}),
85 'E1605': ('Use of the `` operator',
86 'backtick',
87 'Used when the deprecated "``" (backtick) operator is used '
88 'instead of the str() function.',
89 {'scope': WarningScope.NODE,
90 'maxversion': (3, 0),
91 'old_names': [('W0333', 'backtick')]}),
92 'W1601': ('apply built-in referenced',
93 'apply-builtin',
94 'Used when the apply built-in function is referenced '
95 '(missing from Python 3)',
96 {'maxversion': (3, 0)}),
97 'W1602': ('basestring built-in referenced',
98 'basestring-builtin',
99 'Used when the basestring built-in function is referenced '
100 '(missing from Python 3)',
101 {'maxversion': (3, 0)}),
102 'W1603': ('buffer built-in referenced',
103 'buffer-builtin',
104 'Used when the buffer built-in function is referenced '
105 '(missing from Python 3)',
106 {'maxversion': (3, 0)}),
107 'W1604': ('cmp built-in referenced',
108 'cmp-builtin',
109 'Used when the cmp built-in function is referenced '
110 '(missing from Python 3)',
111 {'maxversion': (3, 0)}),
112 'W1605': ('coerce built-in referenced',
113 'coerce-builtin',
114 'Used when the coerce built-in function is referenced '
115 '(missing from Python 3)',
116 {'maxversion': (3, 0)}),
117 'W1606': ('execfile built-in referenced',
118 'execfile-builtin',
119 'Used when the execfile built-in function is referenced '
120 '(missing from Python 3)',
121 {'maxversion': (3, 0)}),
122 'W1607': ('file built-in referenced',
123 'file-builtin',
124 'Used when the file built-in function is referenced '
125 '(missing from Python 3)',
126 {'maxversion': (3, 0)}),
127 'W1608': ('long built-in referenced',
128 'long-builtin',
129 'Used when the long built-in function is referenced '
130 '(missing from Python 3)',
131 {'maxversion': (3, 0)}),
132 'W1609': ('raw_input built-in referenced',
133 'raw_input-builtin',
134 'Used when the raw_input built-in function is referenced '
135 '(missing from Python 3)',
136 {'maxversion': (3, 0)}),
137 'W1610': ('reduce built-in referenced',
138 'reduce-builtin',
139 'Used when the reduce built-in function is referenced '
140 '(missing from Python 3)',
141 {'maxversion': (3, 0)}),
142 'W1611': ('StandardError built-in referenced',
143 'standarderror-builtin',
144 'Used when the StandardError built-in function is referenced '
145 '(missing from Python 3)',
146 {'maxversion': (3, 0)}),
147 'W1612': ('unicode built-in referenced',
148 'unicode-builtin',
149 'Used when the unicode built-in function is referenced '
150 '(missing from Python 3)',
151 {'maxversion': (3, 0)}),
152 'W1613': ('xrange built-in referenced',
153 'xrange-builtin',
154 'Used when the xrange built-in function is referenced '
155 '(missing from Python 3)',
156 {'maxversion': (3, 0)}),
157 'W1614': ('__coerce__ method defined',
158 'coerce-method',
159 'Used when a __coerce__ method is defined '
160 '(method is not used by Python 3)',
161 {'maxversion': (3, 0)}),
162 'W1615': ('__delslice__ method defined',
163 'delslice-method',
164 'Used when a __delslice__ method is defined '
165 '(method is not used by Python 3)',
166 {'maxversion': (3, 0)}),
167 'W1616': ('__getslice__ method defined',
168 'getslice-method',
169 'Used when a __getslice__ method is defined '
170 '(method is not used by Python 3)',
171 {'maxversion': (3, 0)}),
172 'W1617': ('__setslice__ method defined',
173 'setslice-method',
174 'Used when a __setslice__ method is defined '
175 '(method is not used by Python 3)',
176 {'maxversion': (3, 0)}),
177 'W1618': ('import missing `from __future__ import absolute_import`',
178 'no-absolute-import',
179 'Used when an import is not accompanied by '
180 '`from __future__ import absolute_import`'
181 ' (default behaviour in Python 3)',
182 {'maxversion': (3, 0)}),
183 'W1619': ('division w/o __future__ statement',
184 'old-division',
185 'Used for non-floor division w/o a float literal or '
186 '``from __future__ import division``'
187 '(Python 3 returns a float for int division unconditionally)',
188 {'maxversion': (3, 0)}),
189 'W1620': ('Calling a dict.iter*() method',
190 'dict-iter-method',
191 'Used for calls to dict.iterkeys(), itervalues() or iteritems( ) '
192 '(Python 3 lacks these methods)',
193 {'maxversion': (3, 0)}),
194 'W1621': ('Calling a dict.view*() method',
195 'dict-view-method',
196 'Used for calls to dict.viewkeys(), viewvalues() or viewitems( ) '
197 '(Python 3 lacks these methods)',
198 {'maxversion': (3, 0)}),
199 'W1622': ('Called a next() method on an object',
200 'next-method-called',
201 "Used when an object's next() method is called "
202 '(Python 3 uses the next() built-in function)',
203 {'maxversion': (3, 0)}),
204 'W1623': ("Assigning to a class' __metaclass__ attribute",
205 'metaclass-assignment',
206 "Used when a metaclass is specified by assigning to __metaclas s__ "
207 '(Python 3 specifies the metaclass as a class statement argume nt)',
208 {'maxversion': (3, 0)}),
209 'W1624': ('Indexing exceptions will not work on Python 3',
210 'indexing-exception',
211 'Indexing exceptions will not work on Python 3. Use '
212 '`exception.args[index]` instead.',
213 {'maxversion': (3, 0),
214 'old_names': [('W0713', 'indexing-exception')]}),
215 'W1625': ('Raising a string exception',
216 'raising-string',
217 'Used when a string exception is raised. This will not '
218 'work on Python 3.',
219 {'maxversion': (3, 0),
220 'old_names': [('W0701', 'raising-string')]}),
221 'W1626': ('reload built-in referenced',
222 'reload-builtin',
223 'Used when the reload built-in function is referenced '
224 '(missing from Python 3). You can use instead imp.reload '
225 'or importlib.reload.',
226 {'maxversion': (3, 0)}),
227 'W1627': ('__oct__ method defined',
228 'oct-method',
229 'Used when a __oct__ method is defined '
230 '(method is not used by Python 3)',
231 {'maxversion': (3, 0)}),
232 'W1628': ('__hex__ method defined',
233 'hex-method',
234 'Used when a __hex__ method is defined '
235 '(method is not used by Python 3)',
236 {'maxversion': (3, 0)}),
237 'W1629': ('__nonzero__ method defined',
238 'nonzero-method',
239 'Used when a __nonzero__ method is defined '
240 '(method is not used by Python 3)',
241 {'maxversion': (3, 0)}),
242 'W1630': ('__cmp__ method defined',
243 'cmp-method',
244 'Used when a __cmp__ method is defined '
245 '(method is not used by Python 3)',
246 {'maxversion': (3, 0)}),
247 'W1631': ('map is used as implicitly evaluated call',
248 'implicit-map-evaluation',
249 'Used when the map builtin is used as implicitly '
250 'evaluated call, as in "map(func, args)" on a single line. '
251 'This behaviour will not work in Python 3, where '
252 'map is a generator and must be evaluated. '
253 'Prefer a for-loop as alternative.',
254 {'maxversion': (3, 0)}),
255 }
256
257 _missing_builtins = frozenset([
258 'apply',
259 'basestring',
260 'buffer',
261 'cmp',
262 'coerce',
263 'execfile',
264 'file',
265 'long',
266 'raw_input',
267 'reduce',
268 'StandardError',
269 'unicode',
270 'xrange',
271 'reload',
272 ])
273
274 _unused_magic_methods = frozenset([
275 '__coerce__',
276 '__delslice__',
277 '__getslice__',
278 '__setslice__',
279 '__oct__',
280 '__hex__',
281 '__nonzero__',
282 '__cmp__',
283 ])
284
285 def __init__(self, *args, **kwargs):
286 self._future_division = False
287 self._future_absolute_import = False
288 super(Python3Checker, self).__init__(*args, **kwargs)
289
290 def visit_function(self, node):
291 if node.is_method() and node.name in self._unused_magic_methods:
292 method_name = node.name
293 if node.name.startswith('__'):
294 method_name = node.name[2:-2]
295 self.add_message(method_name + '-method', node=node)
296
297 @utils.check_messages('parameter-unpacking')
298 def visit_arguments(self, node):
299 for arg in node.args:
300 if isinstance(arg, astroid.Tuple):
301 self.add_message('parameter-unpacking', node=arg)
302
303 @utils.check_messages('implicit-map-evaluation')
304 def visit_discard(self, node):
305 if (isinstance(node.value, astroid.CallFunc) and
306 isinstance(node.value.func, astroid.Name) and
307 node.value.func.name == 'map'):
308 module = node.value.func.lookup('map')[0]
309 if getattr(module, 'name', None) == '__builtin__':
310 self.add_message('implicit-map-evaluation', node=node)
311
312 def visit_name(self, node):
313 """Detect when a built-in that is missing in Python 3 is referenced."""
314 found_node = node.lookup(node.name)[0]
315 if getattr(found_node, 'name', None) == '__builtin__':
316 if node.name in self._missing_builtins:
317 message = node.name.lower() + '-builtin'
318 self.add_message(message, node=node)
319
320 @utils.check_messages('print-statement')
321 def visit_print(self, node):
322 self.add_message('print-statement', node=node)
323
324 @utils.check_messages('no-absolute-import')
325 def visit_from(self, node):
326 if node.modname == '__future__':
327 for name, _ in node.names:
328 if name == 'division':
329 self._future_division = True
330 elif name == 'absolute_import':
331 self._future_absolute_import = True
332 elif not self._future_absolute_import:
333 self.add_message('no-absolute-import', node=node)
334
335 @utils.check_messages('no-absolute-import')
336 def visit_import(self, node):
337 if not self._future_absolute_import:
338 self.add_message('no-absolute-import', node=node)
339
340 @utils.check_messages('metaclass-assignment')
341 def visit_class(self, node):
342 if '__metaclass__' in node.locals:
343 self.add_message('metaclass-assignment', node=node)
344
345 @utils.check_messages('old-division')
346 def visit_binop(self, node):
347 if not self._future_division and node.op == '/':
348 for arg in (node.left, node.right):
349 if isinstance(arg, astroid.Const) and isinstance(arg.value, floa t):
350 break
351 else:
352 self.add_message('old-division', node=node)
353
354 @utils.check_messages('next-method-called',
355 'dict-iter-method',
356 'dict-view-method')
357 def visit_callfunc(self, node):
358 if not isinstance(node.func, astroid.Getattr):
359 return
360 if any([node.args, node.starargs, node.kwargs]):
361 return
362 if node.func.attrname == 'next':
363 self.add_message('next-method-called', node=node)
364 else:
365 if _check_dict_node(node.func.expr):
366 if node.func.attrname in ('iterkeys', 'itervalues', 'iteritems') :
367 self.add_message('dict-iter-method', node=node)
368 elif node.func.attrname in ('viewkeys', 'viewvalues', 'viewitems '):
369 self.add_message('dict-view-method', node=node)
370
371 @utils.check_messages('indexing-exception')
372 def visit_subscript(self, node):
373 """ Look for indexing exceptions. """
374 try:
375 for infered in node.value.infer():
376 if not isinstance(infered, astroid.Instance):
377 continue
378 if utils.inherit_from_std_ex(infered):
379 self.add_message('indexing-exception', node=node)
380 except astroid.InferenceError:
381 return
382
383 @utils.check_messages('unpacking-in-except')
384 def visit_excepthandler(self, node):
385 """Visit an except handler block and check for exception unpacking."""
386 if isinstance(node.name, (astroid.Tuple, astroid.List)):
387 self.add_message('unpacking-in-except', node=node)
388
389 @utils.check_messages('backtick')
390 def visit_backquote(self, node):
391 self.add_message('backtick', node=node)
392
393 @utils.check_messages('raising-string', 'old-raise-syntax')
394 def visit_raise(self, node):
395 """Visit a raise statement and check for raising
396 strings or old-raise-syntax.
397 """
398 if (node.exc is not None and
399 node.inst is not None and
400 node.tback is None):
401 self.add_message('old-raise-syntax', node=node)
402
403 # Ignore empty raise.
404 if node.exc is None:
405 return
406 expr = node.exc
407 if self._check_raise_value(node, expr):
408 return
409 else:
410 try:
411 value = next(astroid.unpack_infer(expr))
412 except astroid.InferenceError:
413 return
414 self._check_raise_value(node, value)
415
416 def _check_raise_value(self, node, expr):
417 if isinstance(expr, astroid.Const):
418 value = expr.value
419 if isinstance(value, str):
420 self.add_message('raising-string', node=node)
421 return True
422
423
424 class Python3TokenChecker(checkers.BaseTokenChecker):
425 __implements__ = interfaces.ITokenChecker
426 name = 'python3'
427 enabled = False
428
429 msgs = {
430 'E1606': ('Use of long suffix',
431 'long-suffix',
432 'Used when "l" or "L" is used to mark a long integer. '
433 'This will not work in Python 3, since `int` and `long` '
434 'types have merged.',
435 {'maxversion': (3, 0)}),
436 'E1607': ('Use of the <> operator',
437 'old-ne-operator',
438 'Used when the deprecated "<>" operator is used instead '
439 'of "!=". This is removed in Python 3.',
440 {'maxversion': (3, 0),
441 'old_names': [('W0331', 'old-ne-operator')]}),
442 'E1608': ('Use of old octal literal',
443 'old-octal-literal',
444 'Usen when encountering the old octal syntax, '
445 'removed in Python 3. To use the new syntax, '
446 'prepend 0o on the number.',
447 {'maxversion': (3, 0)}),
448 }
449
450 def process_tokens(self, tokens):
451 for idx, (tok_type, token, start, _, _) in enumerate(tokens):
452 if tok_type == tokenize.NUMBER:
453 if token.lower().endswith('l'):
454 # This has a different semantic than lowercase-l-suffix.
455 self.add_message('long-suffix', line=start[0])
456 elif _is_old_octal(token):
457 self.add_message('old-octal-literal', line=start[0])
458 if tokens[idx][1] == '<>':
459 self.add_message('old-ne-operator', line=tokens[idx][2][0])
460
461
462 def register(linter):
463 linter.register_checker(Python3Checker(linter))
464 linter.register_checker(Python3TokenChecker(linter))
OLDNEW
« no previous file with comments | « third_party/pylint/checkers/newstyle.py ('k') | third_party/pylint/checkers/similar.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698