Convert Figma logo to code with AI

asottile logopyupgrade

A tool (and pre-commit hook) to automatically upgrade syntax for newer versions of the language.

3,482
177
3,482
15

Top Related Projects

38,772

The uncompromising Python code formatter

3,428

flake8 is a python tool that glues together pycodestyle, pyflakes, mccabe, and third-party plugins to check the style and quality of some python code.

5,286

It's not just a linter that annoys you!

13,756

A formatter for Python files

A tool that automatically formats Python code to conform to the PEP 8 style guide.

6,445

A Python utility / library to sort imports.

Quick Overview

pyupgrade is a tool for automatically upgrading Python syntax. It modernizes Python code by applying various syntax improvements and best practices, making the code more efficient and easier to read. pyupgrade can be used as a command-line tool or integrated into pre-commit hooks for automated code upgrades.

Pros

  • Automatically updates Python syntax to use newer, more efficient constructs
  • Supports a wide range of Python versions (2.7 to 3.10+)
  • Can be easily integrated into existing workflows and CI/CD pipelines
  • Customizable with various options to control the upgrade process

Cons

  • May occasionally produce unexpected results, requiring manual review
  • Some upgrades might change code behavior in subtle ways
  • Limited to syntax upgrades and doesn't address logical improvements
  • Requires careful testing after application to ensure functionality is preserved

Code Examples

  1. Upgrading string formatting:
# Before
name = "Alice"
age = 30
print("My name is %s and I'm %d years old" % (name, age))

# After
name = "Alice"
age = 30
print(f"My name is {name} and I'm {age} years old")
  1. Simplifying list comprehensions:
# Before
result = [i for i in range(10) if i % 2 == 0 if i % 3 == 0]

# After
result = [i for i in range(10) if i % 2 == 0 and i % 3 == 0]
  1. Updating exception handling:
# Before
try:
    do_something()
except Exception, e:
    print("Error:", e)

# After
try:
    do_something()
except Exception as e:
    print("Error:", e)

Getting Started

To use pyupgrade, first install it using pip:

pip install pyupgrade

Then, you can run pyupgrade on a Python file or directory:

pyupgrade --py310-plus path/to/file_or_directory

To integrate pyupgrade with pre-commit, add the following to your .pre-commit-config.yaml:

repos:
  - repo: https://github.com/asottile/pyupgrade
    rev: v3.3.1
    hooks:
      - id: pyupgrade
        args: [--py310-plus]

Competitor Comparisons

38,772

The uncompromising Python code formatter

Pros of Black

  • Opinionated and consistent formatting across projects
  • Wider adoption and community support
  • Integrates well with popular IDEs and text editors

Cons of Black

  • Less flexibility in code style customization
  • May produce unexpected formatting in some cases
  • Slower performance on large codebases

Code Comparison

Black:

def long_function_name(
    var_one: int, var_two: str, var_three: float, var_four: bool
) -> None:
    print(f"{var_one}, {var_two}, {var_three}, {var_four}")

pyupgrade:

def long_function_name(
    var_one, var_two, var_three, var_four
):
    print("{}, {}, {}, {}".format(var_one, var_two, var_three, var_four))

pyupgrade focuses on upgrading Python syntax to newer versions, while Black is primarily a code formatter. pyupgrade can modernize code constructs, such as updating string formatting methods, while Black ensures consistent formatting across the entire codebase. pyupgrade is more targeted in its approach, making specific syntax improvements, whereas Black applies a comprehensive formatting style to the entire file.

3,428

flake8 is a python tool that glues together pycodestyle, pyflakes, mccabe, and third-party plugins to check the style and quality of some python code.

Pros of flake8

  • Broader scope: Checks for style, logical errors, and complexity
  • Highly customizable with plugins and configuration options
  • Well-established in the Python community with extensive documentation

Cons of flake8

  • Doesn't automatically fix issues, only reports them
  • Can be slower on large codebases due to its comprehensive checks

Code comparison

flake8:

