#11749: Remove unneeded imports
---------------------------+------------------------------------------------
   Reporter:  robertwb     |          Owner:  tbd            
       Type:  enhancement  |         Status:  needs_review   
   Priority:  major        |      Milestone:  sage-4.7.2     
  Component:  performance  |       Keywords:                 
Work_issues:               |       Upstream:  N/A            
   Reviewer:  Keshav Kini  |         Author:  Robert Bradshaw
     Merged:               |   Dependencies:                 
---------------------------+------------------------------------------------

Comment(by robertwb):

 I generated the patch with this code, plus a handful (less than a dozen)
 manual touch-ups where, e.g., symbols were used in eval() strings but not
 directly.

 {{{
 import symtable
 import ast
 import re

 class GlobalImports(ast.NodeVisitor):

     func_depth = 0

     def __init__(self):
         self.modules = {}
         self.names = {}
         self.used = {}

     def visit_FunctionDef(self, node):
         self.func_depth += 1
         self.generic_visit(node)
         self.func_depth -= 1

     def visit_Import(self, node):
         if self.func_depth == 0:
             for alias in node.names:
                 self.modules[alias.asname or alias.name] = alias.name

     def visit_ImportFrom(self, node):
         if self.func_depth == 0:
             for alias in node.names:
                 if alias.name != '*':
                     self.names[alias.asname or alias.name] = (node.module,
 alias.name)

     def visit_Name(self, node):
         self.used[node.id] = True

 def list_unused(file, source=None):
     source = source or open(file).read()
     tree = ast.parse(source)
     visitor = GlobalImports()
     visitor.visit(tree)
 #    print visitor.modules
 #    print visitor.names
 #    print visitor.used
     all = dict(visitor.modules)
     all.update(visitor.names)
     for name in all:
         if name not in visitor.used:
             if '.' in name:
                 if source.count(name) == 1:
                     print "Unused", name
             else:
                 print "Unused", name
 #    symtab = symtable. symtable(source, file, 'exec')
 #    return symtab

 def extract_used(source):
     visitor = GlobalImports()
     tree = ast.parse(source)
     visitor.visit(tree)
     return visitor.used

 def prune_unused_one(file, source=None):
     if source is None:
         source = open(file).read()
     used = extract_used(source)
     import_matcher = re.compile(r'(from (\S+) import (.*))|(import
 (.*))').match

     bad_name = re.compile('[()#]').search
     lines = source.split('\n')
     for lineno, line in enumerate(lines):
         m = import_matcher(line)
         if m:
             g = m.groups()
             if g[0]:
                 if g[1] == '__future__':
                     continue
                 names = g[2].split(',')
             else:
                 names = g[4].split(',')
             #print names
             for ix, name in enumerate(names):
                 name = name.strip()
                 if bad_name(name) or name in ('', '*', '\\'):
                     continue
                 if ' as ' in name:
                     name, alias = name.split(" as ")
                     name = name.strip()
                     alias = alias.strip()
                 else:
                     alias = name
                 if alias not in used:
                     if "." in alias:
                         unused = source.count(alias) == 1
                     else:
                         unused = True
                     if unused:
                         print "Unused %s%s" % (g[1] + "." if g[1] else "",
 name)
                         names[ix] = None
             if None in names:
                 new_names = ", ".join(name.strip() for name in names if
 name is not None)
                 #print names, "->", new_names
                 if new_names:
                     if g[0]:
                         lines[lineno] = "from %s import %s" % (g[1],
 new_names)
                     else:
                         lines[lineno] = "import %s" % new_names
                 else:
                     lines[lineno] = None

     return "\n".join(line for line in lines if line is not None)

 def prune_unused_all(sage_root):
     for root, dirs, files in os.walk('%s/devel/sage/sage/' % sage_root):
         for file in files:
             file = os.path.join(root, file)
             if file.endswith('.py'):
                 if os.path.basename(file) in ('__init__.py', 'all.py',
 'interpreter.py'):
                     continue
                 source = open(file).read()
                 try:
                     new_source = prune_unused_one(file, source)
                     if new_source != source:
                         ast.parse(new_source)
                         print file
                         open(file, 'w').write(new_source)
                         r = os.system("%s/sage -b > /dev/null 2>
 /dev/null" % sage_root)
                         if r == 0:
                             r = os.system("%s/sage -python -c 'import
 sage.all'" % sage_root)
                         if r != 0:
                             print "Failed!"
                             open(file, 'w').write(source)
                         else:
                             print "Good!"
                 except Exception, exn:
                     print file, exn

 }}}

 Then

 {{{
 prune_unused_all('/Users/robertwb/sage/sage-4.7.1')
 }}}

-- 
Ticket URL: <http://trac.sagemath.org/sage_trac/ticket/11749#comment:7>
Sage <http://www.sagemath.org>
Sage: Creating a Viable Open Source Alternative to Magma, Maple, Mathematica, 
and MATLAB

-- 
You received this message because you are subscribed to the Google Groups 
"sage-trac" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to 
[email protected].
For more options, visit this group at 
http://groups.google.com/group/sage-trac?hl=en.

Reply via email to