gobject-introspection r370 - in trunk: . giscanner misc tests



Author: johan
Date: Thu Aug 14 11:23:26 2008
New Revision: 370
URL: http://svn.gnome.org/viewvc/gobject-introspection?rev=370&view=rev

Log:
2008-08-14  Johan Dahlin  <johan gnome org>

    * giscanner/girwriter.py:
    * giscanner/glibast.py:
    * giscanner/glibtransformer.py:
    * giscanner/transformer.py:
    * misc/pyflakes.py:
    * tests/Makefile.am:
    Add pyflakes.py and run it in make check.
    Update the source code to fix the errors reported by
    pyflakes.



Added:
   trunk/misc/pyflakes.py
Modified:
   trunk/ChangeLog
   trunk/giscanner/girwriter.py
   trunk/giscanner/glibast.py
   trunk/giscanner/glibtransformer.py
   trunk/giscanner/transformer.py
   trunk/tests/Makefile.am

Modified: trunk/giscanner/girwriter.py
==============================================================================
--- trunk/giscanner/girwriter.py	(original)
+++ trunk/giscanner/girwriter.py	Thu Aug 14 11:23:26 2008
@@ -73,7 +73,7 @@
     def _write_alias(self, alias):
         attrs = [('name', alias.name), ('target', alias.target)]
         if alias.ctype is not None:
-            attrs.append(('c:type', ntype.ctype))
+            attrs.append(('c:type', alias.ctype))
         self.write_tag('alias', attrs)
 
     def _write_function(self, func, tag_name='function'):

Modified: trunk/giscanner/glibast.py
==============================================================================
--- trunk/giscanner/glibast.py	(original)
+++ trunk/giscanner/glibast.py	Thu Aug 14 11:23:26 2008
@@ -22,9 +22,8 @@
 from .ast import (
     type_names,
     TYPE_STRING, TYPE_INT8, TYPE_UINT8, TYPE_INT16, TYPE_UINT16,
-    TYPE_INT32, TYPE_UINT32, TYPE_INT32, TYPE_UINT32, TYPE_LONG,
-    TYPE_ULONG, TYPE_FLOAT, TYPE_DOUBLE, TYPE_STRING, TYPE_BOOLEAN,
-    TYPE_ANY, TYPE_SIZE, TYPE_SSIZE)
+    TYPE_UINT32, TYPE_INT32, TYPE_LONG, TYPE_ULONG, TYPE_FLOAT,
+    TYPE_DOUBLE, TYPE_BOOLEAN, TYPE_ANY, TYPE_SIZE, TYPE_SSIZE)
 
 # Glib type names
 type_names['gchararray'] = TYPE_STRING

Modified: trunk/giscanner/glibtransformer.py
==============================================================================
--- trunk/giscanner/glibtransformer.py	(original)
+++ trunk/giscanner/glibtransformer.py	Thu Aug 14 11:23:26 2008
@@ -19,14 +19,11 @@
 #
 
 import ctypes
-import os
-import sys
 
 from . import cgobject
 from .odict import odict
 from .ast import (Callback, Enum, Function, Member, Namespace, Parameter,
-                  Property, Return, Sequence, Struct, Field, Type, Alias,
-                  type_name_from_ctype)
+                  Property, Return, Struct, Type, Alias, type_name_from_ctype)
 from .glibast import (GLibBoxed, GLibEnum, GLibEnumMember, GLibFlags,
                       GLibInterface, GLibObject, GLibSignal)
 from .utils import extract_libtool, to_underscores

Modified: trunk/giscanner/transformer.py
==============================================================================
--- trunk/giscanner/transformer.py	(original)
+++ trunk/giscanner/transformer.py	Thu Aug 14 11:23:26 2008
@@ -18,21 +18,17 @@
 # 02110-1301, USA.
 #
 
-import os
-import sys
-
 from giscanner.ast import (Callback, Enum, Function, Namespace, Member,
                            Parameter, Return, Sequence, Struct, Field,
                            Type, Alias, type_name_from_ctype)
-from .glibast import (GLibBoxed, GLibEnum, GLibEnumMember, GLibFlags,
-                      GLibInterface, GLibObject, GLibSignal)
+from .glibast import GLibBoxed, GLibInterface, GLibObject
 from giscanner.sourcescanner import (
-    SourceSymbol, ctype_name, symbol_type_name, CTYPE_POINTER,
-    CTYPE_BASIC_TYPE, CTYPE_UNION, CTYPE_ARRAY,
-    CTYPE_TYPEDEF, CTYPE_VOID, CTYPE_BASIC_TYPE, CTYPE_ENUM,
-    CTYPE_FUNCTION, CTYPE_STRUCT, CSYMBOL_TYPE_FUNCTION,
-    CSYMBOL_TYPE_TYPEDEF, CSYMBOL_TYPE_STRUCT, CSYMBOL_TYPE_ENUM,
-    CSYMBOL_TYPE_UNION, CSYMBOL_TYPE_OBJECT, CSYMBOL_TYPE_MEMBER)
+    SourceSymbol, ctype_name, CTYPE_POINTER,
+    CTYPE_BASIC_TYPE, CTYPE_UNION, CTYPE_ARRAY, CTYPE_TYPEDEF,
+    CTYPE_VOID, CTYPE_ENUM, CTYPE_FUNCTION, CTYPE_STRUCT,
+    CSYMBOL_TYPE_FUNCTION, CSYMBOL_TYPE_TYPEDEF, CSYMBOL_TYPE_STRUCT,
+    CSYMBOL_TYPE_ENUM, CSYMBOL_TYPE_UNION, CSYMBOL_TYPE_OBJECT,
+    CSYMBOL_TYPE_MEMBER)
 from .utils import strip_common_prefix
 
 
@@ -86,8 +82,7 @@
         for node in parser.get_nodes():
             if hasattr(node, 'ctype'):
                 self._ctype_names[node.ctype] = (nsname, node)
-            if isinstance(node, GLibBoxed) or isinstance(node, GLibInterface) \
-                    or isinstance(node, GLibObject):
+            if isinstance(node, (GLibBoxed, GLibInterface, GLibObject)):
                 self._type_names[node.type_name] = (nsname, node)
             elif isinstance(node, Alias):
                 self._alias_names[node.name] = (nsname, node)

Added: trunk/misc/pyflakes.py
==============================================================================
--- (empty file)
+++ trunk/misc/pyflakes.py	Thu Aug 14 11:23:26 2008
@@ -0,0 +1,513 @@
+# -*- test-case-name: pyflakes -*-
+# (c) 2005-2008 Divmod, Inc.
+# See LICENSE file for details
+
+import __builtin__
+import compiler
+import sys
+import os
+
+from compiler import ast
+
+
+class Message(object):
+    message = ''
+    message_args = ()
+    def __init__(self, filename, lineno):
+        self.filename = filename
+        self.lineno = lineno
+    def __str__(self):
+        return '%s:%s: %s' % (self.filename, self.lineno, self.message % self.message_args)
+
+
+class UnusedImport(Message):
+    message = '%r imported but unused'
+    def __init__(self, filename, lineno, name):
+        Message.__init__(self, filename, lineno)
+        self.message_args = (name,)
+
+
+class RedefinedWhileUnused(Message):
+    message = 'redefinition of unused %r from line %r'
+    def __init__(self, filename, lineno, name, orig_lineno):
+        Message.__init__(self, filename, lineno)
+        self.message_args = (name, orig_lineno)
+
+
+class ImportShadowedByLoopVar(Message):
+    message = 'import %r from line %r shadowed by loop variable'
+    def __init__(self, filename, lineno, name, orig_lineno):
+        Message.__init__(self, filename, lineno)
+        self.message_args = (name, orig_lineno)
+
+
+class ImportStarUsed(Message):
+    message = "'from %s import *' used; unable to detect undefined names"
+    def __init__(self, filename, lineno, modname):
+        Message.__init__(self, filename, lineno)
+        self.message_args = (modname,)
+
+
+class UndefinedName(Message):
+    message = 'undefined name %r'
+    def __init__(self, filename, lineno, name):
+        Message.__init__(self, filename, lineno)
+        self.message_args = (name,)
+
+
+class UndefinedLocal(Message):
+    message = "local variable %r (defined in enclosing scope on line %r) referenced before assignment"
+    def __init__(self, filename, lineno, name, orig_lineno):
+        Message.__init__(self, filename, lineno)
+        self.message_args = (name, orig_lineno)
+
+
+class DuplicateArgument(Message):
+    message = 'duplicate argument %r in function definition'
+    def __init__(self, filename, lineno, name):
+        Message.__init__(self, filename, lineno)
+        self.message_args = (name,)
+
+
+class RedefinedFunction(Message):
+    message = 'redefinition of function %r from line %r'
+    def __init__(self, filename, lineno, name, orig_lineno):
+        Message.__init__(self, filename, lineno)
+        self.message_args = (name, orig_lineno)
+
+
+class LateFutureImport(Message):
+    message = 'future import(s) %r after other statements'
+    def __init__(self, filename, lineno, names):
+        Message.__init__(self, filename, lineno)
+        self.message_args = (names,)
+
+
+class Binding(object):
+    """
+    @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):
+        self.name = name
+        self.source = source
+        self.used = False
+
+    def __str__(self):
+        return self.name
+
+    def __repr__(self):
+        return '<%s object %r from line %r at 0x%x>' % (self.__class__.__name__,
+                                                        self.name,
+                                                        self.source.lineno,
+                                                        id(self))
+
+class UnBinding(Binding):
+    '''Created by the 'del' operator.'''
+
+class Importation(Binding):
+    def __init__(self, name, source):
+        name = name.split('.')[0]
+        super(Importation, self).__init__(name, source)
+
+class Assignment(Binding):
+    pass
+
+class FunctionDefinition(Binding):
+    pass
+
+
+class Scope(dict):
+    importStarred = False       # set to True when import * is found
+
+    def __repr__(self):
+        return '<%s at 0x%x %s>' % (self.__class__.__name__, id(self), dict.__repr__(self))
+
+    def __init__(self):
+        super(Scope, self).__init__()
+
+class ClassScope(Scope):
+    pass
+
+
+
+class FunctionScope(Scope):
+    """
+    I represent a name scope for a function.
+
+    @ivar globals: Names declared 'global' in this function.
+    """
+    def __init__(self):
+        super(FunctionScope, self).__init__()
+        self.globals = {}
+
+
+
+class ModuleScope(Scope):
+    pass
+
+
+class Checker(object):
+    nodeDepth = 0
+    traceTree = False
+
+    def __init__(self, tree, filename='(none)'):
+        self.deferred = []
+        self.dead_scopes = []
+        self.messages = []
+        self.filename = filename
+        self.scopeStack = [ModuleScope()]
+        self.futuresAllowed = True
+
+        self.handleChildren(tree)
+        for handler, scope in self.deferred:
+            self.scopeStack = scope
+            handler()
+        del self.scopeStack[1:]
+        self.popScope()
+        self.check_dead_scopes()
+
+    def defer(self, callable):
+        '''Schedule something to be called after just before completion.
+
+        This is used for handling function bodies, which must be deferred
+        because code later in the file might modify the global scope. When
+        `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.deferred.append( (callable, self.scopeStack[:]) )
+
+    def scope(self):
+        return self.scopeStack[-1]
+    scope = property(scope)
+
+    def popScope(self):
+        self.dead_scopes.append(self.scopeStack.pop())
+
+    def check_dead_scopes(self):
+        for scope in self.dead_scopes:
+            for importation in scope.itervalues():
+                if isinstance(importation, Importation) and not importation.used:
+                    self.report(UnusedImport, importation.source.lineno, importation.name)
+
+    def pushFunctionScope(self):
+        self.scopeStack.append(FunctionScope())
+
+    def pushClassScope(self):
+        self.scopeStack.append(ClassScope())
+
+    def report(self, messageClass, *args, **kwargs):
+        self.messages.append(messageClass(self.filename, *args, **kwargs))
+
+    def handleChildren(self, tree):
+        for node in tree.getChildNodes():
+            self.handleNode(node)
+
+    def handleNode(self, node):
+        if self.traceTree:
+            print '  ' * self.nodeDepth + node.__class__.__name__
+        self.nodeDepth += 1
+        nodeType = node.__class__.__name__.upper()
+        if nodeType not in ('STMT', 'FROM'):
+            self.futuresAllowed = False
+        try:
+            handler = getattr(self, nodeType)
+            handler(node)
+        finally:
+            self.nodeDepth -= 1
+        if self.traceTree:
+            print '  ' * self.nodeDepth + 'end ' + node.__class__.__name__
+
+    def ignore(self, node):
+        pass
+
+    STMT = PRINT = PRINTNL = TUPLE = LIST = ASSTUPLE = ASSATTR = \
+    ASSLIST = GETATTR = SLICE = SLICEOBJ = IF = CALLFUNC = DISCARD = \
+    RETURN = ADD = MOD = SUB = NOT = UNARYSUB = INVERT = ASSERT = COMPARE = \
+    SUBSCRIPT = AND = OR = TRYEXCEPT = RAISE = YIELD = DICT = LEFTSHIFT = \
+    RIGHTSHIFT = KEYWORD = TRYFINALLY = WHILE = EXEC = MUL = DIV = POWER = \
+    FLOORDIV = BITAND = BITOR = BITXOR = LISTCOMPFOR = LISTCOMPIF = \
+    AUGASSIGN = BACKQUOTE = UNARYADD = GENEXPR = GENEXPRFOR = GENEXPRIF = \
+    IFEXP = handleChildren
+
+    CONST = PASS = CONTINUE = BREAK = ELLIPSIS = ignore
+
+    def addBinding(self, lineno, value, reportRedef=True):
+        '''Called when a binding is altered.
+
+        - `lineno` is the line of the statement responsible for the change
+        - `value` is the optional new value, a Binding instance, associated
+          with the binding; if None, the binding is deleted if it exists.
+        - if `reportRedef` is True (default), rebinding while unused will be
+          reported.
+        '''
+        if (isinstance(self.scope.get(value.name), FunctionDefinition)
+                    and isinstance(value, FunctionDefinition)):
+            self.report(RedefinedFunction,
+                        lineno, value.name, self.scope[value.name].source.lineno)
+
+        if not isinstance(self.scope, ClassScope):
+            for scope in self.scopeStack[::-1]:
+                if (isinstance(scope.get(value.name), Importation)
+                        and not scope[value.name].used
+                        and reportRedef):
+
+                    self.report(RedefinedWhileUnused,
+                                lineno, value.name, scope[value.name].source.lineno)
+
+        if isinstance(value, UnBinding):
+            try:
+                del self.scope[value.name]
+            except KeyError:
+                self.report(UndefinedName, lineno, value.name)
+        else:
+            self.scope[value.name] = value
+
+
+    def WITH(self, node):
+        """
+        Handle C{with} by adding bindings for the name or tuple of names it
+        puts into scope and by continuing to process the suite within the
+        statement.
+        """
+        # for "with foo as bar", there is no AssName node for "bar".
+        # Instead, there is a Name node. If the "as" expression assigns to
+        # a tuple, it will instead be a AssTuple node of Name nodes.
+        #
+        # Of course these are assignments, not references, so we have to
+        # handle them as a special case here.
+
+        self.handleNode(node.expr)
+
+        if isinstance(node.vars, ast.AssTuple):
+            varNodes = node.vars.nodes
+        elif node.vars is not None:
+            varNodes = [node.vars]
+        else:
+            varNodes = []
+
+        for varNode in varNodes:
+            self.addBinding(varNode.lineno, Assignment(varNode.name, varNode))
+
+        self.handleChildren(node.body)
+
+
+    def GLOBAL(self, node):
+        """
+        Keep track of globals declarations.
+        """
+        if isinstance(self.scope, FunctionScope):
+            self.scope.globals.update(dict.fromkeys(node.names))
+
+    def LISTCOMP(self, node):
+        for qual in node.quals:
+            self.handleNode(qual)
+        self.handleNode(node.expr)
+
+    GENEXPRINNER = LISTCOMP
+
+    def FOR(self, node):
+        """
+        Process bindings for loop variables.
+        """
+        vars = []
+        def collectLoopVars(n):
+            if hasattr(n, 'name'):
+                vars.append(n.name)
+            else:
+                for c in n.getChildNodes():
+                    collectLoopVars(c)
+
+        collectLoopVars(node.assign)
+        for varn in vars:
+            if (isinstance(self.scope.get(varn), Importation)
+                    # unused ones will get an unused import warning
+                    and self.scope[varn].used):
+                self.report(ImportShadowedByLoopVar,
+                            node.lineno, varn, self.scope[varn].source.lineno)
+
+        self.handleChildren(node)
+
+    def NAME(self, node):
+        """
+        Locate the name in locals / function / globals scopes.
+        """
+        # try local scope
+        importStarred = self.scope.importStarred
+        try:
+            self.scope[node.name].used = (self.scope, node.lineno)
+        except KeyError:
+            pass
+        else:
+            return
+
+        # try enclosing function scopes
+
+        for scope in self.scopeStack[-2:0:-1]:
+            importStarred = importStarred or scope.importStarred
+            if not isinstance(scope, FunctionScope):
+                continue
+            try:
+                scope[node.name].used = (self.scope, node.lineno)
+            except KeyError:
+                pass
+            else:
+                return
+
+        # try global scope
+
+        importStarred = importStarred or self.scopeStack[0].importStarred
+        try:
+            self.scopeStack[0][node.name].used = (self.scope, node.lineno)
+        except KeyError:
+            if ((not hasattr(__builtin__, node.name))
+                    and node.name not in ['__file__']
+                    and not importStarred):
+                self.report(UndefinedName, node.lineno, node.name)
+
+
+    def FUNCTION(self, node):
+        if getattr(node, "decorators", None) is not None:
+            self.handleChildren(node.decorators)
+        self.addBinding(node.lineno, FunctionDefinition(node.name, node))
+        self.LAMBDA(node)
+
+    def LAMBDA(self, node):
+        for default in node.defaults:
+            self.handleNode(default)
+
+        def runFunction():
+            args = []
+
+            def addArgs(arglist):
+                for arg in arglist:
+                    if isinstance(arg, tuple):
+                        addArgs(arg)
+                    else:
+                        if arg in args:
+                            self.report(DuplicateArgument, node.lineno, arg)
+                        args.append(arg)
+
+            self.pushFunctionScope()
+            addArgs(node.argnames)
+            for name in args:
+                self.addBinding(node.lineno, Assignment(name, node), reportRedef=False)
+            self.handleNode(node.code)
+            self.popScope()
+
+        self.defer(runFunction)
+
+    def CLASS(self, node):
+        self.addBinding(node.lineno, Assignment(node.name, node))
+        for baseNode in node.bases:
+            self.handleNode(baseNode)
+        self.pushClassScope()
+        self.handleChildren(node.code)
+        self.popScope()
+
+    def ASSNAME(self, node):
+        if node.flags == 'OP_DELETE':
+            if isinstance(self.scope, FunctionScope) and node.name in self.scope.globals:
+                del self.scope.globals[node.name]
+            else:
+                self.addBinding(node.lineno, UnBinding(node.name, node))
+        else:
+            # if the name hasn't already been defined in the current scope
+            if isinstance(self.scope, FunctionScope) and node.name not in self.scope:
+                # for each function or module scope above us
+                for scope in self.scopeStack[:-1]:
+                    if not isinstance(scope, (FunctionScope, ModuleScope)):
+                        continue
+                    # 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 (node.name in scope
+                            and scope[node.name].used
+                            and scope[node.name].used[0] is self.scope
+                            and node.name not in self.scope.globals):
+                        # then it's probably a mistake
+                        self.report(UndefinedLocal,
+                                    scope[node.name].used[1],
+                                    node.name,
+                                    scope[node.name].source.lineno)
+                        break
+
+            self.addBinding(node.lineno, Assignment(node.name, node))
+
+    def ASSIGN(self, node):
+        self.handleNode(node.expr)
+        for subnode in node.nodes[::-1]:
+            self.handleNode(subnode)
+
+    def IMPORT(self, node):
+        for name, alias in node.names:
+            name = alias or name
+            importation = Importation(name, node)
+            self.addBinding(node.lineno, importation)
+
+    def FROM(self, node):
+        if node.modname == '__future__':
+            if not self.futuresAllowed:
+                self.report(LateFutureImport, node.lineno, [n[0] for n in node.names])
+        else:
+            self.futuresAllowed = False
+
+        for name, alias in node.names:
+            if name == '*':
+                self.scope.importStarred = True
+                self.report(ImportStarUsed, node.lineno, node.modname)
+                continue
+            name = alias or name
+            importation = Importation(name, node)
+            if node.modname == '__future__':
+                importation.used = (self.scope, node.lineno)
+            self.addBinding(node.lineno, importation)
+
+def check(codeString, filename):
+    try:
+        tree = compiler.parse(codeString)
+    except (SyntaxError, IndentationError):
+        value = sys.exc_info()[1]
+        try:
+            (lineno, offset, line) = value[1][1:]
+        except IndexError:
+            print >> sys.stderr, 'could not compile %r' % (filename,)
+            return 1
+        if line.endswith("\n"):
+            line = line[:-1]
+        print >> sys.stderr, '%s:%d: could not compile' % (filename, lineno)
+        print >> sys.stderr, line
+        print >> sys.stderr, " " * (offset-2), "^"
+        return 1
+    else:
+        w = Checker(tree, filename)
+        w.messages.sort(lambda a, b: cmp(a.lineno, b.lineno))
+        for warning in w.messages:
+            print warning
+        return len(w.messages)
+
+
+def checkPath(filename):
+    if os.path.exists(filename):
+        return check(file(filename, 'U').read(), filename)
+
+
+def main(args):
+    warnings = 0
+    if args:
+        for arg in args:
+            if os.path.isdir(arg):
+                for dirpath, dirnames, filenames in os.walk(arg):
+                    for filename in filenames:
+                        if filename.endswith('.py'):
+                            warnings += checkPath(
+                                os.path.join(dirpath, filename))
+            else:
+                warnings += checkPath(arg)
+    else:
+        warnings += check(sys.stdin.read(), '<stdin>')
+
+    return warnings > 0
+
+if __name__ == '__main__':
+    sys.exit(main(sys.argv[1:]))

Modified: trunk/tests/Makefile.am
==============================================================================
--- trunk/tests/Makefile.am	(original)
+++ trunk/tests/Makefile.am	Thu Aug 14 11:23:26 2008
@@ -20,4 +20,7 @@
 TESTS = #roundtrips.sh
 
 check-local:
-	find $(top_srcdir)/giscanner -name \*.py | sort | uniq | xargs $(PYTHON) $(top_srcdir)/misc/pep8.py --repeat
+	@echo Running PEP8 on Python sources
+	@find $(top_srcdir)/giscanner -name \*.py | sort | uniq | xargs $(PYTHON) $(top_srcdir)/misc/pep8.py --repeat
+	@echo Running Pyflakes on Python sources
+	@find $(top_srcdir)/giscanner -name \*.py | sort | uniq | xargs $(PYTHON) $(top_srcdir)/misc/pyflakes.py



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]