Ruff v0.0.281 现已发布。可以从 PyPI 或您选择的包管理器安装。
pip install --upgrade ruff
提醒一下:Ruff 是一个用 Rust 编写的极速 Python linter。 Ruff 可以替代 Flake8(以及数十个插件)、isort、pydocstyle、pyupgrade 等,而且执行速度比任何单个工具快数十或数百倍。
在 GitHub 上查看完整的更新日志,或继续阅读亮点。
Ruff 的词法分析器速度提升2-3倍 #
Ruff 的词法分析器现在比以前的版本快2-3倍
group v0.0.280 v0.0.281
----- -------- --------
lexer/large/dataset.py 2.18 665.9±5.64µs 61.1 MB/sec 1.00 304.9±3.79µs 133.4 MB/sec
lexer/numpy/ctypeslib.py 2.39 154.4±0.84µs 107.8 MB/sec 1.00 64.5±0.61µs 258.1 MB/sec
lexer/numpy/globals.py 2.89 18.1±0.14µs 163.3 MB/sec 1.00 6.3±0.06µs 471.8 MB/sec
lexer/pydantic/types.py 2.57 326.4±2.23µs 78.1 MB/sec 1.00 127.2±0.71µs 200.5 MB/sec
词法分析器负责将 Python 源代码标记化为令牌流,然后解析器使用这些令牌流构建抽象语法树 (AST)。词法分析器是 Ruff 分析管道的第一步,并在 Ruff 分析的每个文件上运行。
提升词法分析器性能不仅能提高 linter 性能,还将改善未来利用 Ruff 词法分析器的工具的性能,例如 Ruff 的格式化工具。
新的词法分析器利用了更缓存友好的数据结构,执行更少的分配,并包含针对纯 ASCII 源代码的优化。更多详情请参见 #38。
行尾的 # ruff: noqa
注释现在将被忽略 #
Ruff 允许通过 # ruff: noqa
抑制注释在整个文件中禁用 lint 规则。例如,以下内容将忽略文件中所有 lint 错误:
# ruff: noqa
import os
import sys
类似地,以下内容将忽略文件中所有未使用的导入 (F401
) 错误:
# ruff: noqa: F401
import os
import sys
过去,Ruff 甚至在这些抑制注释出现在行尾时也会遵守,例如:
import os # ruff: noqa: F401
import sys
此类行尾注释很可能是一个错误(相反,应使用 # noqa: F401
来抑制单行错误)。Ruff 现在将对这些行尾的文件级抑制注释发出警告并忽略它们。
新规则:unused-private-type-var
(PYI018
) #
作用是什么? #
检查是否存在未使用的私有 TypeVar
声明。
为何重要? #
已定义但未使用的私有 TypeVar
很可能是错误,应使用、公开或移除以避免混淆。
例如,给定以下代码片段
import typing
_T = typing.TypeVar("_T")
该 TypeVar
未在任何地方使用,因此应使用、公开或移除。
此规则源自 flake8-pyi。
由 @LaBatata101 贡献。
新规则:unused-private-protocol
(PYI046
) #
作用是什么? #
检查是否存在未使用的私有 typing.Protocol
定义。
为何重要? #
已定义但未使用的私有 typing.Protocol
很可能是错误,应使用、公开或移除以避免混淆。
例如,给定以下代码片段
import typing
class _PrivateProtocol(typing.Protocol):
field: int
_PrivateProtocol
类未在任何地方使用,因此应使用、公开或移除。
此规则源自 flake8-pyi。
由 @LaBatata101 贡献。
新规则:unused-private-type-alias
(PYI047
) #
作用是什么? #
检查是否存在未使用的私有 typing.TypeAlias
定义。
为何重要? #
已定义但未使用的私有 typing.TypeAlias
很可能是错误,应使用、公开或移除以避免混淆。
例如,给定以下代码片段
import typing
_UnusedTypeAlias: typing.TypeAlias = int
此类型别名未在任何地方使用,因此应使用、公开或移除。
此规则源自 flake8-pyi。
由 @LaBatata101 贡献。
新规则:unused-private-typed-dict
(PYI049
) #
作用是什么? #
检查是否存在未使用的私有 typing.TypedDict
定义。
为何重要? #
已定义但未使用的私有 typing.TypedDict
很可能是错误,应使用、公开或移除以避免混淆。
例如,给定以下代码片段
import typing
class _UnusedPrivateTypedDict(typing.TypedDict):
field: list[int]
__UnusedPrivateTypedDict
类未在任何地方使用,因此应使用、公开或移除。
此规则源自 flake8-pyi。
由 @LaBatata101 贡献。
新规则:unsupported-method-call-on-all
(PYI056
) #
作用是什么? #
检查 append
、extend
和 remove
方法是否未在 __all__
上调用。
为何重要? #
不同的类型检查器对在 __all__
上调用这些方法的支持程度不同。相反,使用 +=
运算符向 __all__
添加项目,已知所有主要的类型检查器都支持这种方式。
例如,给定以下代码片段
__all__ = ["A"]
__all__.append("B")
相反,请使用增广赋值语句将项目添加到 __all__
__all__ = ["A"]
__all__ += ["B"]
此规则源自 flake8-pyi。
由 @LaBatata101 贡献。
新规则:self-assigning-variable
(PLW0127
) #
作用是什么? #
检查变量的自赋值。
为何重要? #
变量的自赋值是冗余的,很可能是错误。
例如,给定以下代码片段
country = "Poland"
country = country
相反,直接使用变量
country = "Poland"
此规则源自 Pylint。
由 @tjkuson 贡献。
新规则:subprocess-popen-preexec-fn
(PLW1509
) #
作用是什么? #
检查 subprocess.Popen
与 preexec_fn
参数的使用。
为何重要? #
preexec_fn
参数在线程内不安全,因为它可能导致死锁。此外,preexec_fn
计划被弃用。
相反,请考虑使用特定于任务的参数,例如 env
、start_new_session
和 process_group
。这些参数不容易发生死锁,并且更明确。
例如,给定以下代码片段
import os, subprocess
subprocess.Popen(foo, preexec_fn=os.setsid)
subprocess.Popen(bar, preexec_fn=os.setpgid(0, 0))
相反,使用 start_new_session
和 process_group
等参数
import subprocess
subprocess.Popen(foo, start_new_session=True)
subprocess.Popen(bar, process_group=0) # Introduced in Python 3.11
此规则源自 Pylint。
由 @tjkuson 贡献。
新规则:os-sep-split
(PTH206
) #
作用是什么? #
检查 .split(os.sep)
的使用。
为何重要? #
标准库中的 pathlib
模块应该用于路径操作。它提供了一个高级 API,具有对 Path
对象执行常见操作所需的功能。
例如,给定以下代码片段
如果不需要路径的所有部分,则应使用 Path
对象的 name
和 parent
属性。否则,可以使用 parts
属性,如最后一个示例所示。
import os
"path/to/file_name.txt".split(os.sep)[-1]
"path/to/file_name.txt".split(os.sep)[-2]
if any(part in blocklist for part in "my/file/path".split(os.sep)):
...
相反,使用 Path
对象
from pathlib import Path
Path("path/to/file_name.txt").name
Path("path/to/file_name.txt").parent.name
if any(part in blocklist for part in Path("my/file/path").parts):
...
由 @sbrugman 贡献。
新规则:glob
(PTH207
) #
作用是什么? #
检查 glob
和 iglob
的使用。
为何重要? #
与 os
和 glob
提供的低级 API 相比,pathlib
提供了用于路径操作的高级 API。
在可能的情况下,使用 Path
对象方法(如 Path.glob()
)可以提高可读性,优于其低级对应项(例如,glob.glob()
)。
请注意,glob.glob
和 Path.glob
并非完全等同
glob | Path.glob | |
---|---|---|
隐藏文件 | 默认情况下排除隐藏文件。从 Python 3.11 开始,可以使用 include_hidden 关键字来包含隐藏目录。 | 默认情况下包含隐藏文件。 |
迭代器 | iglob 返回一个迭代器。在底层,glob 只是将迭代器转换为列表。 | Path.glob 返回一个迭代器。 |
工作目录 | glob 接受 root_dir 关键字来设置当前工作目录。 | Path.rglob 可用于返回相对路径。 |
双星号通配符(** ) | glob 需要将 recursive 标志设置为 True ,以便 ** 模式匹配任何文件以及零个或多个目录、子目录和符号链接。 | Path.glob 中的 ** 模式表示“此目录及其所有子目录,递归地”。换句话说,它启用了递归全局匹配。 |
例如,给定以下代码片段
import glob
import os
glob.glob(os.path.join(path, "requirements*.txt"))
相反,使用 Path
对象
from pathlib import Path
Path(path).glob("requirements*.txt")
由 @sbrugman 贡献。