facebook/pyrefly

GitHub: facebook/pyrefly

一款由 Meta 采用 Rust 开发的 Python 类型检查器和语言服务器,专注于高性能类型检查与 IDE 集成体验。

Stars: 5425 | Forks: 279

# Pyrefly:一个快速的 Python 类型检查器和语言服务器,具备强大的 IDE 功能 [![pyrefly](https://img.shields.io/endpoint?url=https://pyrefly.org/badge.json)](https://github.com/facebook/pyrefly) [![PyPI](https://img.shields.io/pypi/dm/pyrefly?color=blue&label=pypi)](https://pypi.python.org/pypi/pyrefly) [![Visual Studio Marketplace](https://img.shields.io/visual-studio-marketplace/d/meta.pyrefly?color=blue&label=VS%20Code)](https://marketplace.visualstudio.com/items?itemName=meta.pyrefly) [![Open VSX](https://img.shields.io/open-vsx/dt/meta/pyrefly?color=blue&label=Open%20VSX)](https://open-vsx.org/extension/meta/pyrefly) [![Discord](https://img.shields.io/badge/Discord-%235865F2.svg?logo=discord&logoColor=white)](https://discord.gg/Cf7mFQtW7W) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) Pyrefly 是一个 Python 类型检查器和语言服务器,提供极速的类型检查以及 IDE 功能,例如代码导航、语义高亮和代码补全。它可以作为[命令行工具](https://pyrefly.org/en/docs/installation/)使用,也可以作为扩展用于流行的 IDE 和编辑器,例如 [VSCode](https://marketplace.visualstudio.com/items?itemName=meta.pyrefly)、[Neovim](https://pyrefly.org/en/docs/IDE/#neovim)、[Zed](https://zed.dev/extensions/pyrefly) 以及[更多](https://pyrefly.org/en/docs/IDE/)。 请访问 [Pyrefly 网站](https://pyrefly.org) 获取完整的文档以及如何将 Pyrefly 添加到您选择的编辑器中。 目前正处于活跃开发中,存在一些已知问题。如果您发现错误,请提交 issue。 ### 快速入门 - 在浏览器中试用 pyrefly:[沙盒](https://pyrefly.org/sandbox/) - 获取命令行工具:`pip install pyrefly` - 获取 IDE 扩展:[IDE 安装页面](https://pyrefly.org/en/docs/IDE/) ### 关键特性: - 类型推导:Pyrefly 可以在大多数位置推导类型,除了函数参数。它可以推导变量的类型和返回类型。 - 流类型:Pyrefly 能够理解程序的控制流以细化静态类型。 - 增量检查:Pyrefly 旨在实现模块级别的大规模增量性,具有优化的检查和并行性。 ## 参与其中 如果您有疑问或想报告错误,请[创建一个 issue](https://github.com/facebook/pyrefly/issues)。 请参阅我们的[贡献指南](https://github.com/facebook/pyrefly/blob/main/CONTRIBUTING.md)了解如何为 Pyrefly 做贡献。 加入我们的 [Discord](https://discord.com/invite/Cf7mFQtW7W) 聊聊 Pyrefly 和类型。这也是我们举办双周办公时间的地方。 ## 设计选择 在编写 Python 类型检查器时有许多选择。我们从 [Pyre1](https://pyre-check.org/)、[Pyright](https://github.com/microsoft/pyright) 和 [MyPy](https://mypy.readthedocs.io/en/stable/) 中汲取灵感。一些值得注意的选择: - 我们在大多数位置推导类型,除了函数的参数。我们确实会推导变量的类型和返回类型。举个例子,`def foo(x): return True` 的结果将等同于您编写 `def foo(x: Any) -> bool: ...`。 - 我们尝试将 `[]` 的类型推导为其首次使用的方式,然后将其固定。例如 `xs = []; xs.append(1); xs.append("")` 将推导出 `xs: List[int]`,然后在最后一条语句上报错。 - 我们使用流类型来细化静态类型,例如 `x: int = 4` 不仅知道 `x` 的类型是 `int`,而且知道紧接着对 `x` 的下一次使用将意识到类型是 `Literal[4]`。 - 我们的目标是实现大规模的增量性(在模块级别)和通过并行性进行优化的检查,旨在利用 Rust 的优势使代码更简洁一些。 - 我们预期存在大型的强连通分量模块,并且不尝试利用源代码中的 DAG(有向无环图)形状。 ## 代码布局 Pyrefly 被拆分为多个 crate(主要位于 `crates/` 下): - `pyrefly_util` 是通用工具,与 Python 或类型检查无关。示例包括 IO 包装器、锁定、命令行助手等。 - `pyrefly_derive` 是用于派生诸如 `TypeEq` 和 `Visit` 等 trait 的过程宏。 - `pyrefly_python` 是不涉及类型检查方面的 Python 工具,例如建模模块或 `sys.info`。 - `pyrefly_graph` 提供用于索引值和缓存可能相互依赖的计算的工具。 - `pyrefly_bundled` 是第三方的 [typeshed stubs](https://github.com/python/typeshed)。 - `pyrefly_config` 定义了 Pyrefly 配置,并支持读取 Mypy/Pyright 配置。 - `pyrefly_types` 定义了 Pyrefly 类型及其上的操作。 - `pyrefly_wasm` 定义了编译为 WASM 的沙盒代码。 - `pyrefly` 本身是类型检查器及其他所有内容。 ## 设计 设计的许多细节会定期变化。但构建检查器的基本底板涉及三个步骤: 1. 弄清楚每个模块导出什么。这需要传递地解析所有 `import *` 语句。 2. 对于每个独立的模块,将其转换为绑定(bindings),处理所有语句和作用域信息(包括静态和流)。 3. 求解这些绑定,这可能需要求解其他模块中的绑定。 如果我们遇到不可知的信息(例如递归),我们使用 `Type::Var` 插入占位符,稍后再填充。 对于每个模块,我们按顺序且完整地求解步骤。特别是,我们不尝试先求解特定的标识符(像 [Roslyn](https://github.com/dotnet/roslyn) 或 [TypeScript](https://www.typescriptlang.org/) 那样),也不使用细粒度的增量性(像 [Rust Analyzer](https://github.com/rust-lang/rust-analyzer) 使用 [Salsa](https://github.com/salsa-rs/salsa) 那样)。相反,我们的目标是原始性能和更简单的以模块为中心的设计——如果求解模块中的所有绑定足够快,就没有必要单独求解单个绑定。 ### 绑定示例 给定程序: ``` 1: x: int = 4 2: print(x) ``` 我们可能会生成以下绑定: - `define int@0` = `from builtins import int` - `define x@1` = `4: int@0` - `use x@2` = `x@1` - `anon @2` = `print(x@2)` - `export x` = `x@2` 值得注意的是: - 键是诸如 `define`(某事物的定义)、`use`(某事物的使用)和 `anon`(我们需要进行类型检查但不关心结果的语句)之类的东西。 - 在许多情况下,键的值引用了其他键。 - 一些键是通过 `export` 键和 `import` 值从其他模块导入的。 - 为了消除标识符的歧义,我们使用它们出现的文本位置(在示例中我们使用了 `@line`,但实际上它是文件中的字节偏移量)。 ### `Var` 示例 给定程序: ``` 1: x = 1 2: while test(): 3: x = x 4: print(x) ``` 我们最终得到以下绑定: - `x@1` = `1` - `x@3` = `phi(x@1, x@3)` - `x@4` = `phi(x@1, x@3)` 表达式 `phi` 是两个值的连接点,例如 `phi(int, str)` 将是 `int | str`。我们跳过了 `define` 和 `use` 之间的区别,因为这对于本示例来说不是必需的。 当求解 `x@3` 时,我们遇到了递归。操作上: - 我们开始求解 `x@3`。 - 这需要我们先求解 `x@1`。 - 我们求解 `x@1` 为 `Literal[1]` - 我们开始求解 `x@3`。但我们当前正在求解 `x@3`,所以我们发明一个新的 `Var`(让我们称之为 `?1`)并返回它。 - 我们得出结论 `x@3` 必须是 `Literal[1] | ?1`。 - 由于 `?1` 是由 `x@3` 引入的,我们记录 `?1 = Literal[1] | ?1`。我们可以取其上可达边界并得出结论 `?1 = Literal[1]`。 - 我们将 `x@3` 简化为 `Literal[1]`。
标签:IDE插件, Language Server Protocol, LSP, Meta, MIT许可, Neovim, Pyrefly, Python, Rust, SOC Prime, VS Code扩展, 云安全监控, 代码导航, 代码补全, 可视化界面, 开发工具, 无后门, 类型推断, 网络流量审计, 语言服务器, 逆向工具, 通知系统, 通知系统, 通知系统, 静态分析, 静态类型检查