Skip to content

Commit 24ce381

Browse files
authoredMar 18, 2025
Merge pull request #1278 from PyCQA/pep696
add support for PEP 696 generic defaults
2 parents b852994 + 30627b3 commit 24ce381

File tree

3 files changed

+30
-11
lines changed

3 files changed

+30
-11
lines changed
 

‎pycodestyle.py

+19-9
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@
135135
LAMBDA_REGEX = re.compile(r'\blambda\b')
136136
HUNK_REGEX = re.compile(r'^@@ -\d+(?:,\d+)? \+(\d+)(?:,(\d+))? @@.*$')
137137
STARTSWITH_DEF_REGEX = re.compile(r'^(async\s+def|def)\b')
138+
STARTSWITH_GENERIC_REGEX = re.compile(r'^(async\s+def|def|class|type)\s+\w+\[')
138139
STARTSWITH_TOP_LEVEL_REGEX = re.compile(r'^(async\s+def\s+|def\s+|class\s+|@)')
139140
STARTSWITH_INDENT_STATEMENT_REGEX = re.compile(
140141
r'^\s*({})\b'.format('|'.join(s.replace(' ', r'\s+') for s in (
@@ -1019,12 +1020,13 @@ def whitespace_around_named_parameter_equals(logical_line, tokens):
10191020
E251: return magic(r = real, i = imag)
10201021
E252: def complex(real, image: float=0.0):
10211022
"""
1022-
parens = 0
1023+
paren_stack = []
10231024
no_space = False
10241025
require_space = False
10251026
prev_end = None
10261027
annotated_func_arg = False
10271028
in_def = bool(STARTSWITH_DEF_REGEX.match(logical_line))
1029+
in_generic = bool(STARTSWITH_GENERIC_REGEX.match(logical_line))
10281030

10291031
message = "E251 unexpected spaces around keyword / parameter equals"
10301032
missing_message = "E252 missing whitespace around parameter equals"
@@ -1042,23 +1044,31 @@ def whitespace_around_named_parameter_equals(logical_line, tokens):
10421044
yield (prev_end, missing_message)
10431045
if token_type == tokenize.OP:
10441046
if text in '([':
1045-
parens += 1
1046-
elif text in ')]':
1047-
parens -= 1
1048-
elif in_def and text == ':' and parens == 1:
1047+
paren_stack.append(text)
1048+
elif text in ')]' and paren_stack:
1049+
paren_stack.pop()
1050+
elif (
1051+
text == ':' and (
1052+
# def f(arg: tp = default): ...
1053+
(in_def and paren_stack == ['(']) or
1054+
# def f[T: tp = default](): ...
1055+
# class C[T: tp = default](): ...
1056+
(in_generic and paren_stack == ['['])
1057+
)
1058+
):
10491059
annotated_func_arg = True
1050-
elif parens == 1 and text == ',':
1060+
elif len(paren_stack) == 1 and text == ',':
10511061
annotated_func_arg = False
1052-
elif parens and text == '=':
1053-
if annotated_func_arg and parens == 1:
1062+
elif paren_stack and text == '=':
1063+
if annotated_func_arg and len(paren_stack) == 1:
10541064
require_space = True
10551065
if start == prev_end:
10561066
yield (prev_end, missing_message)
10571067
else:
10581068
no_space = True
10591069
if start != prev_end:
10601070
yield (prev_end, message)
1061-
if not parens:
1071+
if not paren_stack:
10621072
annotated_func_arg = False
10631073

10641074
prev_end = end

‎testing/data/python313.py

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
type Alias[T: (int, str) = str] = list[T]
2+
3+
4+
class C[T: (int, str) = str]:
5+
pass
6+
7+
8+
def f[T: (int, str) = str](t: T) -> T:
9+
pass

‎tests/test_E901.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ def lasting(self, duration=300):
2323
'''
2424
errors = errors_from_src(src)
2525
if sys.version_info < (3, 12): # pragma: <3.12 cover
26-
expected = ['E122:4:1', 'E251:5:13', 'E251:5:15']
26+
expected = ['E122:4:1']
2727
else: # pragma: >=3.12 cover
28-
expected = ['E122:4:1', 'E251:5:13', 'E251:5:15', 'E901:5:1'] # noqa: E501
28+
expected = ['E122:4:1', 'E901:5:1'] # noqa: E501
2929
self.assertEqual(errors, expected)

0 commit comments

Comments
 (0)
Failed to load comments.