Update pyflakes to 0.7.3 BgService

Sun, 05 Jan 2014 22:45:29 +0100

author
T.Rzepka <Tobias.Rzepka@gmail.com>
date
Sun, 05 Jan 2014 22:45:29 +0100
branch
BgService
changeset 3177
5af61402d74d
parent 3174
86047f5f4155
child 3209
c5432abceb25

Update pyflakes to 0.7.3

Plugins/CheckerPlugins/SyntaxChecker/SyntaxCheck.py file | annotate | diff | comparison | revisions
Plugins/CheckerPlugins/SyntaxChecker/SyntaxCheckerDialog.py file | annotate | diff | comparison | revisions
Plugins/CheckerPlugins/SyntaxChecker/pyflakes/__init__.py file | annotate | diff | comparison | revisions
Plugins/CheckerPlugins/SyntaxChecker/pyflakes/checker.py file | annotate | diff | comparison | revisions
Plugins/CheckerPlugins/SyntaxChecker/pyflakes/messages.py file | annotate | diff | comparison | revisions
QScintilla/Editor.py file | annotate | diff | comparison | revisions
Utilities/InternalServices.py file | annotate | diff | comparison | revisions
--- a/Plugins/CheckerPlugins/SyntaxChecker/SyntaxCheck.py	Sat Jan 04 22:14:38 2014 +0100
+++ b/Plugins/CheckerPlugins/SyntaxChecker/SyntaxCheck.py	Sun Jan 05 22:45:29 2014 +0100
@@ -143,8 +143,6 @@
             error = detail.msg
         return (True, fn, int(line), index, code, error, [])
     except ValueError as detail:
-        index = 0
-        code = ""
         try:
             fn = detail.filename
             line = detail.lineno
@@ -153,15 +151,13 @@
             fn = filename
             line = 1
             error = str(detail)
-        return (True, fn, line, index, code, error, [])
+        return (True, fn, line, 0, "", error, [])
     except Exception as detail:
         try:
             fn = detail.filename
             line = detail.lineno
-            index = 0
-            code = ""
             error = detail.msg
-            return (True, fn, line, index, code, error, [])
+            return (True, fn, line, 0, "", error, [])
         except:         # this catchall is intentional
             pass
     
@@ -179,16 +175,16 @@
                     isinstance(warning, ImportStarUsed):
                 continue
             
-            _fn, lineno, message, msg_args = warning.getMessageData()
+            _fn, lineno, col, message, msg_args = warning.getMessageData()
             if "__IGNORE_WARNING__" not in extractLineFlags(
                     lines[lineno - 1].strip()):
                 strings.append([
-                    "FLAKES_WARNING", _fn, lineno, message, msg_args])
+                    "FLAKES_WARNING", _fn, lineno, col, message, msg_args])
     except SyntaxError as err:
         if err.text.strip():
             msg = err.text.strip()
         else:
             msg = err.msg
-        strings.append(["FLAKES_ERROR", filename, err.lineno, msg, ()])
+        strings.append(["FLAKES_ERROR", filename, err.lineno, 0, msg, ()])
     
     return (False, "", -1, -1, "", "", strings)
--- a/Plugins/CheckerPlugins/SyntaxChecker/SyntaxCheckerDialog.py	Sat Jan 04 22:14:38 2014 +0100
+++ b/Plugins/CheckerPlugins/SyntaxChecker/SyntaxCheckerDialog.py	Sun Jan 05 22:45:29 2014 +0100
@@ -241,7 +241,7 @@
         @param code the part of the code where the error occured (str)
         @param error the name of the error (str)
         @param warnings a list of strings containing the warnings
-            (marker, file name, line number, message)
+            (marker, file name, line number, col, message)
         """
         # Check if it's the requested file, otherwise ignore signal
         if fn != self.filename:
@@ -253,12 +253,10 @@
                 fname, line, index, error, code.strip(), False)
         else:
             source = self.source.splitlines()
-            for warning in warnings:
+            for marker, _fn, lineno, col, msg in warnings:
                 self.noResults = False
-                scr_line = source[warning[2] - 1].strip()
-                self.__createResultItem(
-                    warning[1], warning[2], 0,
-                    warning[3], scr_line, True)
+                scr_line = source[lineno - 1].strip()
+                self.__createResultItem(_fn, lineno, col, msg, scr_line, True)
         self.progress += 1
         self.checkProgress.setValue(self.progress)
         self.checkProgressLabel.setPath("")
--- a/Plugins/CheckerPlugins/SyntaxChecker/pyflakes/__init__.py	Sat Jan 04 22:14:38 2014 +0100
+++ b/Plugins/CheckerPlugins/SyntaxChecker/pyflakes/__init__.py	Sun Jan 05 22:45:29 2014 +0100
@@ -1,10 +1,10 @@
 # -*- coding: utf-8 -*-
 
-# Copyright (c) 2010 - 2013 Detlev Offenbach <detlev@die-offenbachs.de>
+# Copyright (c) 2010 - 2014 Detlev Offenbach <detlev@die-offenbachs.de>
 #
 
 """
-Package containg the pyflakes Python2 port adapted for Qt.
+Package containg pyflakes adapted for Qt.
 """
 
 """ License
@@ -32,6 +32,40 @@
 """
 
 """ Changes
+0.7.3 (2013-07-02):
+  - Do not report undefined name for generator expression and dict or
+    set comprehension at class level.
+  - Deprecate `Checker.pushFunctionScope` and `Checker.pushClassScope`:
+    use `Checker.pushScope` instead.
+  - Remove dependency on Unittest2 for the tests.
+
+0.7.2 (2013-04-24):
+  - Fix computation of `DoctestSyntaxError.lineno` and `col`.
+  - Add boolean attribute `Checker.withDoctest` to ignore doctests.
+  - If environment variable `PYFLAKES_NODOCTEST` is set, skip doctests.
+  - Environment variable `PYFLAKES_BUILTINS` accepts a comma-separated
+    list of additional built-in names.
+
+0.7.1 (2013-04-23):
+  - File `bin/pyflakes` was missing in tarball generated with distribute.
+  - Fix reporting errors in non-ASCII filenames (Python 2.x).
+
+0.7.0 (2013-04-17):
+  - Add --version and --help options.
+  - Support `python -m pyflakes` (Python 2.7 and Python 3.x).
+  - Add attribute `Message.col` to report column offset.
+  - Do not report redefinition of variable for a variable used in a list
+    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.
+  - Do not report redefinition of variable when unassigning a module imported
+    for its side-effect.
+  - Support special locals like `__tracebackhide__` for py.test.
+  - Support checking doctests.
+  - Fix issue with Turkish locale where `'i'.upper() == 'i'` in Python 2.
+
 0.6.1 (2013-01-29):
   - Fix detection of variables in augmented assignments.
 
@@ -59,4 +93,4 @@
   - Make sure class names don't get bound until after class definition.
 """
 
-__version__ = '0.6.1'
+__version__ = '0.7.3'
--- a/Plugins/CheckerPlugins/SyntaxChecker/pyflakes/checker.py	Sat Jan 04 22:14:38 2014 +0100
+++ b/Plugins/CheckerPlugins/SyntaxChecker/pyflakes/checker.py	Sun Jan 05 22:45:29 2014 +0100
@@ -1,34 +1,45 @@
 # -*- coding: utf-8 -*-
 
-# Copyright (c) 2010 - 2013 Detlev Offenbach <detlev@die-offenbachs.de>
+# Copyright (c) 2010 - 2014 Detlev Offenbach <detlev@die-offenbachs.de>
 #
 # Original (c) 2005-2010 Divmod, Inc.
 #
-# This module is based on pyflakes for Python2 but was modified to
-# work with eric5
+# This module is based on pyflakes but was modified to work with eric5
+"""
+Main module.
 
-import os.path
+Implement the central Checker class.
+Also, it models the Bindings and Scopes.
+"""
+import doctest
+import os
+import sys
 try:
-    import builtins
+    builtin_vars = dir(__import__('builtins'))
     PY2 = False
 except ImportError:
-    import __builtin__ as builtins      #__IGNORE_WARNING__
+    builtin_vars = dir(__import__('__builtin__'))
     PY2 = True
 
 try:
     import ast
     iter_child_nodes = ast.iter_child_nodes
-except (ImportError, AttributeError):   # Python 2.5
+except ImportError:     # Python 2.5
     import _ast as ast
 
-    def iter_child_nodes(node, astcls=ast.AST):
+    if 'decorator_list' not in ast.ClassDef._fields:
+        # Patch the missing attribute 'decorator_list'
+        ast.ClassDef.decorator_list = ()
+        ast.FunctionDef.decorator_list = property(lambda s: s.decorators)
+
+    def iter_child_nodes(node):
         """
-        Yield all direct child nodes of *node*, that is, all fields that are nodes
-        and all items of fields that are lists of nodes.
+        Yield all direct child nodes of *node*, that is, all fields that
+        are nodes and all items of fields that are lists of nodes.
         """
         for name in node._fields:
             field = getattr(node, name, None)
-            if isinstance(field, astcls):
+            if isinstance(field, ast.AST):
                 yield field
             elif isinstance(field, list):
                 for item in field:
@@ -44,6 +55,15 @@
 from . import messages
 
 
+if PY2:
+    def getNodeType(node_class):
+        # workaround str.upper() which is locale-dependent
+        return str(unicode(node_class.__name__).upper())  # __IGNORE_WARNING__
+else:
+    def getNodeType(node_class):
+        return node_class.__name__.upper()
+
+
 class Binding(object):
     """
     Represents the binding of a value to a name.
@@ -51,6 +71,9 @@
     The checker uses this to keep track of which names have been bound and
     which names have not. See L{Assignment} for a special type of binding that
     is checked with stricter rules.
+
+    @ivar used: pair of (L{Scope}, line-number) indicating the scope and
+                line number that this binding was last used
     """
 
     def __init__(self, name, source):
@@ -68,13 +91,13 @@
                                                         id(self))
 
 
-class UnBinding(Binding):
-    """Created by the 'del' operator."""
-
-
 class Importation(Binding):
     """
     A binding created by an import statement.
+
+    @ivar fullName: The complete name given to the import statement,
+        possibly including multiple dotted components.
+    @type fullName: C{str}
     """
     def __init__(self, name, source):
         self.fullName = name
@@ -140,10 +163,10 @@
 
 class Scope(dict):
     importStarred = False       # set to True when import * is found
-    usesLocals = False
 
     def __repr__(self):
-        return '<%s at 0x%x %s>' % (self.__class__.__name__, id(self), dict.__repr__(self))
+        scope_cls = self.__class__.__name__
+        return '<%s at 0x%x %s>' % (scope_cls, id(self), dict.__repr__(self))
 
 
 class ClassScope(Scope):
@@ -153,10 +176,31 @@
 class FunctionScope(Scope):
     """
     I represent a name scope for a function.
+
+    @ivar globals: Names declared 'global' in this function.
     """
+    usesLocals = False
+    alwaysUsed = set(['__tracebackhide__',
+                      '__traceback_info__', '__traceback_supplement__'])
+
     def __init__(self):
         super(FunctionScope, self).__init__()
-        self.globals = {}
+        # Simplify: manage the special locals as globals
+        self.globals = self.alwaysUsed.copy()
+
+    def unusedAssignments(self):
+        """
+        Return a generator for the assignments which have not been used.
+        """
+        for name, binding in self.items():
+            if (not binding.used and name not in self.globals
+                    and not self.usesLocals
+                    and isinstance(binding, Assignment)):
+                yield name, binding
+
+
+class GeneratorScope(Scope):
+    pass
 
 
 class ModuleScope(Scope):
@@ -179,13 +223,29 @@
 class Checker(object):
     """
     I check the cleanliness and sanity of Python code.
+
+    @ivar _deferredFunctions: Tracking list used by L{deferFunction}.  Elements
+        of the list are two-tuples.  The first element is the callable passed
+        to L{deferFunction}.  The second element is a copy of the scope stack
+        at the time L{deferFunction} was called.
+
+    @ivar _deferredAssignments: Similar to C{_deferredFunctions}, but for
+        callables which are deferred assignment checks.
     """
 
     nodeDepth = 0
+    offset = None
     traceTree = False
-    builtIns = set(dir(builtins)) | set(_MAGIC_GLOBALS)
+    withDoctest = ('PYFLAKES_NODOCTEST' not in os.environ)
+
+    builtIns = set(builtin_vars).union(_MAGIC_GLOBALS)
+    _customBuiltIns = os.environ.get('PYFLAKES_BUILTINS')
+    if _customBuiltIns:
+        builtIns.update(_customBuiltIns.split(','))
+    del _customBuiltIns
 
     def __init__(self, tree, filename='(none)', builtins=None):
+        self._nodeHandlers = {}
         self._deferredFunctions = []
         self._deferredAssignments = []
         self.deadScopes = []
@@ -194,6 +254,7 @@
         if builtins:
             self.builtIns = self.builtIns.union(builtins)
         self.scopeStack = [ModuleScope()]
+        self.exceptHandlers = [()]
         self.futuresAllowed = True
         self.root = tree
         self.handleChildren(tree)
@@ -218,21 +279,22 @@
         `callable` is called, the scope at the time this is called will be
         restored, however it will contain any new bindings added to it.
         """
-        self._deferredFunctions.append((callable, self.scopeStack[:]))
+        self._deferredFunctions.append((callable, self.scopeStack[:], self.offset))
 
     def deferAssignment(self, callable):
         """
         Schedule an assignment handler to be called just after deferred
         function handlers.
         """
-        self._deferredAssignments.append((callable, self.scopeStack[:]))
+        self._deferredAssignments.append((callable, self.scopeStack[:], self.offset))
 
     def runDeferred(self, deferred):
         """
         Run the callables in C{deferred} using their associated scope stack.
         """
-        for handler, scope in deferred:
+        for handler, scope, offset in deferred:
             self.scopeStack = scope
+            self.offset = offset
             handler()
 
     @property
@@ -251,12 +313,13 @@
             export = isinstance(scope.get('__all__'), ExportBinding)
             if export:
                 all = scope['__all__'].names()
-                if not scope.importStarred and os.path.basename(self.filename) != '__init__.py':
+                if not scope.importStarred and \
+                   os.path.basename(self.filename) != '__init__.py':
                     # Look for possible mistakes in the export list
                     undefined = set(all) - set(scope)
                     for name in undefined:
                         self.report(messages.UndefinedExport,
-                                    scope['__all__'].source.lineno, name)
+                                    scope['__all__'].source, name)
             else:
                 all = []
 
@@ -265,13 +328,16 @@
                 if isinstance(importation, Importation):
                     if not importation.used and importation.name not in all:
                         self.report(messages.UnusedImport,
-                                    importation.source.lineno, importation.name)
+                                    importation.source, importation.name)
+
+    def pushScope(self, scopeClass=FunctionScope):
+        self.scopeStack.append(scopeClass())
 
-    def pushFunctionScope(self):
-        self.scopeStack.append(FunctionScope())
+    def pushFunctionScope(self):    # XXX Deprecated
+        self.pushScope(FunctionScope)
 
-    def pushClassScope(self):
-        self.scopeStack.append(ClassScope())
+    def pushClassScope(self):       # XXX Deprecated
+        self.pushScope(ClassScope)
 
     def report(self, messageClass, *args, **kwargs):
         self.messages.append(messageClass(self.filename, *args, **kwargs))
@@ -341,69 +407,76 @@
                 existing = scope.get(value.name)
                 if (isinstance(existing, Importation)
                         and not existing.used
-                        and (not isinstance(value, Importation) or value.fullName == existing.fullName)
+                        and (not isinstance(value, Importation) or
+                             value.fullName == existing.fullName)
                         and reportRedef
                         and not self.differentForks(node, existing.source)):
                     redefinedWhileUnused = True
                     self.report(messages.RedefinedWhileUnused,
-                                node.lineno, value.name, existing.source.lineno)
+                                node, value.name, existing.source)
 
         existing = self.scope.get(value.name)
         if not redefinedWhileUnused and self.hasParent(value.source, ast.ListComp):
             if (existing and reportRedef
-                    and not self.hasParent(existing.source, (ast.For, ast.ListComp))):
+                    and not self.hasParent(existing.source, (ast.For, ast.ListComp))
+                    and not self.differentForks(node, existing.source)):
                 self.report(messages.RedefinedInListComp,
-                            node.lineno, value.name, existing.source.lineno)
+                            node, value.name, existing.source)
 
-        if isinstance(value, UnBinding):
-            try:
-                del self.scope[value.name]
-            except KeyError:
-                self.report(messages.UndefinedName, node.lineno, value.name)
-        elif (isinstance(existing, Definition)
-              and not existing.used
-              and not self.differentForks(node, existing.source)):
+        if (isinstance(existing, Definition)
+                and not existing.used
+                and not self.differentForks(node, existing.source)):
             self.report(messages.RedefinedWhileUnused,
-                        node.lineno, value.name, existing.source.lineno)
+                        node, value.name, existing.source)
         else:
             self.scope[value.name] = value
 
+    def getNodeHandler(self, node_class):
+        try:
+            return self._nodeHandlers[node_class]
+        except KeyError:
+            nodeType = getNodeType(node_class)
+        self._nodeHandlers[node_class] = handler = getattr(self, nodeType)
+        return handler
+
     def handleNodeLoad(self, node):
         name = getNodeName(node)
         if not name:
             return
         # try local scope
-        importStarred = self.scope.importStarred
         try:
-            self.scope[name].used = (self.scope, node.lineno)
+            self.scope[name].used = (self.scope, node)
         except KeyError:
             pass
         else:
             return
 
-        # try enclosing function scopes
-        for scope in self.scopeStack[-2:0:-1]:
+        scopes = [scope for scope in self.scopeStack[:-1]
+                  if isinstance(scope, (FunctionScope, ModuleScope))]
+        if isinstance(self.scope, GeneratorScope) and scopes[-1] != self.scopeStack[-2]:
+            scopes.append(self.scopeStack[-2])
+
+        # try enclosing function scopes and global scope
+        importStarred = self.scope.importStarred
+        for scope in reversed(scopes):
             importStarred = importStarred or scope.importStarred
-            if not isinstance(scope, FunctionScope):
-                continue
             try:
-                scope[name].used = (self.scope, node.lineno)
+                scope[name].used = (self.scope, node)
             except KeyError:
                 pass
             else:
                 return
 
-        # try global scope
-        importStarred = importStarred or self.scopeStack[0].importStarred
-        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)
+        # 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, name)
 
     def handleNodeStore(self, node):
         name = getNodeName(node)
@@ -418,17 +491,18 @@
                 # if the name was defined in that scope, and the name has
                 # been accessed already in the current scope, and hasn't
                 # been declared global
-                if (name in scope and scope[name].used and scope[name].used[0] is self.scope
-                        and name not in self.scope.globals):
+                used = name in scope and scope[name].used
+                if used and used[0] is self.scope and name not in self.scope.globals:
                     # then it's probably a mistake
                     self.report(messages.UndefinedLocal,
-                                scope[name].used[1], name, scope[name].source.lineno)
+                                scope[name].used[1], name, scope[name].source)
                     break
 
         parent = getattr(node, 'parent', None)
         if isinstance(parent, (ast.For, ast.comprehension, ast.Tuple, ast.List)):
             binding = Binding(name, node)
-        elif parent is not None and name == '__all__' and isinstance(self.scope, ModuleScope):
+        elif (parent is not None and name == '__all__' and
+              isinstance(self.scope, ModuleScope)):
             binding = ExportBinding(name, parent.value)
         else:
             binding = Assignment(name, node)
@@ -441,9 +515,12 @@
         if not name:
             return
         if isinstance(self.scope, FunctionScope) and name in self.scope.globals:
-            del self.scope.globals[name]
+            self.scope.globals.remove(name)
         else:
-            self.addBinding(node, UnBinding(name, node))
+            try:
+                del self.scope[name]
+            except KeyError:
+                self.report(messages.UndefinedName, node, name)
 
     def handleChildren(self, tree):
         for node in iter_child_nodes(tree):
@@ -457,32 +534,72 @@
         return isinstance(node, ast.Str) or (isinstance(node, ast.Expr) and
                                              isinstance(node.value, ast.Str))
 
+    def getDocstring(self, node):
+        if isinstance(node, ast.Expr):
+            node = node.value
+        if not isinstance(node, ast.Str):
+            return (None, None)
+        # Computed incorrectly if the docstring has backslash
+        doctest_lineno = node.lineno - node.s.count('\n') - 1
+        return (node.s, doctest_lineno)
+
     def handleNode(self, node, parent):
         if node is None:
             return
-        node.parent = parent
+        if self.offset and getattr(node, 'lineno', None) is not None:
+            node.lineno += self.offset[0]
+            node.col_offset += self.offset[1]
         if self.traceTree:
             print('  ' * self.nodeDepth + node.__class__.__name__)
-        self.nodeDepth += 1
         if self.futuresAllowed and not (isinstance(node, ast.ImportFrom) or
                                         self.isDocstring(node)):
             self.futuresAllowed = False
-        nodeType = node.__class__.__name__.upper()
+        self.nodeDepth += 1
         node.level = self.nodeDepth
+        node.parent = parent
         try:
-            handler = getattr(self, nodeType)
+            handler = self.getNodeHandler(node.__class__)
             handler(node)
         finally:
             self.nodeDepth -= 1
         if self.traceTree:
             print('  ' * self.nodeDepth + 'end ' + node.__class__.__name__)
 
+    _getDoctestExamples = doctest.DocTestParser().get_examples
+
+    def handleDoctests(self, node):
+        try:
+            docstring, node_lineno = self.getDocstring(node.body[0])
+            if not docstring:
+                return
+            examples = self._getDoctestExamples(docstring)
+        except (ValueError, IndexError):
+            # e.g. line 6 of the docstring for <string> has inconsistent
+            # leading whitespace: ...
+            return
+        node_offset = self.offset or (0, 0)
+        self.pushScope()
+        for example in examples:
+            try:
+                tree = compile(example.source, "<doctest>", "exec", ast.PyCF_ONLY_AST)
+            except SyntaxError:
+                e = sys.exc_info()[1]
+                position = (node_lineno + example.lineno + e.lineno,
+                            example.indent + 4 + e.offset)
+                self.report(messages.DoctestSyntaxError, node, position)
+            else:
+                self.offset = (node_offset[0] + node_lineno + example.lineno,
+                               node_offset[1] + example.indent + 4)
+                self.handleChildren(tree)
+                self.offset = node_offset
+        self.popScope()
+
     def ignore(self, node):
         pass
 
     # "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
 
@@ -512,7 +629,7 @@
         Keep track of globals declarations.
         """
         if isinstance(self.scope, FunctionScope):
-            self.scope.globals.update(dict.fromkeys(node.names))
+            self.scope.globals.update(node.names)
 
     NONLOCAL = GLOBAL
 
@@ -522,13 +639,23 @@
             self.handleNode(gen, node)
         self.handleNode(node.elt, node)
 
-    GENERATOREXP = SETCOMP = LISTCOMP
+    def GENERATOREXP(self, node):
+        self.pushScope(GeneratorScope)
+        # handle generators before element
+        for gen in node.generators:
+            self.handleNode(gen, node)
+        self.handleNode(node.elt, node)
+        self.popScope()
+
+    SETCOMP = GENERATOREXP
 
     def DICTCOMP(self, node):
+        self.pushScope(GeneratorScope)
         for gen in node.generators:
             self.handleNode(gen, node)
         self.handleNode(node.key, node)
         self.handleNode(node.value, node)
+        self.popScope()
 
     def FOR(self, node):
         """
@@ -551,7 +678,7 @@
                     # unused ones will get an unused import warning
                     and self.scope[varn].used):
                 self.report(messages.ImportShadowedByLoopVar,
-                            node.lineno, varn, self.scope[varn].source.lineno)
+                            node, varn, self.scope[varn].source)
 
         self.handleChildren(node)
 
@@ -559,12 +686,13 @@
         """
         Handle occurrence of Name (which can be a load/store/delete access.)
         """
-        if node.id == 'locals' and isinstance(node.parent, ast.Call):
-            # we are doing locals() call in current scope
-            self.scope.usesLocals = True
         # Locate the name in locals / function / globals scopes.
         if isinstance(node.ctx, (ast.Load, ast.AugLoad)):
             self.handleNodeLoad(node)
+            if (node.id == 'locals' and isinstance(self.scope, FunctionScope)
+                    and isinstance(node.parent, ast.Call)):
+                # we are doing locals() call in current scope
+                self.scope.usesLocals = True
         elif isinstance(node.ctx, (ast.Store, ast.AugStore)):
             self.handleNodeStore(node)
         elif isinstance(node.ctx, ast.Del):
@@ -575,12 +703,12 @@
             raise RuntimeError("Got impossible expression context: %r" % (node.ctx,))
 
     def FUNCTIONDEF(self, node):
-        if not hasattr(node, 'decorator_list'):   # Python 2.5
-            node.decorator_list = node.decorators
         for deco in node.decorator_list:
             self.handleNode(deco, node)
         self.addBinding(node, FunctionDefinition(node.name, node))
         self.LAMBDA(node)
+        if self.withDoctest:
+            self.deferFunction(lambda: self.handleDoctests(node))
 
     def LAMBDA(self, node):
         args = []
@@ -593,7 +721,7 @@
                     else:
                         if arg.id in args:
                             self.report(messages.DuplicateArgument,
-                                        node.lineno, arg.id)
+                                        node, arg.id)
                         args.append(arg.id)
             addArgs(node.args.args)
             defaults = node.args.defaults
@@ -601,7 +729,7 @@
             for arg in node.args.args + node.args.kwonlyargs:
                 if arg.arg in args:
                     self.report(messages.DuplicateArgument,
-                                node.lineno, arg.arg)
+                                node, arg.arg)
                 args.append(arg.arg)
                 self.handleNode(arg.annotation, node)
             if hasattr(node, 'returns'):    # Only for FunctionDefs
@@ -615,14 +743,14 @@
             if not wildcard:
                 continue
             if wildcard in args:
-                self.report(messages.DuplicateArgument, node.lineno, wildcard)
+                self.report(messages.DuplicateArgument, node, wildcard)
             args.append(wildcard)
         for default in defaults:
             self.handleNode(default, node)
 
         def runFunction():
 
