2010-04-03 24 views
23

Tôi đang viết một chương trình phân loại danh sách các tệp Python theo đó các mô-đun họ nhập. Vì vậy, tôi cần phải quét bộ sưu tập các quảng cáo tệp .py trả về danh sách các mô-đun mà chúng nhập. Ví dụ, nếu một trong số các tập tin tôi nhập khẩu có những dòng sau:Trả về danh sách các mô-đun Python đã nhập được sử dụng trong tập lệnh?

import os 
import sys, gtk 

Tôi muốn nó trở lại:

["os", "sys", "gtk"] 

tôi chơi với modulefinder và viết:

from modulefinder import ModuleFinder 

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

print 'Loaded modules:' 
for name, mod in finder.modules.iteritems(): 
    print '%s ' % name, 

nhưng điều này trả về nhiều hơn chỉ là các mô-đun được sử dụng trong tập lệnh. Như một ví dụ trong một kịch bản mà chỉ có:

import os 
print os.getenv('USERNAME') 

Các module trở về từ các ModuleFinder kịch bản trở lại:

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 

... trong khi tôi chỉ muốn nó trở lại 'os', vì đó là mô-đun được sử dụng trong tập lệnh.

Có ai có thể giúp tôi đạt được điều này không?

CẬP NHẬT: Tôi chỉ muốn làm rõ rằng tôi muốn thực hiện điều này mà không cần chạy tệp Python đang được phân tích và chỉ quét mã.

+0

có bất kỳ mô-đun bổ sung nào được thêm vào thủ công với câu lệnh nhập không? Nếu không, bạn có thể không chỉ tạo một mô-đun trống, chạy công cụ tìm mô-đun của bạn trên đó và sử dụng kết quả bằng cách xóa các mô-đun đó khỏi kết quả của mỗi mô-đun khác không? I E. 'modulefinder (some_module) - modulefinder (empty_module)'? –

Trả lời

12

IMO cách tốt nhất để thực hiện việc này là sử dụng gói http://furius.ca/snakefood/. Tác giả đã thực hiện tất cả các công việc cần thiết để có được không chỉ các mô-đun được nhập trực tiếp mà nó sử dụng AST để phân tích mã cho các phụ thuộc thời gian chạy mà một phân tích tĩnh hơn sẽ bỏ qua.

Làm việc lên một ví dụ lệnh để chứng minh:

sfood ./example.py | sfood-cluster > example.deps 

Điều đó sẽ tạo ra một tập tin phụ thuộc cơ bản của mỗi mô-đun độc đáo. Đối với thậm chí nhiều hơn sử dụng chi tiết:

sfood -r -i ./example.py | sfood-cluster > example.deps 

Để đi bộ một cây và tất cả hàng nhập khẩu, bạn cũng có thể làm điều này trong mã: Xin lưu ý - Các khối AST của thói quen này đã được dỡ bỏ từ nguồn snakefood trong đó có bản quyền này : Bản quyền (C) 2001-2007 Martin Blais. Đã đăng ký Bản quyền.

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)) 

+0

Điều này có vẻ là một công cụ của bên thứ ba cho các đồ thị được tạo ra, nhưng không phải là thứ tôi có thể sử dụng trong mã của tôi ngoài việc sử dụng nó với tiến trình con. Đây có phải là những gì bạn đề nghị không? –

+0

ah - bạn đang tìm kiếm thứ gì đó thực sự cơ bản để bao gồm - hãy để tôi nhai rằng – bear

+0

tất nhiên nó sẽ giúp nếu * chạy * mẫu của tôi - làm việc trên một phiên bản làm việc thực tế:/ – bear

2

Vâng, bạn luôn có thể viết một kịch bản đơn giản để tìm kiếm các tập tin cho import báo cáo. Cái này thấy module tất cả nhập khẩu và các tập tin, bao gồm cả những nhập khẩu trong các chức năng hoặc các lớp:

def find_imports(toCheck): 
    """ 
    Given a filename, returns a list of modules imported by the program. 
    Only modules that can be imported from the current directory 
    will be included. This program does not run the code, so import statements 
    in if/else or try/except blocks will always be included. 
    """ 
    import imp 
    importedItems = [] 
    with open(toCheck, 'r') as pyFile: 
     for line in pyFile: 
      # ignore comments 
      line = line.strip().partition("#")[0].partition("as")[0].split(' ') 
      if line[0] == "import": 
       for imported in line[1:]: 
        # remove commas (this doesn't check for commas if 
        # they're supposed to be there! 
        imported = imported.strip(", ") 
        try: 
         # check to see if the module can be imported 
         # (doesn't actually import - just finds it if it exists) 
         imp.find_module(imported) 
         # add to the list of items we imported 
         importedItems.append(imported) 
        except ImportError: 
         # ignore items that can't be imported 
         # (unless that isn't what you want?) 
         pass 

    return importedItems 

toCheck = raw_input("Which file should be checked: ") 
print find_imports(toCheck) 

này không làm bất cứ điều gì cho from module import something phong cách nhập khẩu, mặc dù điều đó một cách dễ dàng có thể được thêm vào, tùy thuộc vào cách bạn muốn để đối phó với những, cái đó. Nó cũng không làm bất kỳ kiểm tra cú pháp, vì vậy nếu bạn có một số doanh nghiệp vui như import sys gtk, os nó sẽ nghĩ rằng bạn đã nhập khẩu tất cả ba mô-đun mặc dù dòng là một lỗi. Nó cũng không xử lý các câu lệnh loại try/except liên quan đến nhập - nếu nó có thể được nhập, hàm này sẽ liệt kê nó.Nó cũng không hoạt động tốt với nhiều lần nhập mỗi dòng nếu bạn sử dụng từ khóa as. Vấn đề thực sự ở đây là tôi phải viết một trình phân tích cú pháp đầy đủ để thực sự làm điều này một cách chính xác. Mã cho sẵn hoạt động trong nhiều trường hợp, miễn là bạn hiểu có trường hợp góc xác định.

Một vấn đề là nhập tương đối sẽ không thành công nếu tập lệnh này không nằm trong cùng thư mục với tệp đã cho. Bạn có thể muốn thêm thư mục của tập lệnh đã cho vào số sys.path.

+0

Tôi vừa thử tập lệnh này; nó tìm thấy 9 trong 27 câu lệnh nhập khẩu trong một ứng dụng. Điều đó sẽ không hoạt động. – Chelonian

+0

@Chelonian Bạn có thể cụ thể hơn ở đây không? bạn chỉ cần nói "nó không hoạt động". Có gì đặc biệt về việc nhập khẩu chương trình không tìm thấy? – dm76

+0

@ dm76 - Tôi cho rằng mã của bạn chỉ tìm lệnh 'nhập' - và do đó nhớ ' nhập ' lệnh - sử dụng kiểm tra là cách python tôi nghĩ ... –

4

Điều đó tùy thuộc vào mức độ bạn muốn đạt được. Mô-đun đã sử dụng là vấn đề hoàn chỉnh: một số mã python sử dụng nhập khẩu lười biếng để chỉ nhập những thứ họ thực sự sử dụng trên một lần chạy cụ thể, một số tạo ra mọi thứ để nhập động (ví dụ: hệ thống plugin).

python -v sẽ theo dõi các câu lệnh nhập - được cho là điều đơn giản nhất để kiểm tra.

-1

Tôi đang chỉnh sửa câu trả lời gốc để nói điều này. Điều này có thể thực hiện được với đoạn mã như đoạn dưới đây, nhưng việc phân tích cú pháp AST có thể là cách tốt nhất để thực hiện.

def iter_imports(fd): 
    """ Yield only lines that appear to be imports from an iterable. 
     fd can be an open file, a list of lines, etc. 
    """ 
    for line in fd: 
     trimmed = line.strip() 
     if trimmed.startswith('import '): 
      yield trimmed 
     elif trimmed.startswith('from ') and ('import ' in trimmed): 
      yield trimmed 

