一个用于自动升级语言语法的工具。
项目描述
pyupgrade
一个工具(以及pre-commit钩子),用于自动升级语言到较新版本的语法。
安装
pip install pyupgrade
作为pre-commit钩子
有关说明,请参阅pre-commit
示例 .pre-commit-config.yaml
- repo: https://github.com/asottile/pyupgrade
rev: v3.17.0
hooks:
- id: pyupgrade
实现的功能
集合字面量
-set(())
+set()
-set([])
+set()
-set((1,))
+{1}
-set((1, 2))
+{1, 2}
-set([1, 2])
+{1, 2}
-set(x for x in y)
+{x for x in y}
-set([x for x in y])
+{x for x in y}
字典推导式
-dict((a, b) for a, b in y)
+{a: b for a, b in y}
-dict([(a, b) for a, b in y])
+{a: b for a, b in y}
替换在collections.defaultdict
调用中不必要的lambda
-defaultdict(lambda: [])
+defaultdict(list)
-defaultdict(lambda: list())
+defaultdict(list)
-defaultdict(lambda: {})
+defaultdict(dict)
-defaultdict(lambda: dict())
+defaultdict(dict)
-defaultdict(lambda: ())
+defaultdict(tuple)
-defaultdict(lambda: tuple())
+defaultdict(tuple)
-defaultdict(lambda: set())
+defaultdict(set)
-defaultdict(lambda: 0)
+defaultdict(int)
-defaultdict(lambda: 0.0)
+defaultdict(float)
-defaultdict(lambda: 0j)
+defaultdict(complex)
-defaultdict(lambda: '')
+defaultdict(str)
格式说明符
-'{0} {1}'.format(1, 2)
+'{} {}'.format(1, 2)
-'{0}' '{1}'.format(1, 2)
+'{}' '{}'.format(1, 2)
printf样式字符串格式化
可用性
- 除非传递
--keep-percent-format
。
-'%s %s' % (a, b)
+'{} {}'.format(a, b)
-'%r %2f' % (a, b)
+'{!r} {:2f}'.format(a, b)
-'%(a)s %(b)s' % {'a': 1, 'b': 2}
+'{a} {b}'.format(a=1, b=2)
Unicode字面量
-u'foo'
+'foo'
-u"foo"
+'foo'
-u'''foo'''
+'''foo'''
无效的转义序列
# strings with only invalid sequences become raw strings
-'\d'
+r'\d'
# strings with mixed valid / invalid sequences get escaped
-'\n\d'
+'\n\\d'
-u'\d'
+r'\d'
# this fixes a syntax error in python3.3+
-'\N'
+r'\N'
is
/ is not
与常量字面量的比较
在python3.8+中,比较变为SyntaxWarning
,因为这些比较的成功是特定于实现的(由于常见的对象缓存)。
-x is 5
+x == 5
-x is not 5
+x != 5
-x is 'foo'
+x == 'foo'
.encode()
到字节字面量
-'foo'.encode()
+b'foo'
-'foo'.encode('ascii')
+b'foo'
-'foo'.encode('utf-8')
+b'foo'
-u'foo'.encode()
+b'foo'
-'\xa0'.encode('latin1')
+b'\xa0'
在print(...)
中的多余括号
对python-modernize/python-modernize#178的修复
# ok: printing an empty tuple
print(())
# ok: printing a tuple
print((1,))
# ok: parenthesized generator argument
sum((i for i in range(3)), [])
# fixed:
-print(("foo"))
+print("foo")
常量折叠isinstance
/ issubclass
/ except
-isinstance(x, (int, int))
+isinstance(x, int)
-issubclass(y, (str, str))
+issubclass(y, str)
try:
raises()
-except (Error1, Error1, Error2):
+except (Error1, Error2):
pass
unittest弃用别名
重写弃用的unittest方法别名到其非弃用形式。
from unittest import TestCase
class MyTests(TestCase):
def test_something(self):
- self.failUnlessEqual(1, 1)
+ self.assertEqual(1, 1)
- self.assertEquals(1, 1)
+ self.assertEqual(1, 1)
super()
调用
class C(Base):
def f(self):
- super(C, self).f()
+ super().f()
"新式"类
重写类声明
-class C(object): pass
+class C: pass
-class C(B, object): pass
+class C(B): pass
删除__metaclass__ = type
声明
class C:
- __metaclass__ = type
强制str("native")
字面量
-str()
+''
-str("foo")
+"foo"
.encode("utf-8")
-"foo".encode("utf-8")
+"foo".encode()
# coding: ...
注释
根据PEP 3120,Python源代码的默认编码是UTF-8
-# coding: utf-8
x = 1
__future__
导入删除
可用性
- 默认情况下移除
nested_scopes
、generators
、with_statement
、absolute_import
、division
、print_function
、unicode_literals
--py37-plus
还会移除generator_stop
-from __future__ import with_statement
移除不必要的 py3 兼容导入
-from io import open
-from six.moves import map
-from builtins import object # python-future
导入替换
可用性
--py36-plus
(以及其他)将替换导入
一些示例
-from collections import deque, Mapping
+from collections import deque
+from collections.abc import Mapping
-from typing import Sequence
+from collections.abc import Sequence
-from typing_extensions import Concatenate
+from typing import Concatenate
重写 mock
导入
可用性
-from mock import patch
+from unittest.mock import patch
yield
→ yield from
def f():
- for x in y:
- yield x
+ yield from y
- for a, b in c:
- yield (a, b)
+ yield from c
Python2 和旧的 Python3.x 块
import sys
-if sys.version_info < (3,): # also understands `six.PY2` (and `not`), `six.PY3` (and `not`)
- print('py2')
-else:
- print('py3')
+print('py3')
可用性
--py36-plus
将移除 Python <= 3.5 的块--py37-plus
将移除 Python <= 3.6 的块- 等等
# using --py36-plus for this example
import sys
-if sys.version_info < (3, 6):
- print('py3.5')
-else:
- print('py3.6+')
+print('py3.6+')
-if sys.version_info <= (3, 5):
- print('py3.5')
-else:
- print('py3.6+')
+print('py3.6+')
-if sys.version_info >= (3, 6):
- print('py3.6+')
-else:
- print('py3.5')
+print('py3.6+')
注意,没有 else
的 if
块不会被重写,因为这可能会引入语法错误。
移除 six
兼容代码
-six.text_type
+str
-six.binary_type
+bytes
-six.class_types
+(type,)
-six.string_types
+(str,)
-six.integer_types
+(int,)
-six.unichr
+chr
-six.iterbytes
+iter
-six.print_(...)
+print(...)
-six.exec_(c, g, l)
+exec(c, g, l)
-six.advance_iterator(it)
+next(it)
-six.next(it)
+next(it)
-six.callable(x)
+callable(x)
-six.moves.range(x)
+range(x)
-six.moves.xrange(x)
+range(x)
-from six import text_type
-text_type
+str
-@six.python_2_unicode_compatible
class C:
def __str__(self):
return u'C()'
-class C(six.Iterator): pass
+class C: pass
-class C(six.with_metaclass(M, B)): pass
+class C(B, metaclass=M): pass
-@six.add_metaclass(M)
-class C(B): pass
+class C(B, metaclass=M): pass
-isinstance(..., six.class_types)
+isinstance(..., type)
-issubclass(..., six.integer_types)
+issubclass(..., int)
-isinstance(..., six.string_types)
+isinstance(..., str)
-six.b('...')
+b'...'
-six.u('...')
+'...'
-six.byte2int(bs)
+bs[0]
-six.indexbytes(bs, i)
+bs[i]
-six.int2byte(i)
+bytes((i,))
-six.iteritems(dct)
+dct.items()
-six.iterkeys(dct)
+dct.keys()
-six.itervalues(dct)
+dct.values()
-next(six.iteritems(dct))
+next(iter(dct.items()))
-next(six.iterkeys(dct))
+next(iter(dct.keys()))
-next(six.itervalues(dct))
+next(iter(dct.values()))
-six.viewitems(dct)
+dct.items()
-six.viewkeys(dct)
+dct.keys()
-six.viewvalues(dct)
+dct.values()
-six.create_unbound_method(fn, cls)
+fn
-six.get_unbound_function(meth)
+meth
-six.get_method_function(meth)
+meth.__func__
-six.get_method_self(meth)
+meth.__self__
-six.get_function_closure(fn)
+fn.__closure__
-six.get_function_code(fn)
+fn.__code__
-six.get_function_defaults(fn)
+fn.__defaults__
-six.get_function_globals(fn)
+fn.__globals__
-six.raise_from(exc, exc_from)
+raise exc from exc_from
-six.reraise(tp, exc, tb)
+raise exc.with_traceback(tb)
-six.reraise(*sys.exc_info())
+raise
-six.assertCountEqual(self, a1, a2)
+self.assertCountEqual(a1, a2)
-six.assertRaisesRegex(self, e, r, fn)
+self.assertRaisesRegex(e, r, fn)
-six.assertRegex(self, s, r)
+self.assertRegex(s, r)
# note: only for *literals*
-six.ensure_binary('...')
+b'...'
-six.ensure_str('...')
+'...'
-six.ensure_text('...')
+'...'
open
别名
-with io.open('f.txt') as f:
+with open('f.txt') as f:
...
冗余的 open
模式
-open("foo", "U")
+open("foo")
-open("foo", "Ur")
+open("foo")
-open("foo", "Ub")
+open("foo", "rb")
-open("foo", "rUb")
+open("foo", "rb")
-open("foo", "r")
+open("foo")
-open("foo", "rt")
+open("foo")
-open("f", "r", encoding="UTF-8")
+open("f", encoding="UTF-8")
-open("f", "wt")
+open("f", "w")
OSError
别名
# also understands:
# - IOError
# - WindowsError
# - mmap.error and uses of `from mmap import error`
# - select.error and uses of `from select import error`
# - socket.error and uses of `from socket import error`
def throw():
- raise EnvironmentError('boom')
+ raise OSError('boom')
def catch():
try:
throw()
- except EnvironmentError:
+ except OSError:
handle_error()
TimeoutError
别名
可用性
--py310-plus
用于socket.timeout
--py311-plus
用于asyncio.TimeoutError
def throw(a):
if a:
- raise asyncio.TimeoutError('boom')
+ raise TimeoutError('boom')
else:
- raise socket.timeout('boom')
+ raise TimeoutError('boom')
def catch(a):
try:
throw(a)
- except (asyncio.TimeoutError, socket.timeout):
+ except TimeoutError:
handle_error()
typing.Text
str 别名
-def f(x: Text) -> None:
+def f(x: str) -> None:
...
解包列表推导式
-foo, bar, baz = [fn(x) for x in items]
+foo, bar, baz = (fn(x) for x in items)
将 xml.etree.cElementTree
重写为 xml.etree.ElementTree
-import xml.etree.cElementTree as ET
+import xml.etree.ElementTree as ET
-from xml.etree.cElementTree import XML
+from xml.etree.ElementTree import XML
重写原生的 type
-type('')
+str
-type(b'')
+bytes
-type(0)
+int
-type(0.)
+float
typing.NamedTuple
/ typing.TypedDict
py36+ 语法
可用性
- 在命令行上传递了
--py36-plus
。
-NT = typing.NamedTuple('NT', [('a', int), ('b', Tuple[str, ...])])
+class NT(typing.NamedTuple):
+ a: int
+ b: Tuple[str, ...]
-D1 = typing.TypedDict('D1', a=int, b=str)
+class D1(typing.TypedDict):
+ a: int
+ b: str
-D2 = typing.TypedDict('D2', {'a': int, 'b': str})
+class D2(typing.TypedDict):
+ a: int
+ b: str
f-strings
可用性
- 在命令行上传递了
--py36-plus
。
-'{foo} {bar}'.format(foo=foo, bar=bar)
+f'{foo} {bar}'
-'{} {}'.format(foo, bar)
+f'{foo} {bar}'
-'{} {}'.format(foo.bar, baz.womp)
+f'{foo.bar} {baz.womp}'
-'{} {}'.format(f(), g())
+f'{f()} {g()}'
-'{x}'.format(**locals())
+f'{x}'
注意:pyupgrade
故意谨慎,不会创建 f-string,如果这会使表达式更长或如果替换参数足够复杂(因为这可能会降低可读性)。
subprocess.run
:将 universal_newlines
替换为 text
可用性
- 在命令行上传递了
--py37-plus
。
-output = subprocess.run(['foo'], universal_newlines=True)
+output = subprocess.run(['foo'], text=True)
subprocess.run
:将 stdout=subprocess.PIPE, stderr=subprocess.PIPE
替换为 capture_output=True
可用性
- 在命令行上传递了
--py37-plus
。
-output = subprocess.run(['foo'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+output = subprocess.run(['foo'], capture_output=True)
从 @functools.lru_cache()
中移除括号
可用性
- 在命令行上传递了
--py38-plus
。
import functools
-@functools.lru_cache()
+@functools.lru_cache
def expensive():
...
shlex.join
可用性
- 在命令行上传递了
--py38-plus
。
-' '.join(shlex.quote(arg) for arg in cmd)
+shlex.join(cmd)
将 @functools.lru_cache(maxsize=None)
替换为简写
可用性
- 在命令行上传递了
--py39-plus
。
import functools
-@functools.lru_cache(maxsize=None)
+@functools.cache
def expensive():
...
PEP 585 类型重写
可用性
- 文件导入
from __future__ import annotations
- 除非在命令行上传递了
--keep-runtime-typing
- 除非在命令行上传递了
- 在命令行上传递了
--py39-plus
。
-def f(x: List[str]) -> None:
+def f(x: list[str]) -> None:
...
PEP 604 类型重写
可用性
- 文件导入
from __future__ import annotations
- 除非在命令行上传递了
--keep-runtime-typing
- 除非在命令行上传递了
- 在命令行上传递了
--py310-plus
。
-def f() -> Optional[str]:
+def f() -> str | None:
...
-def f() -> Union[int, str]:
+def f() -> int | str:
...
PEP 696 TypeVar 默认值
可用性
- 文件导入
from __future__ import annotations
- 除非在命令行上传递了
--keep-runtime-typing
- 除非在命令行上传递了
- 在命令行上传递了
--py313-plus
。
-def f() -> Generator[int, None, None]:
+def f() -> Generator[int]:
yield 1
-async def f() -> AsyncGenerator[int, None]:
+async def f() -> AsyncGenerator[int]:
yield 1
移除带引号的注解
可用性
- 文件导入
from __future__ import annotations
-def f(x: 'queue.Queue[int]') -> C:
+def f(x: queue.Queue[int]) -> C:
使用 datetime.UTC
别名
可用性
- 在命令行上传递了
--py311-plus
。
import datetime
-datetime.timezone.utc
+datetime.UTC
项目详情
下载文件
下载适合您平台的文件。如果您不确定选择哪个,请了解有关安装包的更多信息。
源分布
pyupgrade-3.17.0.tar.gz (45.4 kB 查看哈希值)
构建分布
pyupgrade-3.17.0-py2.py3-none-any.whl (62.0 kB 查看哈希值)
关闭
pyupgrade-3.17.0.tar.gz的哈希值
算法 | 哈希摘要 | |
---|---|---|
SHA256 | 5dd1dcaf9a016c31508bb9d3d09fd335d736578092f91df52bb26ac30c37919 |
|
MD5 | 3ecb0da978bfd70ae132659880babb79 |
|
BLAKE2b-256 | 7a7915cd93e47b5d670f0e32a540eb3f11bac4b5800cf1f796590eb448c6a768 |