# flake8 will report style issues
def example_function( x ):
    return x+1

pyupgrade:

# pyupgrade will automatically update syntax
dict(a=1, b=2)
# becomes
{'a': 1, 'b': 2}

Summary

flake8 is a comprehensive linting tool that checks for various issues in Python code, including style violations, potential errors, and complexity problems. It's highly customizable but requires manual fixes.

pyupgrade, on the other hand, focuses specifically on upgrading Python syntax to newer versions. It automatically applies changes to modernize code, making it more efficient for keeping codebases up-to-date with the latest Python features.

While flake8 provides a broader analysis of code quality, pyupgrade excels at automating syntax updates. Both tools can be valuable in a Python development workflow, often used in conjunction to improve code quality and maintain modern syntax.

5,286

It's not just a linter that annoys you!

Pros of Pylint

  • Comprehensive static code analysis tool with a wide range of checks
  • Highly configurable with numerous options and plugins
  • Integrates well with various IDEs and development environments

Cons of Pylint

  • Can be slower to run, especially on large codebases
  • May produce false positives or overly strict warnings
  • Steeper learning curve due to its extensive configuration options

Code Comparison

Pylint example:

# pylint: disable=missing-docstring
def example_function(arg1, arg2):
    return arg1 + arg2

Pyupgrade example:

# Before
dict([(k, v) for k, v in items])

# After
{k: v for k, v in items}

Key Differences

Pylint is a comprehensive static code analysis tool that checks for a wide range of issues, including style, errors, and potential bugs. It offers extensive configuration options and can be integrated into various development workflows.

Pyupgrade, on the other hand, is a more focused tool that specifically targets upgrading Python code to use newer language features. It automatically refactors code to use more modern syntax and constructs.

While Pylint provides a broader set of checks and analysis, Pyupgrade excels at modernizing existing codebases with minimal effort. Pylint is better suited for ongoing code quality maintenance, while Pyupgrade is ideal for one-time upgrades or periodic modernization efforts.

13,756

A formatter for Python files

Pros of YAPF

  • More comprehensive code formatting, handling a wider range of Python syntax
  • Highly configurable with style options to match different coding standards
  • Can be used as a library or command-line tool for flexibility

Cons of YAPF

  • May introduce unexpected changes to code structure
  • Slower performance on large codebases compared to PyUpgrade
  • Requires more configuration to achieve desired results

Code Comparison

PyUpgrade focuses on upgrading Python syntax:

# Before
print 'Hello, world!'

# After
print('Hello, world!')

YAPF focuses on overall code formatting:

# Before
def long_function_name(
    var_one, var_two, var_three,
    var_four):
    print(var_one)

# After
def long_function_name(var_one,
                       var_two,
                       var_three,
                       var_four):
    print(var_one)

PyUpgrade is more targeted at modernizing Python code, while YAPF aims to enforce consistent formatting across entire codebases. PyUpgrade is generally faster and more focused, while YAPF offers more comprehensive formatting options at the cost of potential unexpected changes and slower performance.

A tool that automatically formats Python code to conform to the PEP 8 style guide.

Pros of autopep8

  • Broader scope: Covers a wide range of PEP 8 style issues beyond just syntax upgrades
  • More configurable: Offers extensive options for customizing its behavior
  • Integrated with popular editors: Has plugins for various IDEs and text editors

Cons of autopep8

  • Less focused on modernizing code: Primarily aims at PEP 8 compliance rather than updating syntax
  • May introduce unnecessary changes: Sometimes makes stylistic modifications that don't affect functionality

Code Comparison

autopep8:

def example_function(x, y):
    return x+y

result = example_function(1,2)

pyupgrade:

def example_function(x, y):
    return x + y

result = example_function(1, 2)

In this example, autopep8 would focus on PEP 8 compliance (adding spaces around operators), while pyupgrade would primarily target syntax upgrades (not applicable in this simple case).

Summary

