summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFlorent Xicluna <florent.xicluna@gmail.com>2013-03-30 16:57:14 +0100
committerFlorent Xicluna <florent.xicluna@gmail.com>2013-03-30 16:57:14 +0100
commit9f1b092c3ac759aee3243801eb5218d4101dd67b (patch)
treea3a808aa0ffbdc4b7f9dd3ba87ae6c94d0c68c73
parent72449cccd9a1cadd6f41718cbbd6c38bd1cf2709 (diff)
downloadpyflakes-9f1b092c3ac759aee3243801eb5218d4101dd67b.tar.gz
Do not report undefined name if the code is protected with a NameError handler.
-rw-r--r--NEWS.txt2
-rw-r--r--pyflakes/checker.py43
-rw-r--r--pyflakes/test/test_undefined_names.py30
3 files changed, 67 insertions, 8 deletions
diff --git a/NEWS.txt b/NEWS.txt
index a142c55..4e9da3d 100644
--- a/NEWS.txt
+++ b/NEWS.txt
@@ -5,6 +5,8 @@
comprehension in a conditional.
- Do not report redefinition of variable for generator expressions and
set or dict comprehensions.
+ - Do not report undefined name when the code is protected with a
+ `NameError` exception handler.
0.6.1 (2013-01-29):
- Fix detection of variables in augmented assignments.
diff --git a/pyflakes/checker.py b/pyflakes/checker.py
index c8c4082..1de8661 100644
--- a/pyflakes/checker.py
+++ b/pyflakes/checker.py
@@ -211,6 +211,7 @@ class Checker(object):
if builtins:
self.builtIns = self.builtIns.union(builtins)
self.scopeStack = [ModuleScope()]
+ self.exceptHandlers = [()]
self.futuresAllowed = True
self.root = tree
self.handleChildren(tree)
@@ -391,7 +392,6 @@ class Checker(object):
if not name:
return
# try local scope
- importStarred = self.scope.importStarred
try:
self.scope[name].used = (self.scope, node.lineno)
except KeyError:
@@ -400,6 +400,7 @@ class Checker(object):
return
# try enclosing function scopes
+ importStarred = self.scope.importStarred
for scope in self.scopeStack[-2:0:-1]:
importStarred = importStarred or scope.importStarred
if not isinstance(scope, FunctionScope):
@@ -416,12 +417,20 @@ class Checker(object):
try:
self.scopeStack[0][name].used = (self.scope, node.lineno)
except KeyError:
- if not importStarred and name not in self.builtIns:
- if (os.path.basename(self.filename) == '__init__.py' and name == '__path__'):
- # the special name __path__ is valid only in packages
- pass
- else:
- self.report(messages.UndefinedName, node.lineno, name)
+ pass
+ else:
+ return
+
+ # look in the built-ins
+ if importStarred or name in self.builtIns:
+ return
+ if name == '__path__' and os.path.basename(self.filename) == '__init__.py':
+ # the special name __path__ is valid only in packages
+ return
+
+ # protected with a NameError handler?
+ if 'NameError' not in self.exceptHandlers[-1]:
+ self.report(messages.UndefinedName, node.lineno, name)
def handleNodeStore(self, node):
name = getNodeName(node)
@@ -500,7 +509,7 @@ class Checker(object):
# "stmt" type nodes
RETURN = DELETE = PRINT = WHILE = IF = WITH = WITHITEM = RAISE = \
- TRYEXCEPT = TRYFINALLY = TRY = ASSERT = EXEC = EXPR = handleChildren
+ TRYFINALLY = ASSERT = EXEC = EXPR = handleChildren
CONTINUE = BREAK = PASS = ignore
@@ -728,6 +737,24 @@ class Checker(object):
importation.used = (self.scope, node.lineno)
self.addBinding(node, importation)
+ def TRY(self, node):
+ handler_names = []
+ for handler in node.handlers:
+ if isinstance(handler.type, ast.Tuple):
+ for exc_type in handler.type.elts:
+ handler_names.append(exc_type.id)
+ elif handler.type:
+ handler_names.append(handler.type.id)
+ self.exceptHandlers.append(handler_names)
+ for child in node.body:
+ self.handleNode(child, node)
+ self.exceptHandlers.pop()
+ for child in iter_child_nodes(node):
+ if child not in node.body:
+ self.handleNode(child, node)
+
+ TRYEXCEPT = TRY
+
def EXCEPTHANDLER(self, node):
# 3.x: in addition to handling children, we must handle the name of
# the exception, which is not a Name node, but a simple string.
diff --git a/pyflakes/test/test_undefined_names.py b/pyflakes/test/test_undefined_names.py
index 3407495..f242045 100644
--- a/pyflakes/test/test_undefined_names.py
+++ b/pyflakes/test/test_undefined_names.py
@@ -337,6 +337,36 @@ class Test(harness.Test):
self.flakes('(a for a in %srange(10) if a)' %
('x' if version_info < (3,) else ''))
+ def test_undefinedWithErrorHandler(self):
+ """
+ Some compatibility code checks explicitly for NameError.
+ It should not trigger warnings.
+ """
+ self.flakes('''
+ try:
+ socket_map
+ except NameError:
+ socket_map = {}
+ ''')
+ self.flakes('''
+ try:
+ _memoryview.contiguous
+ except (NameError, AttributeError):
+ raise RuntimeError("Python >= 3.3 is required")
+ ''')
+ # If NameError is not explicitly handled, generate a warning
+ self.flakes('''
+ try:
+ socket_map
+ except:
+ socket_map = {}
+ ''', m.UndefinedName)
+ self.flakes('''
+ try:
+ socket_map
+ except Exception:
+ socket_map = {}
+ ''', m.UndefinedName)
class NameTests(TestCase):