From 0a0b5d3968f7a4023d74da8c90d9378c97bac140 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 9 Oct 2023 21:54:29 +0000 Subject: [PATCH 01/39] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/pre-commit/pre-commit-hooks: v4.4.0 → v4.5.0](https://github.com/pre-commit/pre-commit-hooks/compare/v4.4.0...v4.5.0) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index a0e9d2be..7629c99c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.4.0 + rev: v4.5.0 hooks: - id: trailing-whitespace - id: end-of-file-fixer From 50018b8c6094e8897a1d3023f75883932284ba58 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 16 Oct 2023 21:12:29 +0000 Subject: [PATCH 02/39] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/pre-commit/mirrors-mypy: v1.5.1 → v1.6.0](https://github.com/pre-commit/mirrors-mypy/compare/v1.5.1...v1.6.0) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 7629c99c..6aac4222 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -36,6 +36,6 @@ repos: hooks: - id: flake8 - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.5.1 + rev: v1.6.0 hooks: - id: mypy From 3301cd38abdedc7bce794409e5c42d588f4863f4 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 23 Oct 2023 21:44:30 +0000 Subject: [PATCH 03/39] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/pre-commit/mirrors-mypy: v1.6.0 → v1.6.1](https://github.com/pre-commit/mirrors-mypy/compare/v1.6.0...v1.6.1) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 6aac4222..27771efc 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -36,6 +36,6 @@ repos: hooks: - id: flake8 - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.6.0 + rev: v1.6.1 hooks: - id: mypy From 4a32768dfed3eb69115c31b736f9b57bba6d885a Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Fri, 3 Nov 2023 18:00:57 +0200 Subject: [PATCH 04/39] README: Fix asyncio.Timeout -> asyncio.TimeoutError typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1ad0c0a0..706a1116 100644 --- a/README.md +++ b/README.md @@ -555,7 +555,7 @@ Note that `if` blocks without an `else` will not be rewritten as it could introd Availability: - `--py310-plus` for `socket.timeout` -- `--py311-plus` for `asyncio.Timeout` +- `--py311-plus` for `asyncio.TimeoutError` ```diff From 5644554993ef772948bf7288bf3eb6d81dbb66be Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 13 Nov 2023 21:54:01 +0000 Subject: [PATCH 05/39] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/pre-commit/mirrors-mypy: v1.6.1 → v1.7.0](https://github.com/pre-commit/mirrors-mypy/compare/v1.6.1...v1.7.0) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 27771efc..cfff392b 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -36,6 +36,6 @@ repos: hooks: - id: flake8 - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.6.1 + rev: v1.7.0 hooks: - id: mypy From d02a1a46ea1d0bf77751a0a467cce1668db63392 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 27 Nov 2023 21:08:41 +0000 Subject: [PATCH 06/39] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/pre-commit/mirrors-mypy: v1.7.0 → v1.7.1](https://github.com/pre-commit/mirrors-mypy/compare/v1.7.0...v1.7.1) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index cfff392b..948589cb 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -36,6 +36,6 @@ repos: hooks: - id: flake8 - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.7.0 + rev: v1.7.1 hooks: - id: mypy From e44800ddf9d44c26ebbbab0e99128cfc5f404e2c Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 25 Dec 2023 21:34:27 +0000 Subject: [PATCH 07/39] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/pre-commit/mirrors-mypy: v1.7.1 → v1.8.0](https://github.com/pre-commit/mirrors-mypy/compare/v1.7.1...v1.8.0) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 948589cb..7ec1d449 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -36,6 +36,6 @@ repos: hooks: - id: flake8 - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.7.1 + rev: v1.8.0 hooks: - id: mypy From 6bc57c6584239b56f635730348fc1b03f1030bd3 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 8 Jan 2024 21:33:54 +0000 Subject: [PATCH 08/39] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/PyCQA/flake8: 6.1.0 → 7.0.0](https://github.com/PyCQA/flake8/compare/6.1.0...7.0.0) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 7ec1d449..bda8bfe3 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -32,7 +32,7 @@ repos: hooks: - id: autopep8 - repo: https://github.com/PyCQA/flake8 - rev: 6.1.0 + rev: 7.0.0 hooks: - id: flake8 - repo: https://github.com/pre-commit/mirrors-mypy From 86609e82ab66d7acbeff9db60322c378b9ebea76 Mon Sep 17 00:00:00 2001 From: Anthony Sottile Date: Sun, 18 Feb 2024 11:49:36 -0500 Subject: [PATCH 09/39] handle constant folding with comments on multilines better --- pyupgrade/_token_helpers.py | 10 +++++++++- tests/features/exceptions_test.py | 24 ++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/pyupgrade/_token_helpers.py b/pyupgrade/_token_helpers.py index 5936ab75..e932c04f 100644 --- a/pyupgrade/_token_helpers.py +++ b/pyupgrade/_token_helpers.py @@ -369,6 +369,14 @@ def arg_str(tokens: list[Token], start: int, end: int) -> str: return tokens_to_src(tokens[start:end]).strip() +def _arg_str_no_comment(tokens: list[Token], start: int, end: int) -> str: + arg_tokens = [ + token for token in tokens[start:end] + if token.name != 'COMMENT' + ] + return tokens_to_src(arg_tokens).strip() + + def _arg_contains_newline(tokens: list[Token], start: int, end: int) -> bool: while tokens[start].name in {'NL', 'NEWLINE', UNIMPORTANT_WS}: start += 1 @@ -473,7 +481,7 @@ def replace_argument( def constant_fold_tuple(i: int, tokens: list[Token]) -> None: start = find_op(tokens, i, '(') func_args, end = parse_call_args(tokens, start) - arg_strs = [arg_str(tokens, *arg) for arg in func_args] + arg_strs = [_arg_str_no_comment(tokens, *arg) for arg in func_args] unique_args = tuple(dict.fromkeys(arg_strs)) diff --git a/tests/features/exceptions_test.py b/tests/features/exceptions_test.py index e855bf2a..335e1d4d 100644 --- a/tests/features/exceptions_test.py +++ b/tests/features/exceptions_test.py @@ -225,6 +225,30 @@ def test_fix_exceptions_version_specific_noop(s, version): id='leave unrelated error names alone', ), + pytest.param( + 'try: ...\n' + 'except (\n' + ' BaseException,\n' + ' BaseException # b\n' + '): ...\n', + + 'try: ...\n' + 'except BaseException: ...\n', + + id='dedupe with comment. see #932', + ), + pytest.param( + 'try: ...\n' + 'except (\n' + ' A, A,\n' + ' B # b\n' + '): ...\n', + + 'try: ...\n' + 'except (A, B): ...\n', + + id='dedupe other exception, one contains comment. see #932', + ), ), ) def test_fix_exceptions(s, expected): From df17dfa3911b81b4a27190b0eea5b1debc7ffa0a Mon Sep 17 00:00:00 2001 From: Anthony Sottile Date: Sun, 18 Feb 2024 11:53:36 -0500 Subject: [PATCH 10/39] v3.15.1 --- .pre-commit-config.yaml | 2 +- README.md | 2 +- setup.cfg | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index bda8bfe3..614f48fe 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -23,7 +23,7 @@ repos: hooks: - id: add-trailing-comma - repo: https://github.com/asottile/pyupgrade - rev: v3.15.0 + rev: v3.15.1 hooks: - id: pyupgrade args: [--py38-plus] diff --git a/README.md b/README.md index 706a1116..e6b49bb3 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ Sample `.pre-commit-config.yaml`: ```yaml - repo: https://github.com/asottile/pyupgrade - rev: v3.15.0 + rev: v3.15.1 hooks: - id: pyupgrade ``` diff --git a/setup.cfg b/setup.cfg index 53fbdb2d..52643332 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = pyupgrade -version = 3.15.0 +version = 3.15.1 description = A tool to automatically upgrade syntax for newer versions. long_description = file: README.md long_description_content_type = text/markdown From a3f480512cc627c8bdf3893db57ff297e3693a34 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 13 Mar 2024 01:27:43 +0000 Subject: [PATCH 11/39] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/pre-commit/mirrors-mypy: v1.8.0 → v1.9.0](https://github.com/pre-commit/mirrors-mypy/compare/v1.8.0...v1.9.0) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 614f48fe..48fecd24 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -36,6 +36,6 @@ repos: hooks: - id: flake8 - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.8.0 + rev: v1.9.0 hooks: - id: mypy From 03188f97562bcd99e7282fae0c464807bc8468d4 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 18 Mar 2024 23:07:17 +0000 Subject: [PATCH 12/39] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/hhatto/autopep8: v2.0.4 → v2.1.0](https://github.com/hhatto/autopep8/compare/v2.0.4...v2.1.0) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 48fecd24..fec21943 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -28,7 +28,7 @@ repos: - id: pyupgrade args: [--py38-plus] - repo: https://github.com/hhatto/autopep8 - rev: v2.0.4 + rev: v2.1.0 hooks: - id: autopep8 - repo: https://github.com/PyCQA/flake8 From d560e38697352bcf1f0daac458f306aebc24dcd3 Mon Sep 17 00:00:00 2001 From: Anthony Sottile Date: Sun, 24 Mar 2024 13:02:21 -0400 Subject: [PATCH 13/39] only replace to shlex.join if joined with a space --- pyupgrade/_plugins/shlex_join.py | 2 +- tests/features/shlex_join_test.py | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/pyupgrade/_plugins/shlex_join.py b/pyupgrade/_plugins/shlex_join.py index 14067560..1718ff9a 100644 --- a/pyupgrade/_plugins/shlex_join.py +++ b/pyupgrade/_plugins/shlex_join.py @@ -39,7 +39,7 @@ def visit_Call( if ( isinstance(node.func, ast.Attribute) and isinstance(node.func.value, ast.Constant) and - isinstance(node.func.value.value, str) and + node.func.value.value == ' ' and node.func.attr == 'join' and not node.keywords and len(node.args) == 1 and diff --git a/tests/features/shlex_join_test.py b/tests/features/shlex_join_test.py index fc6abe59..e3835f65 100644 --- a/tests/features/shlex_join_test.py +++ b/tests/features/shlex_join_test.py @@ -15,6 +15,12 @@ (3, 8), id='quote from-imported', ), + pytest.param( + 'import shlex\n' + '"wat".join(shlex.quote(arg) for arg in cmd)\n', + (3, 8), + id='not joined with space', + ), pytest.param( 'import shlex\n' '" ".join(shlex.quote(arg) for arg in cmd)\n', From 12af25eb252deaaecb6b259df40d01f42e716dc3 Mon Sep 17 00:00:00 2001 From: Anthony Sottile Date: Sun, 24 Mar 2024 13:07:50 -0400 Subject: [PATCH 14/39] v3.15.2 --- .pre-commit-config.yaml | 2 +- README.md | 2 +- setup.cfg | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index fec21943..02695093 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -23,7 +23,7 @@ repos: hooks: - id: add-trailing-comma - repo: https://github.com/asottile/pyupgrade - rev: v3.15.1 + rev: v3.15.2 hooks: - id: pyupgrade args: [--py38-plus] diff --git a/README.md b/README.md index e6b49bb3..047e51b7 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ Sample `.pre-commit-config.yaml`: ```yaml - repo: https://github.com/asottile/pyupgrade - rev: v3.15.1 + rev: v3.15.2 hooks: - id: pyupgrade ``` diff --git a/setup.cfg b/setup.cfg index 52643332..c68fe32a 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = pyupgrade -version = 3.15.1 +version = 3.15.2 description = A tool to automatically upgrade syntax for newer versions. long_description = file: README.md long_description_content_type = text/markdown From b7dabb0fa92c24c9347ea37b84ea154dd3a2e22e Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 8 Apr 2024 23:36:02 +0000 Subject: [PATCH 15/39] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/pre-commit/pre-commit-hooks: v4.5.0 → v4.6.0](https://github.com/pre-commit/pre-commit-hooks/compare/v4.5.0...v4.6.0) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 02695093..53ee9716 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.5.0 + rev: v4.6.0 hooks: - id: trailing-whitespace - id: end-of-file-fixer From 878f79a6b26fe65492f5594c6683864ea8168c64 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 29 Apr 2024 23:29:00 +0000 Subject: [PATCH 16/39] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/pre-commit/mirrors-mypy: v1.9.0 → v1.10.0](https://github.com/pre-commit/mirrors-mypy/compare/v1.9.0...v1.10.0) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 53ee9716..249c0444 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -36,6 +36,6 @@ repos: hooks: - id: flake8 - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.9.0 + rev: v1.10.0 hooks: - id: mypy From 49e5374ab7dfcb8c2eb86f2027e6f8e3fff13d3d Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 27 May 2024 22:54:28 +0000 Subject: [PATCH 17/39] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/hhatto/autopep8: v2.1.0 → v2.1.1](https://github.com/hhatto/autopep8/compare/v2.1.0...v2.1.1) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 249c0444..ee1ea4e6 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -28,7 +28,7 @@ repos: - id: pyupgrade args: [--py38-plus] - repo: https://github.com/hhatto/autopep8 - rev: v2.1.0 + rev: v2.1.1 hooks: - id: autopep8 - repo: https://github.com/PyCQA/flake8 From 327a5f9fd0e8994953686426365fe4b8df5b13e9 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 3 Jun 2024 23:11:10 +0000 Subject: [PATCH 18/39] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/asottile/reorder-python-imports: v3.12.0 → v3.13.0](https://github.com/asottile/reorder-python-imports/compare/v3.12.0...v3.13.0) - [github.com/hhatto/autopep8: v2.1.1 → v2.2.0](https://github.com/hhatto/autopep8/compare/v2.1.1...v2.2.0) --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index ee1ea4e6..6214b7ae 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -14,7 +14,7 @@ repos: hooks: - id: setup-cfg-fmt - repo: https://github.com/asottile/reorder-python-imports - rev: v3.12.0 + rev: v3.13.0 hooks: - id: reorder-python-imports args: [--py38-plus, --add-import, 'from __future__ import annotations'] @@ -28,7 +28,7 @@ repos: - id: pyupgrade args: [--py38-plus] - repo: https://github.com/hhatto/autopep8 - rev: v2.1.1 + rev: v2.2.0 hooks: - id: autopep8 - repo: https://github.com/PyCQA/flake8 From 7914830fcc359238c9d372c7394bd1cf5eb8c40a Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Fri, 7 Jun 2024 19:30:17 +0100 Subject: [PATCH 19/39] Regenerate typing import replacements using latest reorder-python-imports --- pyupgrade/_plugins/imports.py | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/pyupgrade/_plugins/imports.py b/pyupgrade/_plugins/imports.py index ce032f3e..9574da04 100644 --- a/pyupgrade/_plugins/imports.py +++ b/pyupgrade/_plugins/imports.py @@ -21,7 +21,7 @@ from pyupgrade._token_helpers import indented_amount # GENERATED VIA generate-imports -# Using reorder-python-imports==3.12.0 +# Using reorder-python-imports==3.13.0 REMOVALS = { (3,): { '__future__': { @@ -99,12 +99,10 @@ ('typing_extensions', 'ClassVar'): 'typing', ('typing_extensions', 'Collection'): 'typing', ('typing_extensions', 'Container'): 'typing', - ('typing_extensions', 'ContextManager'): 'typing', ('typing_extensions', 'Coroutine'): 'typing', ('typing_extensions', 'DefaultDict'): 'typing', ('typing_extensions', 'Dict'): 'typing', ('typing_extensions', 'FrozenSet'): 'typing', - ('typing_extensions', 'Generator'): 'typing', ('typing_extensions', 'Generic'): 'typing', ('typing_extensions', 'Hashable'): 'typing', ('typing_extensions', 'IO'): 'typing', @@ -138,8 +136,6 @@ }, (3, 7): { ('mypy_extensions', 'NoReturn'): 'typing', - ('typing_extensions', 'AsyncContextManager'): 'typing', - ('typing_extensions', 'AsyncGenerator'): 'typing', ('typing_extensions', 'ChainMap'): 'typing', ('typing_extensions', 'Counter'): 'typing', ('typing_extensions', 'Deque'): 'typing', @@ -150,8 +146,6 @@ ('mypy_extensions', 'TypedDict'): 'typing', ('typing_extensions', 'Final'): 'typing', ('typing_extensions', 'OrderedDict'): 'typing', - ('typing_extensions', 'SupportsIndex'): 'typing', - ('typing_extensions', 'runtime_checkable'): 'typing', }, (3, 9): { ('typing', 'AsyncGenerator'): 'collections.abc', @@ -217,15 +211,14 @@ }, (3, 12): { ('typing_extensions', 'NamedTuple'): 'typing', - ('typing_extensions', 'Protocol'): 'typing', ('typing_extensions', 'SupportsAbs'): 'typing', ('typing_extensions', 'SupportsBytes'): 'typing', ('typing_extensions', 'SupportsComplex'): 'typing', ('typing_extensions', 'SupportsFloat'): 'typing', + ('typing_extensions', 'SupportsIndex'): 'typing', ('typing_extensions', 'SupportsInt'): 'typing', ('typing_extensions', 'SupportsRound'): 'typing', ('typing_extensions', 'TypeAliasType'): 'typing', - ('typing_extensions', 'TypedDict'): 'typing', ('typing_extensions', 'Unpack'): 'typing', ('typing_extensions', 'dataclass_transform'): 'typing', ('typing_extensions', 'override'): 'typing', From 2a996f9d4107887970dec67d74063c9620a320ed Mon Sep 17 00:00:00 2001 From: Anthony Sottile Date: Sun, 30 Jul 2023 22:18:33 -0400 Subject: [PATCH 20/39] rewrite pep 646 Unpack to splat in `*args` --- pyupgrade/_plugins/typing_pep646_unpack.py | 39 +++++++++++++++++++++ tests/features/typing_pep646_unpack_test.py | 17 +++++++++ 2 files changed, 56 insertions(+) diff --git a/pyupgrade/_plugins/typing_pep646_unpack.py b/pyupgrade/_plugins/typing_pep646_unpack.py index 632814a7..4cee0d23 100644 --- a/pyupgrade/_plugins/typing_pep646_unpack.py +++ b/pyupgrade/_plugins/typing_pep646_unpack.py @@ -37,3 +37,42 @@ def visit_Subscript( if is_name_attr(node.value, state.from_imports, ('typing',), ('Unpack',)): if isinstance(parent, (ast.Subscript, ast.Index)): yield ast_to_offset(node.value), _replace_unpack_with_star + + +def _visit_func( + state: State, + node: ast.AsyncFunctionDef | ast.FunctionDef, + parent: ast.AST, +) -> Iterable[tuple[Offset, TokenFunc]]: + if state.settings.min_version < (3, 11): + return + + vararg = node.args.vararg + if ( + vararg is not None and + isinstance(vararg.annotation, ast.Subscript) and + is_name_attr( + vararg.annotation.value, + state.from_imports, + ('typing',), ('Unpack',), + ) + ): + yield ast_to_offset(vararg.annotation.value), _replace_unpack_with_star + + +@register(ast.AsyncFunctionDef) +def visit_AsyncFunctionDef( + state: State, + node: ast.AsyncFunctionDef, + parent: ast.AST, +) -> Iterable[tuple[Offset, TokenFunc]]: + yield from _visit_func(state, node, parent) + + +@register(ast.FunctionDef) +def visit_FunctionDef( + state: State, + node: ast.FunctionDef, + parent: ast.AST, +) -> Iterable[tuple[Offset, TokenFunc]]: + yield from _visit_func(state, node, parent) diff --git a/tests/features/typing_pep646_unpack_test.py b/tests/features/typing_pep646_unpack_test.py index c1d82e85..d63909ad 100644 --- a/tests/features/typing_pep646_unpack_test.py +++ b/tests/features/typing_pep646_unpack_test.py @@ -21,6 +21,14 @@ ' pass', id='Not inside a subscript', ), + pytest.param( + 'from typing import Unpack\n' + 'from typing import TypedDict\n' + 'class D(TypedDict):\n' + ' x: int\n' + 'def f(**kwargs: Unpack[D]) -> None: pass\n', + id='3.12 TypedDict for kwargs', + ), ), ) def test_fix_pep646_noop(s): @@ -53,6 +61,15 @@ def test_fix_pep646_noop(s): 'class C(Generic[*Shape]):\n' ' pass', ), + pytest.param( + 'from typing import Unpack\n' + 'def f(*args: Unpack[tuple[int, ...]]): pass\n', + + 'from typing import Unpack\n' + 'def f(*args: *tuple[int, ...]): pass\n', + + id='Unpack for *args', + ), ), ) def test_typing_unpack(s, expected): From 32151ac97cbfd7f9dcd22e49516fb32266db45b4 Mon Sep 17 00:00:00 2001 From: Anthony Sottile Date: Sat, 8 Jun 2024 14:28:08 -0400 Subject: [PATCH 21/39] v3.16.0 --- .pre-commit-config.yaml | 2 +- README.md | 2 +- setup.cfg | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 6214b7ae..23c957b9 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -23,7 +23,7 @@ repos: hooks: - id: add-trailing-comma - repo: https://github.com/asottile/pyupgrade - rev: v3.15.2 + rev: v3.16.0 hooks: - id: pyupgrade args: [--py38-plus] diff --git a/README.md b/README.md index 047e51b7..623a20b1 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ Sample `.pre-commit-config.yaml`: ```yaml - repo: https://github.com/asottile/pyupgrade - rev: v3.15.2 + rev: v3.16.0 hooks: - id: pyupgrade ``` diff --git a/setup.cfg b/setup.cfg index c68fe32a..6d2bc052 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = pyupgrade -version = 3.15.2 +version = 3.16.0 description = A tool to automatically upgrade syntax for newer versions. long_description = file: README.md long_description_content_type = text/markdown From c25513c7b2a4b7315161b7c88f1d80a038606bfa Mon Sep 17 00:00:00 2001 From: Anthony Sottile Date: Sat, 8 Jun 2024 15:32:31 -0400 Subject: [PATCH 22/39] remove support for python 3.8 --- .github/workflows/main.yml | 4 ++-- pyupgrade/_plugins/typing_classes.py | 17 +++++---------- pyupgrade/_plugins/typing_pep563.py | 11 ++-------- pyupgrade/_plugins/typing_pep604.py | 24 +++++----------------- pyupgrade/_plugins/typing_pep646_unpack.py | 2 +- setup.cfg | 2 +- 6 files changed, 16 insertions(+), 44 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 3b225b73..804dc78b 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -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 diff --git a/pyupgrade/_plugins/typing_classes.py b/pyupgrade/_plugins/typing_classes.py index 26de3b1b..b2f81f1c 100644 --- a/pyupgrade/_plugins/typing_classes.py +++ b/pyupgrade/_plugins/typing_classes.py @@ -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 diff --git a/pyupgrade/_plugins/typing_pep563.py b/pyupgrade/_plugins/typing_pep563.py index 4bf40c39..7b621209 100644 --- a/pyupgrade/_plugins/typing_pep563.py +++ b/pyupgrade/_plugins/typing_pep563.py @@ -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 diff --git a/pyupgrade/_plugins/typing_pep604.py b/pyupgrade/_plugins/typing_pep604.py index c7b53bc0..2fc829e9 100644 --- a/pyupgrade/_plugins/typing_pep604.py +++ b/pyupgrade/_plugins/typing_pep604.py @@ -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: diff --git a/pyupgrade/_plugins/typing_pep646_unpack.py b/pyupgrade/_plugins/typing_pep646_unpack.py index 4cee0d23..3493792e 100644 --- a/pyupgrade/_plugins/typing_pep646_unpack.py +++ b/pyupgrade/_plugins/typing_pep646_unpack.py @@ -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 diff --git a/setup.cfg b/setup.cfg index 6d2bc052..c25d559d 100644 --- a/setup.cfg +++ b/setup.cfg @@ -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 = From 670379323442cfa8ac40f6859ff37e5dce5ed925 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 17 Jun 2024 23:31:29 +0000 Subject: [PATCH 23/39] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/hhatto/autopep8: v2.2.0 → v2.3.0](https://github.com/hhatto/autopep8/compare/v2.2.0...v2.3.0) - [github.com/PyCQA/flake8: 7.0.0 → 7.1.0](https://github.com/PyCQA/flake8/compare/7.0.0...7.1.0) --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 23c957b9..200e6bbb 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -28,11 +28,11 @@ repos: - id: pyupgrade args: [--py38-plus] - repo: https://github.com/hhatto/autopep8 - rev: v2.2.0 + rev: v2.3.0 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 From f5ce85a1db7fb8dbdc7eca9b982c672929eb9ebe Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 24 Jun 2024 23:17:07 +0000 Subject: [PATCH 24/39] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/hhatto/autopep8: v2.3.0 → v2.3.1](https://github.com/hhatto/autopep8/compare/v2.3.0...v2.3.1) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 200e6bbb..543e4706 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -28,7 +28,7 @@ repos: - id: pyupgrade args: [--py38-plus] - repo: https://github.com/hhatto/autopep8 - rev: v2.3.0 + rev: v2.3.1 hooks: - id: autopep8 - repo: https://github.com/PyCQA/flake8 From ca56d62c65ce49b28ff833d0f63eb3f0932fda2b Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 2 Jul 2024 01:30:10 +0000 Subject: [PATCH 25/39] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/pre-commit/mirrors-mypy: v1.10.0 → v1.10.1](https://github.com/pre-commit/mirrors-mypy/compare/v1.10.0...v1.10.1) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 543e4706..0fde7e97 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -36,6 +36,6 @@ repos: hooks: - id: flake8 - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.10.0 + rev: v1.10.1 hooks: - id: mypy From d2bf4470e478bb4c8c140243d4c720784461bea5 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 22 Jul 2024 23:31:43 +0000 Subject: [PATCH 26/39] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/pre-commit/mirrors-mypy: v1.10.1 → v1.11.0](https://github.com/pre-commit/mirrors-mypy/compare/v1.10.1...v1.11.0) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 0fde7e97..bf1666c4 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -36,6 +36,6 @@ repos: hooks: - id: flake8 - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.10.1 + rev: v1.11.0 hooks: - id: mypy From 459acce3071c4e90d5cd22eeebc3c1e29da62d1c Mon Sep 17 00:00:00 2001 From: Anthony Sottile Date: Sun, 28 Jul 2024 14:13:15 -0400 Subject: [PATCH 27/39] hack around python/mypy#17566 --- pyupgrade/_data.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pyupgrade/_data.py b/pyupgrade/_data.py index ab3a4942..9bd99f74 100644 --- a/pyupgrade/_data.py +++ b/pyupgrade/_data.py @@ -53,7 +53,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]]: From cc45199d0aac43a20d00355eef2dfb04d1e9c2c3 Mon Sep 17 00:00:00 2001 From: Anthony Sottile Date: Sat, 8 Jun 2024 17:02:07 -0400 Subject: [PATCH 28/39] rewrite TypeVar defaults for Generator / AsyncGenerator --- README.md | 18 +++ pyupgrade/_data.py | 1 + .../typing_pep696_typevar_defaults.py | 72 ++++++++++ .../typing_pep696_typevar_defaults_test.py | 126 ++++++++++++++++++ 4 files changed, 217 insertions(+) create mode 100644 pyupgrade/_plugins/typing_pep696_typevar_defaults.py create mode 100644 tests/features/typing_pep696_typevar_defaults_test.py diff --git a/README.md b/README.md index 623a20b1..9e5ef11f 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/pyupgrade/_data.py b/pyupgrade/_data.py index 9bd99f74..3f1efe8a 100644 --- a/pyupgrade/_data.py +++ b/pyupgrade/_data.py @@ -40,6 +40,7 @@ class State(NamedTuple): '__future__', 'asyncio', 'collections', + 'collections.abc', 'functools', 'mmap', 'os', diff --git a/pyupgrade/_plugins/typing_pep696_typevar_defaults.py b/pyupgrade/_plugins/typing_pep696_typevar_defaults.py new file mode 100644 index 00000000..3fe7c87e --- /dev/null +++ b/pyupgrade/_plugins/typing_pep696_typevar_defaults.py @@ -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 diff --git a/tests/features/typing_pep696_typevar_defaults_test.py b/tests/features/typing_pep696_typevar_defaults_test.py new file mode 100644 index 00000000..99bb5f4a --- /dev/null +++ b/tests/features/typing_pep696_typevar_defaults_test.py @@ -0,0 +1,126 @@ +from __future__ import annotations + +import pytest + +from pyupgrade._data import Settings +from pyupgrade._main import _fix_plugins + + +@pytest.mark.parametrize( + ('s', 'version'), + ( + pytest.param( + 'from collections.abc import Generator\n' + 'def f() -> Generator[int, None, None]: yield 1\n', + (3, 12), + id='not 3.13+, no __future__.annotations', + ), + pytest.param( + 'from __future__ import annotations\n' + 'from collections.abc import Generator\n' + 'def f() -> Generator[int]: yield 1\n', + (3, 12), + id='already converted!', + ), + pytest.param( + 'from __future__ import annotations\n' + 'from collections.abc import Generator\n' + 'def f() -> Generator[int, int, None]: yield 1\n' + 'def g() -> Generator[int, int, int]: yield 1\n', + (3, 12), + id='non-None send/return type', + ), + ), +) +def test_fix_pep696_noop(s, version): + assert _fix_plugins(s, settings=Settings(min_version=version)) == s + + +def test_fix_pep696_noop_keep_runtime_typing(): + settings = Settings(min_version=(3, 12), keep_runtime_typing=True) + s = '''\ +from __future__ import annotations +from collections.abc import Generator +def f() -> Generator[int, None, None]: yield 1 +''' + assert _fix_plugins(s, settings=settings) == s + + +@pytest.mark.parametrize( + ('s', 'expected'), + ( + pytest.param( + 'from __future__ import annotations\n' + 'from typing import Generator\n' + 'def f() -> Generator[int, None, None]: yield 1\n', + + 'from __future__ import annotations\n' + 'from collections.abc import Generator\n' + 'def f() -> Generator[int]: yield 1\n', + + id='typing.Generator', + ), + pytest.param( + 'from __future__ import annotations\n' + 'from typing_extensions import Generator\n' + 'def f() -> Generator[int, None, None]: yield 1\n', + + 'from __future__ import annotations\n' + 'from typing_extensions import Generator\n' + 'def f() -> Generator[int]: yield 1\n', + + id='typing_extensions.Generator', + ), + pytest.param( + 'from __future__ import annotations\n' + 'from collections.abc import Generator\n' + 'def f() -> Generator[int, None, None]: yield 1\n', + + 'from __future__ import annotations\n' + 'from collections.abc import Generator\n' + 'def f() -> Generator[int]: yield 1\n', + + id='collections.abc.Generator', + ), + pytest.param( + 'from __future__ import annotations\n' + 'from collections.abc import AsyncGenerator\n' + 'async def f() -> AsyncGenerator[int, None]: yield 1\n', + + 'from __future__ import annotations\n' + 'from collections.abc import AsyncGenerator\n' + 'async def f() -> AsyncGenerator[int]: yield 1\n', + + id='collections.abc.AsyncGenerator', + ), + ), +) +def test_fix_pep696_with_future_annotations(s, expected): + assert _fix_plugins(s, settings=Settings(min_version=(3, 12))) == expected + + +@pytest.mark.parametrize( + ('s', 'expected'), + ( + pytest.param( + 'from collections.abc import Generator\n' + 'def f() -> Generator[int, None, None]: yield 1\n', + + 'from collections.abc import Generator\n' + 'def f() -> Generator[int]: yield 1\n', + + id='Generator', + ), + pytest.param( + 'from collections.abc import AsyncGenerator\n' + 'async def f() -> AsyncGenerator[int, None]: yield 1\n', + + 'from collections.abc import AsyncGenerator\n' + 'async def f() -> AsyncGenerator[int]: yield 1\n', + + id='AsyncGenerator', + ), + ), +) +def test_fix_pep696_with_3_13(s, expected): + assert _fix_plugins(s, settings=Settings(min_version=(3, 13))) == expected From d17f46139b2b6226e0db53af8a504e3c2a571b85 Mon Sep 17 00:00:00 2001 From: Anthony Sottile Date: Sun, 28 Jul 2024 14:36:45 -0400 Subject: [PATCH 29/39] run pyupgrade on itself --- pyupgrade/_plugins/legacy.py | 2 +- pyupgrade/_plugins/percent_format.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pyupgrade/_plugins/legacy.py b/pyupgrade/_plugins/legacy.py index c042a05c..98a6dd61 100644 --- a/pyupgrade/_plugins/legacy.py +++ b/pyupgrade/_plugins/legacy.py @@ -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 diff --git a/pyupgrade/_plugins/percent_format.py b/pyupgrade/_plugins/percent_format.py index db845e7b..0d38fede 100644 --- a/pyupgrade/_plugins/percent_format.py +++ b/pyupgrade/_plugins/percent_format.py @@ -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 From a25bb536e10881d928ee824910cbaab8f4719bd1 Mon Sep 17 00:00:00 2001 From: Anthony Sottile Date: Sun, 28 Jul 2024 14:38:58 -0400 Subject: [PATCH 30/39] v3.17.0 --- .pre-commit-config.yaml | 2 +- README.md | 2 +- setup.cfg | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index bf1666c4..e7c1878c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -23,7 +23,7 @@ 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] diff --git a/README.md b/README.md index 9e5ef11f..507e3082 100644 --- a/README.md +++ b/README.md @@ -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 ``` diff --git a/setup.cfg b/setup.cfg index c25d559d..6ad01de9 100644 --- a/setup.cfg +++ b/setup.cfg @@ -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 From d5bff73a9ccb9aa90dcd1315202940d80b61d37a Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 6 Aug 2024 00:19:59 +0000 Subject: [PATCH 31/39] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/PyCQA/flake8: 7.1.0 → 7.1.1](https://github.com/PyCQA/flake8/compare/7.1.0...7.1.1) - [github.com/pre-commit/mirrors-mypy: v1.11.0 → v1.11.1](https://github.com/pre-commit/mirrors-mypy/compare/v1.11.0...v1.11.1) --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e7c1878c..621f9e28 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -32,10 +32,10 @@ repos: hooks: - id: autopep8 - repo: https://github.com/PyCQA/flake8 - rev: 7.1.0 + rev: 7.1.1 hooks: - id: flake8 - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.11.0 + rev: v1.11.1 hooks: - id: mypy From 6b1de0b72b342815a17dafee3a01c9c2af3147cb Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 26 Aug 2024 23:50:05 +0000 Subject: [PATCH 32/39] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/pre-commit/mirrors-mypy: v1.11.1 → v1.11.2](https://github.com/pre-commit/mirrors-mypy/compare/v1.11.1...v1.11.2) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 621f9e28..bca81f8c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -36,6 +36,6 @@ repos: hooks: - id: flake8 - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.11.1 + rev: v1.11.2 hooks: - id: mypy From 7cc1c3347cbe0dd614cd8e3c98a9cd55764e04be Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 8 Oct 2024 01:45:45 +0000 Subject: [PATCH 33/39] [pre-commit.ci] pre-commit autoupdate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit updates: - [github.com/pre-commit/pre-commit-hooks: v4.6.0 → v5.0.0](https://github.com/pre-commit/pre-commit-hooks/compare/v4.6.0...v5.0.0) --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index bca81f8c..315c420a 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,6 +1,6 @@ repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.6.0 + rev: v5.0.0 hooks: - id: trailing-whitespace - id: end-of-file-fixer From 226decb89f4cb5870efb17090fb1536fbda17441 Mon Sep 17 00:00:00 2001 From: Anthony Sottile Date: Fri, 11 Oct 2024 18:03:20 -0400 Subject: [PATCH 34/39] regenerate symbols for --py313-plus --- pyupgrade/_main.py | 4 ++++ pyupgrade/_plugins/imports.py | 19 ++++++++++++++++++- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/pyupgrade/_main.py b/pyupgrade/_main.py index c8342031..4ac91810 100644 --- a/pyupgrade/_main.py +++ b/pyupgrade/_main.py @@ -378,6 +378,10 @@ def main(argv: Sequence[str] | None = None) -> int: '--py312-plus', action='store_const', dest='min_version', const=(3, 12), ) + parser.add_argument( + '--py313-plus', + action='store_const', dest='min_version', const=(3, 13), + ) args = parser.parse_args(argv) ret = 0 diff --git a/pyupgrade/_plugins/imports.py b/pyupgrade/_plugins/imports.py index 9574da04..dbd44e77 100644 --- a/pyupgrade/_plugins/imports.py +++ b/pyupgrade/_plugins/imports.py @@ -21,7 +21,7 @@ from pyupgrade._token_helpers import indented_amount # GENERATED VIA generate-imports -# Using reorder-python-imports==3.13.0 +# Using reorder-python-imports==3.14.0 REMOVALS = { (3,): { '__future__': { @@ -223,6 +223,23 @@ ('typing_extensions', 'dataclass_transform'): 'typing', ('typing_extensions', 'override'): 'typing', }, + (3, 13): { + ('typing_extensions', 'AsyncContextManager'): 'typing', + ('typing_extensions', 'AsyncGenerator'): 'typing', + ('typing_extensions', 'ContextManager'): 'typing', + ('typing_extensions', 'Generator'): 'typing', + ('typing_extensions', 'NoDefault'): 'typing', + ('typing_extensions', 'ParamSpec'): 'typing', + ('typing_extensions', 'Protocol'): 'typing', + ('typing_extensions', 'ReadOnly'): 'typing', + ('typing_extensions', 'TypeIs'): 'typing', + ('typing_extensions', 'TypeVar'): 'typing', + ('typing_extensions', 'TypeVarTuple'): 'typing', + ('typing_extensions', 'TypedDict'): 'typing', + ('typing_extensions', 'get_protocol_members'): 'typing', + ('typing_extensions', 'is_protocol'): 'typing', + ('typing_extensions', 'runtime_checkable'): 'typing', + }, } REPLACE_MODS = { 'six.moves.BaseHTTPServer': 'http.server', From 28b9414e6448be4cb61f51220f8cb88c582981a8 Mon Sep 17 00:00:00 2001 From: Anthony Sottile Date: Fri, 11 Oct 2024 18:06:52 -0400 Subject: [PATCH 35/39] v3.18.0 --- .pre-commit-config.yaml | 2 +- README.md | 2 +- setup.cfg | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 315c420a..530b40c1 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -23,7 +23,7 @@ repos: hooks: - id: add-trailing-comma - repo: https://github.com/asottile/pyupgrade - rev: v3.17.0 + rev: v3.18.0 hooks: - id: pyupgrade args: [--py38-plus] diff --git a/README.md b/README.md index 507e3082..7c8a4499 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ Sample `.pre-commit-config.yaml`: ```yaml - repo: https://github.com/asottile/pyupgrade - rev: v3.17.0 + rev: v3.18.0 hooks: - id: pyupgrade ``` diff --git a/setup.cfg b/setup.cfg index 6ad01de9..05ada332 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = pyupgrade -version = 3.17.0 +version = 3.18.0 description = A tool to automatically upgrade syntax for newer versions. long_description = file: README.md long_description_content_type = text/markdown From 9827b4d8c9f13dc3bd7c96211bd7c4fdc6e3d0e9 Mon Sep 17 00:00:00 2001 From: Anthony Sottile Date: Fri, 11 Oct 2024 20:00:42 -0400 Subject: [PATCH 36/39] py39+ --- .pre-commit-config.yaml | 4 ++-- pyupgrade/_ast_helpers.py | 2 +- pyupgrade/_data.py | 10 ++++------ pyupgrade/_main.py | 4 ++-- pyupgrade/_plugins/collections_abc.py | 2 +- pyupgrade/_plugins/constant_fold.py | 2 +- pyupgrade/_plugins/datetime_utc_alias.py | 2 +- pyupgrade/_plugins/defauldict_lambda.py | 2 +- pyupgrade/_plugins/default_encoding.py | 2 +- pyupgrade/_plugins/dict_literals.py | 2 +- pyupgrade/_plugins/exceptions.py | 2 +- pyupgrade/_plugins/format_locals.py | 2 +- pyupgrade/_plugins/fstrings.py | 2 +- pyupgrade/_plugins/identity_equality.py | 2 +- pyupgrade/_plugins/imports.py | 6 +++--- pyupgrade/_plugins/io_open.py | 2 +- pyupgrade/_plugins/legacy.py | 4 ++-- pyupgrade/_plugins/lru_cache.py | 2 +- pyupgrade/_plugins/metaclass_type.py | 2 +- pyupgrade/_plugins/mock.py | 2 +- pyupgrade/_plugins/native_literals.py | 2 +- pyupgrade/_plugins/new_style_classes.py | 2 +- pyupgrade/_plugins/open_mode.py | 2 +- pyupgrade/_plugins/percent_format.py | 13 ++++++------- pyupgrade/_plugins/set_literals.py | 2 +- pyupgrade/_plugins/shlex_join.py | 2 +- pyupgrade/_plugins/six_base_classes.py | 2 +- pyupgrade/_plugins/six_calls.py | 2 +- pyupgrade/_plugins/six_metaclasses.py | 2 +- pyupgrade/_plugins/six_remove_decorators.py | 2 +- pyupgrade/_plugins/six_simple.py | 2 +- pyupgrade/_plugins/subprocess_run.py | 2 +- pyupgrade/_plugins/type_of_primitive.py | 2 +- pyupgrade/_plugins/typing_classes.py | 2 +- pyupgrade/_plugins/typing_pep563.py | 4 ++-- pyupgrade/_plugins/typing_pep585.py | 2 +- pyupgrade/_plugins/typing_pep604.py | 2 +- pyupgrade/_plugins/typing_pep646_unpack.py | 2 +- .../_plugins/typing_pep696_typevar_defaults.py | 2 +- pyupgrade/_plugins/typing_text.py | 2 +- pyupgrade/_plugins/unittest_aliases.py | 2 +- pyupgrade/_plugins/unpack_list_comprehension.py | 2 +- pyupgrade/_plugins/versioned_branches.py | 2 +- pyupgrade/_string_helpers.py | 3 +-- pyupgrade/_token_helpers.py | 2 +- 45 files changed, 59 insertions(+), 63 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 530b40c1..b3b02345 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -17,7 +17,7 @@ repos: rev: v3.13.0 hooks: - id: reorder-python-imports - args: [--py38-plus, --add-import, 'from __future__ import annotations'] + args: [--py39-plus, --add-import, 'from __future__ import annotations'] - repo: https://github.com/asottile/add-trailing-comma rev: v3.1.0 hooks: @@ -26,7 +26,7 @@ repos: rev: v3.18.0 hooks: - id: pyupgrade - args: [--py38-plus] + args: [--py39-plus] - repo: https://github.com/hhatto/autopep8 rev: v2.3.1 hooks: diff --git a/pyupgrade/_ast_helpers.py b/pyupgrade/_ast_helpers.py index 8a79d3ab..9763d060 100644 --- a/pyupgrade/_ast_helpers.py +++ b/pyupgrade/_ast_helpers.py @@ -2,7 +2,7 @@ import ast import warnings -from typing import Container +from collections.abc import Container from tokenize_rt import Offset diff --git a/pyupgrade/_data.py b/pyupgrade/_data.py index 3f1efe8a..164d30b2 100644 --- a/pyupgrade/_data.py +++ b/pyupgrade/_data.py @@ -3,12 +3,10 @@ import ast import collections import pkgutil +from collections.abc import Iterable from typing import Callable -from typing import Iterable -from typing import List from typing import NamedTuple from typing import Protocol -from typing import Tuple from typing import TypeVar from tokenize_rt import Offset @@ -16,7 +14,7 @@ from pyupgrade import _plugins -Version = Tuple[int, ...] +Version = tuple[int, ...] class Settings(NamedTuple): @@ -33,8 +31,8 @@ class State(NamedTuple): AST_T = TypeVar('AST_T', bound=ast.AST) -TokenFunc = Callable[[int, List[Token]], None] -ASTFunc = Callable[[State, AST_T, ast.AST], Iterable[Tuple[Offset, TokenFunc]]] +TokenFunc = Callable[[int, list[Token]], None] +ASTFunc = Callable[[State, AST_T, ast.AST], Iterable[tuple[Offset, TokenFunc]]] RECORD_FROM_IMPORTS = frozenset(( '__future__', diff --git a/pyupgrade/_main.py b/pyupgrade/_main.py index 4ac91810..e52b9c66 100644 --- a/pyupgrade/_main.py +++ b/pyupgrade/_main.py @@ -5,8 +5,8 @@ import re import sys import tokenize -from typing import Match -from typing import Sequence +from collections.abc import Sequence +from re import Match from tokenize_rt import NON_CODING_TOKENS from tokenize_rt import parse_string_literal diff --git a/pyupgrade/_plugins/collections_abc.py b/pyupgrade/_plugins/collections_abc.py index f7e879e3..a4bcb10b 100644 --- a/pyupgrade/_plugins/collections_abc.py +++ b/pyupgrade/_plugins/collections_abc.py @@ -2,7 +2,7 @@ import ast import functools -from typing import Iterable +from collections.abc import Iterable from tokenize_rt import Offset diff --git a/pyupgrade/_plugins/constant_fold.py b/pyupgrade/_plugins/constant_fold.py index 07c3de54..5411b95f 100644 --- a/pyupgrade/_plugins/constant_fold.py +++ b/pyupgrade/_plugins/constant_fold.py @@ -1,7 +1,7 @@ from __future__ import annotations import ast -from typing import Iterable +from collections.abc import Iterable from tokenize_rt import Offset diff --git a/pyupgrade/_plugins/datetime_utc_alias.py b/pyupgrade/_plugins/datetime_utc_alias.py index 5280e5db..8c511533 100644 --- a/pyupgrade/_plugins/datetime_utc_alias.py +++ b/pyupgrade/_plugins/datetime_utc_alias.py @@ -2,7 +2,7 @@ import ast import functools -from typing import Iterable +from collections.abc import Iterable from tokenize_rt import Offset diff --git a/pyupgrade/_plugins/defauldict_lambda.py b/pyupgrade/_plugins/defauldict_lambda.py index 1cfa7464..1d6bb0a9 100644 --- a/pyupgrade/_plugins/defauldict_lambda.py +++ b/pyupgrade/_plugins/defauldict_lambda.py @@ -2,7 +2,7 @@ import ast import functools -from typing import Iterable +from collections.abc import Iterable from tokenize_rt import Offset from tokenize_rt import Token diff --git a/pyupgrade/_plugins/default_encoding.py b/pyupgrade/_plugins/default_encoding.py index ab24b9ca..f681510d 100644 --- a/pyupgrade/_plugins/default_encoding.py +++ b/pyupgrade/_plugins/default_encoding.py @@ -1,7 +1,7 @@ from __future__ import annotations import ast -from typing import Iterable +from collections.abc import Iterable from tokenize_rt import Offset from tokenize_rt import Token diff --git a/pyupgrade/_plugins/dict_literals.py b/pyupgrade/_plugins/dict_literals.py index c87e9e89..ca4a2860 100644 --- a/pyupgrade/_plugins/dict_literals.py +++ b/pyupgrade/_plugins/dict_literals.py @@ -2,7 +2,7 @@ import ast import functools -from typing import Iterable +from collections.abc import Iterable from tokenize_rt import Offset from tokenize_rt import Token diff --git a/pyupgrade/_plugins/exceptions.py b/pyupgrade/_plugins/exceptions.py index 37b39faa..4018ebbb 100644 --- a/pyupgrade/_plugins/exceptions.py +++ b/pyupgrade/_plugins/exceptions.py @@ -2,7 +2,7 @@ import ast import functools -from typing import Iterable +from collections.abc import Iterable from typing import NamedTuple from tokenize_rt import Offset diff --git a/pyupgrade/_plugins/format_locals.py b/pyupgrade/_plugins/format_locals.py index cc63c442..af4ae475 100644 --- a/pyupgrade/_plugins/format_locals.py +++ b/pyupgrade/_plugins/format_locals.py @@ -1,7 +1,7 @@ from __future__ import annotations import ast -from typing import Iterable +from collections.abc import Iterable from tokenize_rt import Offset from tokenize_rt import rfind_string_parts diff --git a/pyupgrade/_plugins/fstrings.py b/pyupgrade/_plugins/fstrings.py index e6480756..d9359650 100644 --- a/pyupgrade/_plugins/fstrings.py +++ b/pyupgrade/_plugins/fstrings.py @@ -1,7 +1,7 @@ from __future__ import annotations import ast -from typing import Iterable +from collections.abc import Iterable from tokenize_rt import Offset from tokenize_rt import parse_string_literal diff --git a/pyupgrade/_plugins/identity_equality.py b/pyupgrade/_plugins/identity_equality.py index 2f0d5fd4..9a23c5ec 100644 --- a/pyupgrade/_plugins/identity_equality.py +++ b/pyupgrade/_plugins/identity_equality.py @@ -2,7 +2,7 @@ import ast import functools -from typing import Iterable +from collections.abc import Iterable from tokenize_rt import Offset from tokenize_rt import Token diff --git a/pyupgrade/_plugins/imports.py b/pyupgrade/_plugins/imports.py index dbd44e77..cb7ae93b 100644 --- a/pyupgrade/_plugins/imports.py +++ b/pyupgrade/_plugins/imports.py @@ -4,8 +4,8 @@ import bisect import collections import functools -from typing import Iterable -from typing import Mapping +from collections.abc import Iterable +from collections.abc import Mapping from typing import NamedTuple from tokenize_rt import Offset @@ -298,7 +298,7 @@ # END GENERATED -@functools.lru_cache(maxsize=None) +@functools.cache def _for_version( version: tuple[int, ...], *, diff --git a/pyupgrade/_plugins/io_open.py b/pyupgrade/_plugins/io_open.py index 72fe4493..da20a3d4 100644 --- a/pyupgrade/_plugins/io_open.py +++ b/pyupgrade/_plugins/io_open.py @@ -1,7 +1,7 @@ from __future__ import annotations import ast -from typing import Iterable +from collections.abc import Iterable from tokenize_rt import Offset from tokenize_rt import Token diff --git a/pyupgrade/_plugins/legacy.py b/pyupgrade/_plugins/legacy.py index 98a6dd61..f79219ff 100644 --- a/pyupgrade/_plugins/legacy.py +++ b/pyupgrade/_plugins/legacy.py @@ -4,9 +4,9 @@ import collections import contextlib import functools +from collections.abc import Generator +from collections.abc import Iterable from typing import Any -from typing import Generator -from typing import Iterable from tokenize_rt import Offset from tokenize_rt import Token diff --git a/pyupgrade/_plugins/lru_cache.py b/pyupgrade/_plugins/lru_cache.py index 6d690ecb..cec153f0 100644 --- a/pyupgrade/_plugins/lru_cache.py +++ b/pyupgrade/_plugins/lru_cache.py @@ -2,7 +2,7 @@ import ast import functools -from typing import Iterable +from collections.abc import Iterable from tokenize_rt import Offset from tokenize_rt import Token diff --git a/pyupgrade/_plugins/metaclass_type.py b/pyupgrade/_plugins/metaclass_type.py index 5749050a..0010cf3b 100644 --- a/pyupgrade/_plugins/metaclass_type.py +++ b/pyupgrade/_plugins/metaclass_type.py @@ -1,7 +1,7 @@ from __future__ import annotations import ast -from typing import Iterable +from collections.abc import Iterable from tokenize_rt import Offset from tokenize_rt import Token diff --git a/pyupgrade/_plugins/mock.py b/pyupgrade/_plugins/mock.py index 0e1c391b..bfae7024 100644 --- a/pyupgrade/_plugins/mock.py +++ b/pyupgrade/_plugins/mock.py @@ -1,7 +1,7 @@ from __future__ import annotations import ast -from typing import Iterable +from collections.abc import Iterable from tokenize_rt import Offset from tokenize_rt import Token diff --git a/pyupgrade/_plugins/native_literals.py b/pyupgrade/_plugins/native_literals.py index ba535d64..90d0e390 100644 --- a/pyupgrade/_plugins/native_literals.py +++ b/pyupgrade/_plugins/native_literals.py @@ -2,7 +2,7 @@ import ast import functools -from typing import Iterable +from collections.abc import Iterable from tokenize_rt import Offset from tokenize_rt import Token diff --git a/pyupgrade/_plugins/new_style_classes.py b/pyupgrade/_plugins/new_style_classes.py index f0bb6f4e..6535a5a7 100644 --- a/pyupgrade/_plugins/new_style_classes.py +++ b/pyupgrade/_plugins/new_style_classes.py @@ -1,7 +1,7 @@ from __future__ import annotations import ast -from typing import Iterable +from collections.abc import Iterable from tokenize_rt import Offset diff --git a/pyupgrade/_plugins/open_mode.py b/pyupgrade/_plugins/open_mode.py index f0bfc98c..17730afb 100644 --- a/pyupgrade/_plugins/open_mode.py +++ b/pyupgrade/_plugins/open_mode.py @@ -3,7 +3,7 @@ import ast import functools import itertools -from typing import Iterable +from collections.abc import Iterable from typing import NamedTuple from tokenize_rt import Offset diff --git a/pyupgrade/_plugins/percent_format.py b/pyupgrade/_plugins/percent_format.py index 0d38fede..effc4ddd 100644 --- a/pyupgrade/_plugins/percent_format.py +++ b/pyupgrade/_plugins/percent_format.py @@ -3,12 +3,11 @@ import ast import functools import re -from typing import Generator -from typing import Iterable -from typing import Match +from collections.abc import Generator +from collections.abc import Iterable +from re import Match +from re import Pattern from typing import Optional -from typing import Pattern -from typing import Tuple from tokenize_rt import Offset from tokenize_rt import Token @@ -23,14 +22,14 @@ from pyupgrade._token_helpers import remove_brace from pyupgrade._token_helpers import victims -PercentFormatPart = Tuple[ +PercentFormatPart = tuple[ Optional[str], Optional[str], Optional[str], Optional[str], str, ] -PercentFormat = Tuple[str, Optional[PercentFormatPart]] +PercentFormat = tuple[str, Optional[PercentFormatPart]] MAPPING_KEY_RE = re.compile(r'\(([^()]*)\)') CONVERSION_FLAG_RE = re.compile('[#0+ -]*') diff --git a/pyupgrade/_plugins/set_literals.py b/pyupgrade/_plugins/set_literals.py index 631dc88a..e3369ae4 100644 --- a/pyupgrade/_plugins/set_literals.py +++ b/pyupgrade/_plugins/set_literals.py @@ -2,7 +2,7 @@ import ast import functools -from typing import Iterable +from collections.abc import Iterable from tokenize_rt import Offset from tokenize_rt import Token diff --git a/pyupgrade/_plugins/shlex_join.py b/pyupgrade/_plugins/shlex_join.py index 1718ff9a..ddd5824a 100644 --- a/pyupgrade/_plugins/shlex_join.py +++ b/pyupgrade/_plugins/shlex_join.py @@ -2,7 +2,7 @@ import ast import functools -from typing import Iterable +from collections.abc import Iterable from tokenize_rt import NON_CODING_TOKENS from tokenize_rt import Offset diff --git a/pyupgrade/_plugins/six_base_classes.py b/pyupgrade/_plugins/six_base_classes.py index 77008bce..789fca6f 100644 --- a/pyupgrade/_plugins/six_base_classes.py +++ b/pyupgrade/_plugins/six_base_classes.py @@ -1,7 +1,7 @@ from __future__ import annotations import ast -from typing import Iterable +from collections.abc import Iterable from tokenize_rt import Offset diff --git a/pyupgrade/_plugins/six_calls.py b/pyupgrade/_plugins/six_calls.py index 2434a66f..7bddce84 100644 --- a/pyupgrade/_plugins/six_calls.py +++ b/pyupgrade/_plugins/six_calls.py @@ -2,7 +2,7 @@ import ast import functools -from typing import Iterable +from collections.abc import Iterable from tokenize_rt import Offset from tokenize_rt import Token diff --git a/pyupgrade/_plugins/six_metaclasses.py b/pyupgrade/_plugins/six_metaclasses.py index 9d9e6eb9..b2d47311 100644 --- a/pyupgrade/_plugins/six_metaclasses.py +++ b/pyupgrade/_plugins/six_metaclasses.py @@ -1,7 +1,7 @@ from __future__ import annotations import ast -from typing import Iterable +from collections.abc import Iterable from tokenize_rt import NON_CODING_TOKENS from tokenize_rt import Offset diff --git a/pyupgrade/_plugins/six_remove_decorators.py b/pyupgrade/_plugins/six_remove_decorators.py index 6998ac70..8d7f6178 100644 --- a/pyupgrade/_plugins/six_remove_decorators.py +++ b/pyupgrade/_plugins/six_remove_decorators.py @@ -1,7 +1,7 @@ from __future__ import annotations import ast -from typing import Iterable +from collections.abc import Iterable from tokenize_rt import Offset diff --git a/pyupgrade/_plugins/six_simple.py b/pyupgrade/_plugins/six_simple.py index d490d9e9..cbdf6897 100644 --- a/pyupgrade/_plugins/six_simple.py +++ b/pyupgrade/_plugins/six_simple.py @@ -2,7 +2,7 @@ import ast import functools -from typing import Iterable +from collections.abc import Iterable from tokenize_rt import Offset diff --git a/pyupgrade/_plugins/subprocess_run.py b/pyupgrade/_plugins/subprocess_run.py index 0452e48d..a4534c7d 100644 --- a/pyupgrade/_plugins/subprocess_run.py +++ b/pyupgrade/_plugins/subprocess_run.py @@ -2,7 +2,7 @@ import ast import functools -from typing import Iterable +from collections.abc import Iterable from tokenize_rt import Offset from tokenize_rt import Token diff --git a/pyupgrade/_plugins/type_of_primitive.py b/pyupgrade/_plugins/type_of_primitive.py index 3dd834b3..b32a3146 100644 --- a/pyupgrade/_plugins/type_of_primitive.py +++ b/pyupgrade/_plugins/type_of_primitive.py @@ -2,7 +2,7 @@ import ast import functools -from typing import Iterable +from collections.abc import Iterable from tokenize_rt import Offset from tokenize_rt import Token diff --git a/pyupgrade/_plugins/typing_classes.py b/pyupgrade/_plugins/typing_classes.py index b2f81f1c..92b7c925 100644 --- a/pyupgrade/_plugins/typing_classes.py +++ b/pyupgrade/_plugins/typing_classes.py @@ -2,7 +2,7 @@ import ast import functools -from typing import Iterable +from collections.abc import Iterable from tokenize_rt import Offset from tokenize_rt import Token diff --git a/pyupgrade/_plugins/typing_pep563.py b/pyupgrade/_plugins/typing_pep563.py index 7b621209..a4fd368c 100644 --- a/pyupgrade/_plugins/typing_pep563.py +++ b/pyupgrade/_plugins/typing_pep563.py @@ -3,8 +3,8 @@ import ast import functools import sys -from typing import Iterable -from typing import Sequence +from collections.abc import Iterable +from collections.abc import Sequence from tokenize_rt import Offset diff --git a/pyupgrade/_plugins/typing_pep585.py b/pyupgrade/_plugins/typing_pep585.py index e036c3f7..77dbd7e3 100644 --- a/pyupgrade/_plugins/typing_pep585.py +++ b/pyupgrade/_plugins/typing_pep585.py @@ -2,7 +2,7 @@ import ast import functools -from typing import Iterable +from collections.abc import Iterable from tokenize_rt import Offset diff --git a/pyupgrade/_plugins/typing_pep604.py b/pyupgrade/_plugins/typing_pep604.py index 2fc829e9..9b4039f2 100644 --- a/pyupgrade/_plugins/typing_pep604.py +++ b/pyupgrade/_plugins/typing_pep604.py @@ -3,7 +3,7 @@ import ast import functools import sys -from typing import Iterable +from collections.abc import Iterable from tokenize_rt import NON_CODING_TOKENS from tokenize_rt import Offset diff --git a/pyupgrade/_plugins/typing_pep646_unpack.py b/pyupgrade/_plugins/typing_pep646_unpack.py index 3493792e..9569d3c2 100644 --- a/pyupgrade/_plugins/typing_pep646_unpack.py +++ b/pyupgrade/_plugins/typing_pep646_unpack.py @@ -1,7 +1,7 @@ from __future__ import annotations import ast -from typing import Iterable +from collections.abc import Iterable from tokenize_rt import Offset from tokenize_rt import Token diff --git a/pyupgrade/_plugins/typing_pep696_typevar_defaults.py b/pyupgrade/_plugins/typing_pep696_typevar_defaults.py index 3fe7c87e..97f00f2e 100644 --- a/pyupgrade/_plugins/typing_pep696_typevar_defaults.py +++ b/pyupgrade/_plugins/typing_pep696_typevar_defaults.py @@ -1,7 +1,7 @@ from __future__ import annotations import ast -from typing import Iterable +from collections.abc import Iterable from tokenize_rt import Offset from tokenize_rt import Token diff --git a/pyupgrade/_plugins/typing_text.py b/pyupgrade/_plugins/typing_text.py index 031504b4..4e06dd36 100644 --- a/pyupgrade/_plugins/typing_text.py +++ b/pyupgrade/_plugins/typing_text.py @@ -2,7 +2,7 @@ import ast import functools -from typing import Iterable +from collections.abc import Iterable from tokenize_rt import Offset diff --git a/pyupgrade/_plugins/unittest_aliases.py b/pyupgrade/_plugins/unittest_aliases.py index 4b1a745d..9afe9552 100644 --- a/pyupgrade/_plugins/unittest_aliases.py +++ b/pyupgrade/_plugins/unittest_aliases.py @@ -2,7 +2,7 @@ import ast import functools -from typing import Iterable +from collections.abc import Iterable from tokenize_rt import Offset diff --git a/pyupgrade/_plugins/unpack_list_comprehension.py b/pyupgrade/_plugins/unpack_list_comprehension.py index ce072451..546a3dee 100644 --- a/pyupgrade/_plugins/unpack_list_comprehension.py +++ b/pyupgrade/_plugins/unpack_list_comprehension.py @@ -1,7 +1,7 @@ from __future__ import annotations import ast -from typing import Iterable +from collections.abc import Iterable from tokenize_rt import Offset from tokenize_rt import Token diff --git a/pyupgrade/_plugins/versioned_branches.py b/pyupgrade/_plugins/versioned_branches.py index 05c776ac..1b6d3614 100644 --- a/pyupgrade/_plugins/versioned_branches.py +++ b/pyupgrade/_plugins/versioned_branches.py @@ -1,8 +1,8 @@ from __future__ import annotations import ast +from collections.abc import Iterable from typing import cast -from typing import Iterable from tokenize_rt import Offset from tokenize_rt import Token diff --git a/pyupgrade/_string_helpers.py b/pyupgrade/_string_helpers.py index 1c15655d..edea309c 100644 --- a/pyupgrade/_string_helpers.py +++ b/pyupgrade/_string_helpers.py @@ -4,11 +4,10 @@ import re import string from typing import Optional -from typing import Tuple NAMED_UNICODE_RE = re.compile(r'(? Date: Mon, 21 Oct 2024 19:06:58 -0400 Subject: [PATCH 37/39] prevent dict/set rewrite directly in f-string placeholder this can change the syntax by doubling the curly braces --- pyupgrade/_plugins/dict_literals.py | 1 + pyupgrade/_plugins/set_literals.py | 1 + tests/features/dict_literals_test.py | 4 ++++ tests/features/set_literals_test.py | 8 ++++++++ 4 files changed, 14 insertions(+) diff --git a/pyupgrade/_plugins/dict_literals.py b/pyupgrade/_plugins/dict_literals.py index ca4a2860..1577dc8f 100644 --- a/pyupgrade/_plugins/dict_literals.py +++ b/pyupgrade/_plugins/dict_literals.py @@ -53,6 +53,7 @@ def visit_Call( parent: ast.AST, ) -> Iterable[tuple[Offset, TokenFunc]]: if ( + not isinstance(parent, ast.FormattedValue) and isinstance(node.func, ast.Name) and node.func.id == 'dict' and len(node.args) == 1 and diff --git a/pyupgrade/_plugins/set_literals.py b/pyupgrade/_plugins/set_literals.py index e3369ae4..0b3b21fa 100644 --- a/pyupgrade/_plugins/set_literals.py +++ b/pyupgrade/_plugins/set_literals.py @@ -50,6 +50,7 @@ def visit_Call( parent: ast.AST, ) -> Iterable[tuple[Offset, TokenFunc]]: if ( + not isinstance(parent, ast.FormattedValue) and isinstance(node.func, ast.Name) and node.func.id == 'set' and len(node.args) == 1 and diff --git a/tests/features/dict_literals_test.py b/tests/features/dict_literals_test.py index e1b33bf1..880a01be 100644 --- a/tests/features/dict_literals_test.py +++ b/tests/features/dict_literals_test.py @@ -19,6 +19,10 @@ # Don't rewrite kwargd dicts 'dict(((a, b) for a, b in y), x=1)', 'dict(((a, b) for a, b in y), **kwargs)', + pytest.param( + 'f"{dict((a, b) for a, b in y)}"', + id='directly inside f-string placeholder', + ), ), ) def test_fix_dict_noop(s): diff --git a/tests/features/set_literals_test.py b/tests/features/set_literals_test.py index 15251812..64827ba9 100644 --- a/tests/features/set_literals_test.py +++ b/tests/features/set_literals_test.py @@ -14,6 +14,14 @@ # Don't touch weird looking function calls -- use autopep8 or such # first 'set ((1, 2))', + pytest.param( + 'f"{set((1, 2))}"', + id='set directly inside f-string placeholder', + ), + pytest.param( + 'f"{set(x for x in y)}"', + id='set comp directly inside f-string placeholder', + ), ), ) def test_fix_sets_noop(s): From aa12347a1997d2acdf7a1973af7b5dbce54b9bd0 Mon Sep 17 00:00:00 2001 From: Anthony Sottile Date: Mon, 21 Oct 2024 20:19:17 -0400 Subject: [PATCH 38/39] fix named escapes in FSTRING_MIDDLE --- pyupgrade/_string_helpers.py | 14 ++------------ setup.cfg | 2 +- tests/main_test.py | 2 ++ 3 files changed, 5 insertions(+), 13 deletions(-) diff --git a/pyupgrade/_string_helpers.py b/pyupgrade/_string_helpers.py index edea309c..7855e970 100644 --- a/pyupgrade/_string_helpers.py +++ b/pyupgrade/_string_helpers.py @@ -1,11 +1,11 @@ from __future__ import annotations import codecs -import re import string from typing import Optional -NAMED_UNICODE_RE = re.compile(r'(? str: return ''.join(_convert_tup(tup) for tup in parsed) -def curly_escape(s: str) -> str: - parts = NAMED_UNICODE_RE.split(s) - return ''.join( - part.replace('{', '{{').replace('}', '}}') - if not NAMED_UNICODE_RE.fullmatch(part) - else part - for part in parts - ) - - def is_codec(encoding: str, name: str) -> bool: try: return codecs.lookup(encoding).name == name diff --git a/setup.cfg b/setup.cfg index 05ada332..320b3778 100644 --- a/setup.cfg +++ b/setup.cfg @@ -19,7 +19,7 @@ classifiers = [options] packages = find: install_requires = - tokenize-rt>=5.2.0 + tokenize-rt>=6.1.0 python_requires = >=3.9 [options.packages.find] diff --git a/tests/main_test.py b/tests/main_test.py index a254573a..bf595db3 100644 --- a/tests/main_test.py +++ b/tests/main_test.py @@ -25,6 +25,8 @@ def test_main_noop(tmpdir, capsys): x=version_info def f(): global x, y + +f'hello snowman: \\N{SNOWMAN}' ''' f = tmpdir.join('f.py') f.write(s) From 19364aa1b2ac289ce75e6bbe93994f7b4b9425f6 Mon Sep 17 00:00:00 2001 From: Anthony Sottile Date: Mon, 21 Oct 2024 20:22:29 -0400 Subject: [PATCH 39/39] v3.19.0 --- .pre-commit-config.yaml | 2 +- README.md | 2 +- setup.cfg | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index b3b02345..49c1fae6 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -23,7 +23,7 @@ repos: hooks: - id: add-trailing-comma - repo: https://github.com/asottile/pyupgrade - rev: v3.18.0 + rev: v3.19.0 hooks: - id: pyupgrade args: [--py39-plus] diff --git a/README.md b/README.md index 7c8a4499..35f36f89 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ Sample `.pre-commit-config.yaml`: ```yaml - repo: https://github.com/asottile/pyupgrade - rev: v3.18.0 + rev: v3.19.0 hooks: - id: pyupgrade ``` diff --git a/setup.cfg b/setup.cfg index 320b3778..c496330b 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = pyupgrade -version = 3.18.0 +version = 3.19.0 description = A tool to automatically upgrade syntax for newer versions. long_description = file: README.md long_description_content_type = text/markdown