Skip to content

Commit 3279a4f

Browse files
[3.12] gh-119698: fix a special case in symtable.Class.get_methods (GH-121802) (#121910)
(cherry picked from commit 6682d91) Co-authored-by: Bénédikt Tran <[email protected]>
1 parent a6516de commit 3279a4f

File tree

2 files changed

+64
-2
lines changed

2 files changed

+64
-2
lines changed

Lib/symtable.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -227,14 +227,26 @@ def is_local_symbol(ident):
227227
if is_local_symbol(st.name):
228228
match st.type:
229229
case _symtable.TYPE_FUNCTION:
230+
# generators are of type TYPE_FUNCTION with a ".0"
231+
# parameter as a first parameter (which makes them
232+
# distinguishable from a function named 'genexpr')
233+
if st.name == 'genexpr' and '.0' in st.varnames:
234+
continue
230235
d[st.name] = 1
231236
case _symtable.TYPE_TYPE_PARAM:
232237
# Get the function-def block in the annotation
233238
# scope 'st' with the same identifier, if any.
234239
scope_name = st.name
235240
for c in st.children:
236241
if c.name == scope_name and c.type == _symtable.TYPE_FUNCTION:
237-
d[st.name] = 1
242+
# A generic generator of type TYPE_FUNCTION
243+
# cannot be a direct child of 'st' (but it
244+
# can be a descendant), e.g.:
245+
#
246+
# class A:
247+
# type genexpr[genexpr] = (x for x in [])
248+
assert scope_name != 'genexpr' or '.0' not in c.varnames
249+
d[scope_name] = 1
238250
break
239251
self.__methods = tuple(d)
240252
return self.__methods

Lib/test/test_symtable.py

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
"""
22
Test the API of the symtable module.
33
"""
4+
5+
import textwrap
46
import symtable
57
import unittest
68

@@ -350,7 +352,7 @@ def test_name(self):
350352
self.assertEqual(self.spam.lookup("x").get_name(), "x")
351353
self.assertEqual(self.Mine.get_name(), "Mine")
352354

353-
def test_class_info(self):
355+
def test_class_get_methods(self):
354356
self.assertEqual(self.Mine.get_methods(), ('a_method',))
355357

356358
top = symtable.symtable(TEST_COMPLEX_CLASS_CODE, "?", "exec")
@@ -371,6 +373,54 @@ def test_class_info(self):
371373
'glob_assigned_async_meth', 'glob_assigned_async_meth_pep_695',
372374
))
373375

376+
# Test generator expressions that are of type TYPE_FUNCTION
377+
# but will not be reported by get_methods() since they are
378+
# not functions per se.
379+
#
380+
# Other kind of comprehensions such as list, set or dict
381+
# expressions do not have the TYPE_FUNCTION type.
382+
383+
def check_body(body, expected_methods):
384+
indented = textwrap.indent(body, ' ' * 4)
385+
top = symtable.symtable(f"class A:\n{indented}", "?", "exec")
386+
this = find_block(top, "A")
387+
self.assertEqual(this.get_methods(), expected_methods)
388+
389+
# statements with 'genexpr' inside it
390+
GENEXPRS = (
391+
'x = (x for x in [])',
392+
'x = (x async for x in [])',
393+
'genexpr = (x for x in [])',
394+
'genexpr = (x async for x in [])',
395+
)
396+
397+
for gen in GENEXPRS:
398+
# test generator expression
399+
with self.subTest(gen=gen):
400+
check_body(gen, ())
401+
402+
# test generator expression + variable named 'genexpr'
403+
with self.subTest(gen=gen, isvar=True):
404+
check_body('\n'.join((gen, 'genexpr = 1')), ())
405+
check_body('\n'.join(('genexpr = 1', gen)), ())
406+
407+
for paramlist in ('()', '(x)', '(x, y)', '(z: T)'):
408+
for func in (
409+
f'def genexpr{paramlist}:pass',
410+
f'async def genexpr{paramlist}:pass',
411+
f'def genexpr[T]{paramlist}:pass',
412+
f'async def genexpr[T]{paramlist}:pass',
413+
):
414+
with self.subTest(func=func):
415+
# test function named 'genexpr'
416+
check_body(func, ('genexpr',))
417+
418+
for gen in GENEXPRS:
419+
with self.subTest(gen=gen, func=func):
420+
# test generator expression + function named 'genexpr'
421+
check_body('\n'.join((gen, func)), ('genexpr',))
422+
check_body('\n'.join((func, gen)), ('genexpr',))
423+
374424
def test_filename_correct(self):
375425
### Bug tickler: SyntaxError file name correct whether error raised
376426
### while parsing or building symbol table.

0 commit comments

Comments
 (0)