如何区分标准库调用、第三方库调用和来自存储库的 API 调用?

How can you distinguish between a standard library call, a third-party library call, and an API call from the repository?

提问人:Exploring 提问时间:8/13/2023 最后编辑:blhsingExploring 更新时间:8/25/2023 访问量:303

问:

我正在处理一个项目,我必须确定特定的调用或导入是否为:

  1. 来自我正在使用的语言的标准库 (Python)。(我已经在考虑使用它了)sys.stdlib_module_names
  2. 来自第三方库,或
  3. 从存储库中对某些服务进行的 API 调用。

有没有一种有效的方法或工具可以帮助我快速区分这些类型的调用或导入?我主要使用 Python,但也欢迎其他语言的方法。

我正在处理一个项目,我必须收集在该存储库中进行的库调用数据集。

我正在从事一个项目,我的目标是从 Github 编译在给定存储库中进行的函数调用数据集。

所以首先,我从 Github 下载任何给定的 python 存储库。

那么我的主要目标是:

  • 提取在目标存储库中进行的所有函数调用。
  • 收集这些函数调用的详细信息,包括它们使用的参数。
  • 为此,我使用 Python AST(抽象语法树)解析器来检测和编目函数调用及其各自的参数。
  • 我的整个分析管道都基于利用 AST 模块的 Python 脚本。
  • 现在,我必须确定这些函数调用中哪些源自存储库本身。

例如,如果有一个电话

file_b.py

def abc():
  ....

file_a.py

import numpy as np
from file_b import abc
....
def foo():
   ..
   x = np.linspace(-math.pi, math.pi, 2000)
   y = np.sin(x)
   ...
   ..
   c = abc()

我只想捕获(因为它在该存储库中定义),而不是来自 numpy 模块的调用。abc

python api 设计 代码分析 第三方库

评论

6赞 Karl Knechtel 8/13/2023
为什么需要做出这个决定?通过了解,您将解决什么问题?为什么这被标记为 Java?
0赞 Tim Roberts 8/18/2023
我想说的是,最好的方法是建立一个交叉引用 -- 一个包含你库中所有 API 的一致性。然后,您可以进行简单的文本搜索。但是请注意,如果没有一些复杂的分析,这样做的应用程序可能会使人们无法了解此类信息。from xxx import *
0赞 Exploring 8/18/2023
但是,如何填充该交叉引用列表?我不知道有任何图书馆可以做到这一点。
1赞 Vitalizzare 8/19/2023
您能否添加一些假定活动的细节,一个用例?例如,您认为是将代码作为文本使用,还是在运行代码中使用活动对象?你如何接听电话?你们区分功能和方法吗?等。
0赞 flakes 8/22/2023
我们需要上下文来了解您为什么需要它以及它解决了什么问题。是用于静态分析吗?许可证检查?进口订购?别的?如果没有这些信息,所提供的答案都不可能真正有用,因为问题的范围太模糊了。尽管可能存在通用解决方案,但在考虑实际用例时,该通用解决方案可能是什么会立即变得更加明显。

答:

4赞 ntg 8/18/2023 #1

您可以使用 inspect,因为该模块似乎是为您的目的而编写的。一种简单的区分方法是使用库磁盘中的位置,给定一个使用 inspect 的函数,例如:

import os 
import inspect

# "standart" library 
import numpy as np
# some "local" library 
import cfg
# we can assign it on a variable if needed 
foo=np 
print(1, os.path.dirname(os.path.abspath(foo.__file__))) 
foo=cfg 
print(2, os.path.dirname(os.path.abspath(foo.__file__)))
print()
#we can get the module from any function
unknown_function = np.sort
the_module = inspect.getmodule(unknown_function) 
print(the_module) 
print(3, os.path.dirname(os.path.abspath(the_module.__file__)))

结果是:

1 /home/datalab/workspace/conda/lib/python3.8/site-packages/numpy
2 /home/datalab/workspace/utils

<module 'numpy' from '/home/datalab/workspace/conda/lib/python3.8/site-packages/numpy/__init__.py'>
3 /home/datalab/workspace/conda/lib/python3.8/site-packages/numpy

就您而言,您似乎有 3 个类别。

  • 第一个应该/将源自 conda/pip 安装(您可以使用sys.executable)
  • 来自第三方库的第二个,这应该会产生一个众所周知的路径前缀
  • 第三个将在项目存储库中,这可能是众所周知的,或者例如通过运行(从存储库中)。subprocess.check_output(['git', 'rev-parse', '--show-toplevel'])

在更复杂的情况下,Inspect 可以做的不仅仅是为您提供磁盘上的位置。下面是一个示例和一些代码。在 PythonModuleOfTheWeek 中,有更多的用途,在这里你可以找到一些进一步的例子。

