[inductor] Fix set_linter's handling of f-strings for Python 3.12 and up (fix #159056) (#159252)

Pull Request resolved: https://github.com/pytorch/pytorch/pull/159252
Approved by: https://github.com/Skylion007
This commit is contained in:
Tom Ritchford
2025-07-30 09:32:30 +00:00
committed by PyTorch MergeBot
parent 838924436e
commit 78b9dea754
6 changed files with 103 additions and 93 deletions

View File

@ -16,9 +16,10 @@ def bracket_pairs(tokens: Sequence[TokenInfo]) -> dict[int, int]:
"""Returns a dictionary mapping opening to closing brackets""" """Returns a dictionary mapping opening to closing brackets"""
braces: dict[int, int] = {} braces: dict[int, int] = {}
stack: list[int] = [] stack: list[int] = []
in_fstring = False
for i, t in enumerate(tokens): for i, t in enumerate(tokens):
if t.type == token.OP: if t.type == token.OP and not in_fstring:
if t.string in BRACKETS: if t.string in BRACKETS:
stack.append(i) stack.append(i)
elif inv := BRACKETS_INV.get(t.string): elif inv := BRACKETS_INV.get(t.string):
@ -34,9 +35,11 @@ def bracket_pairs(tokens: Sequence[TokenInfo]) -> dict[int, int]:
raise ParseError(t, f"Mismatched braces '{b}' at {begin}") raise ParseError(t, f"Mismatched braces '{b}' at {begin}")
elif t.type == FSTRING_START: elif t.type == FSTRING_START:
stack.append(FSTRING_START) stack.append(FSTRING_START)
in_fstring = True
elif t.type == FSTRING_END: elif t.type == FSTRING_END:
if stack.pop() != FSTRING_START: if stack.pop() != FSTRING_START:
raise ParseError(t, "Mismatched FSTRING_START/FSTRING_END") raise ParseError(t, "Mismatched FSTRING_START/FSTRING_END")
in_fstring = False
if stack: if stack:
raise ParseError(t, "Left open") raise ParseError(t, "Left open")
return braces return braces

View File

@ -30,6 +30,9 @@ class A:
set = A().set set = A().set
# An f string as in https://github.com/pytorch/pytorch/issues/159056
f_string = f" {h:{w}} "
# Braced sets # Braced sets
set1 = {1} set1 = {1}

View File

