diff --git a/pyflakes/checker.py b/pyflakes/checker.py index 44c6b257..ef9ab22e 100644 --- a/pyflakes/checker.py +++ b/pyflakes/checker.py @@ -16,6 +16,11 @@ import sys import tokenize +try: + from collections import UserDict +except ImportError: + from UserDict import IterableUserDict as UserDict + from pyflakes import messages PY2 = sys.version_info < (3, 0) @@ -537,9 +542,13 @@ def _add_to_names(container): super(ExportBinding, self).__init__(name, source) -class Scope(dict): +class Scope(UserDict, object): importStarred = False # set to True when import * is found + def __init__(self, *args, **kwargs): + super(Scope, self).__init__(*args, **kwargs) + self.deleted_names = [] + def __repr__(self): scope_cls = self.__class__.__name__ return '<%s at 0x%x %s>' % (scope_cls, id(self), dict.__repr__(self)) @@ -559,8 +568,8 @@ class FunctionScope(Scope): alwaysUsed = {'__tracebackhide__', '__traceback_info__', '__traceback_supplement__'} - def __init__(self): - super(FunctionScope, self).__init__() + def __init__(self, *args, **kwargs): + super(FunctionScope, self).__init__(*args, **kwargs) # Simplify: manage the special locals as globals self.globals = self.alwaysUsed.copy() self.returnValue = None # First non-empty return @@ -1023,6 +1032,10 @@ def handleNodeLoad(self, node): in_generators = None importStarred = None + if name in self.scope.deleted_names: + self.report(messages.UndefinedName, node, name) + return + # try enclosing function scopes and global scope for scope in self.scopeStack[-1::-1]: if isinstance(scope, ClassScope): @@ -1093,6 +1106,8 @@ def handleNodeStore(self, node): name = getNodeName(node) if not name: return + if name in self.scope.deleted_names: + self.scope.deleted_names.remove(name) # if the name hasn't already been defined in the current scope if isinstance(self.scope, FunctionScope) and name not in self.scope: # for each function or module scope above us @@ -1146,11 +1161,14 @@ def on_conditional_branch(): if isinstance(self.scope, FunctionScope) and name in self.scope.globals: self.scope.globals.remove(name) + self.scope.deleted_names.append(name) else: try: del self.scope[name] except KeyError: self.report(messages.UndefinedName, node, name) + else: + self.scope.deleted_names.append(name) def _handle_type_comments(self, node): for (lineno, col_offset), comment in self._type_comments.get(node, ()): diff --git a/pyflakes/test/test_undefined_names.py b/pyflakes/test/test_undefined_names.py index c952cbb6..101417b6 100644 --- a/pyflakes/test/test_undefined_names.py +++ b/pyflakes/test/test_undefined_names.py @@ -446,6 +446,43 @@ def _worker(): o = False ''') + def test_delFunctionScope(self): + """ + Global names should not be seen if there are same names + defined in function. + """ + self.flakes(''' + a = 1 + def func(): + a = 2 + del a + a + ''', m.UndefinedName) + + def test_delMethodScope(self): + """ + Global names should not be seen if there are same names + defined in method. + """ + self.flakes(''' + a = 1 + class A(object): + def method(self): + a = 2 + del a + a + ''', m.UndefinedName) + + def test_RedefinedDeletedName(self): + self.flakes(''' + a = 1 + def func(): + a = 2 + del a + a = 3 + a + ''') + def test_globalFromNestedScope(self): """Global names are available from nested scopes.""" self.flakes('''