diff options
author | Florent Xicluna <florent.xicluna@gmail.com> | 2013-03-30 16:57:14 +0100 |
---|---|---|
committer | Florent Xicluna <florent.xicluna@gmail.com> | 2013-03-30 16:57:14 +0100 |
commit | 9f1b092c3ac759aee3243801eb5218d4101dd67b (patch) | |
tree | a3a808aa0ffbdc4b7f9dd3ba87ae6c94d0c68c73 | |
parent | 72449cccd9a1cadd6f41718cbbd6c38bd1cf2709 (diff) | |
download | pyflakes-9f1b092c3ac759aee3243801eb5218d4101dd67b.tar.gz |
Do not report undefined name if the code is protected with a NameError handler.
-rw-r--r-- | NEWS.txt | 2 | ||||
-rw-r--r-- | pyflakes/checker.py | 43 | ||||
-rw-r--r-- | pyflakes/test/test_undefined_names.py | 30 |
3 files changed, 67 insertions, 8 deletions
@@ -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): |