Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: asottile/pyupgrade
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v3.16.0
Choose a base ref
...
head repository: asottile/pyupgrade
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: v3.17.0
Choose a head ref
  • 15 commits
  • 13 files changed
  • 2 contributors

Commits on Jun 8, 2024

  1. remove support for python 3.8

    asottile committed Jun 8, 2024
    Copy the full SHA
    c25513c View commit details
  2. Merge pull request #947 from asottile/drop-3-8

    remove support for python 3.8
    asottile authored Jun 8, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    049b8e3 View commit details

Commits on Jun 17, 2024

  1. [pre-commit.ci] pre-commit autoupdate

    updates:
    - [github.com/hhatto/autopep8: v2.2.0 → v2.3.0](hhatto/autopep8@v2.2.0...v2.3.0)
    - [github.com/PyCQA/flake8: 7.0.0 → 7.1.0](PyCQA/flake8@7.0.0...7.1.0)
    pre-commit-ci[bot] authored Jun 17, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    6703793 View commit details

Commits on Jun 19, 2024

  1. Merge pull request #950 from asottile/pre-commit-ci-update-config

    [pre-commit.ci] pre-commit autoupdate
    asottile authored Jun 19, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    0f2f56d View commit details

Commits on Jun 24, 2024

  1. [pre-commit.ci] pre-commit autoupdate

    updates:
    - [github.com/hhatto/autopep8: v2.3.0 → v2.3.1](hhatto/autopep8@v2.3.0...v2.3.1)
    pre-commit-ci[bot] authored Jun 24, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    f5ce85a View commit details

Commits on Jun 25, 2024

  1. Merge pull request #951 from asottile/pre-commit-ci-update-config

    [pre-commit.ci] pre-commit autoupdate
    asottile authored Jun 25, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    f17a8c7 View commit details

Commits on Jul 2, 2024

  1. [pre-commit.ci] pre-commit autoupdate

    updates:
    - [github.com/pre-commit/mirrors-mypy: v1.10.0 → v1.10.1](pre-commit/mirrors-mypy@v1.10.0...v1.10.1)
    pre-commit-ci[bot] authored Jul 2, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    ca56d62 View commit details

Commits on Jul 5, 2024

  1. Merge pull request #953 from asottile/pre-commit-ci-update-config

    [pre-commit.ci] pre-commit autoupdate
    asottile authored Jul 5, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    a7a9e1b View commit details

Commits on Jul 22, 2024

  1. [pre-commit.ci] pre-commit autoupdate

    updates:
    - [github.com/pre-commit/mirrors-mypy: v1.10.1 → v1.11.0](pre-commit/mirrors-mypy@v1.10.1...v1.11.0)
    pre-commit-ci[bot] authored Jul 22, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    d2bf447 View commit details

Commits on Jul 28, 2024

  1. hack around python/mypy#17566

    asottile committed Jul 28, 2024
    Copy the full SHA
    459acce View commit details
  2. Merge pull request #955 from asottile/pre-commit-ci-update-config

    [pre-commit.ci] pre-commit autoupdate
    asottile authored Jul 28, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    bc45bf1 View commit details
  3. Copy the full SHA
    cc45199 View commit details
  4. run pyupgrade on itself

    asottile committed Jul 28, 2024
    Copy the full SHA
    d17f461 View commit details
  5. Merge pull request #948 from asottile/pep-696

    rewrite TypeVar defaults for Generator / AsyncGenerator
    asottile authored Jul 28, 2024

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature.
    Copy the full SHA
    4e28911 View commit details
  6. v3.17.0

    asottile committed Jul 28, 2024
    Copy the full SHA
    a25bb53 View commit details
4 changes: 2 additions & 2 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
@@ -10,10 +10,10 @@ jobs:
main-windows:
uses: asottile/workflows/.github/workflows/tox.yml@v1.6.0
with:
env: '["py38"]'
env: '["py39"]'
os: windows-latest
main-linux:
uses: asottile/workflows/.github/workflows/tox.yml@v1.6.0
with:
env: '["py38", "py39", "py310", "py311", "py312"]'
env: '["py39", "py310", "py311", "py312"]'
os: ubuntu-latest
8 changes: 4 additions & 4 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -23,19 +23,19 @@ repos:
hooks:
- id: add-trailing-comma
- repo: https://github.com/asottile/pyupgrade
rev: v3.16.0
rev: v3.17.0
hooks:
- id: pyupgrade
args: [--py38-plus]
- repo: https://github.com/hhatto/autopep8
rev: v2.2.0
rev: v2.3.1
hooks:
- id: autopep8
- repo: https://github.com/PyCQA/flake8
rev: 7.0.0
rev: 7.1.0
hooks:
- id: flake8
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.10.0
rev: v1.11.0
hooks:
- id: mypy
20 changes: 19 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -21,7 +21,7 @@ Sample `.pre-commit-config.yaml`:

