8000 Support matching ASYNC and AWAIT as keywords. · python/cpython@dbeda93 · GitHub
[go: up one dir, main page]

Skip to content

Commit dbeda93

Browse files
committed
Support matching ASYNC and AWAIT as keywords.
With this change, you can match 'await' to mean the NAME token possible with <3.8, and the AWAIT token for the proper position. Before this change, your only option is to match TOKEN and filter in your fixer. As an example of code improved by this, Bowler can now be more explicit about what it expects to find here, as seen at https://github.com/facebookincubator/Bowler/blob/da3ec9a88c41d050d591e0c9e15b44849f0c9631/bowler/query.py#L338 I did a cursory reading of https://bugs.python.org/issue30406 and https://bugs.python.org/issue35975 and think this should be compatible.
1 parent d9bd8ec commit dbeda93

File tree

3 files changed

+32
-13
lines changed

3 files changed

+32
-13
lines changed

Lib/lib2to3/patcomp.py

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -145,11 +145,16 @@ def compile_basic(self, nodes, repeat=None):
145145
elif node.type == token.NAME:
146146
value = node.value
147147
if value.isupper():
148-
if value not in TOKEN_MAP:
149-
raise PatternSyntaxError("Invalid token: %r" % value)
148+
# Map named tokens to the type value for a LeafPattern
149+
if value == 'TOKEN':
150+
type = None
151+
else:
152+
type = getattr(token, value)
153+
if not type:
154+
raise PatternSyntaxError("Invalid token: %r" % value)
150155
if nodes[1:]:
151156
raise PatternSyntaxError("Can't have details for token")
152-
return pytree.LeafPattern(TOKEN_MAP[value])
157+
return pytree.LeafPattern(type)
153158
else:
154159
if value == "any":
155160
type = None
@@ -175,14 +180,8 @@ def get_int(self, node):
175180
return int(node.value)
176181

177182

178-
# Map named tokens to the type value for a LeafPattern
179-
TOKEN_MAP = {"NAME": token.NAME,
180-
"STRING": token.STRING,
181-
"NUMBER": token.NUMBER,
182-
"TOKEN": None}
183-
184-
185183
def _type_of_literal(value):
184+
# Special case: you can't match ASYNC or AWAIT in their new keyword forms this way.
186185
if value[0].isalpha():
187186
return token.NAME
188187
elif value in grammar.opmap:
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
from lib2to3.fixer_base import BaseFix
2+
from lib2to3.fixer_util import Name
3+
4+
class FixAwait(BaseFix):
5+
"""
6+
Find calls to await and change their target.
7+
"""
8+
9+
PATTERN = """power < [AWAIT] name='b' any* >"""
10+
11+
def transform(self, node, results):
12+
name = results["name"]
13+
name.replace(Name("bar", name.prefix))

Lib/lib2to3/tests/test_refactor.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ def tearDown(self):
3737
def check_instances(self, instances, classes):
3838
for inst, cls in zip(instances, classes):
3939
if not isinstance(inst, cls):
40-
self.fail("%s are not instances of %s" % instances, classes)
40+
self.fail("%s are not instances of %s" % (instances, classes))
4141

4242
def rt(self, options=None, fixers=_DEFAULT_FIXERS, explicit=None):
4343
return refactor.RefactoringTool(fixers, options, explicit)
@@ -55,7 +55,7 @@ def test_write_unchanged_files_option(self):
5555
self.assertTrue(rt.write_unchanged_files)
5656

5757
def test_fixer_loading_helpers(self):
58-
contents = ["explicit", "first", "last", "parrot", "preorder"]
58+
contents = ["await", "explicit", "first", "last", "parrot", "preorder"]
5959
non_prefixed = refactor.get_all_fix_names("myfixes")
6060
prefixed = refactor.get_all_fix_names("myfixes", False)
6161
full_names = refactor.get_fixers_from_package("myfixes")
@@ -132,6 +132,7 @@ class SimpleFix(fixer_base.BaseFix):
132132

133133
def test_fixer_loading(self):
134134
from myfixes.fix_first import FixFirst
135+
from myfixes.fix_await import FixAwait
135136
from myfixes.fix_last import FixLast
136137
from myfixes.fix_parrot import FixParrot
137138
from myfixes.fix_preorder import FixPreorder
@@ -140,7 +141,7 @@ def test_fixer_loading(self):
140141
pre, post = rt.get_fixers()
141142

142143
self.check_instances(pre, [FixPreorder])
143-
self.check_instances(post, [FixFirst, FixParrot, FixLast])
144+
self.check_instances(post, [FixFirst, FixAwait, FixParrot, FixLast])
144145

145146
def test_naughty_fixers(self):
146147
self.assertRaises(ImportError, self.rt, fixers=["not_here"])
@@ -331,3 +332,9 @@ def test_explicit(self):
331332
break
332333
else:
333334
self.fail("explicit fixer not loaded")
335+
336+
def test_refactor_await(self):
337+
rt = self.rt()
338+
input = "async def a(): await b()\n\n"
339+
tree = rt.refactor_string(input, "<test>")
340+
self.assertNotEqual(str(tree), input)

0 commit comments

Comments
 (0)
0