在 Python 中使用 API 图¶
API 图是用于引用外部库中定义的函数、类和方法的统一接口。
关于本文¶
本文介绍了如何使用 API 图引用库代码中定义的类和函数。您可以在定义远程流源等内容时使用 API 图方便地引用外部库函数。
模块导入¶
API 图中最常见的入口点是导入外部模块或包的地方。例如,您可以使用 semmle.python.ApiGraphs
模块中定义的 API::moduleImport
方法访问与 re
库对应的 API 图节点,如下面的代码片段所示。
import python
import semmle.python.ApiGraphs
select API::moduleImport("re")
此查询选择与 re
模块对应的 API 图节点。此节点代表已导入 re
模块的事实,而不是程序中发生导入的特定位置。因此,每个项目最多有一个结果,并且它不会有有用的位置,因此您必须单击“显示 1 个非源结果”才能看到它。
要查找 re
模块在程序中的引用位置,可以使用 getAValueReachableFromSource
方法。以下查询选择当前数据库中对 re
模块的所有引用。
import python
import semmle.python.ApiGraphs
select API::moduleImport("re").getAValueReachableFromSource()
请注意,getAValueReachableFromSource
方法考虑了本地流,因此以下代码片段中的 my_re_compile
正确地被识别为对 re.compile
函数的引用。
from re import compile as re_compile
my_re_compile = re_compile
r = my_re_compile(".*")
如果您只需要立即使用,而不考虑本地流,那么可以使用 asSource
方法代替。
请注意,给定的模块名称*不应*包含任何点。因此,类似于 API::moduleImport("flask.views")
的内容将不会按预期执行。相反,这应该分解为访问 flask
的 API 图节点的 views
成员,如下一节所述。
访问属性¶
给定 API 图中的一个节点,您可以使用 getMember
方法访问其属性。使用上面的 re.compile
示例,您现在可以找到对 re.compile
的引用。
import python
import semmle.python.ApiGraphs
select API::moduleImport("re").getMember("compile").getAValueReachableFromSource()
除了 getMember
之外,您还可以使用 getUnknownMember
方法查找对 API 组件的引用,其中名称在静态情况下未知。您可以使用 getAMember
方法访问所有成员,包括已知和未知成员。
调用和类实例化¶
要跟踪外部库中定义的类的实例,或调用外部定义的函数的结果,可以使用 getReturn
方法。以下代码片段查找所有使用 re.compile
返回值的地方
import python
import semmle.python.ApiGraphs
select API::moduleImport("re").getMember("compile").getReturn().getAValueReachableFromSource()
请注意,这包括对 re.compile
结果的所有使用,包括通过本地流可访问的那些使用。要仅获取对 re.compile
的*调用*,可以使用 asSource
代替 getAValueReachableFromSource
。由于这是一种常见情况,因此您可以在使用 getReturn
后跟 asSource
而不是使用 getACall
。这将产生一个 API::CallNode
,它需要一个简短的描述。
API::CallNode``s are not ``API::Node``s. Instead they are ``DataFlow::Node``s with some convenience predicates that allows you to recover ``API::Node``s for the return value as well as for arguments to the call. This enables you to constrain the call in various ways using the API graph. The following snippet finds all calls to ``re.compile
其中 pattern
参数来自使用 argparse
库解析命令行参数。
import python
import semmle.python.ApiGraphs
from API::CallNode call
where
call = API::moduleImport("re").getMember("compile").getACall() and
call.getParameter(0, "pattern") =
API::moduleImport("argparse")
.getMember("ArgumentParser")
.getReturn()
.getMember("parse_args")
.getMember(_)
select call
请注意,API 图不区分类实例化和函数调用。就它而言,两者都只是调用 API 图节点的地方。
子类¶
对于许多库来说,主要的用法模式是扩展一个或多个库类。要跟踪 API 图中的这一点,可以使用 getASubclass
方法获取对应于此节点所有直接子类的 API 图节点。要查找*所有*子类,请使用 *
或 +
来重复应用该方法,例如在 getASubclass*
中。
请注意,getASubclass
不会考虑在未提取的库代码中发生的任何子类化。因此,您可能需要在编写的模型中考虑这一点。例如,flask.views.View
类有一个预定义的子类 MethodView
。要查找 View
的所有子类,您还必须明确包含 MethodView
的子类。
import python
import semmle.python.ApiGraphs
API::Node viewClass() {
result =
API::moduleImport("flask").getMember("views").getMember(["View", "MethodView"]).getASubclass*()
}
select viewClass().getAValueReachableFromSource()
请注意使用集合文字 ["View", "MethodView"]
来同时匹配这两个类。
内置函数和类¶
您可以使用 API::builtin
方法访问内置函数和类,并提供内置函数的名称作为参数。
例如,要查找对内置 open
函数的所有调用,可以使用以下代码片段。
import python
import semmle.python.ApiGraphs
select API::builtin("open").getACall()