Statistics
| Revision:

gvsig-scripting / org.gvsig.scripting / trunk / org.gvsig.scripting / org.gvsig.scripting.app / org.gvsig.scripting.app.mainplugin / src / main / resources-plugin / scripting / lib / pylint / checkers / python3.py @ 745

History | View | Annotate | Download (24.9 KB)

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, print_function
16

    
17
import re
18
import tokenize
19

    
20
import astroid
21
from astroid import bases
22

    
23
from pylint import checkers, interfaces
24
from pylint.utils import WarningScope
25
from pylint.checkers import utils
26

    
27

    
28
_ZERO = re.compile("^0+$")
29

    
30
def _is_old_octal(literal):
31
    if _ZERO.match(literal):
32
        return False
33
    if re.match('0\d+', literal):
34
        try:
35
            int(literal, 8)
36
        except ValueError:
37
            return False
38
        return True
39

    
40
def _check_dict_node(node):
41
    inferred_types = set()
42
    try:
43
        inferred = node.infer()
44
        for inferred_node in inferred:
45
            inferred_types.add(inferred_node)
46
    except (astroid.InferenceError, astroid.UnresolvableName):
47
        pass
48
    return (not inferred_types
49
            or any(isinstance(x, astroid.Dict) for x in inferred_types))
50

    
51
def _is_builtin(node):
52
    return getattr(node, 'name', None) in ('__builtin__', 'builtins')
53

    
54
_ACCEPTS_ITERATOR = {'iter', 'list', 'tuple', 'sorted', 'set', 'sum', 'any',
55
                     'all', 'enumerate', 'dict'}
56

    
57
def _in_iterating_context(node):
58
    """Check if the node is being used as an iterator.
59

60
    Definition is taken from lib2to3.fixer_util.in_special_context().
61
    """
62
    parent = node.parent
63
    # Since a call can't be the loop variant we only need to know if the node's
64
    # parent is a 'for' loop to know it's being used as the iterator for the
65
    # loop.
66
    if isinstance(parent, astroid.For):
67
        return True
68
    # Need to make sure the use of the node is in the iterator part of the
69
    # comprehension.
70
    elif isinstance(parent, astroid.Comprehension):
71
        if parent.iter == node:
72
            return True
73
    # Various built-ins can take in an iterable or list and lead to the same
74
    # value.
75
    elif isinstance(parent, astroid.Call):
76
        if isinstance(parent.func, astroid.Name):
77
            parent_scope = parent.func.lookup(parent.func.name)[0]
78
            if _is_builtin(parent_scope) and parent.func.name in _ACCEPTS_ITERATOR:
79
                return True
80
        elif isinstance(parent.func, astroid.Attribute):
81
            if parent.func.attrname == 'join':
82
                return True
83
    # If the call is in an unpacking, there's no need to warn,
84
    # since it can be considered iterating.
85
    elif (isinstance(parent, astroid.Assign) and
86
          isinstance(parent.targets[0], (astroid.List, astroid.Tuple))):
87
        if len(parent.targets[0].elts) > 1:
88
            return True
89
    return False
90

    
91

    
92
class Python3Checker(checkers.BaseChecker):
93

    
94
    __implements__ = interfaces.IAstroidChecker
95
    enabled = False
96
    name = 'python3'
97

    
98
    msgs = {
99
        # Errors for what will syntactically break in Python 3, warnings for
100
        # everything else.
101
        'E1601': ('print statement used',
102
                  'print-statement',
103
                  'Used when a print statement is used '
104
                  '(`print` is a function in Python 3)',
105
                  {'maxversion': (3, 0)}),
106
        'E1602': ('Parameter unpacking specified',
107
                  'parameter-unpacking',
108
                  'Used when parameter unpacking is specified for a function'
109
                  "(Python 3 doesn't allow it)",
110
                  {'maxversion': (3, 0)}),
111
        'E1603': ('Implicit unpacking of exceptions is not supported '
112
                  'in Python 3',
113
                  'unpacking-in-except',
114
                  'Python3 will not allow implicit unpacking of '
115
                  'exceptions in except clauses. '
116
                  'See http://www.python.org/dev/peps/pep-3110/',
117
                  {'maxversion': (3, 0),
118
                   'old_names': [('W0712', 'unpacking-in-except')]}),
119
        'E1604': ('Use raise ErrorClass(args) instead of '
120
                  'raise ErrorClass, args.',
121
                  'old-raise-syntax',
122
                  "Used when the alternate raise syntax "
123
                  "'raise foo, bar' is used "
124
                  "instead of 'raise foo(bar)'.",
125
                  {'maxversion': (3, 0),
126
                   'old_names': [('W0121', 'old-raise-syntax')]}),
127
        'E1605': ('Use of the `` operator',
128
                  'backtick',
129
                  'Used when the deprecated "``" (backtick) operator is used '
130
                  'instead  of the str() function.',
131
                  {'scope': WarningScope.NODE,
132
                   'maxversion': (3, 0),
133
                   'old_names': [('W0333', 'backtick')]}),
134
        'E1609': ('Import * only allowed at module level',
135
                  'import-star-module-level',
136
                  'Used when the import star syntax is used somewhere '
137
                  'else than the module level.',
138
                  {'maxversion': (3, 0)}),
139
        'W1601': ('apply built-in referenced',
140
                  'apply-builtin',
141
                  'Used when the apply built-in function is referenced '
142
                  '(missing from Python 3)',
143
                  {'maxversion': (3, 0)}),
144
        'W1602': ('basestring built-in referenced',
145
                  'basestring-builtin',
146
                  'Used when the basestring built-in function is referenced '
147
                  '(missing from Python 3)',
148
                  {'maxversion': (3, 0)}),
149
        'W1603': ('buffer built-in referenced',
150
                  'buffer-builtin',
151
                  'Used when the buffer built-in function is referenced '
152
                  '(missing from Python 3)',
153
                  {'maxversion': (3, 0)}),
154
        'W1604': ('cmp built-in referenced',
155
                  'cmp-builtin',
156
                  'Used when the cmp built-in function is referenced '
157
                  '(missing from Python 3)',
158
                  {'maxversion': (3, 0)}),
159
        'W1605': ('coerce built-in referenced',
160
                  'coerce-builtin',
161
                  'Used when the coerce built-in function is referenced '
162
                  '(missing from Python 3)',
163
                  {'maxversion': (3, 0)}),
164
        'W1606': ('execfile built-in referenced',
165
                  'execfile-builtin',
166
                  'Used when the execfile built-in function is referenced '
167
                  '(missing from Python 3)',
168
                  {'maxversion': (3, 0)}),
169
        'W1607': ('file built-in referenced',
170
                  'file-builtin',
171
                  'Used when the file built-in function is referenced '
172
                  '(missing from Python 3)',
173
                  {'maxversion': (3, 0)}),
174
        'W1608': ('long built-in referenced',
175
                  'long-builtin',
176
                  'Used when the long built-in function is referenced '
177
                  '(missing from Python 3)',
178
                  {'maxversion': (3, 0)}),
179
        'W1609': ('raw_input built-in referenced',
180
                  'raw_input-builtin',
181
                  'Used when the raw_input built-in function is referenced '
182
                  '(missing from Python 3)',
183
                  {'maxversion': (3, 0)}),
184
        'W1610': ('reduce built-in referenced',
185
                  'reduce-builtin',
186
                  'Used when the reduce built-in function is referenced '
187
                  '(missing from Python 3)',
188
                  {'maxversion': (3, 0)}),
189
        'W1611': ('StandardError built-in referenced',
190
                  'standarderror-builtin',
191
                  'Used when the StandardError built-in function is referenced '
192
                  '(missing from Python 3)',
193
                  {'maxversion': (3, 0)}),
194
        'W1612': ('unicode built-in referenced',
195
                  'unicode-builtin',
196
                  'Used when the unicode built-in function is referenced '
197
                  '(missing from Python 3)',
198
                  {'maxversion': (3, 0)}),
199
        'W1613': ('xrange built-in referenced',
200
                  'xrange-builtin',
201
                  'Used when the xrange built-in function is referenced '
202
                  '(missing from Python 3)',
203
                  {'maxversion': (3, 0)}),
204
        'W1614': ('__coerce__ method defined',
205
                  'coerce-method',
206
                  'Used when a __coerce__ method is defined '
207
                  '(method is not used by Python 3)',
208
                  {'maxversion': (3, 0)}),
209
        'W1615': ('__delslice__ method defined',
210
                  'delslice-method',
211
                  'Used when a __delslice__ method is defined '
212
                  '(method is not used by Python 3)',
213
                  {'maxversion': (3, 0)}),
214
        'W1616': ('__getslice__ method defined',
215
                  'getslice-method',
216
                  'Used when a __getslice__ method is defined '
217
                  '(method is not used by Python 3)',
218
                  {'maxversion': (3, 0)}),
219
        'W1617': ('__setslice__ method defined',
220
                  'setslice-method',
221
                  'Used when a __setslice__ method is defined '
222
                  '(method is not used by Python 3)',
223
                  {'maxversion': (3, 0)}),
224
        'W1618': ('import missing `from __future__ import absolute_import`',
225
                  'no-absolute-import',
226
                  'Used when an import is not accompanied by '
227
                  '``from __future__ import absolute_import`` '
228
                  '(default behaviour in Python 3)',
229
                  {'maxversion': (3, 0)}),
230
        'W1619': ('division w/o __future__ statement',
231
                  'old-division',
232
                  'Used for non-floor division w/o a float literal or '
233
                  '``from __future__ import division`` '
234
                  '(Python 3 returns a float for int division unconditionally)',
235
                  {'maxversion': (3, 0)}),
236
        'W1620': ('Calling a dict.iter*() method',
237
                  'dict-iter-method',
238
                  'Used for calls to dict.iterkeys(), itervalues() or iteritems() '
239
                  '(Python 3 lacks these methods)',
240
                  {'maxversion': (3, 0)}),
241
        'W1621': ('Calling a dict.view*() method',
242
                  'dict-view-method',
243
                  'Used for calls to dict.viewkeys(), viewvalues() or viewitems() '
244
                  '(Python 3 lacks these methods)',
245
                  {'maxversion': (3, 0)}),
246
        'W1622': ('Called a next() method on an object',
247
                  'next-method-called',
248
                  "Used when an object's next() method is called "
249
                  '(Python 3 uses the next() built-in function)',
250
                  {'maxversion': (3, 0)}),
251
        'W1623': ("Assigning to a class's __metaclass__ attribute",
252
                  'metaclass-assignment',
253
                  "Used when a metaclass is specified by assigning to __metaclass__ "
254
                  '(Python 3 specifies the metaclass as a class statement argument)',
255
                  {'maxversion': (3, 0)}),
256
        'W1624': ('Indexing exceptions will not work on Python 3',
257
                  'indexing-exception',
258
                  'Indexing exceptions will not work on Python 3. Use '
259
                  '`exception.args[index]` instead.',
260
                  {'maxversion': (3, 0),
261
                   'old_names': [('W0713', 'indexing-exception')]}),
262
        'W1625': ('Raising a string exception',
263
                  'raising-string',
264
                  'Used when a string exception is raised. This will not '
265
                  'work on Python 3.',
266
                  {'maxversion': (3, 0),
267
                   'old_names': [('W0701', 'raising-string')]}),
268
        'W1626': ('reload built-in referenced',
269
                  'reload-builtin',
270
                  'Used when the reload built-in function is referenced '
271
                  '(missing from Python 3). You can use instead imp.reload '
272
                  'or importlib.reload.',
273
                  {'maxversion': (3, 0)}),
274
        'W1627': ('__oct__ method defined',
275
                  'oct-method',
276
                  'Used when a __oct__ method is defined '
277
                  '(method is not used by Python 3)',
278
                  {'maxversion': (3, 0)}),
279
        'W1628': ('__hex__ method defined',
280
                  'hex-method',
281
                  'Used when a __hex__ method is defined '
282
                  '(method is not used by Python 3)',
283
                  {'maxversion': (3, 0)}),
284
        'W1629': ('__nonzero__ method defined',
285
                  'nonzero-method',
286
                  'Used when a __nonzero__ method is defined '
287
                  '(method is not used by Python 3)',
288
                  {'maxversion': (3, 0)}),
289
        'W1630': ('__cmp__ method defined',
290
                  'cmp-method',
291
                  'Used when a __cmp__ method is defined '
292
                  '(method is not used by Python 3)',
293
                  {'maxversion': (3, 0)}),
294
        # 'W1631': replaced by W1636
295
        'W1632': ('input built-in referenced',
296
                  'input-builtin',
297
                  'Used when the input built-in is referenced '
298
                  '(backwards-incompatible semantics in Python 3)',
299
                  {'maxversion': (3, 0)}),
300
        'W1633': ('round built-in referenced',
301
                  'round-builtin',
302
                  'Used when the round built-in is referenced '
303
                  '(backwards-incompatible semantics in Python 3)',
304
                  {'maxversion': (3, 0)}),
305
        'W1634': ('intern built-in referenced',
306
                  'intern-builtin',
307
                  'Used when the intern built-in is referenced '
308
                  '(Moved to sys.intern in Python 3)',
309
                  {'maxversion': (3, 0)}),
310
        'W1635': ('unichr built-in referenced',
311
                  'unichr-builtin',
312
                  'Used when the unichr built-in is referenced '
313
                  '(Use chr in Python 3)',
314
                  {'maxversion': (3, 0)}),
315
        'W1636': ('map built-in referenced when not iterating',
316
                  'map-builtin-not-iterating',
317
                  'Used when the map built-in is referenced in a non-iterating '
318
                  'context (returns an iterator in Python 3)',
319
                  {'maxversion': (3, 0),
320
                   'old_names': [('W1631', 'implicit-map-evaluation')]}),
321
        'W1637': ('zip built-in referenced when not iterating',
322
                  'zip-builtin-not-iterating',
323
                  'Used when the zip built-in is referenced in a non-iterating '
324
                  'context (returns an iterator in Python 3)',
325
                  {'maxversion': (3, 0)}),
326
        'W1638': ('range built-in referenced when not iterating',
327
                  'range-builtin-not-iterating',
328
                  'Used when the range built-in is referenced in a non-iterating '
329
                  'context (returns an iterator in Python 3)',
330
                  {'maxversion': (3, 0)}),
331
        'W1639': ('filter built-in referenced when not iterating',
332
                  'filter-builtin-not-iterating',
333
                  'Used when the filter built-in is referenced in a non-iterating '
334
                  'context (returns an iterator in Python 3)',
335
                  {'maxversion': (3, 0)}),
336
        'W1640': ('Using the cmp argument for list.sort / sorted',
337
                  'using-cmp-argument',
338
                  'Using the cmp argument for list.sort or the sorted '
339
                  'builtin should be avoided, since it was removed in '
340
                  'Python 3. Using either `key` or `functools.cmp_to_key` '
341
                  'should be preferred.',
342
                  {'maxversion': (3, 0)}),
343
    }
344

    
345
    _bad_builtins = frozenset([
346
        'apply',
347
        'basestring',
348
        'buffer',
349
        'cmp',
350
        'coerce',
351
        'execfile',
352
        'file',
353
        'input',  # Not missing, but incompatible semantics
354
        'intern',
355
        'long',
356
        'raw_input',
357
        'reduce',
358
        'round',  # Not missing, but incompatible semantics
359
        'StandardError',
360
        'unichr',
361
        'unicode',
362
        'xrange',
363
        'reload',
364
    ])
365

    
366
    _unused_magic_methods = frozenset([
367
        '__coerce__',
368
        '__delslice__',
369
        '__getslice__',
370
        '__setslice__',
371
        '__oct__',
372
        '__hex__',
373
        '__nonzero__',
374
        '__cmp__',
375
    ])
376

    
377
    def __init__(self, *args, **kwargs):
378
        self._future_division = False
379
        self._future_absolute_import = False
380
        super(Python3Checker, self).__init__(*args, **kwargs)
381

    
382
    def visit_module(self, node): # pylint: disable=unused-argument
383
        """Clear checker state after previous module."""
384
        self._future_division = False
385
        self._future_absolute_import = False
386

    
387
    def visit_functiondef(self, node):
388
        if node.is_method() and node.name in self._unused_magic_methods:
389
            method_name = node.name
390
            if node.name.startswith('__'):
391
                method_name = node.name[2:-2]
392
            self.add_message(method_name + '-method', node=node)
393

    
394
    @utils.check_messages('parameter-unpacking')
395
    def visit_arguments(self, node):
396
        for arg in node.args:
397
            if isinstance(arg, astroid.Tuple):
398
                self.add_message('parameter-unpacking', node=arg)
399

    
400
    def visit_name(self, node):
401
        """Detect when a "bad" built-in is referenced."""
402
        found_node = node.lookup(node.name)[0]
403
        if _is_builtin(found_node):
404
            if node.name in self._bad_builtins:
405
                message = node.name.lower() + '-builtin'
406
                self.add_message(message, node=node)
407

    
408
    @utils.check_messages('print-statement')
409
    def visit_print(self, node):
410
        self.add_message('print-statement', node=node)
411

    
412
    @utils.check_messages('no-absolute-import', 'import-star-module-level')
413
    def visit_importfrom(self, node):
414
        if node.modname == '__future__':
415
            for name, _ in node.names:
416
                if name == 'division':
417
                    self._future_division = True
418
                elif name == 'absolute_import':
419
                    self._future_absolute_import = True
420
        elif not self._future_absolute_import:
421
            self.add_message('no-absolute-import', node=node)
