在 python 源/脚本中查找依赖项

Giu*_*upo 5 python dependencies abstract-syntax-tree

我有一堆简单的 Python 脚本,带有简单的表达式[1],例如:

C = A+B
D = C * 4
Run Code Online (Sandbox Code Playgroud)

我需要执行它们,但最重要的是我需要知道我依赖的对象是什么;在前面的例子中,对象AB是外部依赖关系。例如。鉴于我在名为source的 var 中有以前的代码,我希望能够:

deps = { "A" : 1 , "B": 2}
exec source in deps
Run Code Online (Sandbox Code Playgroud)

所以绝对有必要知道如何构建 dict deps

我潜伏在ast Python 模块中,但我不知道。


[1] 简单的数学聚合,在一定程度上是for循环,仅此而已。

unu*_*tbu 4

您可以使用标准库中的tokenize模块对 Python 源代码进行标记。这将允许您找到脚本中使用的所有变量名称。

现在假设我们将“非依赖性”定义为紧邻符号之前的任何变量名称=。然后,根据您的脚本代码的实际简单程度(请参阅下面的注意事项),您也许能够通过这种方式确定不是非依赖项的变量名称:

import tokenize
import io
import token
import collections
import keyword

kwset = set(keyword.kwlist)
class Token(collections.namedtuple('Token', 'num val start end line')):
    @property
    def name(self):
        return token.tok_name[self.num]

source = '''
C = A+B
D = C * 4
'''

lastname = None
names = set()
not_dep = set()
for tok in tokenize.generate_tokens(io.BytesIO(source).readline):
    tok = Token(*tok)
    print(tok.name, tok.val)
    if tok.name == 'NAME':
        names.add(tok.val)
        lastname = tok.val
    if tok.name == 'OP' and tok.val == '=':
        not_dep.add(lastname)

print(names)
# set(['A', 'C', 'B', 'D'])
print(not_dep)
# set(['C', 'D'])

deps = dict.fromkeys(names - not_dep - kwset, 1)
print(deps)
# {'A': 1, 'B': 1}
Run Code Online (Sandbox Code Playgroud)

注意事项

  • 如果您的脚本包含简单赋值以外的语句,则names可能会填充不需要的变量名称。例如,

    import numpy
    
    Run Code Online (Sandbox Code Playgroud)

    'import'会将和添加'numpy'到集合中names

  • 如果您的脚本包含使用左侧元组解包的分配,例如

    E, F = 1, 2
    
    Run Code Online (Sandbox Code Playgroud)

    那么上面的简单代码只会识别出这F不是依赖项。