Gib eine Liste der importierten Python-Module zurück, die in einem Skript verwendet werden?

Ich schreibe ein Programm, das eine Liste von Python-Dateien kategorisiert, mit welchen Modulen sie importieren. Als solches brauche ich, um die Sammlung von .py-Dateien zu scannen, um eine Liste zu sehen, welche Module sie importieren. Als Beispiel, wenn eine der Dateien, die ich importiere, die folgenden Zeilen hat:

import os import sys, gtk 

Ich möchte, dass es zurückkommt:

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

Ich habe mit Modulfinder gespielt und geschrieben:

 from modulefinder import ModuleFinder finder = ModuleFinder() finder.run_script('testscript.py') print 'Loaded modules:' for name, mod in finder.modules.iteritems(): print '%s ' % name, 

Aber das gibt mehr als nur die im Skript verwendeten Module. Als Beispiel in einem Skript, das nur hat:

 import os print os.getenv('USERNAME') 

Die vom ModulFinder-Skript zurückgegebenen Module kehren zurück:

 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 

… wohin ich will, dass es 'os' zurückkehrt, da das Modul im Skript verwendet wurde.

Kann mir jemand helfen, das zu erreichen?

UPDATE : Ich möchte nur klären, dass ich das gerne machen möchte, ohne die Python-Datei zu analysieren und nur den Code zu scannen.

9 Solutions collect form web for “Gib eine Liste der importierten Python-Module zurück, die in einem Skript verwendet werden?”

IMO der beste Weg todo dies ist, um die http://furius.ca/snakefood/ Paket verwenden. Der Autor hat alle erforderlichen Arbeiten getan, um nicht nur direkt importierte Module zu bekommen, sondern es nutzt die AST, um den Code für Laufzeitabhängigkeiten zu analysieren, die eine statischere Analyse vermissen würde.

Erarbeitung eines Befehlsbeispiels, um zu demonstrieren:

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

Das erzeugt eine grundlegende Abhängigkeitsdatei jedes einzelnen Moduls. Für noch mehr Details:

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

Um einen Baum zu gehen und alle Importe zu finden, kannst du das auch im Code machen: Bitte beachten Sie, dass die AST-Chunks dieser Routine von der Snakefood-Quelle aufgehoben wurden, die dieses Urheberrecht hat: Copyright (C) 2001-2007 Martin Blais. Alle Rechte vorbehalten.

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

Es hängt davon ab, wie gründlich du sein möchtest. Gebrauchtmodule sind ein komplettes Problem: Einige Python-Code verwendet faulen Import, um nur Dinge zu importieren, die sie tatsächlich auf einem bestimmten Lauf verwenden, manche erzeugen Dinge dynamisch importieren (zB Plugin-Systeme).

Python -v wird importieren Aussagen – seine wohl die einfachste Sache zu überprüfen.

Nun, man könnte immer ein einfaches Skript schreiben, das die Datei nach import durchsucht. Dies findet alle importierten Module und Dateien, einschließlich der in Funktionen oder Klassen importierten:

 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) 

Dies macht nichts für from module import something Stil importiert, obwohl das leicht hinzugefügt werden könnte, je nachdem, wie Sie mit denen umgehen wollen. Es macht auch keine Syntax-Überprüfung, also wenn Sie einige lustige Geschäfte wie import sys gtk, os es wird denken, dass Sie alle drei Module importiert haben, obwohl die Zeile ein Fehler ist. Es geht auch nicht mit try / except Typ Aussagen in Bezug auf Import – wenn es importiert werden könnte, diese Funktion wird es auflisten. Es geht auch nicht gut mit mehreren Importen pro Zeile, wenn Sie das as Schlüsselwort verwenden. Die eigentliche Frage hier ist, dass ich einen vollständigen Parser schreiben muss, um das wirklich richtig zu machen. Der gegebene Code funktioniert in vielen Fällen, solange du verstehst, gibt es definitive Eckfälle.

Ein Problem ist, dass relative Importe fehlschlagen, wenn dieses Skript nicht im selben Verzeichnis wie die angegebene Datei ist. Sie können das Verzeichnis des angegebenen Skripts zu sys.path .

Dies funktioniert – mit importlib, um das Modul tatsächlich zu importieren und zu prüfen, um die Mitglieder zu bekommen:

 #! /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 !!!!" 

Ausgabe :

 tony@laptop .../~:$ ./test.py path sys 

Für die Mehrheit der Skripte, die nur Module auf der obersten Ebene importieren, reicht es aus, die Datei als Modul zu laden und ihre Mitglieder für Module zu scannen:

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

Dies simuliert das Modul als Skript ausgeführt, indem Sie den Namen des Moduls auf '__main__' . Es sollte also auch ein funky dynamisches Modul laden. Die einzigen Module, die es nicht erfassen wird, sind diejenigen, die nur in lokale Bereiche importiert werden.

Ich suchte etwas Ähnliches und ich fand ein Juwel in einem Paket namens PyScons . Der Scanner macht genau das, was du willst (in 7 Zeilen), mit einem import_hook. Hier ist ein abgekürztes Beispiel:

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

Vielleicht möchten Sie versuchen, diese (Wortspiel beabsichtigt):

 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) 

Ausgänge

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

Ihre importierten Module sind grouped['IMPORT_NAME'] .

Es funktioniert eigentlich ganz gut mit

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

Ich bearbeite meine ursprüngliche Antwort, um das zu sagen. Dies ist machbar mit einem Code-Snippet wie die unten, aber das Parsing der AST kann der beste Weg zu gehen.

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

Eine weitere String-Parsing wird benötigt, um nur die Modulnamen zu greifen. Dies erkennt keine Fälle, in denen mehrzeilige Strings oder doc Strings die Wörter "import" oder "from X import" enthalten. Aus diesem Grund schlug ich vor, die AST zu analysieren.

  • Welches Python-Modul eignet sich für die Datenmanipulation in einer Liste?
  • Python-Modul-Import - relative Pfade Problem
  • Generieren Sie Zufallszahlen mit einer gegebenen (numerischen) Verteilung
  • Wie bekomme ich Python, um das Modul von $ HOME / lib / python über / usr / lib / python zu bevorzugen?
  • Abfangen von Modulaufrufen?
  • Was ist __init__.py für?
  • Wie bekomme ich eine Liste von lokal installierten Python-Modulen?
  • Python Import Typ Erkennung
  • Aufrufbare Module
  • Django sagt - Kein Modul namens 'Blog'
  • Python-Modul und __all__
  • Python ist die beste Programmiersprache der Welt.