-            self.pushFunctionScope()
+            self.pushScope()
             for name in args:
                 self.addBinding(node, Argument(name, node), reportRedef=False)
             if isinstance(node.body, list):
@@ -637,12 +765,8 @@
                 """
                 Check to see if any assignments have not been used.
                 """
-                for name, binding in self.scope.items():
-                    if (not binding.used and name not in self.scope.globals
-                            and not self.scope.usesLocals
-                            and isinstance(binding, Assignment)):
-                        self.report(messages.UnusedVariable,
-                                    binding.source.lineno, name)
+                for name, binding in self.scope.unusedAssignments():
+                    self.report(messages.UnusedVariable, binding.source, name)
             self.deferAssignment(checkUnusedAssignments)
             self.popScope()
 
@@ -654,15 +778,16 @@
         classes, and the body of its definition.  Additionally, add its name to
         the current scope.
         """
-        # no class decorator in Python 2.5
-        for deco in getattr(node, 'decorator_list', ''):
+        for deco in node.decorator_list:
             self.handleNode(deco, node)
         for baseNode in node.bases:
             self.handleNode(baseNode, node)
         if not PY2:
             for keywordNode in node.keywords:
                 self.handleNode(keywordNode, node)
-        self.pushClassScope()
+        self.pushScope(ClassScope)
+        if self.withDoctest:
+            self.deferFunction(lambda: self.handleDoctests(node))
         for stmt in node.body:
             self.handleNode(stmt, node)
         self.popScope()
@@ -688,21 +813,42 @@
         if node.module == '__future__':
             if not self.futuresAllowed:
                 self.report(messages.LateFutureImport,
-                            node.lineno, [n.name for n in node.names])
+                            node, [n.name for n in node.names])
         else:
             self.futuresAllowed = False
 
         for alias in node.names:
             if alias.name == '*':
                 self.scope.importStarred = True
-                self.report(messages.ImportStarUsed, node.lineno, node.module)
+                self.report(messages.ImportStarUsed, node, node.module)
                 continue
             name = alias.asname or alias.name
             importation = Importation(name, node)
             if node.module == '__future__':
-                importation.used = (self.scope, node.lineno)
+                importation.used = (self.scope, node)
             self.addBinding(node, importation)
 
+    def TRY(self, node):
+        handler_names = []
+        # List the exception handlers
+        for handler in node.handlers:
+            if isinstance(handler.type, ast.Tuple):
+                for exc_type in handler.type.elts:
+                    handler_names.append(getNodeName(exc_type))
+            elif handler.type:
+                handler_names.append(getNodeName(handler.type))
+        # Memorize the except handlers and process the body
+        self.exceptHandlers.append(handler_names)
+        for child in node.body:
+            self.handleNode(child, node)
+        self.exceptHandlers.pop()
+        # Process the other nodes: "except:", "else:", "finally:"
+        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.
--- a/Plugins/CheckerPlugins/SyntaxChecker/pyflakes/messages.py	Sat Jan 04 22:14:38 2014 +0100
+++ b/Plugins/CheckerPlugins/SyntaxChecker/pyflakes/messages.py	Sun Jan 05 22:45:29 2014 +0100
@@ -1,14 +1,14 @@
 # -*- coding: utf-8 -*-
 
-# Copyright (c) 2010 - 2013 Detlev Offenbach <detlev@die-offenbachs.de>
+# Copyright (c) 2010 - 2014 Detlev Offenbach <detlev@die-offenbachs.de>
 #
-# Original (c) 2005 Divmod, Inc.  See LICENSE file for details
+# Original (c) 2005 Divmod, Inc.  See __init__.py file for details
 #
-# This module is based on pyflakes for Python2 but was heavily hacked to
+# This module is based on pyflakes but was heavily hacked to
 # work within Eric5 and Qt (translatable messages)
 
 """
-Module implementing the messages for pyflakes.
+Provide the class Message and its subclasses.
 """
 
 # Tell 'lupdate' which strings to keep for translation.
@@ -21,25 +21,26 @@
     """
     message = ''
     message_args = ()
-    
-    def __init__(self, filename, lineno):
+
+    def __init__(self, filename, loc):
         """
         Constructor
         
         @param filename name of the file (string)
-        @param lineno line number (integer)
+        @param loc location of warning (object)
         """
         self.filename = filename
-        self.lineno = lineno
-    
+        self.lineno = loc.lineno
+        self.col = getattr(loc, 'col_offset', 0)
+
     def __str__(self):
         """
         Special method return a string representation of the instance object.
         
         @return string representation of the object (string)
         """
-        return '%s:%s: %s' % (
-            self.filename, self.lineno, self.message % self.message_args)
+        return '%s:%s: %s' % (self.filename, self.lineno,
+                              self.message % self.message_args)
     
     def getMessageData(self):
         """
@@ -48,7 +49,8 @@
         @return tuple containing file name, line number and message
             (string, integer, string)
         """
-        return (self.filename, self.lineno, self.message, self.message_args)
+        return (self.filename, self.lineno, self.col, self.message,
+                self.message_args)
 
 
 class UnusedImport(Message):
@@ -58,16 +60,16 @@
     message = QT_TRANSLATE_NOOP(
         'py3Flakes',
         '{0!r} imported but unused.')
-    
-    def __init__(self, filename, lineno, name):
+
+    def __init__(self, filename, loc, name):
         """
         Constructor
         
         @param filename name of the file (string)
-        @param lineno line number (integer)
+        @param loc location of warning (object)
         @param name name of the unused import (string)
         """
-        Message.__init__(self, filename, lineno)
+        Message.__init__(self, filename, loc)
         self.message_args = (name,)
 
 
@@ -79,17 +81,17 @@
         'py3Flakes',
         'Redefinition of unused {0!r} from line {1!r}.')
 
-    def __init__(self, filename, lineno, name, orig_lineno):
+    def __init__(self, filename, loc, name, orig_loc):
         """
         Constructor
         
         @param filename name of the file (string)
-        @param lineno line number (integer)
+        @param loc location of warning (object)
         @param name name of the redefined object (string)
-        @param orig_lineno line number of the original definition (integer)
+        @param orig_loc location of the original definition (object)
         """
-        Message.__init__(self, filename, lineno)
-        self.message_args = (name, orig_lineno)
+        Message.__init__(self, filename, loc)
+        self.message_args = (name, orig_loc.lineno)
 
 
 class RedefinedInListComp(Message):
@@ -100,17 +102,17 @@
         'py3Flakes',
         'List comprehension redefines {0!r} from line {1!r}.')
 
-    def __init__(self, filename, lineno, name, orig_lineno):
+    def __init__(self, filename, loc, name, orig_loc):
         """
         Constructor
         
         @param filename name of the file (string)
-        @param lineno line number (integer)
+        @param loc location of warning (object)
         @param name name of the redefined object (string)
-        @param orig_lineno line number of the original definition (integer)
+        @param orig_loc location of the original definition (object)
         """
-        Message.__init__(self, filename, lineno)
-        self.message_args = (name, orig_lineno)
+        Message.__init__(self, filename, loc)
+        self.message_args = (name, orig_loc.lineno)
 
 
 class ImportShadowedByLoopVar(Message):
@@ -120,18 +122,18 @@
     message = QT_TRANSLATE_NOOP(
         'py3Flakes',
         'Import {0!r} from line {1!r} shadowed by loop variable.')
-    
-    def __init__(self, filename, lineno, name, orig_lineno):
+
+    def __init__(self, filename, loc, name, orig_loc):
         """
         Constructor
         
         @param filename name of the file (string)
-        @param lineno line number (integer)
+        @param loc location of warning (object)
         @param name name of the shadowed import (string)
-        @param orig_lineno line number of the import (integer)
+        @param orig_loc location of the import (object)
         """
-        Message.__init__(self, filename, lineno)
-        self.message_args = (name, orig_lineno)
+        Message.__init__(self, filename, loc)
+        self.message_args = (name, orig_loc.lineno)
 
 
 class ImportStarUsed(Message):
@@ -141,16 +143,16 @@
     message = QT_TRANSLATE_NOOP(
         'py3Flakes',
         "'from {0} import *' used; unable to detect undefined names.")
-    
-    def __init__(self, filename, lineno, modname):
+
+    def __init__(self, filename, loc, modname):
         """
         Constructor
         
         @param filename name of the file (string)
-        @param lineno line number (integer)
+        @param loc location of warning (object)
         @param modname name of the module imported using star import (string)
         """
-        Message.__init__(self, filename, lineno)
+        Message.__init__(self, filename, loc)
         self.message_args = (modname,)
 
 
@@ -159,19 +161,39 @@
     Class defining the "Undefined Name" message.
     """
     message = QT_TRANSLATE_NOOP('py3Flakes', 'Undefined name {0!r}.')
-    
-    def __init__(self, filename, lineno, name):
+
+    def __init__(self, filename, loc, name):
         """
         Constructor
         
         @param filename name of the file (string)
-        @param lineno line number (integer)
+        @param loc location of warning (object)
         @param name undefined name (string)
         """
-        Message.__init__(self, filename, lineno)
+        Message.__init__(self, filename, loc)
         self.message_args = (name,)
 
 
+class DoctestSyntaxError(Message):
+    """
+    Class defining the "Syntax error in doctest" message.
+    """
+    message = QT_TRANSLATE_NOOP('py3Flakes', 'Syntax error in doctest.')
+
+    def __init__(self, filename, loc, position=None):
+        """
+        Constructor
+        
+        @param filename name of the file (string)
+        @param loc location of warning (object)
+        @keyparam position of warning if existent (object)
+        """
+        Message.__init__(self, filename, loc)
+        if position:
+            (self.lineno, self.col) = position
+        self.message_args = ()
+
+
 class UndefinedExport(Message):
     """
     Class defining the "Undefined Export" message.
@@ -179,16 +201,16 @@
     message = QT_TRANSLATE_NOOP(
         'py3Flakes',
         'Undefined name {0!r} in __all__.')
-    
-    def __init__(self, filename, lineno, name):
+
+    def __init__(self, filename, loc, name):
         """
         Constructor
         
         @param filename name of the file (string)
-        @param lineno line number (integer)
+        @param loc location of warning (object)
         @param name undefined exported name (string)
         """
-        Message.__init__(self, filename, lineno)
+        Message.__init__(self, filename, loc)
         self.message_args = (name,)
 
 
@@ -200,18 +222,18 @@
         'py3Flakes',
         "Local variable {0!r} (defined in enclosing scope on line {1!r})"
         " referenced before assignment.")
