mirror of
https://github.com/python/cpython.git
synced 2025-09-26 18:29:57 +00:00
[3.12] gh-119698: fix a special case in symtable.Class.get_methods
(GH-121802) (#121910)
(cherry picked from commit 6682d91678
)
Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com>
This commit is contained in:
parent
a6516de08b
commit
3279a4fbff
2 changed files with 64 additions and 2 deletions
|
@ -227,6 +227,11 @@ class Class(SymbolTable):
|
||||||
if is_local_symbol(st.name):
|
if is_local_symbol(st.name):
|
||||||
match st.type:
|
match st.type:
|
||||||
case _symtable.TYPE_FUNCTION:
|
case _symtable.TYPE_FUNCTION:
|
||||||
|
# generators are of type TYPE_FUNCTION with a ".0"
|
||||||
|
# parameter as a first parameter (which makes them
|
||||||
|
# distinguishable from a function named 'genexpr')
|
||||||
|
if st.name == 'genexpr' and '.0' in st.varnames:
|
||||||
|
continue
|
||||||
d[st.name] = 1
|
d[st.name] = 1
|
||||||
case _symtable.TYPE_TYPE_PARAM:
|
case _symtable.TYPE_TYPE_PARAM:
|
||||||
# Get the function-def block in the annotation
|
# Get the function-def block in the annotation
|
||||||
|
@ -234,7 +239,14 @@ class Class(SymbolTable):
|
||||||
scope_name = st.name
|
scope_name = st.name
|
||||||
for c in st.children:
|
for c in st.children:
|
||||||
if c.name == scope_name and c.type == _symtable.TYPE_FUNCTION:
|
if c.name == scope_name and c.type == _symtable.TYPE_FUNCTION:
|
||||||
d[st.name] = 1
|
# A generic generator of type TYPE_FUNCTION
|
||||||
|
# cannot be a direct child of 'st' (but it
|
||||||
|
# can be a descendant), e.g.:
|
||||||
|
#
|
||||||
|
# class A:
|
||||||
|
# type genexpr[genexpr] = (x for x in [])
|
||||||
|
assert scope_name != 'genexpr' or '.0' not in c.varnames
|
||||||
|
d[scope_name] = 1
|
||||||
break
|
break
|
||||||
self.__methods = tuple(d)
|
self.__methods = tuple(d)
|
||||||
return self.__methods
|
return self.__methods
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
"""
|
"""
|
||||||
Test the API of the symtable module.
|
Test the API of the symtable module.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
import textwrap
|
||||||
import symtable
|
import symtable
|
||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
|
@ -350,7 +352,7 @@ class SymtableTest(unittest.TestCase):
|
||||||
self.assertEqual(self.spam.lookup("x").get_name(), "x")
|
self.assertEqual(self.spam.lookup("x").get_name(), "x")
|
||||||
self.assertEqual(self.Mine.get_name(), "Mine")
|
self.assertEqual(self.Mine.get_name(), "Mine")
|
||||||
|
|
||||||
def test_class_info(self):
|
def test_class_get_methods(self):
|
||||||
self.assertEqual(self.Mine.get_methods(), ('a_method',))
|
self.assertEqual(self.Mine.get_methods(), ('a_method',))
|
||||||
|
|
||||||
top = symtable.symtable(TEST_COMPLEX_CLASS_CODE, "?", "exec")
|
top = symtable.symtable(TEST_COMPLEX_CLASS_CODE, "?", "exec")
|
||||||
|
@ -371,6 +373,54 @@ class SymtableTest(unittest.TestCase):
|
||||||
'glob_assigned_async_meth', 'glob_assigned_async_meth_pep_695',
|
'glob_assigned_async_meth', 'glob_assigned_async_meth_pep_695',
|
||||||
))
|
))
|
||||||
|
|
||||||
|
# Test generator expressions that are of type TYPE_FUNCTION
|
||||||
|
# but will not be reported by get_methods() since they are
|
||||||
|
# not functions per se.
|
||||||
|
#
|
||||||
|
# Other kind of comprehensions such as list, set or dict
|
||||||
|
# expressions do not have the TYPE_FUNCTION type.
|
||||||
|
|
||||||
|
def check_body(body, expected_methods):
|
||||||
|
indented = textwrap.indent(body, ' ' * 4)
|
||||||
|
top = symtable.symtable(f"class A:\n{indented}", "?", "exec")
|
||||||
|
this = find_block(top, "A")
|
||||||
|
self.assertEqual(this.get_methods(), expected_methods)
|
||||||
|
|
||||||
|
# statements with 'genexpr' inside it
|
||||||
|
GENEXPRS = (
|
||||||
|
'x = (x for x in [])',
|
||||||
|
'x = (x async for x in [])',
|
||||||
|
'genexpr = (x for x in [])',
|
||||||
|
'genexpr = (x async for x in [])',
|
||||||
|
)
|
||||||
|
|
||||||
|
for gen in GENEXPRS:
|
||||||
|
# test generator expression
|
||||||
|
with self.subTest(gen=gen):
|
||||||
|
check_body(gen, ())
|
||||||
|
|
||||||
|
# test generator expression + variable named 'genexpr'
|
||||||
|
with self.subTest(gen=gen, isvar=True):
|
||||||
|
check_body('\n'.join((gen, 'genexpr = 1')), ())
|
||||||
|
check_body('\n'.join(('genexpr = 1', gen)), ())
|
||||||
|
|
||||||
|
for paramlist in ('()', '(x)', '(x, y)', '(z: T)'):
|
||||||
|
for func in (
|
||||||
|
f'def genexpr{paramlist}:pass',
|
||||||
|
f'async def genexpr{paramlist}:pass',
|
||||||
|
f'def genexpr[T]{paramlist}:pass',
|
||||||
|
f'async def genexpr[T]{paramlist}:pass',
|
||||||
|
):
|
||||||
|
with self.subTest(func=func):
|
||||||
|
# test function named 'genexpr'
|
||||||
|
check_body(func, ('genexpr',))
|
||||||
|
|
||||||
|
for gen in GENEXPRS:
|
||||||
|
with self.subTest(gen=gen, func=func):
|
||||||
|
# test generator expression + function named 'genexpr'
|
||||||
|
check_body('\n'.join((gen, func)), ('genexpr',))
|
||||||
|
check_body('\n'.join((func, gen)), ('genexpr',))
|
||||||
|
|
||||||
def test_filename_correct(self):
|
def test_filename_correct(self):
|
||||||
### Bug tickler: SyntaxError file name correct whether error raised
|
### Bug tickler: SyntaxError file name correct whether error raised
|
||||||
### while parsing or building symbol table.
|
### while parsing or building symbol table.
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue