查找指定符号的Python代码
项目描述
symboex
查找指定符号的Python代码
阅读symboex:搜索Python中的函数和类,然后将它们导入到LLM中以获取此项目的背景信息。
安装
使用pip
安装此工具
pip install symbex
或使用Homebrew
brew install simonw/llm/symbex
使用方法
symboex
可以搜索Python文件顶层出现的函数和类名。
要搜索当前目录及其所有子目录中的每个.py
文件,请按如下方式运行
symbex my_function
您可以同时搜索多个符号
symbex my_function MyClass
支持通配符 - 要搜索每个test_
函数,请运行此命令(注意单引号,以防止shell将*
解释为通配符)
symbex 'test_*'
要搜索类中的方法,使用class.method
表示法
symbex Entry.get_absolute_url
这里也支持通配符
symbex 'Entry.*'
symbex '*.get_absolute_url'
symbex '*.get_*'
或者要查看每个类的每个方法
symbex '*.*'
要搜索特定文件,请使用-f
选项传递该文件。您可以通过多次传递此选项来搜索多个文件。
symbex MyClass -f my_file.py
要搜索特定目录及其所有子目录,请使用-d/--directory
选项
symbex Database -d ~/projects/datasette
如果您知道要检查的模块可以由Python导入,则可以使用-m/--module name
选项。以下示例显示了asyncio
包中每个符号的签名
symbex -m asyncio -s --imports
您可以使用--stdlib
搜索包含Python标准库的目录。这可以用于快速查找特定Python库函数的源代码
symbex --stdlib -in to_thread
-in
的解释如下。如果您提供了 --stdlib
但没有提供任何 -d
或 -f
选项,则 --silent
将自动开启,因为标准库否则会产生许多不同的警告。
输出开始如下
# from asyncio.threads import to_thread
async def to_thread(func, /, *args, **kwargs):
"""Asynchronously run function *func* in a separate thread.
# ...
您可以使用 -x/--exclude
选项排除指定目录中的文件
symbex Database -d ~/projects/datasette -x ~/projects/datasette/tests
如果 symbex
遇到它无法解析的任何 Python 代码,它将打印一条警告消息并继续搜索
# Syntax error in path/badcode.py: expected ':' (<unknown>, line 1)
通过传递 --silent
来抑制这些警告
symbex MyClass --silent
过滤器
除了搜索符号之外,您还可以将过滤器应用于结果。
以下过滤器可用:
--function
- 仅函数--class
- 仅类--async
- 仅async def
函数--unasync
- 仅非异步函数--documented
- 拥有文档字符串的函数/类--undocumented
- 没有文档字符串的函数/类--public
- 公共函数/类 - 没有前缀_name
(或为__*__
方法)--private
- 私有函数/类 - 有前缀_name
并且不是__*__
--dunder
- 匹配__*__
的函数 - 通常应与*.*
一起使用以找到所有 dunder 方法--typed
- 至少有一个类型注解的函数--untyped
- 没有类型注解的函数--partially-typed
- 拥有一些类型注解但不是所有的函数--fully-typed
- 对于每个参数和返回值都有类型注解的函数--no-init
- 排除__init__(self)
方法。当与--fully-typed '*.*'
结合使用时很有用,以避免返回作为完全类型化的分类的__init__(self)
方法,因为__init__
不需要参数或返回类型注解。
例如,要查看项目中每个没有类型注解的 async def
函数的签名
symbex -s --async --untyped
对于类方法,而不是函数,您可以将过滤器与符号搜索参数 *.*
结合使用。
此示例显示了每个具有所有参数和返回值类型注解的 Python 标准库类方法的完整源代码
symbex --fully-typed --no-init '*.*' --stdlib
要找到所有公共函数和方法,它们缺乏文档,仅显示每个函数的签名
symbex '*' '*.*' --public --undocumented --signatures
示例输出
在一个新的 Datasette 检出中,我运行了这个命令
symbex MessagesDebugView get_long_description
这是命令的输出
# File: setup.py Line: 5
def get_long_description():
with open(
os.path.join(os.path.dirname(os.path.abspath(__file__)), "README.md"),
encoding="utf8",
) as fp:
return fp.read()
# File: datasette/views/special.py Line: 60
class PatternPortfolioView(View):
async def get(self, request, datasette):
await datasette.ensure_permissions(request.actor, ["view-instance"])
return Response.html(
await datasette.render_template(
"patterns.html",
request=request,
view_name="patterns",
)
)
仅签名
-s/--signatures
选项将列出函数和类的签名,例如
symbex -s -f symbex/lib.py
# File: symbex/lib.py Line: 107
def function_definition(function_node: AST):
# File: symbex/lib.py Line: 13
def find_symbol_nodes(code: str, filename: str, symbols: Iterable[str]) -> List[Tuple[(AST, Optional[str])]]:
# File: symbex/lib.py Line: 175
def class_definition(class_def):
# File: symbex/lib.py Line: 209
def annotation_definition(annotation: AST) -> str:
# File: symbex/lib.py Line: 227
def read_file(path):
# File: symbex/lib.py Line: 253
class TypeSummary:
# File: symbex/lib.py Line: 258
def type_summary(node: AST) -> Optional[TypeSummary]:
# File: symbex/lib.py Line: 304
def quoted_string(s):
# File: symbex/lib.py Line: 315
def import_line_for_function(function_name: str, filepath: str, possible_root_dirs: List[str]) -> str:
# File: symbex/lib.py Line: 37
def code_for_node(code: str, node: AST, class_name: str, signatures: bool, docstrings: bool) -> Tuple[(str, int)]:
# File: symbex/lib.py Line: 71
def add_docstring(definition: str, node: AST, docstrings: bool, is_method: bool) -> str:
# File: symbex/lib.py Line: 82
def match(name: str, symbols: Iterable[str]) -> bool:
这可以与其他选项结合使用,或者您可以直接运行 symbex -s
以查看当前目录及其子目录中的每个符号。
要包括估计的导入路径,如 # from symbex.lib import match
,请使用 --imports
。这些将相对于您指定的目录进行计算,或者您可以传递一个或多个 --sys-path
选项以请求相对于这些目录进行导入计算,就像它们位于 sys.path
一样。
~/dev/symbex/symbex match --imports -s --sys-path ~/dev/symbex
示例输出
# File: symbex/lib.py Line: 82
# from symbex.lib import match
def match(name: str, symbols: Iterable[str]) -> bool:
要抑制 # File: ...
注释,请使用 --no-file
或 -n
。
因此,要同时显示导入路径并抑制文件注释,请使用 -in
作为快捷方式。
symbex -in match
输出
# from symbex.lib import match
def match(name: str, symbols: Iterable[str]) -> bool:
要包括签名中的文档字符串,请使用 --docstrings
symbex match --docstrings -f symbex/lib.py
示例输出
# File: symbex/lib.py Line: 82
def match(name: str, symbols: Iterable[str]) -> bool:
"Returns True if name matches any of the symbols, resolving wildcards"
计数符号
如果您只想计数与您的过滤器匹配的函数和类的数量,请使用 --count
选项。以下是如何计数您的类
symbex --class --count
或者计数每个异步测试函数
symbex --async 'test_*' --count
结构化输出
LLM 默认输出纯文本(实际上是有效的 Python 代码,多亏了它使用注释的方式)。
您可以使用以下选项请求输出 CSV、TSV、JSON 或每行换行符分隔的 JSON:
--json
:JSON 数组,例如[{"id": "...", "code": "..."}]
--nl
:每行换行符分隔的 JSON,例如{"id": "...", "code": "..."}
每行一个--csv
:CSV,以id,code
作为标题行--tsv
:TSV,以id\tcode
作为标题行
在每种情况下,ID 都将是包含符号的文件路径,后跟一个冒号,然后是符号的行号,例如
{
"id": "symbex/lib.py:82",
"code": "def match(name: str, symbols: Iterable[str]) -> bool:"
}
如果您传递 -i/--imports
,则 ID 将是导入行
{
"id": "from symbex.lib import match",
"code": "def match(name: str, symbols: Iterable[str]) -> bool:"
}
传递 --id-prefix 'something:'
以在 ID 的开头添加指定的前缀。
以下示例将生成包含所有测试函数的 CSV 文件,使用导入样式的 ID 和前缀 test:
symbex 'test_*' \
--function \
--imports \
--csv > tests.csv
与 LLM 一起使用
此工具主要是为与 LLM 一起使用而设计的,LLM 是一个用于处理大型语言模型的 CLI 工具。
symbex
可以轻松获取特定的类或函数并将其传递给 llm
命令。
例如,我在 Datasette 仓库根目录下运行了以下命令
symbex Response | llm --system 'Explain this code, succinctly'
并得到了以下结果
此代码定义了一个自定义的
Response
类,其中包含用于返回 HTTP 响应的方法。它包括设置 cookies、返回 HTML、文本和 JSON 响应以及重定向到不同 URL 的方法。`asgi_send` 方法使用 ASGI(异步服务器网关接口)协议将响应发送到客户端。
结构化输出功能旨在与 LLM 嵌入 一起使用。您可以使用 llm embed-multi 生成代码库中每个符号的嵌入,如下所示
symbex '*' '*:*' --nl | \
llm embed-multi symbols - \
--format nl --database embeddings.db --store
这将创建一个名为 embeddings.db
的数据库,其中包含您的所有符号以及嵌入向量。
然后您可以像这样搜索您的代码
llm similar symbols -d embeddings.db -c 'test csv' | jq
替换匹配的符号
可以使用 --replace
选项使用标准输入的内容替换单个匹配符号。
给定一个名为 my_code.py
的文件,其内容如下
def first_function():
# This will be ignored
pass
def second_function():
# This will be replaced
pass
运行以下命令
echo "def second_function(a, b):
# This is a replacement implementation
return a + b + 3
" | symbex second_function --replace
结果将是更新后的 my_code.py
,其中包含以下内容
def first_function():
# This will be ignored
pass
def second_function(a, b):
# This is a replacement implementation
return a + b + 3
应谨慎使用此功能!我建议仅对已提交到 Git 的代码使用此功能,这样您可以使用 git diff
查看它所做的更改,并使用 git checkout my_code.py
进行撤销。
通过运行命令替换匹配的符号
可以使用 --rexec COMMAND
选项通过运行命令并使用其输出替换单个匹配符号。
该命令将使用匹配符号的定义作为其标准输入运行。该命令的输出将用作替换文本。
以下示例使用 sed
在每个匹配行的开头添加 #
,从而注释掉匹配的功能
symbex first_function --rexec "sed 's/^/# /'"
这将就地修改第一个函数,使其看起来像这样
# def first_function():
# # This will be ignored
# pass
一个更令人兴奋的示例使用 LLM。此示例将使用 gpt-3.5-turbo
模型添加类型提示并生成文档字符串
symbex second_function \
--rexec "llm --system 'add type hints and a docstring'"
我对此代码运行了以下命令
def first_function():
# This will be ignored
pass
def second_function(a, b):
return a + b + 3
第二个函数就地更新为以下内容
def second_function(a: int, b: int) -> int:
"""
Returns the sum of two integers (a and b) plus 3.
Parameters:
a (int): The first integer.
b (int): The second integer.
Returns:
int: The sum of a and b plus 3.
"""
return a + b + 3
在 CI 中使用
--check
选项会导致 symbex
在找到任何查询匹配项时返回非零退出代码。
您可以在CI中使用此功能来防止未添加文档的公共函数被添加。
symbex --function --public --undocumented --check
如果有任何未文档化的函数,此功能将静默失败,但会设置退出代码1
。
将此作为GitHub Actions等CI工具的步骤应导致测试失败。
运行此命令以查看上一个命令的退出代码。
echo $?
--check
默认情况下不会输出任何内容。添加--count
以输出匹配符号的计数,或添加-s/--signatures
以输出匹配符号的签名,例如
symbex --function --public --undocumented --check --count
类似工具
- pyastgrep由Luke Plant提供,它提供了使用XPath查看和搜索Python AST的高级功能。
- cq是一个工具,允许您“使用类似CSS的选择器提取代码片段”,它使用Tree-sitter构建,主要针对JavaScript和TypeScript。
symbex --help
Usage: symbex [OPTIONS] [SYMBOLS]...
Find symbols in Python code and print the code for them.
Example usage:
# Search current directory and subdirectories
symbex my_function MyClass
# Search using a wildcard
symbex 'test_*'
# Find a specific class method
symbex 'MyClass.my_method'
# Find class methods using wildcards
symbex '*View.handle_*'
# Search a specific file
symbex MyClass -f my_file.py
# Search within a specific directory and its subdirectories
symbex Database -d ~/projects/datasette
# View signatures for all symbols in current directory and subdirectories
symbex -s
# View signatures for all test functions
symbex 'test_*' -s
# View signatures for all async functions with type definitions
symbex --async --typed -s
# Count the number of --async functions in the project
symbex --async --count
# Replace my_function with a new implementation:
echo "def my_function(a, b):
# This is a replacement implementation
return a + b + 3
" | symbex my_function --replace
# Replace my_function with the output of a command:
symbex first_function --rexec "sed 's/^/# /'"
# This uses sed to comment out the function body
Options:
--version Show the version and exit.
-f, --file FILE Files to search
-d, --directory DIRECTORY Directories to search
--stdlib Search the Python standard library
-x, --exclude DIRECTORY Directories to exclude
-s, --signatures Show just function and class signatures
-n, --no-file Don't include the # File: comments in the output
-i, --imports Show 'from x import y' lines for imported symbols
-m, --module TEXT Modules to search within
--sys-path TEXT Calculate imports relative to these on sys.path
--docs, --docstrings Show function and class signatures plus docstrings
--count Show count of matching symbols
--silent Silently ignore Python files with parse errors
--function Filter functions
--async Filter async functions
--unasync Filter non-async functions
--class Filter classes
--documented Filter functions with docstrings
--undocumented Filter functions without docstrings
--public Filter for symbols without a _ prefix
--private Filter for symbols with a _ prefix
--dunder Filter for symbols matching __*__
--typed Filter functions with type annotations
--untyped Filter functions without type annotations
--partially-typed Filter functions with partial type annotations
--fully-typed Filter functions with full type annotations
--no-init Filter to exclude any __init__ methods
--check Exit with non-zero code if any matches found
--replace Replace matching symbol with text from stdin
--rexec TEXT Replace with the result of piping to this tool
--csv Output as CSV
--tsv Output as TSV
--json Output as JSON
--nl Output as newline-delimited JSON
--id-prefix TEXT Prefix to use for symbol IDs
--help Show this message and exit.
开发
要为此工具做出贡献,首先检出代码。然后创建一个新的虚拟环境
cd symbex
python -m venv venv
source venv/bin/activate
现在安装依赖项和测试依赖项
pip install -e '.[test]'
要运行测试
pytest
只需
您还可以安装just并使用它来运行测试和linters,例如
just
或列出命令
just -l
Available recipes:
black # Apply Black
cog # Rebuild docs with cog
default # Run tests and linters
lint # Run linters
test *options # Run pytest with supplied options
项目详情
下载文件
下载适合您平台的文件。如果您不确定要选择哪个,请了解更多关于安装包的信息。
源分布
构建分布
symbex-1.4.tar.gz的散列
算法 | 散列摘要 | |
---|---|---|
SHA256 | f786d5d1d9ef0e5b0856ebf54cc12f7c42a63ea073e274f88b6024cf50490164 |
|
MD5 | f1d08c058a3bf6d961a58a09c47c4fae |
|
BLAKE2b-256 | f340390ce5881d488d104d7ee10ec2414d93d70c9477f70f7e7b31819bf804db |
symbex-1.4-py3-none-any.whl的散列
算法 | 散列摘要 | |
---|---|---|
SHA256 | be5b64ee094b803ccfbfa903c1b1e3a004d6575bd8ddaaa83c7c0ea7bdf613e3 |
|
MD5 | 12ac38de91e8af10ad83ad9b6bf5730c |
|
BLAKE2b-256 | b72f72613fe3a428a9d2a3190e36b8c0d6586cb4b1196b90a7cd39516f5e7ccd |