diff options
author | MacBox7 <ajankit2304@gmail.com> | 2018-06-30 13:14:23 +0530 |
---|---|---|
committer | MacBox7 <ajankit2304@gmail.com> | 2018-08-01 23:33:17 +0530 |
commit | fb1f223ac2141772f140be6f1108d02009c1d658 (patch) | |
tree | 0bdd1cdc10a1864d388b418a09a5a0119b35d524 | |
parent | 9dd73ec54411a563410872b47e76f0f89f34ecfe (diff) | |
download | pyflakes-fb1f223ac2141772f140be6f1108d02009c1d658.tar.gz |
checker.py: Handle UnboundLocal error for builtins
This patch ensures that UnboundLocalError is not
ignored for Python builtins. Populates ModuleScope
with Builtin nodes to manage them as bindings.
Fixes https://github.com/PyCQA/pyflakes/issues/350
-rw-r--r-- | pyflakes/checker.py | 27 | ||||
-rw-r--r-- | pyflakes/messages.py | 13 | ||||
-rw-r--r-- | pyflakes/test/test_builtin.py | 41 |
3 files changed, 68 insertions, 13 deletions
diff --git a/pyflakes/checker.py b/pyflakes/checker.py index 1c30dda..2ce6250 100644 --- a/pyflakes/checker.py +++ b/pyflakes/checker.py @@ -180,6 +180,18 @@ class Definition(Binding): """ +class Builtin(Definition): + """A definition created for all Python builtins.""" + + def __init__(self, name): + super(Builtin, self).__init__(name, None) + + def __repr__(self): + return '<%s object %r at 0x%x>' % (self.__class__.__name__, + self.name, + id(self)) + + class UnhandledKeyType(object): """ A dictionary key of a type that we cannot or do not check for duplicates. @@ -516,6 +528,8 @@ class Checker(object): raise RuntimeError('No scope implemented for the node %r' % tree) self.exceptHandlers = [()] self.root = tree + for builtin in self.builtIns: + self.addBinding(None, Builtin(builtin)) self.handleChildren(tree) self.runDeferred(self._deferredFunctions) # Set _deferredFunctions to None so that deferFunction will fail @@ -699,7 +713,8 @@ class Checker(object): break existing = scope.get(value.name) - if existing and not self.differentForks(node, existing.source): + if (existing and not isinstance(existing, Builtin) and + not self.differentForks(node, existing.source)): parent_stmt = self.getParent(value.source) if isinstance(existing, Importation) and isinstance(parent_stmt, ast.For): @@ -765,10 +780,6 @@ class Checker(object): if in_generators is not False: in_generators = isinstance(scope, GeneratorScope) - # look in the built-ins - if name in self.builtIns: - return - if importStarred: from_list = [] @@ -943,9 +954,7 @@ class Checker(object): self.scopeStack = [self.scopeStack[0]] node_offset = self.offset or (0, 0) self.pushScope(DoctestScope) - underscore_in_builtins = '_' in self.builtIns - if not underscore_in_builtins: - self.builtIns.add('_') + self.addBinding(None, Builtin('_')) for example in examples: try: tree = compile(example.source, "<doctest>", "exec", ast.PyCF_ONLY_AST) @@ -961,8 +970,6 @@ class Checker(object): node_offset[1] + example.indent + 4) self.handleChildren(tree) self.offset = node_offset - if not underscore_in_builtins: - self.builtIns.remove('_') self.popScope() self.scopeStack = saved_stack diff --git a/pyflakes/messages.py b/pyflakes/messages.py index eb87a72..bd70693 100644 --- a/pyflakes/messages.py +++ b/pyflakes/messages.py @@ -100,12 +100,19 @@ class UndefinedExport(Message): class UndefinedLocal(Message): - message = ('local variable %r (defined in enclosing scope on line %r) ' - 'referenced before assignment') + message = 'local variable %r {0} referenced before assignment' + + default = 'defined in enclosing scope on line %r' + builtin = 'defined as a builtin' def __init__(self, filename, loc, name, orig_loc): Message.__init__(self, filename, loc) - self.message_args = (name, orig_loc.lineno) + if orig_loc is None: + self.message = self.message.format(self.builtin) + self.message_args = name + else: + self.message = self.message.format(self.default) + self.message_args = (name, orig_loc.lineno) class DuplicateArgument(Message): diff --git a/pyflakes/test/test_builtin.py b/pyflakes/test/test_builtin.py new file mode 100644 index 0000000..7150ddb --- /dev/null +++ b/pyflakes/test/test_builtin.py @@ -0,0 +1,41 @@ +""" +Tests for detecting redefinition of builtins. +""" +from sys import version_info + +from pyflakes import messages as m +from pyflakes.test.harness import TestCase, skipIf + + +class TestBuiltins(TestCase): + + def test_builtin_unbound_local(self): + self.flakes(''' + def foo(): + a = range(1, 10) + range = a + return range + + foo() + + print(range) + ''', m.UndefinedLocal) + + def test_global_shadowing_builtin(self): + self.flakes(''' + def f(): + global range + range = None + print(range) + + f() + ''') + + @skipIf(version_info >= (3,), 'not an UnboundLocalError in Python 3') + def test_builtin_in_comprehension(self): + self.flakes(''' + def f(): + [range for range in range(1, 10)] + + f() + ''', m.UndefinedLocal) |