422
        if node.names[0][0] == '*':
423
            if not isinstance(node.scope(), astroid.Module):
424
                self.add_message('import-star-module-level', node=node)
425

    
426
    @utils.check_messages('no-absolute-import')
427
    def visit_import(self, node):
428
        if not self._future_absolute_import:
429
            self.add_message('no-absolute-import', node=node)
430

    
431
    @utils.check_messages('metaclass-assignment')
432
    def visit_classdef(self, node):
433
        if '__metaclass__' in node.locals:
434
            self.add_message('metaclass-assignment', node=node)
435

    
436
    @utils.check_messages('old-division')
437
    def visit_binop(self, node):
438
        if not self._future_division and node.op == '/':
439
            for arg in (node.left, node.right):
440
                if isinstance(arg, astroid.Const) and isinstance(arg.value, float):
441
                    break
442
            else:
443
                self.add_message('old-division', node=node)
444

    
445
    def _check_cmp_argument(self, node):
446
        # Check that the `cmp` argument is used
447
        kwargs = []
448
        if (isinstance(node.func, astroid.Attribute)
449
                and node.func.attrname == 'sort'):
450
            inferred = utils.safe_infer(node.func.expr)
451
            if not inferred:
452
                return
453

    
454
            builtins_list = "{}.list".format(bases.BUILTINS)
455
            if (isinstance(inferred, astroid.List)
456
                    or inferred.qname() == builtins_list):
457
                kwargs = node.keywords
458

    
459
        elif (isinstance(node.func, astroid.Name)
460
              and node.func.name == 'sorted'):
461
            inferred = utils.safe_infer(node.func)
462
            if not inferred:
463
                return
464

    
465
            builtins_sorted = "{}.sorted".format(bases.BUILTINS)
466
            if inferred.qname() == builtins_sorted:
467
                kwargs = node.keywords
468

    
469
        for kwarg in kwargs or []:
470
            if kwarg.arg == 'cmp':
471
                self.add_message('using-cmp-argument', node=node)
472
                return
473

    
474
    def visit_call(self, node):
475
        self._check_cmp_argument(node)
476

    
477
        if isinstance(node.func, astroid.Attribute):
478
            if any([node.args, node.keywords]):
479
                return
480
            if node.func.attrname == 'next':
481
                self.add_message('next-method-called', node=node)
482
            else:
483
                if _check_dict_node(node.func.expr):
484
                    if node.func.attrname in ('iterkeys', 'itervalues', 'iteritems'):
485
                        self.add_message('dict-iter-method', node=node)
486
                    elif node.func.attrname in ('viewkeys', 'viewvalues', 'viewitems'):
487
                        self.add_message('dict-view-method', node=node)
