Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 6682d91

Browse files
authored
gh-119698: fix a special case in symtable.Class.get_methods (#121802)
1 parent cffad5c commit 6682d91

File tree

2 files changed

+68
-2
lines changed

2 files changed

+68
-2
lines changed

Lib/symtable.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -249,14 +249,26 @@ def is_local_symbol(ident):
249249
if is_local_symbol(st.name):
250250
match st.type:
251251
case _symtable.TYPE_FUNCTION:
252+
# generators are of type TYPE_FUNCTION with a ".0"
253+
# parameter as a first parameter (which makes them
254+
# distinguishable from a function named 'genexpr')
255+
if st.name == 'genexpr' and '.0' in st.varnames:
256+
continue
252257
d[st.name] = 1
253258
case _symtable.TYPE_TYPE_PARAMETERS:
254259
# Get the function-def block in the annotation
255260
# scope 'st' with the same identifier, if any.
256261
scope_name = st.name
257262
for c in st.children:
258263
if c.name == scope_name and c.type == _symtable.TYPE_FUNCTION:
259-
d[st.name] = 1
264+
# A generic generator of type TYPE_FUNCTION
265+
# cannot be a direct child of 'st' (but it
266+
# can be a descendant), e.g.:
267+
#
268+
# class A:
269+
# type genexpr[genexpr] = (x for x in [])
270+
assert scope_name != 'genexpr' or '.0' not in c.varnames
271+
d[scope_name] = 1
260272
break
261273
self.__methods = tuple(d)
262274
return self.__methods

Lib/test/test_symtable.py

Lines changed: 55 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

@@ -356,7 +358,7 @@ def test_name(self):
356358
self.assertEqual(self.spam.lookup("x").get_name(), "x")
357359
self.assertEqual(self.Mine.get_name(), "Mine")
358360

359-
def test_class_info(self):
361+
def test_class_get_methods(self):
360362
self.assertEqual(self.Mine.get_methods(), ('a_method',))
361363

362364
top = symtable.symtable(TEST_COMPLEX_CLASS_CODE, "?", "exec")
@@ -377,6 +379,58 @@ def test_class_info(self):
377379
'glob_assigned_async_meth', 'glob_assigned_async_meth_pep_695',
378380
))
379381

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

0 commit comments

Comments
 (0)