```yaml
- repo: https://github.com/asottile/pyupgrade
rev: v3.16.0
rev: v3.17.0
hooks:
- id: pyupgrade
```
@@ -754,6 +754,24 @@ Availability:
...
```

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

```diff
-def f() -> Generator[int, None, None]:
+def f() -> Generator[int]:
yield 1
```

```diff
-async def f() -> AsyncGenerator[int, None]:
+async def f() -> AsyncGenerator[int]:
yield 1
```

### remove quoted annotations

4 changes: 3 additions & 1 deletion pyupgrade/_data.py
Original file line number Diff line number Diff line change
@@ -40,6 +40,7 @@ class State(NamedTuple):
'__future__',
'asyncio',
'collections',
'collections.abc',
'functools',
'mmap',
'os',
@@ -53,7 +54,8 @@ class State(NamedTuple):
'typing_extensions',
))

FUNCS = collections.defaultdict(list)
FUNCS: ASTCallbackMapping # python/mypy#17566
FUNCS = collections.defaultdict(list) # type: ignore[assignment]


def register(tp: type[AST_T]) -> Callable[[ASTFunc[AST_T]], ASTFunc[AST_T]]:
2 changes: 1 addition & 1 deletion pyupgrade/_plugins/legacy.py
Original file line number Diff line number Diff line change
@@ -91,7 +91,7 @@ def __init__(self) -> None:
self.yield_offsets: set[Offset] = set()

@contextlib.contextmanager
def _scope(self, node: ast.AST) -> Generator[None, None, None]:
def _scope(self, node: ast.AST) -> Generator[None]:
self._scopes.append(Scope(node))
try:
yield
2 changes: 1 addition & 1 deletion pyupgrade/_plugins/percent_format.py
Original file line number Diff line number Diff line change
@@ -46,7 +46,7 @@ def _must_match(regex: Pattern[str], string: str, pos: int) -> Match[str]:


def _parse_percent_format(s: str) -> tuple[PercentFormat, ...]:
def _parse_inner() -> Generator[PercentFormat, None, None]:
def _parse_inner() -> Generator[PercentFormat]:
string_start = 0
string_end = 0
in_fmt = False
17 changes: 5 additions & 12 deletions pyupgrade/_plugins/typing_classes.py
Original file line number Diff line number Diff line change
@@ -2,7 +2,6 @@

import ast
import functools
import sys
from typing import Iterable

from tokenize_rt import Offset
@@ -24,19 +23,13 @@ def _unparse(node: ast.expr) -> str:
elif isinstance(node, ast.Attribute):
return ''.join((_unparse(node.value), '.', node.attr))
elif isinstance(node, ast.Subscript):
if sys.version_info >= (3, 9): # pragma: >=3.9 cover
node_slice: ast.expr = node.slice
elif isinstance(node.slice, ast.Index): # pragma: <3.9 cover
node_slice = node.slice.value
else:
raise AssertionError(f'expected Slice: {ast.dump(node)}')
if isinstance(node_slice, ast.Tuple):
if len(node_slice.elts) == 1:
slice_s = f'{_unparse(node_slice.elts[0])},'
if isinstance(node.slice, ast.Tuple):
if len(node.slice.elts) == 1:
slice_s = f'{_unparse(node.slice.elts[0])},'
else:
slice_s = ', '.join(_unparse(elt) for elt in node_slice.elts)
slice_s = ', '.join(_unparse(elt) for elt in node.slice.elts)
else:
slice_s = _unparse(node_slice)
slice_s = _unparse(node.slice)
return f'{_unparse(node.value)}[{slice_s}]'
elif (
isinstance(node, ast.Constant) and
11 changes: 2 additions & 9 deletions pyupgrade/_plugins/typing_pep563.py
Original file line number Diff line number Diff line change
@@ -90,15 +90,8 @@ def _process_call(node: ast.Call) -> Iterable[ast.AST]:
def _process_subscript(node: ast.Subscript) -> Iterable[ast.AST]:
name = _get_name(node.value)
if name == 'Annotated':
if sys.version_info >= (3, 9): # pragma: >=3.9 cover
node_slice = node.slice
elif isinstance(node.slice, ast.Index): # pragma: <3.9 cover
node_slice: ast.AST = node.slice.value
else: # pragma: <3.9 cover
node_slice = node.slice

if isinstance(node_slice, ast.Tuple) and node_slice.elts:
yield node_slice.elts[0]
if isinstance(node.slice, ast.Tuple) and node.slice.elts:
yield node.slice.elts[0]
elif name != 'Literal':
yield node.slice

24 changes: 5 additions & 19 deletions pyupgrade/_plugins/typing_pep604.py
Original file line number Diff line number Diff line change
@@ -153,14 +153,7 @@ def visit_Subscript(

# don't rewrite forward annotations (unless we know they will be dequoted)
if 'annotations' not in state.from_imports['__future__']:
if (
(sys.version_info >= (3, 9) and _any_arg_is_str(node.slice)) or
(
sys.version_info < (3, 9) and
isinstance(node.slice, ast.Index) and
_any_arg_is_str(node.slice.value)
)
):
if _any_arg_is_str(node.slice):
return

if is_name_attr(
@@ -171,19 +164,12 @@ def visit_Subscript(
):
yield ast_to_offset(node), _fix_optional
elif is_name_attr(node.value, state.from_imports, ('typing',), ('Union',)):
if sys.version_info >= (3, 9): # pragma: >=3.9 cover
node_slice = node.slice
elif isinstance(node.slice, ast.Index): # pragma: <3.9 cover
node_slice: ast.AST = node.slice.value
else: # pragma: <3.9 cover
node_slice = node.slice # unexpected slice type

if isinstance(node_slice, ast.Slice): # not a valid annotation
if isinstance(node.slice, ast.Slice): # not a valid annotation
return

if isinstance(node_slice, ast.Tuple):
if node_slice.elts:
arg_count = len(node_slice.elts)
if isinstance(node.slice, ast.Tuple):
if node.slice.elts:
arg_count = len(node.slice.elts)
else:
return # empty Union
else:
2 changes: 1 addition & 1 deletion pyupgrade/_plugins/typing_pep646_unpack.py
Original file line number Diff line number Diff line change
@@ -35,7 +35,7 @@ def visit_Subscript(
return

if is_name_attr(node.value, state.from_imports, ('typing',), ('Unpack',)):
if isinstance(parent, (ast.Subscript, ast.Index)):
if isinstance(parent, ast.Subscript):
yield ast_to_offset(node.value), _replace_unpack_with_star


72 changes: 72 additions & 0 deletions pyupgrade/_plugins/typing_pep696_typevar_defaults.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
from __future__ import annotations

import ast
from typing import Iterable

from tokenize_rt import Offset
from tokenize_rt import Token

from pyupgrade._ast_helpers import ast_to_offset
from pyupgrade._ast_helpers import is_name_attr
from pyupgrade._data import register
from pyupgrade._data import State
from pyupgrade._data import TokenFunc
from pyupgrade._token_helpers import find_op
from pyupgrade._token_helpers import parse_call_args


def _fix_typevar_default(i: int, tokens: list[Token]) -> None:
j = find_op(tokens, i, '[')
args, end = parse_call_args(tokens, j)
# remove the trailing `None` arguments
del tokens[args[0][1]:args[-1][1]]


def _should_rewrite(state: State) -> bool:
return (
state.settings.min_version >= (3, 13) or (
not state.settings.keep_runtime_typing and
state.in_annotation and
'annotations' in state.from_imports['__future__']
)
)


def _is_none(node: ast.AST) -> bool:
return isinstance(node, ast.Constant) and node.value is None


@register(ast.Subscript)
def visit_Subscript(
state: State,
node: ast.Subscript,
parent: ast.AST,
) -> Iterable[tuple[Offset, TokenFunc]]:
if not _should_rewrite(state):
return

if (
is_name_attr(
node.value,
state.from_imports,
('collections.abc', 'typing', 'typing_extensions'),
('Generator',),
) and
isinstance(node.slice, ast.Tuple) and
len(node.slice.elts) == 3 and
_is_none(node.slice.elts[1]) and
_is_none(node.slice.elts[2])
):
yield ast_to_offset(node), _fix_typevar_default
elif (
is_name_attr(
node.value,
state.from_imports,
('collections.abc', 'typing', 'typing_extensions'),
('AsyncGenerator',),
) and
isinstance(node.slice, ast.Tuple) and
len(node.slice.elts) == 2 and
_is_none(node.slice.elts[1])
):
yield ast_to_offset(node), _fix_typevar_default
4 changes: 2 additions & 2 deletions setup.cfg
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[metadata]
name = pyupgrade
version = 3.16.0
version = 3.17.0
description = A tool to automatically upgrade syntax for newer versions.
long_description = file: README.md
long_description_content_type = text/markdown
@@ -20,7 +20,7 @@ classifiers =
packages = find:
install_requires =
tokenize-rt>=5.2.0
python_requires = >=3.8.1
python_requires = >=3.9

[options.packages.find]
exclude =
Loading