跳转到主要内容

将Python 3.6+代码编译为Python 2.7+兼容代码

项目描述

logo

lib3to6

编译Python 3.6+代码为Python 2.7+兼容代码。其想法与Babel https://babel.node.org.cn/ 相似。使用最新解释器开发,并使用(大多数)新语言功能,同时保持向后兼容性。

项目/仓库

MIT License Supported Python Versions CalVer v202107.1047 PyPI Version PyPI Downloads

代码质量/CI

GitHub CI Status GitLab CI Status Type Checked with mypy Code Coverage Code Style: sjfmt

名称 角色
Manuel Barkhau (mbarkhau@gmail.com) 作者/维护者 2018-09 -

动机:简化从旧解释器的过渡

特别是对于包来说,你可能不希望强制所有用户都使用 Python 3.10,因此保持向后兼容是一个好主意。然而,对于开发来说,使用最新的解释器是个好主意,理想情况下,你可以使用较新的类型注解功能来检查你的代码,但在分发时省略这些注解,因为它们在运行时并不需要。

如果你的现有项目使用 Python2.7,可能无法腾出大量时间来更新所有代码,然后切换到 Python3 开始运行。更糟糕的是,你可能仍然只编写针对 Python2.7 的代码,因为这是你的生产代码实际运行的版本。使用 lib3to6,你可以开始使用 Python3 进行开发和集成(确保向前兼容性),同时在你必须部署 Python2 的情况下保持向后兼容。

Python版本和兼容性

编译输出使用以下方式测试:

  • Python 3.9
  • Python 3.8
  • Python 3.7
  • Python 3.6
  • Python 3.5
  • Python 2.7
  • PyPy 3.6
  • PyPy 3.5

使用以下方式运行转换器的测试套件:

  • Python 3.9
  • Python 3.8
  • Python 3.7
  • Python 3.6
  • PyPy 3.6

编译输出可能与其他版本的 Python 兼容,例如 <=2.6>=3.0 <=3.4,但这些并未经过测试。

使用注意事项

lib3to6 不会添加任何自己的运行时依赖,但它会注入代码,例如临时变量和从标准库(特别是 itertoolsbuiltins)的导入。任何更改都只会添加常数 O(1) 的开销。

lib3to6 执行乐观的 AST 转换,假设你不会在代码中做任何太过疯狂的事情。这样的转换示例是支持 PEP3102 - 关键字仅参数lib3to6 将函数签名更改为使用 **kwargs 并添加从 kwargs 中提取的局部变量。

$ cat kwonly_args_demo.py
def compare(a, b, *, key=None):
    ...
$ lib3to6 kwonly_args_demo.py
def compare(a, b, **kwargs):
    key = kwargs.get('key', None)
    pass

这意味着你使用 inspect 模块获取到的函数签名可能不是 lib3to6 输出的预期。

按文件启用/禁用

lib3to6==v202008.1042 开始,支持根据文件选择性地启用/禁用转换。

任何以 # lib3to6: disabled 注释开始的文件将不会进行转换。对于这些文件,你必须自己处理向前/向后兼容性。

# -*- coding: utf-8 -*-
# lib3to6: disabled
"""A module written to work both with Python2 and 3.

This module doesn't need to be transpiled by lib3to6.
"""

from __future__ import print_function
...

import sys

PY3 = sys.version_info[0] > 2

if PY3:
    ...
else:
    ...

除了选择退出,你也可以选择进入方式。你必须切换 default_mode 参数

# setup.py
package_dir = {"": "src"}

if any(arg.startswith("bdist") for arg in sys.argv):
    import lib3to6
    package_dir = lib3to6.fix(package_dir, default_mode='disabled')

这将保持所有文件不变,除非它们带有 # lib3to6: enabled 注释。

# lib3to6: enabled
"""A module written to work both with Python2 and 3.

This module doesn't need to be transpiled by lib3to6.
"""

name: str = "Wörld"
print(f"Hello {world}!")

使用setup.py集成

命令行命令 lib3to6 <filename> 对于演示目的很好,但为了与你的项目集成,你可能更喜欢在 setup.py 文件中使用它。欢迎为其他类型的集成提供贡献。

# setup.py

import sys
import setuptools

packages = setuptools.find_packages(".")
package_dir = {"": "."}

install_requires = ['typing;python_version<"3.5"']

if any(arg.startswith("bdist") for arg in sys.argv):
    import lib3to6
    package_dir = lib3to6.fix(
        package_dir,
        target_version="2.7",
        install_requires=install_requires,
        default_mode='enabled',
    )

setuptools.setup(
    name="my-module",
    version="0.1.0",
    packages=packages,
    package_dir=package_dir,
    install_requires=install_requires,
    classifiers=[
        "Programming Language :: Python",
        "Programming Language :: Python :: 2",
        "Programming Language :: Python :: 3",
        ...
    ],
)

当你构建你的包时,生成的分发的内容将是 lib3to6 转换的代码。

~/my-module $ python setup.py bdist_wheel --python-tag=py2.py3
running bdist_wheel
...
~/my-module$ ls -1 dist/
my_module-201808.1-py2.py3-none-any.whl

~/my-module$ python3 -m pip install dist/my_module-201808.1-py2.py3-none-any.whl
Processing ./dist/my_module-201808.1-py2.py3-none-any.whl
Installing collected packages: my-module
Successfully installed my-module-201808.1

~/my-module$ python2 -m pip install dist/my_module-201808.1-py2.py3-none-any.whl
Processing ./dist/my_module-201808.1-py2.py3-none-any.whl
Installing collected packages: my-module
Successfully installed my-module-201808.1

在测试时,确保你不是从本地目录导入 my_module,这很可能是原始源代码。你可以通过操作你的 PYTHONPATH 或简单地切换目录来处理...

~/$ python3 -c "import my_module"
/home/user/my-module/my_module/__init__.py
Hello 世界 from 3.6.5!

~/my-module$ cd ..
~/$ python3 -c "import my_module"
/home/user/envs/py36/lib/python3.6/site-packages/my_module/__init__.py
Hello 世界 from 3.6.5!

~$ python2 -c "import my_module"
/home/user/envs/py27/lib/python2.7/site-packages/my_module/__init__.py
Hello 世界 from 2.7.15!

自动转换

并非所有新的语言特性在旧版本中都有语义等价物。在可以检测到这些特性的范围内,在它们被使用时将报告错误。

请注意,如果你要针对的最低版本 Python 已经支持新的语法,则不会应用修复。转换按特性引入的时间顺序排列。

PEP 572: 赋值表达式(也称为 Walrus 运算符)

# Since 3.8
if match1 := pattern1.match(data):
    result = match1.group(1)

# From 2.7 to 3.7
match1 = pattern1.match(data)
if match1:
    result = match1.group(1)

有些嵌套在条件中的表达式并不容易处理,在这种情况下,lib3to6 将竭尽全力。

# Since 3.8
while (block := f.read(4096)) != '':
    process(block)

# For [2.7 - 3.7]
__loop_condition = True
while __loop_condition:
    block = f.read(4096)
    __loop_condition = block != ''
    if __loop_condition:
        process(block)

PEP 563: 延迟评估注解

# Since 3.7
class SelfRef:
    def method(self) -> SelfRef:
        pass

# From 3.0 to 3.6
class SelfRef:
    def method(self) -> 'SelfRef':
        pass

请注意,这不是应用于所有注解的愚蠢转换,它仅应用于前向引用的注解。后向引用保持不变。

# Since 3.7
class BackRef:
    def method(self) -> ForwardRef:
        pass

class ForwardRef:
    def method(self) -> BackRef:
        pass


# From 3.0 to 3.6
class BackRef:
    def method(self) -> 'ForwardRef':
        pass

class ForwardRef:
    def method(self) -> BackRef:
        pass

如果您正在支持 Python 2.7,则当然会省略注解。

PEP 498:格式化字符串字面量。

# Since 3.6
who = "World"
print(f"Hello {who}!")

# From 2.7 to 3.5
who = "World"
print("Hello {0}!".format(who))

修正程序还会将较新的 {var=} 语法转换为旧语法,即使您在低于 3.8 的 Python 版本上使用 lib3to6。

# Since 3.6
who = "World"
print(f"Hello {who=}!")

# From 2.7 to 3.5
print("Hello who={0}!".format(who))

省略注解

# Since 3.0
def foo(bar: int) -> str:
    pass

# In 2.7
def foo(bar):
    pass

PEP 515:数字字面量中的下划线

# Since 3.6
num = 1_234_567

# From 2.7 to 3.5
num = 1234567

解包泛化

对于字面量...

# Since 3.4
x = [*[1, 2], 3]

# From 2.7 to 3.3
x = [1, 2, 3]

对于可变参数...

# Since 3.4
foo(0, *a, *b)

# From 2.7 to 3.3
foo(*([0] + list(a) + list(b))

对于关键字参数...

# Since 3.4
foo(**x, y=22, **z)

# From 2.7 to 3.3
import itertools
foo(**dict(itertools.chain(x.items(), {'y': 22}.items(), z.items())))

请注意,导入语句只会添加到您的模块中一次。

关键字参数

# Since 3.6
def kwonly_func(*, kwonly_arg=1):
    ...

# From 2.7 to 3.5
def kwonly_func(**kwargs):
    kwonly_arg = kwargs.get('kwonly_arg', 1)
    ...

将基于类的类型化 NamedTuple 使用转换为赋值

import typing

# Since 3.5
class Bar(typing.NamedTuple):
    x: int
    y: str

# From 2.7 to 3.4
Bar = typing.NamedTuple('Bar', [('x', int), ('y', str)])

新式类

# Since 3.0
class Bar:
  pass

# Before 3.0
class Bar(object):
  pass

未来导入

适用于您的目标版本的每个文件都前置了所有 __future__ 导入。

# -*- coding: utf-8 -*-
# This file is part of the <X> project
# ...
"""A docstring."""

x = True

使用 target-version=27(默认)。

# -*- coding: utf-8 -*-
# This file is part of the <X> project
# ...
"""A docstring."""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals

x = True

使用 target-version=3.7

# -*- coding: utf-8 -*-
# This file is part of the <X> project
# ...
"""A docstring."""
from __future__ import annotations

x = True

请注意,lib3to6 主要在 ast 级别工作,但对于出现在文件顶部的任何注释都作例外处理。这些注释将保持原样,因此您的 shebang、文件编码和许可头将得到保留。

不支持的功能

一个(显然不完整的)列表,其中包含一些不支持的特性,这些特性要么因为涉及语义变化,要么因为无法通过简单的 ast 转换来在不同的 Python 版本间工作。

  • PEP 492 - async/await
  • PEP 465 - @/__matmul__ 运算符
  • PEP 380 - yield from 语法
  • PEP 584 - dict 的联合运算符
  • 有序字典(自 Python 3.6 起支持)

带有回滚的模块

一些新模块有回滚,lib3to6 会指向这些回滚

  • typing
  • pathlib -> pathlib2
  • secrets -> python2-secrets
  • ipaddress -> py2-ipaddress
  • csv -> backports.csv
  • lzma -> backports.lzma
  • enum -> enum34

有关应用这些警告和错误的模块的完整列表,请查看 MAYBE_UNUSABLE_MODULES in src/lib3to6/checkers_backports.py

对于一些模块,回滚使用了与标准库中原模块相同的模块名。默认情况下,lib3to6 只会警告此类模块的使用,因为它无法检测您是使用回滚包中的模块(好)还是从标准库中(如果您的目标版本中不可用则为坏)。

将此类回滚作为依赖项添加的一个好方法是使用 依赖规范 来限定需求,这样,使用较新解释器的用户将使用内置模块,而不是安装他们不需要的回滚包。

这些可以在 install_requiresrequirements.txt 文件中使用。

import setuptools

setuptools.setup(
    name="my-package",
    install_requires=['typing;python_version<"3.5"'],
    ...
)

对于测试,您还可以将这些作为空格分隔的参数传递给 lib3to6 命令行工具

$ lib3to6 my_script.py > /dev/null
WARNING - my_script.py@1: Use of import 'enum'.
    This module is only available since Python 3.5,
    but you configured target_version=2.7.
WARNING - my_script.py@2: Use of import 'typing'.
    This module is only available since Python 3.5,
    but you configured target_version=2.7.

import enum
import typing
...

$ lib3to6 `--install-requires='typing'` my_script.py > /dev/null
Traceback (most recent call last):
  ...
  File "/home/user/.../lib3to6/src/lib3to6/checkers_backports.py", line 134, in __call__
    raise common.CheckError(errmsg, node)
lib3to6.common.CheckError: my_script.py@1 - Prohibited import 'enum'.
    This module is available since Python 3.4,
    but you configured target_version='2.7'.
    Use 'https://pypi.ac.cn/project/enum34' instead.

$ lib3to6 `--install-requires='typing enum34'` my_script.py
import enum
import typing
...

动机

该项目的主要动机是能够在不牺牲与较旧版本的 Python 兼容性的情况下使用 mypy

# my_module/__init__.py
def hello(who: str) -> None:
    import sys
    print(f"Hello {who} from {sys.version.split()[0]}!")


print(__file__)
hello("世界")
$ pip install lib3to6
$ python -m lib3to6 my_module/__init__.py
# -*- coding: utf-8 -*-
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals


def hello(who):
    import sys
    print('Hello {0} from {1}!'.format(who, sys.version.split()[0]))


print(__file__)
hello('世界')

应用修正以尽可能接近 Python 3 代码的语义,即使在 Python 2.7 解释器上运行。

在上述内容中已应用的一些修正

- PEP263 magic comment to declare the coding of the python
  source file. This allows the string literal `"世界"` to
  be decoded correctly.
- `__future__` imports have been added. This includes the well
  known print statement -> function change. The unicode_literals
- Type annotations have been removed
- `f""` string -> `"".format()` conversion

兼容性很重要

我看到了对 lib3to6 的一个常见直觉,那就是我们不应该关心旧版本的 Python,特别是 Python 2.7。我谦逊地建议您考虑那些对他们的开发环境拥有完全控制权并且仅使用 CPython 的人。截至本文撰写之时(2020 年 8 月),替代解释器支持的最新的语言版本如下

解释器 版本
Stackless 3.7
PyPy 3.6
MicroPython 3.4
IronPython 2.7
Jython 2.7

请注意,即使是无栈Python,它在跟进新语言特性方面所付出的努力最少,但它仍然落后于CPython。即使你只关心CPython,也要注意,最新的解释器可能不会在用户关心的平台上可用。例如,在PythonAnywhere.com上,最新的CPython版本是3.5,PyPy是2.7。

如果你正在编写一个库,它不需要任何新的运行时特性,例如异步/等待或有序字典,那么我谦卑地建议你不要不必要地阻止使用这些平台用户使用你的库。

从用户的角度来看,只支持Python的最新版本可能被视为傲慢,但你的维护时间不是免费的,你不需要对你的库用户承担任何责任。Lib3to6存在是为了最小化你维护向后兼容性的努力。如果你在将lib3to6集成到你的打包过程中遇到困难,请报告一个问题:gitlab.com/mbarkhau/lib3to6/-/issues

关于测试你的项目

使用lib3to6的项目应该有一个测试套件,该测试套件使用你想要支持的最早的Python版本执行,使用由lib3to6生成的转换输出。虽然你可以使用较新的Python版本进行开发,但你不应盲目信任lib3to6,因为它在最新解释器上测试时很容易引入向后不兼容的更改。最明显的例子是,如果库在Python 2上产生bytes而在Python 3上产生str,lib3to6就无法为你做很多事情。

我发现测试项目的最简单方法是用python setup.py bdist_wheel创建一个分布,并对上述修改的setup.py进行修改,安装它并针对已安装的模块运行测试套件。

工作原理

该项目在Python抽象语法树(AST)级别工作。AST被转换,使其仅使用在旧版Python中也是有效的结构。例如,它将f字符串转换为普通字符串,使用str.format方法。

>>> import sys
>>> sys.version_info
'3.6.5'
>>> import lib3to6
>>> py3_source = 'f"Hello {1 + 1}!"'
>>> cfg = {"fixers": ["f_string_to_str_format"]}
>>> py2_source = lib3to6.transpile_module(cfg, py3_source)

>>> print(py3_source)
f"Hello {1 + 1}!"
>>> print(py2_source)
# -*- coding: utf-8 -*-
"Hello {0}!".format(1 + 1)

在更低的级别上,这种转换基于对ast.JoinedStr节点的检测,该节点被转换成可以反序列化为在旧版Python中也能工作的Python语法的AST。

>>> print(lib3to6.parsedump_ast(py3_source))
Module(body=[Expr(value=JoinedStr(values=[
    Str(s='Hello '),
    FormattedValue(
        value=BinOp(left=Num(n=1), op=Add(), right=Num(n=1)),
        conversion=-1,
        format_spec=None,
    ),
    Str(s='!'),
]))])
>>> print(lib3to6.parsedump_ast(py2_source))
Module(body=[Expr(value=Call(
    func=Attribute(
        value=Str(s='Hello {0}!'),
        attr='format',
        ctx=Load(),
    ),
    args=[BinOp(left=Num(n=1), op=Add(), right=Num(n=1))],
    keywords=[]
))])

检查错误

当然,这并不涵盖兼容性的各个方面。API的变化无法以这种方式自动翻译。

一个明显的例子是,无法转换使用asyncawait的代码。在这种情况下,lib3to6将简单地引发一个CheckError。但这仅适用于你的源代码,所以如果你导入使用asyncawait的库,直到你在Python 2.7上运行测试,一切可能看起来都很正常。

一个更微妙的问题是内置的open函数的语义变化。

$ cat open_example.py
with open("myfile.txt", mode="w", encoding="utf-8") as fobj:
    fobj.write("Hello Wörld!")
$ python2 open_example.py
Traceback (most recent call last):
  File "<string>", line 1, in <module>
TypeError: 'encoding' is an invalid keyword argument for this function

通常,有替代方法可以编写等效的代码,使其在所有版本的Python上都能工作。对于这些常见的兼容性问题,lib3to6将引发错误并建议一个替代方案,例如在这种情况下使用io.open

$ lib3to6 open_example.py
Traceback (Most recent call last):
11  lib3to6      <module>         --> sys.exit(main())
764 core.py      __call__         --> return self.main(*args, **kwargs)
717 core.py      main             --> rv = self.invoke(ctx)
956 core.py      invoke           --> return ctx.invoke(self.callback, **ctx.params)
555 core.py      invoke           --> return callback(*args, **kwargs)
55  __main__.py  main             --> fixed_source_text = transpile.transpile_module(cfg, source_text)
260 transpile.py transpile_module --> checker(cfg, module_tree)
158 checkers.py  __call__         --> raise common.CheckError(msg, node)
CheckError: Prohibited keyword argument 'encoding' to builtin.open. on line 1 of open_example.py

在这里,lib3to6将给你一个CheckError,但是编写代码以使这种语法翻译在Python 3和Python 2中在语义上是等效的仍然是你的责任。

lib3to6使用Python的ast模块来解析你的代码。这意味着你需要一个现代Python解释器来从现代Python转换到旧版Python解释器。你无法转换解释器无法解析的特性。预期用途是用于使用最现代Python版本的库的开发者,但希望他们的库能在旧版本上运行。

贡献

你可以做出的最基本贡献是提供最小化、可复制的代码示例,这些代码应该被转换,或者应该引发一个错误。

项目托管在 gitlab.com/mbarkhau/lib3to6 上,主要是因为CI/CD在那里配置。GitHub仅用作副本/备份(而且这似乎是许多人寻找东西的地方)。

您只需几个命令就可以开始本地开发。

user@host:~/ $ git clone https://gitlab.com/mbarkhau/lib3to6.git
user@host:~/ $ cd lib3to6/
user@host:~/lib3to6/ ⎇master $ make help
user@host:~/lib3to6/ ⎇master $ make conda     # creates conda environments
...
user@host:~/lib3to6/ ⎇master $ ls ~/miniconda3/envs/
user@host:~/lib3to6_pypy35 lib3to6_py27 lib3to6_py36 lib3to6_py37 lib3to6_py38

Makefile 中的目标是设置为使用虚拟环境。

user@host:~/lib3to6/ ⎇master $ make fmt
All done!  🍰 21 files left unchanged.

user@host:~/lib3to6/ ⎇master $ make lint mypy devtest
isort ... ok
sjfmt ... ok
flake8 .. ok
mypy .... ok
...

对于调试,您可能仍然希望激活虚拟环境。

user@host:~/lib3to6/ ⎇master $ source activate
user@host:~/lib3to6/ ⎇master (lib3to6_py38) $ ipython
Python 3.8.2 | packaged by conda-forge | (default, Apr 24 2020, 08:20:52)
Type 'copyright', 'credits' or 'license' for more information
IPython 7.14.0 -- An enhanced Interactive Python. Type '?' for help.

In [1]: import lib3to6

In [2]: lib3to6.__file__
Out[2]: '/home/user/lib3to6/src/lib3to6/__init__.py'

项目状态(截至2021-01-01):测试版

我用Python 3.9进行了测试,与Python 3.8相比,只需要进行少量更新。我已经在几个项目中使用了这个库超过两年,而且没有太多问题。这些项目的例子包括

未来工作

在一个理想的世界里,项目将涵盖在 http://python-future.org 上记录的所有情况,并且可以

  1. 转译为在任何版本上都能工作的代码
  2. 引发错误,理想情况下指向python-future.org上的页面和部分或其他描述编写向后兼容代码的替代方法的文档。

https://docs.pythonlang.cn/3.X/whatsnew/ 也包含了关于API更改的许多信息,可能需要检查,但只有当它们足够常见时,才会编写检查和修复程序(欢迎补丁)。

替代方案

自从开始这个项目以来,我了解到了 py-backwards 项目,它的方法非常、非常相似。我还没有对其进行评估,以确定它可能更适合哪些项目。

可能要实现的一些功能

  • PEP 380 - yield from gen 语法可能通过扩展为 for x in gen: yield x 以基本形式得到支持。但这在语义上并不等价,我不知道是否值得正确实现
  • PEP 465 - @ 操作符可以通过将使用该操作符的所有情况替换为 __matmul__ 方法调用来完成。

常见问题解答

  • 问:标语“兼容性很重要”不是讽刺的吗,考虑到需要Python 3.6+来构建wheel吗?

  • 答:讽刺并没有丢失。问题是,如何解析比Python解释器本身支持的Python新版本源代码。您可以在较旧的Python版本上安装lib3to6,但您将仅限于该版本支持的功能。例如,您无法在Python 3.5上使用f""字符串,但大多数注释将正常工作。

  • 问:为什么还要保留python2.7?让它死去吧!

  • 答:的确如此,lib3to6可以帮助解决这个问题。假设您正在处理一个旧代码库。在代码库迁移和测试期间,其他所有开发工作都停止等待,这是不现实的。

    相反,通常只有增量方法才是唯一的选择。使用lib3to6,代码库的各个模块可以迁移到Python 3,而其余代码库保持不变。项目仍然可以在Python 2.7环境中运行,同时开发人员越来越多地转向使用Python 3。

    此外,lib3to6不仅用于与Python 2.7兼容,它还允许您使用新功能,如f""字符串和变量注解,同时仍然与较旧的Python版本保持兼容。

  • 问:为什么不叫 lib3to2

  • A:我无法诚实地对lib3to2说太多。它似乎没有得到维护,查看源代码后,我觉得在AST级别编写一些新的内容可能会更容易。与3to2相比,lib3to6的应用范围更广泛,即使你只关心将Python 3.6转换为3.5也可以使用它。

https://github.com/mbarkhau/lib3to6的变更日志

v202107.1045

  • 修复带列表参数的类型注解

v202107.1044

  • 修复带属性访问的类型注解

v202101.1043

  • 针对Python 3.9和mypy 0.800的修复

v202009.1042

  • 新功能#6:使用# lib3to6: disabled/# lib3to6: enabled实现按文件启用/禁用

v202006.1041

  • 新功能:增加了大量文档。
  • 新功能#5:添加检测无效导入,并指向可用的回滚版本。使用install_requires选项将已安装的回滚版本加入白名单。
  • 新功能:检查器产生更好的错误信息。
  • 新功能:使用lib3to6 CLI命令时,diff的着色。
  • 新功能:检查目标版本不支持yield from语法的情况。
  • 新功能:检查目标版本不支持@运算符的情况。
  • 修复#3:--target-version参数被忽略gitlab../issues/3
  • 修复#4:当目标版本不支持时,移除from __future__ import X
  • 修复#4:将Forward Reference Annotations转换为字符串gitlab../issues/4 感谢Faidon Liambotis在测试和调试方面的帮助 ❤️。
  • 修复:对于--target-version=3.0或更高版本,不要应用关键字仅参数修复器。

v202002.0031

  • 针对Python 3.8的兼容性修复
  • 添加对f-string =指定的支持
  • 添加对walrus运算符:=的支持(除了在列表推导式内)

v201902.0030

  • 修复Python 2内置函数有时没有被正确覆盖的问题。
  • 修复pypy兼容性测试
  • 改进mypy覆盖率

v201812.0021-beta

  • 递归应用一些修复器。

v201812.0020-alpha

  • 迁移到gitlab.com
  • 使用bootstrapit
  • 基于pycalver使用修复的bug

v201809.0019-alpha

  • CheckErrors包括日志行号

  • Transpile错误现在包括文件名

  • 添加了对重命名模块的修复器,例如 .. code-block:: diff

     - import queue
     + try:
     +     import queue
     + except ImportError:
     +     import Queue as queue
    

v201808.0014-alpha

  • 改进对package_dir的处理
  • 改为使用CalVer Versioning <https://calver.org/>_
  • 移除控制台脚本,改为简单的python -m lib3to6
  • 重命名为three2six -> lib3to6

项目详情


下载文件

下载适用于您平台的文件。如果您不确定选择哪个,请了解更多关于 安装包 的信息。

源分布

lib3to6-202107.1047.tar.gz (69.6 kB 查看哈希值)

上传时间

构建分布

lib3to6-202107.1047-py36.py37.py38-none-any.whl (43.7 kB 查看哈希值)

上传时间 Python 3.6 Python 3.7 Python 3.8