diff --git a/Doc/library/symtable.rst b/Doc/library/symtable.rst index f5e6f9f8acfdb8..a9653c8ed6e96f 100644 --- a/Doc/library/symtable.rst +++ b/Doc/library/symtable.rst @@ -180,6 +180,10 @@ Examining Symbol Tables Return a tuple containing names of :term:`free (closure) variables ` in this function. + .. method:: get_cells() + + Return a tuple containing names of :term:`cell variables ` in this table. + .. class:: Class @@ -291,6 +295,12 @@ Examining Symbol Tables Return ``True`` if the symbol is referenced in its block, but not assigned to. + .. method:: is_cell() + + Return ``True`` if the symbol is referenced from a nested block. + + .. versionadded:: 3.15 + .. method:: is_free_class() Return *True* if a class-scoped symbol is free from diff --git a/Doc/whatsnew/3.15.rst b/Doc/whatsnew/3.15.rst index 1d4961d7293631..4b678e49a44533 100644 --- a/Doc/whatsnew/3.15.rst +++ b/Doc/whatsnew/3.15.rst @@ -737,6 +737,13 @@ ssl (Contributed by Ron Frederick in :gh:`138252`.) +symtable +-------- + +* Add :meth:`symtable.Function.get_cells` and :meth:`symtable.Symbol.is_cell` methods. + (Contributed by Yashp002 in :gh:`143504`.) + + sys --- diff --git a/Lib/symtable.py b/Lib/symtable.py index 4c832e68f94cbd..7a9c189d331ac8 100644 --- a/Lib/symtable.py +++ b/Lib/symtable.py @@ -184,6 +184,7 @@ class Function(SymbolTable): __frees = None __globals = None __nonlocals = None + __cells = None def __idents_matching(self, test_func): return tuple(ident for ident in self.get_identifiers() @@ -229,6 +230,14 @@ def get_frees(self): self.__frees = self.__idents_matching(is_free) return self.__frees + def get_cells(self): + """Return a tuple of cell variable names in the table. + """ + if self.__cells is None: + is_cell = lambda x: _get_scope(x) == CELL + self.__cells = self.__idents_matching(is_cell) + return self.__cells + class Class(SymbolTable): @@ -342,6 +351,10 @@ def is_free(self): """ return bool(self.__scope == FREE) + def is_cell(self): + """Return *True* if the symbol is a cell variable.""" + return bool(self.__scope == CELL) + def is_free_class(self): """Return *True* if a class-scoped symbol is free from the perspective of a method.""" diff --git a/Lib/test/test_symtable.py b/Lib/test/test_symtable.py index 094ab8f573e7ba..c748243110df9f 100644 --- a/Lib/test/test_symtable.py +++ b/Lib/test/test_symtable.py @@ -255,6 +255,7 @@ def test_function_info(self): self.assertEqual(sorted(func.get_locals()), expected) self.assertEqual(sorted(func.get_globals()), ["bar", "glob", "some_assigned_global_var"]) self.assertEqual(self.internal.get_frees(), ("x",)) + self.assertEqual(self.spam.get_cells(), ("some_var", "x",)) def test_globals(self): self.assertTrue(self.spam.lookup("glob").is_global()) @@ -284,6 +285,9 @@ def test_local(self): def test_free(self): self.assertTrue(self.internal.lookup("x").is_free()) + def test_cells(self): + self.assertTrue(self.spam.lookup("x").is_cell()) + def test_referenced(self): self.assertTrue(self.internal.lookup("x").is_referenced()) self.assertTrue(self.spam.lookup("internal").is_referenced()) diff --git a/Misc/NEWS.d/next/Library/2026-01-24-13-49-05.gh-issue-143594.nilGlg.rst b/Misc/NEWS.d/next/Library/2026-01-24-13-49-05.gh-issue-143594.nilGlg.rst new file mode 100644 index 00000000000000..ab2c3dc106b471 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-01-24-13-49-05.gh-issue-143594.nilGlg.rst @@ -0,0 +1,2 @@ +Add :meth:`symtable.Function.get_cells` and :meth:`symtable.Symbol.is_cell` methods. +