@ -47,7 +47,7 @@
"char": 7, "char": 7,
"code": "SET_LINTER", "code": "SET_LINTER",
"description": null, "description": null,
"line": 35, "line": 38,
"name": "Builtin `set` is deprecated", "name": "Builtin `set` is deprecated",
"original": null, "original": null,
"path": "tools/test/set_linter_testdata/python_code.py.txt", "path": "tools/test/set_linter_testdata/python_code.py.txt",
@ -58,7 +58,7 @@
"char": 9, "char": 9,
"code": "SET_LINTER", "code": "SET_LINTER",
"description": null, "description": null,
"line": 35, "line": 38,
"name": "Builtin `set` is deprecated", "name": "Builtin `set` is deprecated",
"original": null, "original": null,
"path": "tools/test/set_linter_testdata/python_code.py.txt", "path": "tools/test/set_linter_testdata/python_code.py.txt",
@ -69,7 +69,7 @@
"char": 7, "char": 7,
"code": "SET_LINTER", "code": "SET_LINTER",
"description": null, "description": null,
"line": 36, "line": 39,
"name": "Builtin `set` is deprecated", "name": "Builtin `set` is deprecated",
"original": null, "original": null,
"path": "tools/test/set_linter_testdata/python_code.py.txt", "path": "tools/test/set_linter_testdata/python_code.py.txt",
@ -80,7 +80,7 @@
"char": 12, "char": 12,
"code": "SET_LINTER", "code": "SET_LINTER",
"description": null, "description": null,
"line": 36, "line": 39,
"name": "Builtin `set` is deprecated", "name": "Builtin `set` is deprecated",
"original": null, "original": null,
"path": "tools/test/set_linter_testdata/python_code.py.txt", "path": "tools/test/set_linter_testdata/python_code.py.txt",
@ -91,7 +91,7 @@
"char": 15, "char": 15,
"code": "SET_LINTER", "code": "SET_LINTER",
"description": null, "description": null,
"line": 38, "line": 41,
"name": "Builtin `set` is deprecated", "name": "Builtin `set` is deprecated",
"original": null, "original": null,
"path": "tools/test/set_linter_testdata/python_code.py.txt", "path": "tools/test/set_linter_testdata/python_code.py.txt",
@ -102,7 +102,7 @@
"char": 36, "char": 36,
"code": "SET_LINTER", "code": "SET_LINTER",
"description": null, "description": null,
"line": 38, "line": 41,
"name": "Builtin `set` is deprecated", "name": "Builtin `set` is deprecated",
"original": null, "original": null,
"path": "tools/test/set_linter_testdata/python_code.py.txt", "path": "tools/test/set_linter_testdata/python_code.py.txt",
@ -113,7 +113,7 @@
"char": 17, "char": 17,
"code": "SET_LINTER", "code": "SET_LINTER",
"description": null, "description": null,
"line": 41, "line": 44,
"name": "Builtin `set` is deprecated", "name": "Builtin `set` is deprecated",
"original": null, "original": null,
"path": "tools/test/set_linter_testdata/python_code.py.txt", "path": "tools/test/set_linter_testdata/python_code.py.txt",
@ -124,7 +124,7 @@
"char": 22, "char": 22,
"code": "SET_LINTER", "code": "SET_LINTER",
"description": null, "description": null,
"line": 41, "line": 44,
"name": "Builtin `set` is deprecated", "name": "Builtin `set` is deprecated",
"original": null, "original": null,
"path": "tools/test/set_linter_testdata/python_code.py.txt", "path": "tools/test/set_linter_testdata/python_code.py.txt",
@ -135,7 +135,7 @@
"char": 30, "char": 30,
"code": "SET_LINTER", "code": "SET_LINTER",
"description": null, "description": null,
"line": 41, "line": 44,
"name": "Builtin `set` is deprecated", "name": "Builtin `set` is deprecated",
"original": null, "original": null,
"path": "tools/test/set_linter_testdata/python_code.py.txt", "path": "tools/test/set_linter_testdata/python_code.py.txt",
@ -146,7 +146,7 @@
"char": 50, "char": 50,
"code": "SET_LINTER", "code": "SET_LINTER",
"description": null, "description": null,
"line": 41, "line": 44,
"name": "Builtin `set` is deprecated", "name": "Builtin `set` is deprecated",
"original": null, "original": null,
"path": "tools/test/set_linter_testdata/python_code.py.txt", "path": "tools/test/set_linter_testdata/python_code.py.txt",
@ -157,7 +157,7 @@
"char": 10, "char": 10,
"code": "SET_LINTER", "code": "SET_LINTER",
"description": null, "description": null,
"line": 44, "line": 47,
"name": "Builtin `set` is deprecated", "name": "Builtin `set` is deprecated",
"original": null, "original": null,
"path": "tools/test/set_linter_testdata/python_code.py.txt", "path": "tools/test/set_linter_testdata/python_code.py.txt",
@ -168,7 +168,7 @@
"char": 51, "char": 51,
"code": "SET_LINTER", "code": "SET_LINTER",
"description": null, "description": null,
"line": 44, "line": 47,
"name": "Builtin `set` is deprecated", "name": "Builtin `set` is deprecated",
"original": null, "original": null,
"path": "tools/test/set_linter_testdata/python_code.py.txt", "path": "tools/test/set_linter_testdata/python_code.py.txt",
@ -179,7 +179,7 @@
"char": 75, "char": 75,
"code": "SET_LINTER", "code": "SET_LINTER",
"description": null, "description": null,
"line": 44, "line": 47,
"name": "Builtin `set` is deprecated", "name": "Builtin `set` is deprecated",
"original": null, "original": null,
"path": "tools/test/set_linter_testdata/python_code.py.txt", "path": "tools/test/set_linter_testdata/python_code.py.txt",
@ -190,7 +190,7 @@
"char": 77, "char": 77,
"code": "SET_LINTER", "code": "SET_LINTER",
"description": null, "description": null,
"line": 44, "line": 47,
"name": "Builtin `set` is deprecated", "name": "Builtin `set` is deprecated",
"original": null, "original": null,
"path": "tools/test/set_linter_testdata/python_code.py.txt", "path": "tools/test/set_linter_testdata/python_code.py.txt",
@ -203,9 +203,9 @@
"description": null, "description": null,
"line": null, "line": null,
"name": "Suggested fixes for set_linter", "name": "Suggested fixes for set_linter",
"original": "# Basic tests\nimport tempfile\n\nprint(f\"{tempfile.gettempdir()}/memory_snapshot.pickle\")\n\nignored = set() # noqa: set_linter\na = set()\nb = \"set()\"\nc = set\nd = c.set\nf = (\n set(\n )\n)\nignored = (\n set( # noqa: set_linter\n )\n)\n\n# Non-sets\n\nd = {}\nlong_string = \"\"\" set()\nset() set x.set set()\n\\\"\"\"\"\n\nclass A:\n def set(self, x):\n self.x = x\n\nset = A().set\n\n# Braced sets\n\nset1 = {1}\nset2 = {1, 2}\n\niterator_set = {i for i in range(10)}\n\n# A dict with two sets.\ndict_set = {\"a\": {2, 3}, \"b\": {i for i in range(3)}}\n\n# A set containing an object constructed with a dict and a set\nsos_set = {Something({i: i + 1 for i in range(3)}, {i + 1 for i in range(3)})}\n", "original": "# Basic tests\nimport tempfile\n\nprint(f\"{tempfile.gettempdir()}/memory_snapshot.pickle\")\n\nignored = set() # noqa: set_linter\na = set()\nb = \"set()\"\nc = set\nd = c.set\nf = (\n set(\n )\n)\nignored = (\n set( # noqa: set_linter\n )\n)\n\n# Non-sets\n\nd = {}\nlong_string = \"\"\" set()\nset() set x.set set()\n\\\"\"\"\"\n\nclass A:\n def set(self, x):\n self.x = x\n\nset = A().set\n\n# An f string as in https://github.com/pytorch/pytorch/issues/159056\nf_string = f\" {h:{w}} \"\n\n# Braced sets\n\nset1 = {1}\nset2 = {1, 2}\n\niterator_set = {i for i in range(10)}\n\n# A dict with two sets.\ndict_set = {\"a\": {2, 3}, \"b\": {i for i in range(3)}}\n\n# A set containing an object constructed with a dict and a set\nsos_set = {Something({i: i + 1 for i in range(3)}, {i + 1 for i in range(3)})}\n",
"path": "tools/test/set_linter_testdata/python_code.py.txt", "path": "tools/test/set_linter_testdata/python_code.py.txt",
"replacement": "# Basic tests\nimport tempfile\nfrom torch.utils._ordered_set import OrderedSet\n\n\nprint(f\"{tempfile.gettempdir()}/memory_snapshot.pickle\")\n\nignored = set() # noqa: set_linter\na = OrderedSet()\nb = \"set()\"\nc = OrderedSet\nd = c.set\nf = (\n OrderedSet(\n )\n)\nignored = (\n set( # noqa: set_linter\n )\n)\n\n# Non-sets\n\nd = {}\nlong_string = \"\"\" set()\nset() set x.set set()\n\\\"\"\"\"\n\nclass A:\n def set(self, x):\n self.x = x\n\nset = A().set\n\n# Braced sets\n\nset1 = OrderedSet([1])\nset2 = OrderedSet([1, 2])\n\niterator_set = OrderedSet([i for i in range(10)])\n\n# A dict with two sets.\ndict_set = {\"a\": OrderedSet([2, 3]), \"b\": OrderedSet([i for i in range(3)])}\n\n# A set containing an object constructed with a dict and a set\nsos_set = OrderedSet([Something({i: i + 1 for i in range(3)}, OrderedSet([i + 1 for i in range(3)]))])\n", "replacement": "# Basic tests\nimport tempfile\nfrom torch.utils._ordered_set import OrderedSet\n\n\nprint(f\"{tempfile.gettempdir()}/memory_snapshot.pickle\")\n\nignored = set() # noqa: set_linter\na = OrderedSet()\nb = \"set()\"\nc = OrderedSet\nd = c.set\nf = (\n OrderedSet(\n )\n)\nignored = (\n set( # noqa: set_linter\n )\n)\n\n# Non-sets\n\nd = {}\nlong_string = \"\"\" set()\nset() set x.set set()\n\\\"\"\"\"\n\nclass A:\n def set(self, x):\n self.x = x\n\nset = A().set\n\n# An f string as in https://github.com/pytorch/pytorch/issues/159056\nf_string = f\" {h:{w}} \"\n\n# Braced sets\n\nset1 = OrderedSet([1])\nset2 = OrderedSet([1, 2])\n\niterator_set = OrderedSet([i for i in range(10)])\n\n# A dict with two sets.\ndict_set = {\"a\": OrderedSet([2, 3]), \"b\": OrderedSet([i for i in range(3)])}\n\n# A set containing an object constructed with a dict and a set\nsos_set = OrderedSet([Something({i: i + 1 for i in range(3)}, OrderedSet([i + 1 for i in range(3)]))])\n",
"severity": "error" "severity": "error"
} }
] ]

View File

@ -30,106 +30,106 @@ tools/test/set_linter_testdata/python_code.py.txt:12:4: Builtin `set` is depreca
13 | ) 13 | )
14 | ) 14 | )
tools/test/set_linter_testdata/python_code.py.txt:35:8: Builtin `set` is deprecated tools/test/set_linter_testdata/python_code.py.txt:38:8: Builtin `set` is deprecated
33 | # Braced sets 36 | # Braced sets
34 |
35 | set1 = {1}
^
36 | set2 = {1, 2}
37 | 37 |
38 | set1 = {1}
^
39 | set2 = {1, 2}
40 |
tools/test/set_linter_testdata/python_code.py.txt:35:10: Builtin `set` is deprecated tools/test/set_linter_testdata/python_code.py.txt:38:10: Builtin `set` is deprecated
33 | # Braced sets 36 | # Braced sets
34 | 37 |
35 | set1 = {1} 38 | set1 = {1}
^ ^
36 | set2 = {1, 2} 39 | set2 = {1, 2}
37 | 40 |
tools/test/set_linter_testdata/python_code.py.txt:36:8: Builtin `set` is deprecated tools/test/set_linter_testdata/python_code.py.txt:39:8: Builtin `set` is deprecated
34 | 37 |
35 | set1 = {1} 38 | set1 = {1}
36 | set2 = {1, 2} 39 | set2 = {1, 2}
^ ^
37 | 40 |
38 | iterator_set = {i for i in range(10)} 41 | iterator_set = {i for i in range(10)}
tools/test/set_linter_testdata/python_code.py.txt:36:13: Builtin `set` is deprecated tools/test/set_linter_testdata/python_code.py.txt:39:13: Builtin `set` is deprecated
34 | 37 |
35 | set1 = {1} 38 | set1 = {1}
36 | set2 = {1, 2} 39 | set2 = {1, 2}
^ ^
37 | 40 |
38 | iterator_set = {i for i in range(10)} 41 | iterator_set = {i for i in range(10)}
tools/test/set_linter_testdata/python_code.py.txt:38:16: Builtin `set` is deprecated tools/test/set_linter_testdata/python_code.py.txt:41:16: Builtin `set` is deprecated
36 | set2 = {1, 2} 39 | set2 = {1, 2}
37 | 40 |
38 | iterator_set = {i for i in range(10)} 41 | iterator_set = {i for i in range(10)}
^ ^
39 | 42 |
40 | # A dict with two sets. 43 | # A dict with two sets.
tools/test/set_linter_testdata/python_code.py.txt:38:37: Builtin `set` is deprecated tools/test/set_linter_testdata/python_code.py.txt:41:37: Builtin `set` is deprecated
36 | set2 = {1, 2} 39 | set2 = {1, 2}
37 | 40 |
38 | iterator_set = {i for i in range(10)} 41 | iterator_set = {i for i in range(10)}
^ ^
39 | 42 |
40 | # A dict with two sets. 43 | # A dict with two sets.
tools/test/set_linter_testdata/python_code.py.txt:41:18: Builtin `set` is deprecated tools/test/set_linter_testdata/python_code.py.txt:44:18: Builtin `set` is deprecated
39 | 42 |
40 | # A dict with two sets. 43 | # A dict with two sets.
41 | dict_set = {"a": {2, 3}, "b": {i for i in range(3)}} 44 | dict_set = {"a": {2, 3}, "b": {i for i in range(3)}}
^ ^
42 | 45 |
43 | # A set containing an object constructed with a dict and a set 46 | # A set containing an object constructed with a dict and a set
tools/test/set_linter_testdata/python_code.py.txt:41:23: Builtin `set` is deprecated tools/test/set_linter_testdata/python_code.py.txt:44:23: Builtin `set` is deprecated
39 | 42 |
40 | # A dict with two sets. 43 | # A dict with two sets.
41 | dict_set = {"a": {2, 3}, "b": {i for i in range(3)}} 44 | dict_set = {"a": {2, 3}, "b": {i for i in range(3)}}
^ ^
42 | 45 |
43 | # A set containing an object constructed with a dict and a set 46 | # A set containing an object constructed with a dict and a set
tools/test/set_linter_testdata/python_code.py.txt:41:31: Builtin `set` is deprecated tools/test/set_linter_testdata/python_code.py.txt:44:31: Builtin `set` is deprecated
39 | 42 |
40 | # A dict with two sets. 43 | # A dict with two sets.
41 | dict_set = {"a": {2, 3}, "b": {i for i in range(3)}} 44 | dict_set = {"a": {2, 3}, "b": {i for i in range(3)}}
^ ^
42 | 45 |
43 | # A set containing an object constructed with a dict and a set 46 | # A set containing an object constructed with a dict and a set
tools/test/set_linter_testdata/python_code.py.txt:41:51: Builtin `set` is deprecated tools/test/set_linter_testdata/python_code.py.txt:44:51: Builtin `set` is deprecated
39 | 42 |
40 | # A dict with two sets. 43 | # A dict with two sets.
41 | dict_set = {"a": {2, 3}, "b": {i for i in range(3)}} 44 | dict_set = {"a": {2, 3}, "b": {i for i in range(3)}}
^ ^
42 | 45 |
43 | # A set containing an object constructed with a dict and a set 46 | # A set containing an object constructed with a dict and a set
tools/test/set_linter_testdata/python_code.py.txt:44:11: Builtin `set` is deprecated tools/test/set_linter_testdata/python_code.py.txt:47:11: Builtin `set` is deprecated
42 | 45 |
43 | # A set containing an object constructed with a dict and a set 46 | # A set containing an object constructed with a dict and a set
44 | sos_set = {Something({i: i + 1 for i in range(3)}, {i + 1 for i in range(3)})} 47 | sos_set = {Something({i: i + 1 for i in range(3)}, {i + 1 for i in range(3)})}
^ ^
tools/test/set_linter_testdata/python_code.py.txt:44:52: Builtin `set` is deprecated tools/test/set_linter_testdata/python_code.py.txt:47:52: Builtin `set` is deprecated
42 | 45 |
43 | # A set containing an object constructed with a dict and a set 46 | # A set containing an object constructed with a dict and a set
44 | sos_set = {Something({i: i + 1 for i in range(3)}, {i + 1 for i in range(3)})} 47 | sos_set = {Something({i: i + 1 for i in range(3)}, {i + 1 for i in range(3)})}
^ ^
tools/test/set_linter_testdata/python_code.py.txt:44:76: Builtin `set` is deprecated tools/test/set_linter_testdata/python_code.py.txt:47:76: Builtin `set` is deprecated
42 | 45 |
43 | # A set containing an object constructed with a dict and a set 46 | # A set containing an object constructed with a dict and a set
44 | sos_set = {Something({i: i + 1 for i in range(3)}, {i + 1 for i in range(3)})} 47 | sos_set = {Something({i: i + 1 for i in range(3)}, {i + 1 for i in range(3)})}
^ ^
tools/test/set_linter_testdata/python_code.py.txt:44:78: Builtin `set` is deprecated tools/test/set_linter_testdata/python_code.py.txt:47:78: Builtin `set` is deprecated
42 | 45 |
43 | # A set containing an object constructed with a dict and a set 46 | # A set containing an object constructed with a dict and a set
44 | sos_set = {Something({i: i + 1 for i in range(3)}, {i + 1 for i in range(3)})} 47 | sos_set = {Something({i: i + 1 for i in range(3)}, {i + 1 for i in range(3)})}
^ ^

View File

@ -32,6 +32,9 @@ class A:
set = A().set set = A().set
# An f string as in https://github.com/pytorch/pytorch/issues/159056
f_string = f" {h:{w}} "
# Braced sets # Braced sets
set1 = OrderedSet([1]) set1 = OrderedSet([1])

View File

@ -77,6 +77,7 @@ class TestSetLinter(LinterTestCase):
("{i for i in range(2, 3)}", 1), ("{i for i in range(2, 3)}", 1),
("{1, 2}", 1), ("{1, 2}", 1),
("{One({'a': 1}), Two([{}, {2}, {1, 2}])}", 3), ("{One({'a': 1}), Two([{}, {2}, {1, 2}])}", 3),
('f" {h:{w}} "', 0),
) )
for s, expected in TESTS: for s, expected in TESTS:
pf = SetLinter.make_file(s) pf = SetLinter.make_file(s)