簡體   English   中英

如何在不導入的情況下從 python 文件中獲取類和函數的列表

[英]How to get a list of classes and functions from a python file without importing it

我有一個 python 文件,其中定義了一些類和函數:

class A(object):
    def __init__(self, an_arg, a_default_arg=None):
        pass

def doStuff(an_other_arg, an_other_default_arg=None):
    pass

我想得到這個文件中所有類和函數的列表。 (他們的名字和參數定義就夠了)

現在,我知道您可以使用__import__(module_descriptor)inspect來執行此操作,但這不是一個選項,因為我正在掃描的文件來自不受信任的來源。

我的第一反應是嘗試創建一個安全的環境來導入它們,但根據其他 stackoverflow 問題,這似乎是不可能的。

您可以使用ast模塊來解析源文件,而無需實際執行任何代碼。 然后您可以遍歷節點樹以獲取函數和類名稱/參數。

import ast

def show_info(functionNode):
    print("Function name:", functionNode.name)
    print("Args:")
    for arg in functionNode.args.args:
        #import pdb; pdb.set_trace()
        print("\tParameter name:", arg.arg)


filename = "untrusted.py"
with open(filename) as file:
    node = ast.parse(file.read())

functions = [n for n in node.body if isinstance(n, ast.FunctionDef)]
classes = [n for n in node.body if isinstance(n, ast.ClassDef)]

for function in functions:
    show_info(function)

for class_ in classes:
    print("Class name:", class_.name)
    methods = [n for n in class_.body if isinstance(n, ast.FunctionDef)]
    for method in methods:
        show_info(method)

結果:

Function name: doStuff
Args:
        Parameter name: an_other_arg
        Parameter name: an_other_default_arg
Class name: A
Function name: __init__
Args:
        Parameter name: self
        Parameter name: an_arg
        Parameter name: a_default_arg

除了實際執行文件之外,沒有什么可以為您提供對這個問題的 100% 准確答案。 Python 中有太多方法可以動態影響命名空間:從別處導入名稱、有條件地執行定義、通過修改__dict__直接操作命名空間等。

如果您只能接受靜態定義,那么 Python 的內置ast (抽象語法樹)模塊可能是最簡單的解決方案。 您可以安全地將文件編譯為 AST,然后在其頂層查找defclass語句。 (在類的情況下,您將在類主體中尋找def __init__ 。不要忘記類沒有自己的__init__ ,而只是從超類繼承一個的可能性!)

接受的解決方案是不完整的。 考慮以下文件:

def regular_function():
    def nested_function():
        pass

async def async_function():
    pass

接受的解決方案只會打印:

Function name: regular_function
Args:

要獲取所有功能,我們需要進行兩個更改:

  1. 遍歷整個 AST,而不僅僅是頂級節點
  2. 處理async函數和常規函數

這是更正后的代碼,用於查找函數:

import ast

from pathlib import Path

parsed_ast = ast.parse(Path(__file__).read_text())

functions = [
    node
    for node in ast.walk(parsed_ast)
    if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef))
]

for function in functions:
    print(f"Function name: {function.name}")
    print(f"Args: {', '.join([arg.arg for arg in function.args.args])}")

請注意,這違反了 AST walk 的用途。 對於更復雜的事情,請考慮使用NodeVisitor

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM