Skip to content

Commit 1268511

Browse files
authored
Merge pull request #745 from tusharsadhwani/unpack
Add PEP646 Unpack plugin
2 parents b1f3615 + 848751f commit 1268511

2 files changed

Lines changed: 99 additions & 0 deletions

File tree

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
from __future__ import annotations
2+
3+
import ast
4+
from typing import Iterable
5+
6+
from tokenize_rt import Offset
7+
from tokenize_rt import Token
8+
9+
from pyupgrade._ast_helpers import ast_to_offset
10+
from pyupgrade._ast_helpers import is_name_attr
11+
from pyupgrade._data import register
12+
from pyupgrade._data import State
13+
from pyupgrade._data import TokenFunc
14+
from pyupgrade._token_helpers import find_closing_bracket
15+
from pyupgrade._token_helpers import find_token
16+
from pyupgrade._token_helpers import remove_brace
17+
18+
19+
def _replace_unpack_with_star(i: int, tokens: list[Token]) -> None:
20+
start = find_token(tokens, i, '[')
21+
end = find_closing_bracket(tokens, start)
22+
23+
remove_brace(tokens, end)
24+
# replace `Unpack` with `*`
25+
tokens[i:start + 1] = [tokens[i]._replace(name='OP', src='*')]
26+
27+
28+
@register(ast.Subscript)
29+
def visit_Subscript(
30+
state: State,
31+
node: ast.Subscript,
32+
parent: ast.AST,
33+
) -> Iterable[tuple[Offset, TokenFunc]]:
34+
if state.settings.min_version < (3, 11):
35+
return
36+
37+
if is_name_attr(node.value, state.from_imports, ('typing',), ('Unpack',)):
38+
if isinstance(parent, (ast.Subscript, ast.Index)):
39+
yield ast_to_offset(node.value), _replace_unpack_with_star
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
from __future__ import annotations
2+
3+
import pytest
4+
5+
from pyupgrade._data import Settings
6+
from pyupgrade._main import _fix_plugins
7+
8+
9+
@pytest.mark.parametrize(
10+
('s',),
11+
(
12+
pytest.param(
13+
'from typing import Unpack\n'
14+
'foo(Unpack())',
15+
id='Not a subscript',
16+
),
17+
pytest.param(
18+
'from typing import TypeVarTuple, Unpack\n'
19+
'Shape = TypeVarTuple("Shape")\n'
20+
'class Foo(Unpack[Shape]):\n'
21+
' pass',
22+
id='Not inside a subscript',
23+
),
24+
),
25+
)
26+
def test_fix_pep646_noop(s):
27+
assert _fix_plugins(s, settings=Settings(min_version=(3, 11))) == s
28+
assert _fix_plugins(s, settings=Settings(min_version=(3, 10))) == s
29+
30+
31+
@pytest.mark.parametrize(
32+
('s', 'expected'),
33+
(
34+
(
35+
'from typing import Generic, TypeVarTuple, Unpack\n'
36+
"Shape = TypeVarTuple('Shape')\n"
37+
'class C(Generic[Unpack[Shape]]):\n'
38+
' pass',
39+
40+
'from typing import Generic, TypeVarTuple, Unpack\n'
41+
"Shape = TypeVarTuple('Shape')\n"
42+
'class C(Generic[*Shape]):\n'
43+
' pass',
44+
),
45+
(
46+
'from typing import Generic, TypeVarTuple, Unpack\n'
47+
"Shape = TypeVarTuple('Shape')\n"
48+
'class C(Generic[Unpack [Shape]]):\n'
49+
' pass',
50+
51+
'from typing import Generic, TypeVarTuple, Unpack\n'
52+
"Shape = TypeVarTuple('Shape')\n"
53+
'class C(Generic[*Shape]):\n'
54+
' pass',
55+
),
56+
),
57+
)
58+
def test_typing_unpack(s, expected):
59+
assert _fix_plugins(s, settings=Settings(min_version=(3, 11))) == expected
60+
assert _fix_plugins(s, settings=Settings(min_version=(3, 10))) == s

0 commit comments

Comments
 (0)