Skip to content

Commit

Permalink
pythongh-110045: Update symtable module for PEP 695 (python#110066)
Browse files Browse the repository at this point in the history
  • Loading branch information
JelleZijlstra committed Sep 29, 2023
1 parent 2e37a38 commit 7dc2c50
Show file tree
Hide file tree
Showing 5 changed files with 63 additions and 5 deletions.
12 changes: 11 additions & 1 deletion Doc/library/symtable.rst
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,13 @@ Examining Symbol Tables
.. method:: get_type()

Return the type of the symbol table. Possible values are ``'class'``,
``'module'``, and ``'function'``.
``'module'``, ``'function'``, ``'annotation'``, ``'TypeVar bound'``,
``'type alias'``, and ``'type parameter'``. The latter four refer to
different flavors of :ref:`annotation scopes <annotation-scopes>`.

.. versionchanged:: 3.12
Added ``'annotation'``, ``'TypeVar bound'``, ``'type alias'``,
and ``'type parameter'`` as possible return values.

.. method:: get_id()

Expand All @@ -49,6 +55,10 @@ Examining Symbol Tables
Return the table's name. This is the name of the class if the table is
for a class, the name of the function if the table is for a function, or
``'top'`` if the table is global (:meth:`get_type` returns ``'module'``).
For type parameter scopes (which are used for generic classes, functions,
and type aliases), it is the name of the underlying class, function, or
type alias. For type alias scopes, it is the name of the type alias.
For :class:`~typing.TypeVar` bound scopes, it is the name of the ``TypeVar``.

.. method:: get_lineno()

Expand Down
15 changes: 11 additions & 4 deletions Lib/symtable.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,17 +62,24 @@ def __repr__(self):
def get_type(self):
"""Return the type of the symbol table.
The values returned are 'class', 'module' and
'function'.
The values returned are 'class', 'module', 'function',
'annotation', 'TypeVar bound', 'type alias', and 'type parameter'.
"""
if self._table.type == _symtable.TYPE_MODULE:
return "module"
if self._table.type == _symtable.TYPE_FUNCTION:
return "function"
if self._table.type == _symtable.TYPE_CLASS:
return "class"
assert self._table.type in (1, 2, 3), \
"unexpected type: {0}".format(self._table.type)
if self._table.type == _symtable.TYPE_ANNOTATION:
return "annotation"
if self._table.type == _symtable.TYPE_TYPE_VAR_BOUND:
return "TypeVar bound"
if self._table.type == _symtable.TYPE_TYPE_ALIAS:
return "type alias"
if self._table.type == _symtable.TYPE_TYPE_PARAM:
return "type parameter"
assert False, f"unexpected type: {self._table.type}"

def get_id(self):
"""Return an identifier for the table.
Expand Down
31 changes: 31 additions & 0 deletions Lib/test/test_symtable.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,15 @@ def foo():
def namespace_test(): pass
def namespace_test(): pass
type Alias = int
type GenericAlias[T] = list[T]
def generic_spam[T](a):
pass
class GenericMine[T: int]:
pass
"""


Expand All @@ -59,20 +68,42 @@ class SymtableTest(unittest.TestCase):
internal = find_block(spam, "internal")
other_internal = find_block(spam, "other_internal")
foo = find_block(top, "foo")
Alias = find_block(top, "Alias")
GenericAlias = find_block(top, "GenericAlias")
GenericAlias_inner = find_block(GenericAlias, "GenericAlias")
generic_spam = find_block(top, "generic_spam")
generic_spam_inner = find_block(generic_spam, "generic_spam")
GenericMine = find_block(top, "GenericMine")
GenericMine_inner = find_block(GenericMine, "GenericMine")
T = find_block(GenericMine, "T")

def test_type(self):
self.assertEqual(self.top.get_type(), "module")
self.assertEqual(self.Mine.get_type(), "class")
self.assertEqual(self.a_method.get_type(), "function")
self.assertEqual(self.spam.get_type(), "function")
self.assertEqual(self.internal.get_type(), "function")
self.assertEqual(self.foo.get_type(), "function")
self.assertEqual(self.Alias.get_type(), "type alias")
self.assertEqual(self.GenericAlias.get_type(), "type parameter")
self.assertEqual(self.GenericAlias_inner.get_type(), "type alias")
self.assertEqual(self.generic_spam.get_type(), "type parameter")
self.assertEqual(self.generic_spam_inner.get_type(), "function")
self.assertEqual(self.GenericMine.get_type(), "type parameter")
self.assertEqual(self.GenericMine_inner.get_type(), "class")
self.assertEqual(self.T.get_type(), "TypeVar bound")

def test_id(self):
self.assertGreater(self.top.get_id(), 0)
self.assertGreater(self.Mine.get_id(), 0)
self.assertGreater(self.a_method.get_id(), 0)
self.assertGreater(self.spam.get_id(), 0)
self.assertGreater(self.internal.get_id(), 0)
self.assertGreater(self.foo.get_id(), 0)
self.assertGreater(self.Alias.get_id(), 0)
self.assertGreater(self.GenericAlias.get_id(), 0)
self.assertGreater(self.generic_spam.get_id(), 0)
self.assertGreater(self.GenericMine.get_id(), 0)

def test_optimized(self):
self.assertFalse(self.top.is_optimized())
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Update the :mod:`symtable` module to support the new scopes introduced by
:pep:`695`.
8 changes: 8 additions & 0 deletions Modules/symtablemodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,14 @@ symtable_init_constants(PyObject *m)
if (PyModule_AddIntConstant(m, "TYPE_CLASS", ClassBlock) < 0) return -1;
if (PyModule_AddIntConstant(m, "TYPE_MODULE", ModuleBlock) < 0)
return -1;
if (PyModule_AddIntConstant(m, "TYPE_ANNOTATION", AnnotationBlock) < 0)
return -1;
if (PyModule_AddIntConstant(m, "TYPE_TYPE_VAR_BOUND", TypeVarBoundBlock) < 0)
return -1;
if (PyModule_AddIntConstant(m, "TYPE_TYPE_ALIAS", TypeAliasBlock) < 0)
return -1;
if (PyModule_AddIntConstant(m, "TYPE_TYPE_PARAM", TypeParamBlock) < 0)
return -1;

if (PyModule_AddIntMacro(m, LOCAL) < 0) return -1;
if (PyModule_AddIntMacro(m, GLOBAL_EXPLICIT) < 0) return -1;
Expand Down

0 comments on commit 7dc2c50

Please sign in to comment.