8000 updates for python 3.14 (#842) · PyCQA/pyflakes@e5d4ba1 · GitHub
[go: up one dir, main page]

8000 Skip to content

Commit e5d4ba1

Browse files
authored
updates for python 3.14 (#842)
1 parent 78ee531 commit e5d4ba1

File tree

6 files changed

+67
-7
lines changed

6 files changed

+67
-7
lines changed

.github/workflows/test.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ jobs:
1212
strategy:
1313
fail-fast: false
1414
matrix:
15-
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13", "pypy-3.9"]
15+
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14-dev", "pypy-3.9"]
1616
os: [ubuntu-latest]
1717
# Include minimum py3 + maximum py3 + pypy3 on Windows
1818
include:

pyflakes/checker.py

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1216,7 +1216,10 @@ def _enter_annotation(self, ann_type=AnnotationState.BARE):
12161216
def _in_postponed_annotation(self):
12171217
return (
12181218
self._in_annotation == AnnotationState.STRING or
1219-
self.annotationsFutureEnabled
1219+
(
1220+
self._in_annotation == AnnotationState.BARE and
1221+
(self.annotationsFutureEnabled or sys.version_info >= (3, 14))
1222+
)
12201223
)
12211224

12221225
def handleChildren(self, tree, omit=None):
@@ -1350,7 +1353,7 @@ def handleAnnotation(self, annotation, node):
13501353
annotation.col_offset,
13511354
messages.ForwardAnnotationSyntaxError,
13521355
))
1353-
elif self.annotationsFutureEnabled:
1356+
elif self.annotationsFutureEnabled or sys.version_info >= (3, 14):
13541357
self.handle_annotation_always_deferred(annotation, node)
13551358
else:
13561359
self.handleNode(annotation, node)
@@ -1776,6 +1779,20 @@ def JOINEDSTR(self, node):
17761779
finally:
17771780
self._in_fstring = orig
17781781

1782+
def TEMPLATESTR(self, node):
1783+
if not any(isinstance(x, ast.Interpolation) for x in node.values):
1784+
self.report(messages.TStringMissingPlaceholders, node)
1785+
1786+
# similar to f-strings, conversion / etc. flags are parsed as f-strings
1787+
# without placeholders
1788+
self._in_fstring, orig = True, self._in_fstring
1789+
try:
1790+
self.handleChildren(node)
1791+
finally:
1792+
self._in_fstring = orig
1793+
1794+
INTERPOLATION = handleChildren
1795+
17791796
def DICT(self, node):
17801797
# Complain if there are duplicate keys with different values
17811798
# If they have the same value it's not going to cause potentially

pyflakes/messages.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,10 @@ class FStringMissingPlaceholders(Message):
266266
message = 'f-string is missing placeholders'
267267

268268

269+
class TStringMissingPlaceholders(Message):
270+
message = 't-string is missing placeholders'
271+
272+
269273
class StringDotFormatExtraPositionalArguments(Message):
270274
message = "'...'.format(...) has unused arguments at position(s): %s"
271275

pyflakes/test/test_imports.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from sys import version_info
2+
13
from pyflakes import messages as m
24
from pyflakes.checker import (
35
FutureImportation,
@@ -6,7 +8,7 @@
68
StarImportation,
79
SubmoduleImportation,
810
)
9-
from pyflakes.test.harness import TestCase, skip
11+
from pyflakes.test.harness import TestCase, skip, skipIf
1012

1113

1214
class TestImportationObject(TestCase):
@@ -990,12 +992,14 @@ def test_futureImportUsed(self):
990992
assert print_function is not division
991993
''')
992994

995+
@skipIf(version_info >= (3, 14), 'in 3.14+ this is a SyntaxError')
993996
def test_futureImportUndefined(self):
994997
"""Importing undefined names from __future__ fails."""
995998
self.flakes('''
996999
from __future__ import print_statement
9971000
''', m.FutureFeatureNotDefined)
9981001

1002+
@skipIf(version_info >= (3, 14), 'in 3.14+ this is a SyntaxError')
9991003
def test_futureImportStar(self):
10001004
"""Importing '*' from __future__ fails."""
10011005
self.flakes('''

pyflakes/test/test_other.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1766,6 +1766,13 @@ def test_f_string(self):
17661766
print(f'\x7b4*baz\N{RIGHT CURLY BRACKET}')
17671767
''')
17681768

1769+
@skipIf(version_info < (3, 14), 'new in Python 3.14')
1770+
def test_t_string(self):
1771+
self.flakes('''
1772+
baz = 0
1773+
tmpl = t'hello {baz}'
1774+
''')
1775+
17691776
def test_assign_expr(self):
17701777
"""Test PEP 572 assignment expressions are treated as usage / write."""
17711778
self.flakes('''
@@ -1837,6 +1844,15 @@ def test_f_string_without_placeholders(self):
18371844
print(f'{x:>2} {y:>2}')
18381845
''')
18391846

1847+
@skipIf(version_info < (3, 14), 'new in Python 3.14')
1848+
def test_t_string_missing_placeholders(self):
1849+
self.flakes("t'foo'", m.TStringMissingPlaceholders)
1850+
# make sure this does not trigger the f-string placeholder error
1851+
self.flakes('''
1852+
x = y = 5
1853+
tmpl = t'{x:0{y}}'
1854+
''')
1855+
18401856
def test_invalid_dot_format_calls(self):
18411857
self.flakes('''
18421858
'{'.format(1)

pyflakes/test/test_type_annotations.py

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,9 @@ def bar():
152152
""", m.RedefinedWhileUnused)
153153

154154
def test_variable_annotations(self):
155+
def undefined_names_before_py314(*, n: int):
156+
return (m.UndefinedName,) * n if version_info < (3, 14) else ()
157+
155158
self.flakes('''
156159
name: str
157160
age: int
@@ -264,23 +267,24 @@ def f(bar: str): pass
264267
self.flakes('''
265268
def f(a: A) -> A: pass
266269
class A: pass
267-
''', m.UndefinedName, m.UndefinedName)
270+
''', *undefined_names_before_py314(n=2))
271+
268272
self.flakes('''
269273
def f(a: 'A') -> 'A': return a
270274
class A: pass
271275
''')
272276
self.flakes('''
273277
a: A
274278
class A: pass
275-
''', m.UndefinedName)
279+
''', *undefined_names_before_py314(n=1))
276280
self.flakes('''
277281
a: 'A'
278282
class A: pass
279283
''')
280284
self.flakes('''
281285
T: object
282286
def f(t: T): pass
283-
''', m.UndefinedName)
287+
''', *undefined_names_before_py314(n=1))
284288
self.flakes('''
285289
T: object
286290
def g(t: 'T'): pass
@@ -410,6 +414,21 @@ def f(t: T): pass
410414
def g(t: 'T'): pass
411415
''')
412416

417+
def test_annotations_do_not_define_names_with_future_annotations(self):
418+
self.flakes('''
419+
from __future__ import annotations
420+
def f():
421+
x: str
422+
print(x)
423+
''', m.UndefinedName)
424+
425+
@skipIf(version_info < (3, 14), 'new in Python 3.14')
426+
def test_postponed_annotations_py314(self):
427+
self.flakes('''
428+
def f(x: C) -> None: pass
429+
class C: pass
430+
''')
431+
413432
def test_type_annotation_clobbers_all(self):
414433
self.flakes('''\
415434
from typing import TYPE_CHECKING, List

0 commit comments

Comments
 (0)
0