Ruff v0.1.8 现已发布,并支持可选地格式化文档字符串中的 Python 代码示例。可以从 PyPI 或您选择的包管理器安装。

pip install --upgrade ruff

提醒一下:Ruff 是一个用 Rust 编写的极速 Python linter 和格式化工具。 Ruff 可以替代 Black、Flake8(以及数十个插件)、isort、pydocstyle、pyupgrade 等,同时执行速度比任何单个工具快几十甚至几百倍。

GitHub 上查看完整的更新日志,或继续阅读亮点内容。

格式化文档字符串中的代码片段

我们最近发布了 Ruff 格式化工具,一个极速的 Python 代码格式化工具。

在 v0.1.8 中,格式化工具新增了格式化文档字符串中 Python 代码片段的能力,支持以下格式:

  • Python doctest 格式。
  • CommonMark 围栏式代码块,带有以下信息字符串:pythonpypython3py3。没有信息字符串的围栏式代码块被认为是 Python 代码示例,因此也会被格式化。
  • reStructuredText 字面块。尽管字面块可能包含 Python 之外的内容,但这旨在反映 Python 生态系统中长期以来的一个惯例,即字面块通常包含 Python 代码。
  • reStructuredText code-blocksourcecode 指令。与 Markdown 一样,Python 识别的语言名称有 pythonpypython3py3

这是一个快速示例。首先,我们将创建一个内容如下的 ruff.toml 配置文件:

[format]
# Docstring formatting is opt-in. Enable it by setting this option to `true`.
docstring-code-format = true

其次,我们将创建一个 Python 文件 sample.py。我们将在一个文档字符串中使用多种不同的格式来演示 ruff format 的广泛支持:

def f(x):
    """
    Something about `f`. And an example in doctest format:

    >>> f( '''can't fool me'''
    ... )

    Markdown is also supported:

    ```py tab="plugin.py"
    foo, bar, quux = this_is_a_long_line(lion, hippo, giraffe, zebra, penguin, lemur, bear)
    ```

    Fenced code blocks without labels are assumed to be Python:

    ```
    f(  x  )
    ```

    As are reStructuredText literal blocks::

        f(  x  )

    And reStructuredText code blocks are also supported:

    .. code-block:: python
        :linenos:

        def definitely_converges(n):
            if n%2==0:
                return n//2
            else:
                return 3*n+1

    Code examples that aren't valid Python get skipped automatically::

        println!( "Don't mistake me for Python" );
    """
    pass

最后,我们通过 ruff format 运行它:

ruff format --config ruff.toml sample.py

完成后,sample.py 应该看起来像:

def f(x):
    """
    Something about `f`. And an example in doctest format:

    >>> f('''can't fool me''')

    Markdown is also supported:

    ```py tab="plugin.py"
    foo, bar, quux = this_is_a_long_line(
        lion, hippo, giraffe, zebra, penguin, lemur, bear
    )
    ```

    Fenced code blocks without labels are assumed to be Python:

    ```
    f(x)
    ```

    As are reStructuredText literal blocks::

        f(x)

    And reStructuredText code blocks are also supported:

    .. code-block:: python
        :linenos:

        def definitely_converges(n):
            if n % 2 == 0:
                return n // 2
            else:
                return 3 * n + 1

    Code examples that aren't valid Python get skipped automatically::

        println!( "Don't mistake me for Python" );
    """
    pass

不是有效 Python 的代码片段将不会被格式化。同样,如果 ruff format 格式化后会生成一个无效的 Python 程序,那么它也会静默跳过该代码片段。

用户还可以配置用于格式化文档字符串中 Python 代码片段的行长度限制。默认的 dynamic 指示格式化工具遵循周围 Python 代码的行长度限制设置。dynamic 确保即使代码片段位于缩进的文档字符串内部,也会遵循为周围 Python 代码配置的行长度限制。

用户还可以为文档字符串中的代码片段配置固定的行长度限制,这对于确保生成文档中的代码片段遵循一致的行长度(无论原始源代码的缩进程度如何)非常有用。

例如,这将行长度限制设置为 20

[format]
docstring-code-format = true
docstring-code-line-length = 20

这是我们的 sample.py 来测试它:

def g(x):
    """
    Another example demonstrating how long lines get wrapped.

    >>> foo, bar, quux = this_is_a_long_line(lion, hippo, giraffe, zebra, penguin, lemur, bear)

    """
    pass

现在再次运行 ruff format

ruff format --config ruff.toml sample.py

然后 sample.py 应该变成这样:

def g(x):
    """
    Another example demonstrating how long lines get wrapped.

    >>> (
    ...     foo,
    ...     bar,
    ...     quux,
    ... ) = this_is_a_long_line(
    ...     lion,
    ...     hippo,
    ...     giraffe,
    ...     zebra,
    ...     penguin,
    ...     lemur,
    ...     bear,
    ... )

    """
    pass

文档字符串片段格式化目前是可选加入的,但在未来版本中将变为可选退出。

类型注解的自动引用

在之前的版本中,Ruff 获得了识别那些仅用于类型注解的导入绑定,并自动将这些导入移动到 if TYPE_CHECKING: 块中的能力。例如,请参阅 typing-only-third-party-import。将导入移动到类型检查块是 Python 中避免循环导入和减少程序启动时间的常见模式。

迄今为止,这条规则一直受到 Python 要求大多数类型注解在运行时必须可访问的事实阻碍。例如,以下片段将在运行时失败,因为 Python 需要评估 Sequence[int] 才能将其添加到模块的 __annotations__ 字段中:

from typing import TYPE_CHECKING

if TYPE_CHECKING:
    # Sequence is only available to type-checkers, and not at runtime...
    from collections.abc import Sequence


# But Python needs to evaluate `Sequence[int]` here.
def func(value: Sequence[int]) -> None:
    ...

一个常见的解决方案是在文件顶部添加 from __future__ import annotations,这指示 Python 延迟评估类型注解。然而,添加 from __future__ import annotations 是一个重要的语义变化,并可能导致其他问题。

在 v0.1.8 中,Ruff 获得了自动引用此类类型注解的能力,如果这样做可以使导入保留或移动到类型检查块中。例如,Ruff 现在可以自动将上述片段重写为:

from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from collections.abc import Sequence


def func(value: "Sequence[int]") -> None:
    ...

此行为通过 quote-annotations 设置可选加入,并且当存在 from __future__ import annotations 时无效。

[flake8-type-checking]
quote-annotations = true

贡献者

衷心感谢为本次发布做出贡献的人们: