[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"""
braces: dict[int, int] = {}
stack: list[int] = []
in_fstring = False
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:
stack.append(i)
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}")
elif t.type == FSTRING_START:
stack.append(FSTRING_START)
in_fstring = True
elif t.type == FSTRING_END:
if stack.pop() != FSTRING_START:
raise ParseError(t, "Mismatched FSTRING_START/FSTRING_END")
in_fstring = False
if stack:
raise ParseError(t, "Left open")
return braces

View File

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

View File

@ -47,7 +47,7 @@
"char": 7,
"code": "SET_LINTER",
"description": null,
"line": 35,
"line": 38,
"name": "Builtin `set` is deprecated",
"original": null,
"path": "tools/test/set_linter_testdata/python_code.py.txt",
@ -58,7 +58,7 @@
"char": 9,
"code": "SET_LINTER",
"description": null,
"line": 35,
"line": 38,
"name": "Builtin `set` is deprecated",
"original": null,
"path": "tools/test/set_linter_testdata/python_code.py.txt",
@ -69,7 +69,7 @@
"char": 7,
"code": "SET_LINTER",
"description": null,
"line": 36,
"line": 39,
"name": "Builtin `set` is deprecated",
"original": null,
"path": "tools/test/set_linter_testdata/python_code.py.txt",
@ -80,7 +80,7 @@
"char": 12,
"code": "SET_LINTER",
"description": null,
"line": 36,
"line": 39,
"name": "Builtin `set` is deprecated",
"original": null,
"path": "tools/test/set_linter_testdata/python_code.py.txt",
@ -91,7 +91,7 @@
"char": 15,
"code": "SET_LINTER",
"description": null,
"line": 38,
"line": 41,
"name": "Builtin `set` is deprecated",
"original": null,
"path": "tools/test/set_linter_testdata/python_code.py.txt",
@ -102,7 +102,7 @@
"char": 36,
"code": "SET_LINTER",
"description": null,
"line": 38,
"line": 41,
"name": "Builtin `set` is deprecated",
"original": null,
"path": "tools/test/set_linter_testdata/python_code.py.txt",
@ -113,7 +113,7 @@
"char": 17,
"code": "SET_LINTER",
"description": null,
"line": 41,
"line": 44,
"name": "Builtin `set` is deprecated",
"original": null,
"path": "tools/test/set_linter_testdata/python_code.py.txt",
@ -124,7 +124,7 @@
"char": 22,
"code": "SET_LINTER",
"description": null,
"line": 41,
"line": 44,
"name": "Builtin `set` is deprecated",
"original": null,
"path": "tools/test/set_linter_testdata/python_code.py.txt",
@ -135,7 +135,7 @@
"char": 30,
"code": "SET_LINTER",
"description": null,
"line": 41,
"line": 44,
"name": "Builtin `set` is deprecated",
"original": null,
"path": "tools/test/set_linter_testdata/python_code.py.txt",
@ -146,7 +146,7 @@
"char": 50,
"code": "SET_LINTER",
"description": null,
"line": 41,
"line": 44,
"name": "Builtin `set` is deprecated",
"original": null,
"path": "tools/test/set_linter_testdata/python_code.py.txt",
@ -157,7 +157,7 @@
"char": 10,
"code": "SET_LINTER",
"description": null,
"line": 44,
"line": 47,
"name": "Builtin `set` is deprecated",
"original": null,
"path": "tools/test/set_linter_testdata/python_code.py.txt",
@ -168,7 +168,7 @@
"char": 51,
"code": "SET_LINTER",
"description": null,
"line": 44,
"line": 47,
"name": "Builtin `set` is deprecated",
"original": null,
"path": "tools/test/set_linter_testdata/python_code.py.txt",
@ -179,7 +179,7 @@
"char": 75,
"code": "SET_LINTER",
"description": null,
"line": 44,
"line": 47,
"name": "Builtin `set` is deprecated",
"original": null,
"path": "tools/test/set_linter_testdata/python_code.py.txt",
@ -190,7 +190,7 @@
"char": 77,
"code": "SET_LINTER",
"description": null,
"line": 44,
"line": 47,
"name": "Builtin `set` is deprecated",
"original": null,
"path": "tools/test/set_linter_testdata/python_code.py.txt",
@ -203,9 +203,9 @@
"description": null,
"line": null,
"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",
"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"
}
]

View File

@ -30,106 +30,106 @@ tools/test/set_linter_testdata/python_code.py.txt:12:4: Builtin `set` is depreca
13 | )
14 | )
tools/test/set_linter_testdata/python_code.py.txt:35:8: Builtin `set` is deprecated
33 | # Braced sets
34 |
35 | set1 = {1}
^
36 | set2 = {1, 2}
tools/test/set_linter_testdata/python_code.py.txt:38:8: Builtin `set` is deprecated
36 | # Braced sets
37 |
38 | set1 = {1}
^
39 | set2 = {1, 2}
40 |
tools/test/set_linter_testdata/python_code.py.txt:35:10: Builtin `set` is deprecated
33 | # Braced sets
34 |
35 | set1 = {1}
tools/test/set_linter_testdata/python_code.py.txt:38:10: Builtin `set` is deprecated
36 | # Braced sets
37 |
38 | set1 = {1}
^
36 | set2 = {1, 2}
37 |
39 | set2 = {1, 2}
40 |
tools/test/set_linter_testdata/python_code.py.txt:36:8: Builtin `set` is deprecated
34 |
35 | set1 = {1}
36 | set2 = {1, 2}
tools/test/set_linter_testdata/python_code.py.txt:39:8: Builtin `set` is deprecated
37 |
38 | set1 = {1}
39 | set2 = {1, 2}
^
37 |
38 | iterator_set = {i for i in range(10)}
40 |
41 | iterator_set = {i for i in range(10)}
tools/test/set_linter_testdata/python_code.py.txt:36:13: Builtin `set` is deprecated
34 |
35 | set1 = {1}
36 | set2 = {1, 2}
tools/test/set_linter_testdata/python_code.py.txt:39:13: Builtin `set` is deprecated
37 |
38 | set1 = {1}
39 | set2 = {1, 2}
^
37 |
38 | iterator_set = {i for i in range(10)}
40 |
41 | iterator_set = {i for i in range(10)}
tools/test/set_linter_testdata/python_code.py.txt:38:16: Builtin `set` is deprecated
36 | set2 = {1, 2}
37 |
38 | iterator_set = {i for i in range(10)}
tools/test/set_linter_testdata/python_code.py.txt:41:16: Builtin `set` is deprecated
39 | set2 = {1, 2}
40 |
41 | iterator_set = {i for i in range(10)}
^
39 |
40 | # A dict with two sets.
42 |
43 | # A dict with two sets.
tools/test/set_linter_testdata/python_code.py.txt:38:37: Builtin `set` is deprecated
36 | set2 = {1, 2}
37 |
38 | iterator_set = {i for i in range(10)}
tools/test/set_linter_testdata/python_code.py.txt:41:37: Builtin `set` is deprecated
39 | set2 = {1, 2}
40 |
41 | iterator_set = {i for i in range(10)}
^
39 |
40 | # A dict with two sets.
42 |
43 | # A dict with two sets.
tools/test/set_linter_testdata/python_code.py.txt:41:18: Builtin `set` is deprecated
39 |
40 | # A dict with two sets.
41 | dict_set = {"a": {2, 3}, "b": {i for i in range(3)}}
tools/test/set_linter_testdata/python_code.py.txt:44:18: Builtin `set` is deprecated
42 |
43 | # A dict with two sets.
44 | dict_set = {"a": {2, 3}, "b": {i for i in range(3)}}
^
42 |
43 | # A set containing an object constructed with a dict and a set
45 |
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
39 |
40 | # A dict with two sets.
41 | dict_set = {"a": {2, 3}, "b": {i for i in range(3)}}
tools/test/set_linter_testdata/python_code.py.txt:44:23: Builtin `set` is deprecated
42 |
43 | # A dict with two sets.
44 | dict_set = {"a": {2, 3}, "b": {i for i in range(3)}}
^
42 |
43 | # A set containing an object constructed with a dict and a set
45 |
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
39 |
40 | # A dict with two sets.
41 | dict_set = {"a": {2, 3}, "b": {i for i in range(3)}}
tools/test/set_linter_testdata/python_code.py.txt:44:31: Builtin `set` is deprecated
42 |
43 | # A dict with two sets.
44 | dict_set = {"a": {2, 3}, "b": {i for i in range(3)}}
^
42 |
43 | # A set containing an object constructed with a dict and a set
45 |
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
39 |
40 | # A dict with two sets.
41 | dict_set = {"a": {2, 3}, "b": {i for i in range(3)}}
tools/test/set_linter_testdata/python_code.py.txt:44:51: Builtin `set` is deprecated
42 |
43 | # A dict with two sets.
44 | dict_set = {"a": {2, 3}, "b": {i for i in range(3)}}
^
42 |
43 | # A set containing an object constructed with a dict and a set
45 |
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
42 |
43 | # 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)})}
tools/test/set_linter_testdata/python_code.py.txt:47:11: Builtin `set` is deprecated
45 |
46 | # A set containing an object constructed with a dict and a set
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
42 |
43 | # 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)})}
tools/test/set_linter_testdata/python_code.py.txt:47:52: Builtin `set` is deprecated
45 |
46 | # A set containing an object constructed with a dict and a set
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
42 |
43 | # 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)})}
tools/test/set_linter_testdata/python_code.py.txt:47:76: Builtin `set` is deprecated
45 |
46 | # A set containing an object constructed with a dict and a set
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
42 |
43 | # 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)})}
tools/test/set_linter_testdata/python_code.py.txt:47:78: Builtin `set` is deprecated
45 |
46 | # A set containing an object constructed with a dict and a set
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
# An f string as in https://github.com/pytorch/pytorch/issues/159056
f_string = f" {h:{w}} "
# Braced sets
set1 = OrderedSet([1])

View File

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