假设我有这样的项目结构
src
??? app
??? main.py
??? db
? ??? database.py
??? models
? ??? model_a.py
? ??? model_b.py
??? tests
??? test_x.py
??? test_y.py
Run Code Online (Sandbox Code Playgroud)
我想检查哪个文件使用了另一个文件中的类或函数。我在 main.py 中有一个名为 Test 的类
class Test:
pass
Run Code Online (Sandbox Code Playgroud)
我在model_a,
from ..main import Test
Run Code Online (Sandbox Code Playgroud)
但在model_b我用
from ..main import Test
from ..db.database import Data
Run Code Online (Sandbox Code Playgroud)
我想检查哪个文件使用另一个文件,就像tree命令一样,只需要一个文件夹名称就足够了,所以我尝试了一种旧方法,但它效率低下,很脏,这不是我所期望的。方法是我在srcnamed 中创建了一个文件check.py,我导入了所有包
from app.db import database
from app.models import model_a, model_b
from app.tests import test_x, test_y
from app import main
print('__file__={0:<35} | __name__={1:<20} | __package__={2:<20}'.format(__file__,__name__,str(__package__)))
Run Code Online (Sandbox Code Playgroud)
我在所有文件的底部添加了这一行
print('__file__={0:<35} | __name__={1:<20} | __package__={2:<20}'.format(__file__,__name__,str(__package__)))
Run Code Online (Sandbox Code Playgroud)
所以当我跑步时,check.py我得到了这个结果
__file__=/home/yagiz/Desktop/struct/src/app/main.py | __name__=app.main | __package__=app
__file__=/home/yagiz/Desktop/struct/src/app/db/database.py | __name__=app.db.database | __package__=app.db
__file__=/home/yagiz/Desktop/struct/src/app/models/model_a.py | __name__=app.models.model_a | __package__=app.models
__file__=/home/yagiz/Desktop/struct/src/app/models/model_b.py | __name__=app.models.model_b | __package__=app.models
__file__=/home/yagiz/Desktop/struct/src/app/tests/test_x.py | __name__=app.tests.test_x | __package__=app.tests
__file__=/home/yagiz/Desktop/struct/src/app/tests/test_y.py | __name__=app.tests.test_y | __package__=app.tests
__file__=/home/yagiz/Desktop/struct/src/check.py | __name__=__main__ | __package__=None
Run Code Online (Sandbox Code Playgroud)
结果很脏,不符合我的期望有没有办法获得这样的输出?
main.py = app/models/model_a, app/models/model_b # These files imports something from main.py
models_b = None # No file imports from models_b
Run Code Online (Sandbox Code Playgroud)
我抬起头的源代码modulefinder,我发现它增加了在每个import语句是不是对我有用的一个badmodule。
这是怎么回事,首先我创建了一个函数,我还创建了另一个项目结构。
src
??? empty.py
??? __init__.py
??? main.py
??? module_finder.py
??? other
? ??? other.py
??? test
? ??? some_app.py
??? this_imports.py
Run Code Online (Sandbox Code Playgroud)
这是module_finder.py包含我的功能的
from modulefinder import ModuleFinder
file_names = ["this_imports.py", "main.py", "test/some_app.py", "other/other.py", "empty.py"]
def check_imports(file_names):
finder = ModuleFinder()
for file in file_names:
finder.run_script(file)
print("\n", file)
for name, mod in finder.modules.items():
print('%s: ' % name, end='')
print(','.join(list(mod.globalnames.keys())[:3]))
print('\n'.join(finder.badmodules.keys()))
Run Code Online (Sandbox Code Playgroud)
空文件是空的(如预期的那样),在main.py我有
class Test:
pass
Run Code Online (Sandbox Code Playgroud)
在this_imports.py我只有
from src.main import Test
Run Code Online (Sandbox Code Playgroud)
在other/other.py我有
from src.main import Test
from src.test import DifferentTest
Run Code Online (Sandbox Code Playgroud)
而对于最后一个test/some_app.py我有
from src.main import Test
class DifferentTest:
pass
Run Code Online (Sandbox Code Playgroud)
所以结果应该是:
empty.py = None
main.py = None
other/other.py = src.main , src.test
test/some_app.py = src.main
this_imports.py = src.main
Run Code Online (Sandbox Code Playgroud)
但是该函数给出了错误的结果,这是输出:
Filename: this_imports.py
__main__: Test
src.main
Filename: main.py
__main__: Test,__module__,__qualname__
src.main
Filename: test/some_app.py
__main__: Test,__module__,__qualname__
src.main
Filename: other/other.py
__main__: Test,__module__,__qualname__
src.main
src.test
Filename: empty.py
__main__: Test,__module__,__qualname__
src.main
src.test
Run Code Online (Sandbox Code Playgroud)
您正在寻找的是在包模块中找到导入依赖项。您可以对包目录运行静态分析并解析语法树 ( ) 中的导入节点ast,并构建依赖关系图。像下面这样:
import os
from ast import NodeVisitor, parse
import networkx as nx
class Dependency():
def __init__(self, root):
self.root = root
self.base = os.path.basename(root)
self.dependency = nx.DiGraph()
self.visitor = NodeVisitor()
self.visitor.visit_ImportFrom = self.visit_ImportFrom
self.current_node = None
self.dependency.add_node = self.base
def visit_ImportFrom(self, node):
self.dependency.add_edge(node.module, self.current_node)
self.visitor.generic_visit(node)
def run(self):
for root, dirs, files in os.walk(self.root):
for file in files:
full_path = os.path.join(root+os.sep, file)
loc = full_path.split(self.root+os.sep)[1].replace(os.sep,'.')
self.current_node = self.base+'.'+loc
with open(full_path) as fp:
src = fp.read()
tree = parse(src)
self.visitor.generic_visit(tree)
dependency = {}
for src, target in nx.dfs_edges(self.dependency):
if src in dependency:
dependency[src].add(target)
else:
dependency[src] = set([target])
return dependency
Run Code Online (Sandbox Code Playgroud)
对于要映射导入依赖项的任何包的根位置,您需要执行以下操作:
root = "path/to/your/src"
d = Dependency(root)
d.run()
Run Code Online (Sandbox Code Playgroud)
这将返回依赖树(作为字典)。注意,我们只解析了ImportFrom,您需要添加Import使其完整。另外,这里所有的导入都被假定为绝对的(即没有..等)。如果需要,您也可以添加它(检查节点level的字段ImportFrom来执行此操作)。
| 归档时间: |
|
| 查看次数: |
353 次 |
| 最近记录: |