-    
-    def __init__(self, filename, lineno, name, orig_lineno):
+
+    def __init__(self, filename, loc, name, orig_loc):
         """
         Constructor
         
         @param filename name of the file (string)
-        @param lineno line number (integer)
+        @param loc location of warning (object)
         @param name name of the prematurely referenced variable (string)
-        @param orig_lineno line number of the variable definition (integer)
+        @param orig_loc location of the variable definition (object)
         """
-        Message.__init__(self, filename, lineno)
-        self.message_args = (name, orig_lineno)
+        Message.__init__(self, filename, loc)
+        self.message_args = (name, orig_loc.lineno)
 
 
 class DuplicateArgument(Message):
@@ -221,16 +243,16 @@
     message = QT_TRANSLATE_NOOP(
         'py3Flakes',
         'Duplicate argument {0!r} in function definition.')
-    
-    def __init__(self, filename, lineno, name):
+
+    def __init__(self, filename, loc, name):
         """
         Constructor
         
         @param filename name of the file (string)
-        @param lineno line number (integer)
+        @param loc location of warning (object)
         @param name name of the duplicate argument (string)
         """
-        Message.__init__(self, filename, lineno)
+        Message.__init__(self, filename, loc)
         self.message_args = (name,)
 
 
@@ -241,18 +263,18 @@
     message = QT_TRANSLATE_NOOP(
         'py3Flakes',
         'Redefinition of {0!r} from line {1!r}.')
-    
-    def __init__(self, filename, lineno, name, orig_lineno):
+
+    def __init__(self, filename, loc, name, orig_loc):
         """
         Constructor
         
         @param filename name of the file (string)
-        @param lineno line number (integer)
+        @param loc location of warning (object)
         @param name name of the redefined function (string)
-        @param orig_lineno line number of the original definition (integer)
+        @param orig_loc location of the original definition (object)
         """
-        Message.__init__(self, filename, lineno)
-        self.message_args = (name, orig_lineno)
+        Message.__init__(self, filename, loc)
+        self.message_args = (name, orig_loc.lineno)
 
 
 class LateFutureImport(Message):
@@ -262,16 +284,16 @@
     message = QT_TRANSLATE_NOOP(
         'py3Flakes',
         'Future import(s) {0!r} after other statements.')
-    
-    def __init__(self, filename, lineno, names):
+
+    def __init__(self, filename, loc, names):
         """
         Constructor
         
         @param filename name of the file (string)
-        @param lineno line number (integer)
+        @param loc location of warning (object)
         @param names names of the imported futures (string)
         """
-        Message.__init__(self, filename, lineno)
+        Message.__init__(self, filename, loc)
         self.message_args = (names,)
 
 
@@ -285,14 +307,14 @@
     message = QT_TRANSLATE_NOOP(
         'py3Flakes',
         'Local variable {0!r} is assigned to but never used.')
-    
-    def __init__(self, filename, lineno, names):
+
+    def __init__(self, filename, loc, names):
         """
         Constructor
         
         @param filename name of the file (string)
-        @param lineno line number (integer)
+        @param loc location of warning (object)
         @param names names of the unused variable (string)
         """
-        Message.__init__(self, filename, lineno)
+        Message.__init__(self, filename, loc)
         self.message_args = (names,)
--- a/QScintilla/Editor.py	Sat Jan 04 22:14:38 2014 +0100
+++ b/QScintilla/Editor.py	Sun Jan 05 22:45:29 2014 +0100
@@ -8,9 +8,9 @@
 """
 from __future__ import unicode_literals
 try:
-    str = unicode   # __IGNORE_WARNING__
-    chr = unichr   # __IGNORE_WARNING__
-except (NameError):
+    str = unicode
+    chr = unichr
+except NameError:
     pass
 
 import os
@@ -4994,7 +4994,7 @@
         @param code the part of the code where the error occured (str)
         @param error the name of the error (str)
         @param warnings a list of strings containing the warnings
-            (marker, file name, line number, message)
+            (marker, file name, line number, col, message)
         """
         # Check if it's the requested file, otherwise ignore signal
         if fn != self.fileName and (
@@ -5007,8 +5007,8 @@
         if nok:
             self.toggleSyntaxError(line, index, True, error)
         else:
-            for warning in warnings:
-                self.toggleWarning(warning[2], True, warning[3])
+            for marker, _fn, lineno, col, msg in warnings:
+                self.toggleWarning(lineno, True, msg)
 
     def __initOnlineSyntaxCheck(self):
         """
--- a/Utilities/InternalServices.py	Sat Jan 04 22:14:38 2014 +0100
+++ b/Utilities/InternalServices.py	Sun Jan 05 22:45:29 2014 +0100
@@ -83,17 +83,17 @@
         @param code the part of the code where the error occured (str)
         @param error the name of the error (str)
         @param warnings a list of strings containing the warnings
-            (marker, file name, line number, message)
+            (marker, file name, line number, col, message, list(msg_args))
         """
         for warning in warnings:
             # Translate messages
             msg_args = warning.pop()
             translated = QApplication.translate(
-                'py3Flakes', warning[3]).format(*msg_args)
+                'py3Flakes', warning[4]).format(*msg_args)
             # Avoid leading "u" at Python2 unicode strings
             if translated.startswith("u'"):
                 translated = translated[1:]
-            warning[3] = translated.replace(" u'", " '")
+            warning[4] = translated.replace(" u'", " '")
         
         self.syntaxChecked.emit(
             fn, nok, fname, line, index, code, error, warnings)

eric ide

mercurial