OLD | NEW |
(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 'W1632': ('input built-in referenced', |
| 256 'input-builtin', |
| 257 'Used when the input built-in is referenced ' |
| 258 '(backwards-incompatible semantics in Python 3)', |
| 259 {'maxversion': (3, 0)}), |
| 260 'W1633': ('round built-in referenced', |
| 261 'round-builtin', |
| 262 'Used when the round built-in is referenced ' |
| 263 '(backwards-incompatible semantics in Python 3)', |
| 264 {'maxversion': (3, 0)}), |
| 265 } |
| 266 |
| 267 _bad_builtins = frozenset([ |
| 268 'apply', |
| 269 'basestring', |
| 270 'buffer', |
| 271 'cmp', |
| 272 'coerce', |
| 273 'execfile', |
| 274 'file', |
| 275 'input', # Not missing, but incompatible semantics |
| 276 'long', |
| 277 'raw_input', |
| 278 'reduce', |
| 279 'round', # Not missing, but incompatible semantics |
| 280 'StandardError', |
| 281 'unicode', |
| 282 'xrange', |
| 283 'reload', |
| 284 ]) |
| 285 |
| 286 _unused_magic_methods = frozenset([ |
| 287 '__coerce__', |
| 288 '__delslice__', |
| 289 '__getslice__', |
| 290 '__setslice__', |
| 291 '__oct__', |
| 292 '__hex__', |
| 293 '__nonzero__', |
| 294 '__cmp__', |
| 295 ]) |
| 296 |
| 297 def __init__(self, *args, **kwargs): |
| 298 self._future_division = False |
| 299 self._future_absolute_import = False |
| 300 super(Python3Checker, self).__init__(*args, **kwargs) |
| 301 |
| 302 def visit_function(self, node): |
| 303 if node.is_method() and node.name in self._unused_magic_methods: |
| 304 method_name = node.name |
| 305 if node.name.startswith('__'): |
| 306 method_name = node.name[2:-2] |
| 307 self.add_message(method_name + '-method', node=node) |
| 308 |
| 309 @utils.check_messages('parameter-unpacking') |
| 310 def visit_arguments(self, node): |
| 311 for arg in node.args: |
| 312 if isinstance(arg, astroid.Tuple): |
| 313 self.add_message('parameter-unpacking', node=arg) |
| 314 |
| 315 @utils.check_messages('implicit-map-evaluation') |
| 316 def visit_discard(self, node): |
| 317 if (isinstance(node.value, astroid.CallFunc) and |
| 318 isinstance(node.value.func, astroid.Name) and |
| 319 node.value.func.name == 'map'): |
| 320 module = node.value.func.lookup('map')[0] |
| 321 if getattr(module, 'name', None) == '__builtin__': |
| 322 self.add_message('implicit-map-evaluation', node=node) |
| 323 |
| 324 def visit_name(self, node): |
| 325 """Detect when a "bad" built-in is referenced.""" |
| 326 found_node = node.lookup(node.name)[0] |
| 327 if getattr(found_node, 'name', None) == '__builtin__': |
| 328 if node.name in self._bad_builtins: |
| 329 message = node.name.lower() + '-builtin' |
| 330 self.add_message(message, node=node) |
| 331 |
| 332 @utils.check_messages('print-statement') |
| 333 def visit_print(self, node): |
| 334 self.add_message('print-statement', node=node) |
| 335 |
| 336 @utils.check_messages('no-absolute-import') |
| 337 def visit_from(self, node): |
| 338 if node.modname == '__future__': |
| 339 for name, _ in node.names: |
| 340 if name == 'division': |
| 341 self._future_division = True |
| 342 elif name == 'absolute_import': |
| 343 self._future_absolute_import = True |
| 344 elif not self._future_absolute_import: |
| 345 self.add_message('no-absolute-import', node=node) |
| 346 |
| 347 @utils.check_messages('no-absolute-import') |
| 348 def visit_import(self, node): |
| 349 if not self._future_absolute_import: |
| 350 self.add_message('no-absolute-import', node=node) |
| 351 |
| 352 @utils.check_messages('metaclass-assignment') |
| 353 def visit_class(self, node): |
| 354 if '__metaclass__' in node.locals: |
| 355 self.add_message('metaclass-assignment', node=node) |
| 356 |
| 357 @utils.check_messages('old-division') |
| 358 def visit_binop(self, node): |
| 359 if not self._future_division and node.op == '/': |
| 360 for arg in (node.left, node.right): |
| 361 if isinstance(arg, astroid.Const) and isinstance(arg.value, floa
t): |
| 362 break |
| 363 else: |
| 364 self.add_message('old-division', node=node) |
| 365 |
| 366 @utils.check_messages('next-method-called', |
| 367 'dict-iter-method', |
| 368 'dict-view-method') |
| 369 def visit_callfunc(self, node): |
| 370 if not isinstance(node.func, astroid.Getattr): |
| 371 return |
| 372 if any([node.args, node.starargs, node.kwargs]): |
| 373 return |
| 374 if node.func.attrname == 'next': |
| 375 self.add_message('next-method-called', node=node) |
| 376 else: |
| 377 if _check_dict_node(node.func.expr): |
| 378 if node.func.attrname in ('iterkeys', 'itervalues', 'iteritems')
: |
| 379 self.add_message('dict-iter-method', node=node) |
| 380 elif node.func.attrname in ('viewkeys', 'viewvalues', 'viewitems
'): |
| 381 self.add_message('dict-view-method', node=node) |
| 382 |
| 383 @utils.check_messages('indexing-exception') |
| 384 def visit_subscript(self, node): |
| 385 """ Look for indexing exceptions. """ |
| 386 try: |
| 387 for infered in node.value.infer(): |
| 388 if not isinstance(infered, astroid.Instance): |
| 389 continue |
| 390 if utils.inherit_from_std_ex(infered): |
| 391 self.add_message('indexing-exception', node=node) |
| 392 except astroid.InferenceError: |
| 393 return |
| 394 |
| 395 @utils.check_messages('unpacking-in-except') |
| 396 def visit_excepthandler(self, node): |
| 397 """Visit an except handler block and check for exception unpacking.""" |
| 398 if isinstance(node.name, (astroid.Tuple, astroid.List)): |
| 399 self.add_message('unpacking-in-except', node=node) |
| 400 |
| 401 @utils.check_messages('backtick') |
| 402 def visit_backquote(self, node): |
| 403 self.add_message('backtick', node=node) |
| 404 |
| 405 @utils.check_messages('raising-string', 'old-raise-syntax') |
| 406 def visit_raise(self, node): |
| 407 """Visit a raise statement and check for raising |
| 408 strings or old-raise-syntax. |
| 409 """ |
| 410 if (node.exc is not None and |
| 411 node.inst is not None and |
| 412 node.tback is None): |
| 413 self.add_message('old-raise-syntax', node=node) |
| 414 |
| 415 # Ignore empty raise. |
| 416 if node.exc is None: |
| 417 return |
| 418 expr = node.exc |
| 419 if self._check_raise_value(node, expr): |
| 420 return |
| 421 else: |
| 422 try: |
| 423 value = next(astroid.unpack_infer(expr)) |
| 424 except astroid.InferenceError: |
| 425 return |
| 426 self._check_raise_value(node, value) |
| 427 |
| 428 def _check_raise_value(self, node, expr): |
| 429 if isinstance(expr, astroid.Const): |
| 430 value = expr.value |
| 431 if isinstance(value, str): |
| 432 self.add_message('raising-string', node=node) |
| 433 return True |
| 434 |
| 435 |
| 436 class Python3TokenChecker(checkers.BaseTokenChecker): |
| 437 __implements__ = interfaces.ITokenChecker |
| 438 name = 'python3' |
| 439 enabled = False |
| 440 |
| 441 msgs = { |
| 442 'E1606': ('Use of long suffix', |
| 443 'long-suffix', |
| 444 'Used when "l" or "L" is used to mark a long integer. ' |
| 445 'This will not work in Python 3, since `int` and `long` ' |
| 446 'types have merged.', |
| 447 {'maxversion': (3, 0)}), |
| 448 'E1607': ('Use of the <> operator', |
| 449 'old-ne-operator', |
| 450 'Used when the deprecated "<>" operator is used instead ' |
| 451 'of "!=". This is removed in Python 3.', |
| 452 {'maxversion': (3, 0), |
| 453 'old_names': [('W0331', 'old-ne-operator')]}), |
| 454 'E1608': ('Use of old octal literal', |
| 455 'old-octal-literal', |
| 456 'Usen when encountering the old octal syntax, ' |
| 457 'removed in Python 3. To use the new syntax, ' |
| 458 'prepend 0o on the number.', |
| 459 {'maxversion': (3, 0)}), |
| 460 } |
| 461 |
| 462 def process_tokens(self, tokens): |
| 463 for idx, (tok_type, token, start, _, _) in enumerate(tokens): |
| 464 if tok_type == tokenize.NUMBER: |
| 465 if token.lower().endswith('l'): |
| 466 # This has a different semantic than lowercase-l-suffix. |
| 467 self.add_message('long-suffix', line=start[0]) |
| 468 elif _is_old_octal(token): |
| 469 self.add_message('old-octal-literal', line=start[0]) |
| 470 if tokens[idx][1] == '<>': |
| 471 self.add_message('old-ne-operator', line=tokens[idx][2][0]) |
| 472 |
| 473 |
| 474 def register(linter): |
| 475 linter.register_checker(Python3Checker(linter)) |
| 476 linter.register_checker(Python3TokenChecker(linter)) |
OLD | NEW |