Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

handle del inside function definition #467

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 21 additions & 3 deletions pyflakes/checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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))
Expand All @@ -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
Expand Down Expand Up @@ -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):
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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, ()):
Expand Down
37 changes: 37 additions & 0 deletions pyflakes/test/test_undefined_names.py
Original file line number Diff line number Diff line change
Expand Up @@ -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('''
Expand Down