返回脚本中使用的导入Python模块列表?

Jon*_*con 28 python module

我正在编写一个程序,用于对导入模块的Python文件列表进行分类.因此,我需要扫描.py文件的集合,并返回它们导入的模块的列表.例如,如果我导入的其中一个文件包含以下行:

import os
import sys, gtk
Run Code Online (Sandbox Code Playgroud)

我希望它返回:

["os", "sys", "gtk"]
Run Code Online (Sandbox Code Playgroud)

我玩modulefinder并写道:

from modulefinder import ModuleFinder

finder = ModuleFinder()
finder.run_script('testscript.py')

print 'Loaded modules:'
for name, mod in finder.modules.iteritems():
    print '%s ' % name,
Run Code Online (Sandbox Code Playgroud)

但这不仅仅返回脚本中使用的模块.作为脚本中的一个例子,它只包含:

import os
print os.getenv('USERNAME')
Run Code Online (Sandbox Code Playgroud)

从ModuleFinder脚本返回的模块返回:

tokenize  heapq  __future__  copy_reg  sre_compile  _collections  cStringIO  _sre  functools  random  cPickle  __builtin__  subprocess  cmd  gc  __main__  operator  array  select  _heapq  _threading_local  abc  _bisect  posixpath  _random  os2emxpath  tempfile  errno  pprint  binascii  token  sre_constants  re  _abcoll  collections  ntpath  threading  opcode  _struct  _warnings  math  shlex  fcntl  genericpath  stat  string  warnings  UserDict  inspect  repr  struct  sys  pwd  imp  getopt  readline  copy  bdb  types  strop  _functools  keyword  thread  StringIO  bisect  pickle  signal  traceback  difflib  marshal  linecache  itertools  dummy_thread  posix  doctest  unittest  time  sre_parse  os  pdb  dis
Run Code Online (Sandbox Code Playgroud)

...而我只是想让它返回'os',因为那是脚本中使用的模块.

谁能帮我实现这个目标?

更新:我只是想澄清一下,如果不运行分析的Python文件,只想扫描代码,我想这样做.

小智 15

IMO最好的办法就是使用http://furius.ca/snakefood/包.作者已经完成了所有必需的工作,不仅可以获得直接导入的模块,还可以使用AST来解析更静态分析会遗漏的运行时依赖性代码.

编写了一个命令示例来演示:

sfood ./example.py | sfood-cluster > example.deps
Run Code Online (Sandbox Code Playgroud)

这将生成每个唯一模块的基本依赖文件.有关更多细节,请使用:

sfood -r -i ./example.py | sfood-cluster > example.deps
Run Code Online (Sandbox Code Playgroud)

要走树并查找所有导入,您也可以在代码中执行此操作:请注意 - 此例程的AST块取自具有此版权的蛇食源:版权所有(C)2001-2007 Martin Blais.版权所有.

 import os
 import compiler
 from compiler.ast import Discard, Const
 from compiler.visitor import ASTVisitor

 def pyfiles(startPath):
     r = []
     d = os.path.abspath(startPath)
     if os.path.exists(d) and os.path.isdir(d):
         for root, dirs, files in os.walk(d):
             for f in files:
                 n, ext = os.path.splitext(f)
                 if ext == '.py':
                     r.append([d, f])
     return r

 class ImportVisitor(object):
     def __init__(self):
         self.modules = []
         self.recent = []
     def visitImport(self, node):
         self.accept_imports()
         self.recent.extend((x[0], None, x[1] or x[0], node.lineno, 0)
                            for x in node.names)
     def visitFrom(self, node):
         self.accept_imports()
         modname = node.modname
         if modname == '__future__':
             return # Ignore these.
         for name, as_ in node.names:
             if name == '*':
                 # We really don't know...
                 mod = (modname, None, None, node.lineno, node.level)
             else:
                 mod = (modname, name, as_ or name, node.lineno, node.level)
             self.recent.append(mod)
     def default(self, node):
         pragma = None
         if self.recent:
             if isinstance(node, Discard):
                 children = node.getChildren()
                 if len(children) == 1 and isinstance(children[0], Const):
                     const_node = children[0]
                     pragma = const_node.value
         self.accept_imports(pragma)
     def accept_imports(self, pragma=None):
         self.modules.extend((m, r, l, n, lvl, pragma)
                             for (m, r, l, n, lvl) in self.recent)
         self.recent = []
     def finalize(self):
         self.accept_imports()
         return self.modules

 class ImportWalker(ASTVisitor):
     def __init__(self, visitor):
         ASTVisitor.__init__(self)
         self._visitor = visitor
     def default(self, node, *args):
         self._visitor.default(node)
         ASTVisitor.default(self, node, *args) 

 def parse_python_source(fn):
     contents = open(fn, 'rU').read()
     ast = compiler.parse(contents)
     vis = ImportVisitor() 

     compiler.walk(ast, vis, ImportWalker(vis))
     return vis.finalize()

 for d, f in pyfiles('/Users/bear/temp/foobar'):
     print d, f
     print parse_python_source(os.path.join(d, f)) 