def main(): 
    # File name to read. 
    filename = '/my/path/myfile.py' 
    # Safely open the file, exit on error 
    try: 
     with open(filename) as f: 
      # Iterate over the lines in this file, and generate a list of 
      # lines that appear to be imports. 
      import_lines = list(iter_imports(f)) 
    except (IOError, OSError) as exIO: 
     print('Error opening file: {}\n{}'.format(filename, exIO)) 
     return 1 
    else: 
     # From here, import_lines should be a list of lines like this: 
     #  from module import thing 
     #  import os, sys 
     #  from module import * 
     # Do whatever you need to do with the import lines. 
     print('\n'.join(import_lines)) 

    return 0 

if __name__ == '__main__': 
    sys.exit(main()) 

Phân tích chuỗi tiếp theo sẽ chỉ cần lấy các tên mô-đun. Điều này không bắt các trường hợp trong đó chuỗi nhiều dòng hoặc chuỗi tài liệu chứa các từ 'nhập' hoặc 'từ nhập X'. Đây là lý do tại sao tôi đề nghị phân tích cú pháp AST.

+0

Bạn có thể cung cấp một số mã trong câu trả lời của mình không? –

+0

wow, stackoverflow đã giết mã đó. Tôi đã làm gì sai? Dù sao thì phải mất nhiều hơn một chút nhưng nếu bạn chỉ tìm kiếm nhập khẩu thì kịch bản sẽ nhỏ hơn so với những gì codecounter là ngay bây giờ. –

+0

Chỉnh sửa câu trả lời thay vì thêm mã làm nhận xét và nó sẽ ổn. –

0

Đối với phần lớn các kịch bản mà chỉ module nhập khẩu ở cấp cao nhất, nó là khá đủ để tải các tập tin như một mô-đun, và quét các thành viên của nó đối với các module:

import sys,io,imp,types 
scriptname = 'myfile.py' 
with io.open(scriptname) as scriptfile: 
    code = compile(scriptfile.readall(),scriptname,'exec') 
newmodule = imp.new_module('__main__') 
exec(codeobj,newmodule.__dict__) 
scriptmodules = [name for name in dir(newmodule) if isinstance(newmodule.__dict__[name],types.ModuleType)] 

này mô phỏng các mô-đun được chạy dưới dạng tập lệnh, bằng cách đặt tên của mô-đun thành '__main__'. Do đó, nó cũng sẽ nắm bắt được tải mô-đun năng động sôi nổi. Các mô-đun duy nhất nó sẽ không nắm bắt được những người được nhập khẩu chỉ vào phạm vi địa phương.

+0

Tôi gặp lỗi ('code = compile (scriptfile.readall(), scriptname, 'exec') AttributeError: đối tượng 'TextIOWrapper' không có thuộc tính 'readall'') Thay đổi' io.open' bằng 'io.FileIO' giải quyết nhưng không thể mở các mô-đun do người dùng xác định ... – Llopis

2

này hoạt động - sử dụng importlib để thực sự nhập khẩu các mô-đun, và kiểm tra để có được các thành viên:

#! /usr/bin/env python 
# 
# test.py 
# 
# Find Modules 
# 
import inspect, importlib as implib 

if __name__ == "__main__": 
    mod = implib.import_module("example") 
    for i in inspect.getmembers(mod, inspect.ismodule): 
     print i[0] 

#! /usr/bin/env python 
# 
# example.py 
# 
import sys 
from os import path 

if __name__ == "__main__": 
    print "Hello World !!!!" 

Output:

[email protected] .../~:$ ./test.py 
path 
sys 

0

tôi đang tìm kiếm một cái gì đó tương tự và tôi tìm thấy một viên ngọc trong một gói có tên là PyScons. Máy quét chỉ làm những gì bạn muốn (trong 7 dòng), sử dụng một import_hook. Dưới đây là một ví dụ viết tắt:

import modulefinder, sys 

class SingleFileModuleFinder(modulefinder.ModuleFinder): 

    def import_hook(self, name, caller, *arg, **kwarg): 
     if caller.__file__ == self.name: 
      # Only call the parent at the top level. 
      return modulefinder.ModuleFinder.import_hook(self, name, caller, *arg, **kwarg) 

    def __call__(self, node): 

     self.name = str(node) 

     self.run_script(self.name) 

if __name__ == '__main__': 
    # Example entry, run with './script.py filename' 
    print 'looking for includes in %s' % sys.argv[1] 

    mf = SingleFileModuleFinder() 
    mf(sys.argv[1]) 

    print '\n'.join(mf.modules.keys()) 
1

Bạn có thể muốn thử dis (ý định chơi chữ):

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) 

đầu ra

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

module nhập của bạn là grouped['IMPORT_NAME'].

0

Nó thực sự làm việc khá tốt với

print [key for key in locals().keys() 
    if isinstance(locals()[key], type(sys)) and not key.startswith('__')] 
+0

Việc này không tìm thấy bất kỳ mô-đun nào được nhập bằng cú pháp "từ thanh nhập foo". –

0

Cảm ơn Tony Suffolk cho kiểm tra, mẫu importlib ... tôi đã xây dựng mô-đun wee này và tất cả các bạn đang chào đón để sử dụng nó nếu nó giúp bạn. Trả lại, yaaaay!

import timeit 
import os 
import inspect, importlib as implib 
import textwrap as twrap 

def src_modules(filename): 
    assert (len(filename)>1) 

    mod = implib.import_module(filename.split(".")[0]) 
    ml_alias = [] 
    ml_actual = [] 
    ml_together = [] 
    ml_final = [] 
    for i in inspect.getmembers(mod, inspect.ismodule): 
     ml_alias.append(i[0]) 
     ml_actual.append((str(i[1]).split(" ")[1])) 
     ml_together = zip(ml_actual, ml_alias) 
    for t in ml_together: 
     (a,b) = t 
     ml_final.append(a+":="+b) 

    return ml_final 

def l_to_str(itr): 
    assert(len(itr)>0) 

    itr.sort() 
    r_str = "" 
    for i in itr: 
     r_str += i+" " 
    return r_str 

def src_info(filename, start_time=timeit.default_timer()): 
    assert (len(filename)>1) 

    filename_in = filename 
    filename = filename_in.split(".")[0] 

    if __name__ == filename: 
     output_module = filename 
    else: 
     output_module = __name__ 

    print ("\n" + (80 * "#")) 
    print (" runtime ~= {0} ms".format(round(((timeit.default_timer() - start_time)*1000),3))) 
    print (" source file --> '{0}'".format(filename_in)) 
    print (" output via --> '{0}'".format(output_module)) 
    print (" modules used in '{0}':".format(filename)) 
    print (" "+"\n ".join(twrap.wrap(l_to_str(src_modules(filename)), 75))) 
    print (80 * "#") 

    return "" 


if __name__ == "__main__": 
    src_info(os.path.basename(__file__)) 


## how to use in X file: 
# 
# import print_src_info 
# import os 
# 
# < ... your code ... > 
# 
# if __name__ == "__main__": 
#  print_src_info.src_info(os.path.basename(__file__)) 


## example output: 
# 
# ################################################################################ 
# runtime ~= 0.049 ms 
# source file --> 'print_src_info.py' 
# output via --> '__main__' 
# modules used in 'print_src_info': 
# 'importlib':=implib 'inspect':=inspect 'os':=os 'textwrap':=twrap 
# 'timeit':=timeit 
# ################################################################################ 
1

Tôi hiểu rằng bài đăng này rất cũ nhưng tôi đã tìm được giải pháp lý tưởng. Tôi đã đưa ra ý tưởng này:

def find_modules(code): 
    modules = [] 
    code = code.splitlines() 
    for item in code: 
     if item[:7] == "import " and ", " not in item: 
      if " as " in item: 
       modules.append(item[7:item.find(" as ")]) 
      else: 
       modules.append(item[7:]) 
     elif item[:5] == "from ": 
      modules.append(item[5:item.find(" import ")]) 

     elif ", " in item: 
      item = item[7:].split(", ") 
      modules = modules+item 

     else: 
      print(item) 
    return modules 

code = """ 
import foo 
import bar 
from baz import eggs 
import mymodule as test 
import hello, there, stack 
""" 
print(find_modules(code)) 

nó từ, như dấu phẩy và báo cáo nhập thông thường. không yêu cầu phụ thuộc và làm việc với các dòng mã khác.

Mã in trên:

['foo', 'bar', 'baz', 'mymodule', 'hello', 'there', 'stack'] 

Chỉ cần đặt mã của bạn trong find_modules chức năng.

Các vấn đề liên quan