A tool to automatically upgrade syntax for newer versions.
Project description
pyupgrade
A tool (and pre-commit hook) to automatically upgrade syntax for newer versions of the language.
Installation
pip install pyupgrade
As a pre-commit hook
See pre-commit for instructions
Sample .pre-commit-config.yaml
:
- repo: https://github.com/asottile/pyupgrade
rev: v1.27.0
hooks:
- id: pyupgrade
Implemented features
Set literals
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}
Dictionary comprehensions
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}
Python2.7+ Format Specifiers
'{0} {1}'.format(1, 2) # '{} {}'.format(1, 2)
'{0}' '{1}'.format(1, 2) # '{}' '{}'.format(1, 2)
printf-style string formatting
Availability:
- Unless
--keep-percent-format
is passed.
'%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 literals
Availability:
- File imports
from __future__ import unicode_literals
--py3-plus
is passed on the commandline.
u'foo' # 'foo'
u"foo" # 'foo'
u'''foo''' # '''foo'''
Invalid escape sequences
# strings with only invalid sequences become raw strings
'\d' # r'\d'
# strings with mixed valid / invalid sequences get escaped
'\n\d' # '\n\\d'
# `ur` is not a valid string prefix in python3
u'\d' # u'\\d'
# this fixes a syntax error in python3.3+
'\N' # r'\N'
# note: pyupgrade is timid in one case (that's usually a mistake)
# in python2.x `'\u2603'` is the same as `'\\u2603'` without `unicode_literals`
# but in python3.x, that's our friend ☃
is
/ is not
comparison to constant literals
In python3.8+, comparison to literals becomes a SyntaxWarning
as the success
of those comparisons is implementation specific (due to common object caching).
x is 5 # x == 5
x is not 5 # x != 5
x is 'foo' # x == foo
ur
string literals
ur'...'
literals are not valid in python 3.x
ur'foo' # u'foo'
ur'\s' # u'\\s'
# unicode escapes are left alone
ur'\u2603' # u'\u2603'
ur'\U0001f643' # u'\U0001f643'
.encode()
to bytes literals
'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'
Long literals
5L # 5
5l # 5
123456789123456789123456789L # 123456789123456789123456789
Octal literals
0755 # 0o755
05 # 5
extraneous parens in print(...)
A fix for python-modernize/python-modernize#178
print(()) # ok: printing an empty tuple
print((1,)) # ok: printing a tuple
sum((i for i in range(3)), []) # ok: parenthesized generator argument
print(("foo")) # print("foo")
super()
calls
Availability:
--py3-plus
is passed on the commandline.
class C(Base):
def f(self):
super(C, self).f() # super().f()
"new style" classes
Availability:
--py3-plus
is passed on the commandline.
class C(object): pass # class C: pass
class C(B, object): pass # class C(B): pass
forced str("native")
literals
Availability:
--py3-plus
is passed on the commandline.
str() # "''"
str("foo") # "foo"
.encode("utf-8")
Availability:
--py3-plus
is passed on the commandline.
"foo".encode("utf-8") # "foo".encode()
# coding: ...
comment
Availability:
--py3-plus
is passed on the commandline.
as of PEP 3120, the default encoding for python source is UTF-8
-# coding: utf-8
x = 1
__future__
import removal
Availability:
- by default removes
nested_scopes
,generators
,with_statement
--py3-plus
will also removeabsolute_import
/division
/print_function
/unicode_literals
--py37-plus
will also removegenerator_stop
-from __future__ import with_statement
Remove unnecessary py3-compat imports
Availability:
--py3-plus
is passed on the commandline.
-from io import open
-from six.moves import map
-from builtins import object # python-future
yield
=> yield from
Availability:
--py3-plus
is passed on the commandline.
def f():
for x in y: # yield from y
yield x
for a, b in c: # yield from c
yield (a, b)
if PY2
blocks
Availability:
--py3-plus
is passed on the commandline.
# input
if six.PY2: # also understands `six.PY3` and `not` and `sys.version_info`
print('py2')
else:
print('py3')
# output
print('py3')
remove six
compatibility code
Availability:
--py3-plus
is passed on the commandline.
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)
from six import text_type
text_type # str
@six.python_2_unicode_compatible # decorator is removed
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, metaclass=M): pass
class C(B): 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.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
alias
Availability:
--py3-plus
is passed on the commandline.
# input
with io.open('f.txt') as f:
pass
# output
with open('f.txt') as f:
pass
OSError
aliases
Availability:
--py3-plus
is passed on the commandline.
# input
# 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`
try:
raise EnvironmentError('boom')
except EnvironmentError:
raise
# output
try:
raise OSError('boom')
except OSError:
raise
f-strings
Availability:
--py36-plus
is passed on the commandline.
'{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()}'
note: pyupgrade
is intentionally timid and will not create an f-string
if it would make the expression longer or if the substitution parameters are
anything but simple names or dotted names (as this can decrease readability).
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Hashes for pyupgrade-1.27.0-py2.py3-none-any.whl
Algorithm | Hash digest | |
---|---|---|
SHA256 | 927d9945ade12a28c7a2f30cbd7f5af02b02846158ebf819c466f58a5720187e |
|
MD5 | 23be5193832742b4a7dfb8f4d4036b93 |
|
BLAKE2b-256 | ca7d1b91603bf4b81c84bc1e280f29a040393c9ee43fd6b1349c3036f429c57f |