Вернуть список импортированных модулей Python, используемых в скрипте? - PullRequest
28 голосов
/ 04 апреля 2010

Я пишу программу, которая классифицирует список файлов Python, по которым импортируются модули. Поэтому мне нужно сканировать коллекцию файлов .py и возвращать список модулей, которые они импортируют. Например, если один из импортируемых мной файлов имеет следующие строки:

import os
import sys, gtk

Я бы хотел вернуть:

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

Я играл с 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,

но это возвращает больше, чем просто модули, используемые в скрипте. В качестве примера в сценарии, который просто имеет:

import os
print os.getenv('USERNAME')

Модули, возвращаемые из скрипта 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

... тогда как я просто хочу, чтобы он возвращал 'os', поскольку именно этот модуль использовался в скрипте.

Может ли кто-нибудь помочь мне достичь этого?

ОБНОВЛЕНИЕ : Я просто хочу уточнить, что я хотел бы сделать это без запуска анализируемого файла Python и только сканирования кода.

Ответы [ 12 ]

0 голосов
/ 15 октября 2013

Для большинства скриптов, которые импортируют модули только на верхнем уровне, вполне достаточно загрузить файл как модуль и отсканировать его элементы на наличие модулей:

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

Имитирует работу модуля в виде скрипта, устанавливая имя модуля на '__main__'. Поэтому он также должен фиксировать динамичную динамическую загрузку модуля. Единственные модули, которые он не захватит, это те, которые импортированы только в локальные области.

0 голосов
/ 29 июля 2012

Я редактирую свой оригинальный ответ, чтобы сказать это. Это можно сделать с помощью фрагмента кода, подобного приведенному ниже, но синтаксический анализ AST может быть лучшим способом.

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

Дальнейший разбор строк будет необходим, чтобы получить только имена модулей. Это не распространяется на случаи, когда многострочные строки или строки документа содержат слова «импорт» или «из X import». Вот почему я предложил разбор AST.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...