Skip to main content

A tool to automatically upgrade syntax for newer versions.

Project description

Build Status Azure DevOps coverage pre-commit.ci status

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: v2.29.1
    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}

Generator expressions for some built-in functions (pep 289)

min([i for i in range(3)])  # min(i for i in range(3))
max([i for i in range(3)])  # max(i for i in range(3))
sum([i for i in range(3)])  # sum(i for i in range(3))
''.join([str(i) for i in range(3)])  # ''.join(str(i) for i in range(3))

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")

unittest deprecated aliases

Rewrites deprecated unittest method aliases to their non-deprecated forms.

Availability:

  • More deprecated aliases are rewritten with --py3-plus
 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() 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.

rewrites class declaration

class C(object): pass     # class C: pass
class C(B, object): pass  # class C(B): pass

removes __metaclass__ = type declaration

-__metaclass__ = type

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 remove absolute_import / division / print_function / unicode_literals
  • --py37-plus will also remove generator_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

rewrite mock imports

Availability:

-from mock import patch
+from unittest.mock import patch

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)

Python2 and old Python3.x blocks

Availability:

  • --py3-plus is passed on the commandline.
# input
import sys
if sys.version_info < (3,):  # also understands `six.PY2` (and `not`), `six.PY3` (and `not`)
    print('py2')
else:
    print('py3')
# output
import sys
print('py3')

Availability:

  • --py36-plus will remove Python <= 3.5 only blocks
  • --py37-plus will remove Python <= 3.6 only blocks
  • so on and so forth
# using --py36-plus for this example
# input
import sys
if sys.version_info < (3, 6):
    print('py3.5')
else:
    print('py3.6+')

if sys.version_info <= (3, 5):
    print('py3.5')
else:
    print('py3.6+')

if sys.version_info >= (3, 6):
    print('py3.6+')
else:
    print('py3.5')

# output
import sys
print('py3.6+')

print('py3.6+')

print('py3.6+')

Note that if blocks without an else will not be rewriten as it could introduce a syntax error.

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)
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  # 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.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 alias

Availability:

  • --py3-plus is passed on the commandline.
-with io.open('f.txt') as f:
+with open('f.txt') as f:
     ...

redundant open modes

Availability:

  • --py3-plus is passed on the commandline.
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")

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

typing.Text str alias

Availability:

  • --py3-plus is passed on the commandline.
-def f(x: Text) -> None:
+def f(x: str) -> None:
     ...

Unpacking list comprehensions

Availability:

  • --py3-plus is passed on the commandline.
-foo, bar, baz = [fn(x) for x in items]
+foo, bar, baz = (fn(x) for x in items)

Rewrite xml.etree.cElementTree to xml.etree.ElementTree

Availability:

  • --py3-plus is passed on the commandline.
-import xml.etree.cElementTree as ET
+import xml.etree.ElementTree as ET
-from xml.etree.cElementTree import XML
+from xml.etree.ElementTree import XML

Rewrite type of primitive

Availability:

  • --py3-plus is passed on the commandline.
-type('')
+str
-type(b'')
+bytes
-type(0)
+int
-type(0.)
+float

typing.NamedTuple / typing.TypedDict py36+ syntax

Availability:

  • --py36-plus is passed on the commandline.
# input
NT = typing.NamedTuple('NT', [('a', int), ('b', Tuple[str, ...])])

D1 = typing.TypedDict('D1', a=int, b=str)

D2 = typing.TypedDict('D2', {'a': int, 'b': str})

# output

class NT(typing.NamedTuple):
    a: int
    b: Tuple[str, ...]

class D1(typing.TypedDict):
    a: int
    b: str

class D2(typing.TypedDict):
    a: int
    b: str

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 sufficiently complicated (as this can decrease readability).

subprocess.run: replace universal_newlines with text

Availability:

  • --py37-plus is passed on the commandline.
-output = subprocess.run(['foo'], universal_newlines=True)
+output = subprocess.run(['foo'], text=True)

subprocess.run: replace stdout=subprocess.PIPE, stderr=subprocess.PIPE with capture_output=True

Availability:

  • --py37-plus is passed on the commandline.
-output = subprocess.run(['foo'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+output = subprocess.run(['foo'], capture_output=True)

remove parentheses from @functools.lru_cache()

Availability:

  • --py38-plus is passed on the commandline.
 import functools

-@functools.lru_cache()
+@functools.lru_cache
 def expensive():
     ...

replace @functools.lru_cache(maxsize=None) with shorthand

Availability:

  • --py39-plus is passed on the commandline.
 import functools

-@functools.lru_cache(maxsize=None)
+@functools.cache
 def expensive():
     ...

pep 585 typing rewrites

Availability:

  • File imports from __future__ import annotations
    • Unless --keep-runtime-typing is passed on the commandline.
  • --py39-plus is passed on the commandline.
-def f(x: List[str]) -> None:
+def f(x: list[str]) -> None:
     ...

pep 604 typing rewrites

Availability:

  • File imports from __future__ import annotations
    • Unless --keep-runtime-typing is passed on the commandline.
  • --py310-plus is passed on the commandline.
-def f() -> Optional[str]:
+def f() -> str | None:
     ...
-def f() -> Union[int, str]:
+def f() -> int | str:
     ...

remove quoted annotations

Availability:

  • File imports from __future__ import annotations
  • --py311-plus is passed on the commandline.
-def f(x: 'queue.Queue[int]') -> C:
+def f(x: queue.Queue[int]) -> C:

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.

Files for pyupgrade, version 2.29.1
Filename, size File type Python version Upload date Hashes
Filename, size pyupgrade-2.29.1-py2.py3-none-any.whl (53.4 kB) File type Wheel Python version py2.py3 Upload date Hashes View
Filename, size pyupgrade-2.29.1.tar.gz (41.1 kB) File type Source Python version None Upload date Hashes View

Supported by

AWS AWS Cloud computing Datadog Datadog Monitoring Facebook / Instagram Facebook / Instagram PSF Sponsor Fastly Fastly CDN Google Google Object Storage and Download Analytics Huawei Huawei PSF Sponsor Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Salesforce Salesforce PSF Sponsor Sentry Sentry Error logging StatusPage StatusPage Status page