From 78232ae7de9c00ca4edfbc506074843ed3592c50 Mon Sep 17 00:00:00 2001 From: Hanaasagi Date: Wed, 4 Sep 2019 13:34:07 +0000 Subject: [PATCH 1/4] fix: stop check variable in outer scope when it's deleted in current scope --- pyflakes/checker.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/pyflakes/checker.py b/pyflakes/checker.py index 8196fa4c..2f1b4c31 100644 --- a/pyflakes/checker.py +++ b/pyflakes/checker.py @@ -540,6 +540,9 @@ def _add_to_names(container): class Scope(dict): importStarred = False # set to True when import * is found + def __init__(self): + self.deleted_names = [] + def __repr__(self): scope_cls = self.__class__.__name__ return '<%s at 0x%x %s>' % (scope_cls, id(self), dict.__repr__(self)) @@ -1021,6 +1024,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): @@ -1144,11 +1151,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, ()): From 96e0da8e1b9ce2ab0e1e5afa4c6b58b5a7485134 Mon Sep 17 00:00:00 2001 From: Hanaasagi Date: Wed, 4 Sep 2019 23:19:11 +0900 Subject: [PATCH 2/4] refactor: inherit from UserDict --- pyflakes/checker.py | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/pyflakes/checker.py b/pyflakes/checker.py index 2f1b4c31..bc79a2cf 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,10 +542,11 @@ 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): + def __init__(self, *args, **kwargs): + super(Scope, self).__init__(*args, **kwargs) self.deleted_names = [] def __repr__(self): @@ -562,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 From 2d07acffbb92911900580f0299d998af6b620d3c Mon Sep 17 00:00:00 2001 From: Hanaasagi Date: Wed, 4 Sep 2019 23:34:58 +0900 Subject: [PATCH 3/4] test: Add test for issue #428 --- pyflakes/test/test_undefined_names.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/pyflakes/test/test_undefined_names.py b/pyflakes/test/test_undefined_names.py index c952cbb6..e62e8aac 100644 --- a/pyflakes/test/test_undefined_names.py +++ b/pyflakes/test/test_undefined_names.py @@ -446,6 +446,33 @@ 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_globalFromNestedScope(self): """Global names are available from nested scopes.""" self.flakes(''' From 98b9068e0d447b15b0c26746e1db64e8527db55a Mon Sep 17 00:00:00 2001 From: Hanaasagi Date: Thu, 5 Sep 2019 08:37:05 +0900 Subject: [PATCH 4/4] fix: handle names redefined --- pyflakes/checker.py | 2 ++ pyflakes/test/test_undefined_names.py | 10 ++++++++++ 2 files changed, 12 insertions(+) diff --git a/pyflakes/checker.py b/pyflakes/checker.py index bc79a2cf..428a08f4 100644 --- a/pyflakes/checker.py +++ b/pyflakes/checker.py @@ -1104,6 +1104,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 diff --git a/pyflakes/test/test_undefined_names.py b/pyflakes/test/test_undefined_names.py index e62e8aac..101417b6 100644 --- a/pyflakes/test/test_undefined_names.py +++ b/pyflakes/test/test_undefined_names.py @@ -473,6 +473,16 @@ def method(self): 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('''