488
        elif isinstance(node.func, astroid.Name):
489
            found_node = node.func.lookup(node.func.name)[0]
490
            if _is_builtin(found_node):
491
                if node.func.name in ('filter', 'map', 'range', 'zip'):
492
                    if not _in_iterating_context(node):
493
                        checker = '{}-builtin-not-iterating'.format(node.func.name)
494
                        self.add_message(checker, node=node)
495

    
496

    
497
    @utils.check_messages('indexing-exception')
498
    def visit_subscript(self, node):
499
        """ Look for indexing exceptions. """
500
        try:
501
            for infered in node.value.infer():
502
                if not isinstance(infered, astroid.Instance):
503
                    continue
504
                if utils.inherit_from_std_ex(infered):
505
                    self.add_message('indexing-exception', node=node)
506
        except astroid.InferenceError:
507
            return
508

    
509
    @utils.check_messages('unpacking-in-except')
510
    def visit_excepthandler(self, node):
511
        """Visit an except handler block and check for exception unpacking."""
512
        if isinstance(node.name, (astroid.Tuple, astroid.List)):
513
            self.add_message('unpacking-in-except', node=node)
514

    
515
    @utils.check_messages('backtick')
516
    def visit_repr(self, node):
517
        self.add_message('backtick', node=node)
518

    
519
    @utils.check_messages('raising-string', 'old-raise-syntax')
520
    def visit_raise(self, node):
521
        """Visit a raise statement and check for raising
522
        strings or old-raise-syntax.
523
        """
524
        if (node.exc is not None and
525
                node.inst is not None and
526
                node.tback is None):
527
            self.add_message('old-raise-syntax', node=node)
528

    
529
        # Ignore empty raise.
530
        if node.exc is None:
531
            return
532
        expr = node.exc
533
        if self._check_raise_value(node, expr):
534
            return
535
        else:
536
            try:
537
                value = next(astroid.unpack_infer(expr))
538
            except astroid.InferenceError:
539
                return
540
            self._check_raise_value(node, value)
541

    
542
    def _check_raise_value(self, node, expr):
543
        if isinstance(expr, astroid.Const):
544
            value = expr.value
545
            if isinstance(value, str):
546
                self.add_message('raising-string', node=node)
547
                return True
548

    
549

    
550
class Python3TokenChecker(checkers.BaseTokenChecker):
551
    __implements__ = interfaces.ITokenChecker
552
    name = 'python3'
553
    enabled = False
554

    
555
    msgs = {
556
        'E1606': ('Use of long suffix',
557
                  'long-suffix',
558
                  'Used when "l" or "L" is used to mark a long integer. '
559
                  'This will not work in Python 3, since `int` and `long` '
560
                  'types have merged.',
561
                  {'maxversion': (3, 0)}),
562
        'E1607': ('Use of the <> operator',
563
                  'old-ne-operator',
564
                  'Used when the deprecated "<>" operator is used instead '
565
                  'of "!=". This is removed in Python 3.',
566
                  {'maxversion': (3, 0),
567
                   'old_names': [('W0331', 'old-ne-operator')]}),
568
        'E1608': ('Use of old octal literal',
569
                  'old-octal-literal',
570
                  'Usen when encountering the old octal syntax, '
571
                  'removed in Python 3. To use the new syntax, '
572
                  'prepend 0o on the number.',
573
                  {'maxversion': (3, 0)}),
574
    }
575

    
576
    def process_tokens(self, tokens):
577
        for idx, (tok_type, token, start, _, _) in enumerate(tokens):
578
            if tok_type == tokenize.NUMBER:
579
                if token.lower().endswith('l'):
580
                    # This has a different semantic than lowercase-l-suffix.
581
                    self.add_message('long-suffix', line=start[0])
582
                elif _is_old_octal(token):
583
                    self.add_message('old-octal-literal', line=start[0])
584
            if tokens[idx][1] == '<>':
585
                self.add_message('old-ne-operator', line=tokens[idx][2][0])
586

    
587

    
588
def register(linter):
589
    linter.register_checker(Python3Checker(linter))
590
    linter.register_checker(Python3TokenChecker(linter))