autopep8 is a comprehensive tool for PEP 8 compliance with broad editor support, while pyupgrade focuses specifically on modernizing Python syntax. The choice between them depends on whether you prioritize general code style or keeping up with the latest Python features.

6,445

A Python utility / library to sort imports.

Pros of isort

  • Focuses specifically on import sorting and organization
  • Provides more granular control over import grouping and ordering
  • Integrates well with popular IDEs and text editors

Cons of isort

  • Limited to import-related modifications
  • May require more configuration to achieve desired results
  • Can sometimes produce unexpected results with complex import structures

Code Comparison

isort example:

import sys
from os import path
import requests
from . import local_module

# isort will reorganize these imports

pyupgrade example:

def old_style_formatting():
    return '%s %s' % ('Hello', 'World')

# pyupgrade will convert this to f-string

Both tools serve different purposes: isort focuses on organizing imports, while pyupgrade aims to modernize Python code syntax. isort is more specialized and offers finer control over import organization, but is limited to that specific task. pyupgrade, on the other hand, covers a broader range of Python code improvements but doesn't handle import sorting. The choice between them depends on the specific needs of your project and whether you're looking for import organization or general code modernization.

Convert Figma logo designs to code with AI

Visual Copilot

Introducing Visual Copilot: A new AI model to turn Figma designs to high quality code using your components.

Try Visual Copilot

README

build status 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: v3.17.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}

Replace unnecessary lambdas in collections.defaultdict calls

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

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

-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'
-u'\d'
+r'\d'
 # this fixes a syntax error in python3.3+
-'\N'
+r'\N'

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'

.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'

extraneous parens in print(...)

A fix for 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")

constant fold 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 deprecated aliases

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

 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

 class C(Base):
     def f(self):
-        super(C, self).f()
+        super().f()

"new style" classes

rewrites class declaration

-class C(object): pass
+class C: pass
-class C(B, object): pass
+class C(B): pass

removes __metaclass__ = type declaration

 class C:
-    __metaclass__ = type

forced str("native") literals

-str()
+''
-str("foo")
+"foo"

.encode("utf-8")

-"foo".encode("utf-8")
+"foo".encode()

# coding: ... comment

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, absolute_import, division, print_function, unicode_literals
  • --py37-plus will also remove generator_stop
-from __future__ import with_statement

Remove unnecessary py3-compat imports

-from io import open
-from six.moves import map
-from builtins import object  # python-future

import replacements

Availability:

  • --py36-plus (and others) will replace imports

see also reorder-python-imports

some examples:

-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

rewrite mock imports

Availability:

-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 and old Python3.x blocks

 import sys
-if sys.version_info < (3,):  # also understands `six.PY2` (and `not`), `six.PY3` (and `not`)
-    print('py2')
-else:
-    print('py3')
+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

 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+')

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

remove six compatibility code

-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 alias

-with io.open('f.txt') as f:
+with open('f.txt') as f:
     ...

redundant open modes

-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 aliases

 # 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 aliases

Availability:

  • --py310-plus for socket.timeout
  • --py311-plus for 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 alias

-def f(x: Text) -> None:
+def f(x: str) -> None:
     ...

Unpacking list comprehensions

-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

-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

-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.
-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

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()}'
-'{x}'.format(**locals())
+f'{x}'

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():
     ...

shlex.join

Availability:

  • --py38-plus is passed on the commandline.
-' '.join(shlex.quote(arg) for arg in cmd)
+shlex.join(cmd)

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:
     ...

pep 696 TypeVar defaults

Availability:

  • File imports from __future__ import annotations
    • Unless --keep-runtime-typing is passed on the commandline.
  • --py313-plus is passed on the commandline.
-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

remove quoted annotations

Availability:

  • File imports from __future__ import annotations
-def f(x: 'queue.Queue[int]') -> C:
+def f(x: queue.Queue[int]) -> C:

use datetime.UTC alias

Availability:

  • --py311-plus is passed on the commandline.
 import datetime

-datetime.timezone.utc
+datetime.UTC