实用说明:导入模块意味着运行外部代码,因此请确保您信任该代码,或者使用某种沙盒环境/方式运行它。但如何做后者本身就是一个问题。

理论说明:在极端情况下,我认为这个问题是无法确定的。形式证明可能涉及使用一个停止函数和另一个非停止函数。因此,任何可以区分两者的分析都将解决图灵的停顿问题。对于我们使用 inspect 的情况,这意味着存在 导入它们的模块可能需要很长时间。实际上,这应该不是问题,因为任何合理的模块都应该能够在合理的时间内导入。

评论

0赞 blhsing 8/25/2023
我想知道为什么我之前的评论在没有任何解释的情况下被删除了。我提出了我认为关于这个答案的合理问题,即 OP 专门询问了存储库的静态分析(在目标部分),而这个答案建议的解决方案需要在修改后实际运行代码,这可能需要适当的环境设置、配置和外部依赖关系,可能会产生副作用, 如果有条件地拨打某些电话,则仍然无法覆盖所有电话。
0赞 ntg 8/25/2023
我不认为是我删除了评论(或者我可以这样做,除非这是最近的?我写了一条评论,说我真的很感激你写了降级的原因,尽管我不同意,然后提到恕我直言,问题是关于如何识别第三个库调用,即使有静态分析。然后我更好地阅读了您的评论,并意识到您对运行代码而不是仅仅分析代码有顾虑,因此我删除了我的评论并计划编写一篇新的评论,涉及如何解决安全性以及为什么需要运行代码
1赞 ntg 8/25/2023
解释问题所问内容的论点只能通过“运行”来完成,而不仅仅是静态分析,这与ocaml等语言提供的不变性有关,它需要我的计算机科学知识的一部分,我已经有一段时间没有接触过了。从本质上讲,将变量指向函数。Python 代码可以以仅通过静态分析无法预测的方式更改该函数的值(因为 Python 的图灵完备性以及 python 函数不是不可变的事实——我认为,我对那里的适当证明很生疏)因此与决定停止相同,
1赞 ntg 8/25/2023
你不能仅通过静态分析来决定一个函数的值(例如,假设该函数是停止函数,然后遵循图灵的停止不可判定性)。当然,这并不意味着您应该在计算机中运行任何代码。这就是为什么我删除了对您的评论的回复,并计划写一个更好的回复,涉及您应该如何在沙盒中运行不受信任的代码,但这不是问题的一部分,而是一个新问题......
0赞 Head Wizard Locke 8/25/2023 #2

https://www.pylint.org/ 的 Pylint 提供了您需要的静态分析工具以及众多编辑器和 IDE 集成。

Pylint 输出可以推送到文本文件,您可以自定义输出的格式,然后使用自定义脚本对其进行解析。所述脚本可以隔离和标记与您的 3 个类别相关的输出行,或者您希望从输出日志中调出的其他类别和细节。

配置选项包括标准检查器和扩展,您也可以编写它们,

  • 你可以告诉它忽略特定的模块(--ignored-modules)
  • 您可以将路径添加到源根目录 (--source-roots) 列表,用于确定位于源根目录下的模块的包命名空间
  • 您可以为给定文件生成依赖关系图 (--int-import-graph)
  • 您可以强制导入顺序将模块识别为第三方库 (--known-third-party) 的一部分
  • 还有更多!亲眼看看吧。

如上所述,转换模块是一种 Pylint 插件,可以针对特定模块或框架库进行定制。此外,自定义检查器可以将模块分析为原始文件流、一系列标记(流)或用于模块的 AST 表示形式的 AST。详见:https://pylint.pycqa.org/en/latest/development_guide/how_tos/custom_checkers.html#write-a-checkerpylint 插件对特定函数使用进行警告?

请注意,在编写脚本时,您可以使用 inspect 或 dir() 函数来检查模块,以帮助确定它们的来源。请参见:https://www.javatpoint.com/list-all-functions-from-a-python-module

例如:

import module
dir(module)

艺术

from inspect import getmembers,isfunction
import stats
print(f for f in getmembers(stats) if isfunction(f1]))

您还可以使用正则表达式和字符串解析来检查 pylint 的输出日志并相应地处理它们。虽然我之前提到过这一点,但我想强调这一点。

AST - 抽象语法树 - 帮助 Python 应用程序处理 Python 抽象语法语法的树,一个 python AST 可以遍历,每个节点及其节点都可以追溯到一个源。有关从 AST 确定模块源的其他信息,请参阅以下其他答案:

您还可以在这篇中型文章中了解有关使用 AST 的更多信息:https://medium.com/@wshanshan/intro-to-python-ast-module-bbd22cd505f7

在这篇 Pybit.es 文章中:https://pybit.es/articles/ast-intro/