Run Code Online (Sandbox Code Playgroud)

  • Snakefood不支持python3。 (3认同)

Jay*_*mon 8

我最近需要给定 python 脚本的所有依赖项,我采用了与其他答案不同的方法。我只关心顶级模块模块名称(例如,我想要foofrom import foo.bar)。

这是使用ast 模块的代码:

import ast


modules = set()

def visit_Import(node):
    for name in node.names:
        modules.add(name.name.split(".")[0])

def visit_ImportFrom(node):
    # if node.module is missing it's a "from . import ..." statement
    # if level > 0 it's a "from .submodule import ..." statement
    if node.module is not None and node.level == 0:
        modules.add(node.module.split(".")[0])

node_iter = ast.NodeVisitor()
node_iter.visit_Import = visit_Import
node_iter.visit_ImportFrom = visit_ImportFrom
Run Code Online (Sandbox Code Playgroud)

使用foo.py包含以下内容的 python 文件进行测试:

# foo.py
import sys, os
import foo1
from foo2 import bar
from foo3 import bar as che
import foo4 as boo
import foo5.zoo
from foo6 import *
from . import foo7, foo8
from .foo12 import foo13
from foo9 import foo10, foo11

def do():
    import bar1
    from bar2 import foo
    from bar3 import che as baz
Run Code Online (Sandbox Code Playgroud)

我可以foo.py通过执行以下操作来获取所有模块:

with open("foo.py") as f:
    node_iter.visit(ast.parse(f.read()))
print(modules)
Run Code Online (Sandbox Code Playgroud)

这会给我这个输出:

set(['bar1', 'bar3', 'bar2', 'sys', 'foo9', 'foo4', 'foo5', 'foo6', 'os', 'foo1', 'foo2', 'foo3'])
Run Code Online (Sandbox Code Playgroud)


Gui*_*tas 6

您可能要尝试使用dis(双关语):

import dis
from collections import defaultdict
from pprint import pprint

statements = """
from __future__ import (absolute_import,
                        division)
import os
import collections, itertools
from math import *
from gzip import open as gzip_open
from subprocess import check_output, Popen
"""

instructions = dis.get_instructions(statements)
imports = [__ for __ in instructions if 'IMPORT' in __.opname]

grouped = defaultdict(list)
for instr in imports:
    grouped[instr.opname].append(instr.argval)

pprint(grouped)
Run Code Online (Sandbox Code Playgroud)

输出

defaultdict(<class 'list'>,
            {'IMPORT_FROM': ['absolute_import',
                             'division',
                             'open',
                             'check_output',
                             'Popen'],
             'IMPORT_NAME': ['__future__',
                             'os',
                             'collections',
                             'itertools',
                             'math',
                             'gzip',
                             'subprocess'],
             'IMPORT_STAR': [None]})
Run Code Online (Sandbox Code Playgroud)

您导入的模块为grouped['IMPORT_NAME']


lif*_*ess 5

这取决于你想要多么彻底.使用过的模块是一个完整的问题:一些python代码使用延迟导入只导入他们在特定运行中实际使用的东西,有些生成要动态导入的东西(例如插件系统).

python -v将跟踪import语句 - 它可以说是最简单的检查方法.