1414#include "pycore_object.h" // _PyObject_GC_UNTRACK()
1515#include "pycore_opcode_metadata.h" // _PyOpcode_Caches
1616#include "pycore_optimizer.h" // _Py_Executors_InvalidateDependency()
17+ #include "pycore_symtable.h" // _PyST_IsClassClosureName()
1718#include "pycore_tuple.h" // _PyTuple_FromPair
1819#include "pycore_unicodeobject.h" // _PyUnicode_Equal()
1920#include "pycore_weakref.h" // FT_CLEAR_WEAKREFS()
@@ -94,6 +95,28 @@ framelocalsproxy_hasval(_PyInterpreterFrame *frame, PyCodeObject *co, int i)
9495 return true;
9596}
9697
98+ /* 1 = include, 0 = skip duplicate, -1 = error.
99+ * Class-closure names are not slot-reused, so localsplus can hold both a
100+ * comprehension local and a free; track only those names in seen. */
101+ static int
102+ framelocalsproxy_include_name (PyObject * seen , PyObject * name )
103+ {
104+ if (!_PyST_IsClassClosureName (name )) {
105+ return 1 ;
106+ }
107+ int found = PySet_Contains (seen , name );
108+ if (found < 0 ) {
109+ return -1 ;
110+ }
111+ if (found ) {
112+ return 0 ;
113+ }
114+ if (PySet_Add (seen , name ) < 0 ) {
115+ return -1 ;
116+ }
117+ return 1 ;
118+ }
119+
97120static int
98121framelocalsproxy_getkeyindex (PyFrameObject * frame , PyObject * key , bool read , PyObject * * value_ptr )
99122{
@@ -380,13 +403,24 @@ framelocalsproxy_keys(PyObject *self, PyObject *Py_UNUSED(ignored))
380403 if (names == NULL ) {
381404 return NULL ;
382405 }
406+ PyObject * seen = PySet_New (NULL );
407+ if (seen == NULL ) {
408+ Py_DECREF (names );
409+ return NULL ;
410+ }
383411
384412 for (int i = 0 ; i < co -> co_nlocalsplus ; i ++ ) {
385413 if (framelocalsproxy_hasval (frame -> f_frame , co , i )) {
386414 PyObject * name = PyTuple_GET_ITEM (co -> co_localsplusnames , i );
415+ int include = framelocalsproxy_include_name (seen , name );
416+ if (include < 0 ) {
417+ goto error ;
418+ }
419+ if (!include ) {
420+ continue ;
421+ }
387422 if (PyList_Append (names , name ) < 0 ) {
388- Py_DECREF (names );
389- return NULL ;
423+ goto error ;
390424 }
391425 }
392426 }
@@ -401,15 +435,21 @@ framelocalsproxy_keys(PyObject *self, PyObject *Py_UNUSED(ignored))
401435
402436 while (PyDict_Next (frame -> f_extra_locals , & i , & key , & value )) {
403437 if (PyList_Append (names , key ) < 0 ) {
404- Py_DECREF (names );
405- return NULL ;
438+ goto error ;
406439 }
407440 }
408441 }
409442
443+ Py_DECREF (seen );
410444 return names ;
445+
446+ error :
447+ Py_DECREF (seen );
448+ Py_DECREF (names );
449+ return NULL ;
411450}
412451
452+
413453static void
414454framelocalsproxy_dealloc (PyObject * self )
415455{
@@ -589,14 +629,28 @@ framelocalsproxy_values(PyObject *self, PyObject *Py_UNUSED(ignored))
589629 if (values == NULL ) {
590630 return NULL ;
591631 }
632+ PyObject * seen = PySet_New (NULL );
633+ if (seen == NULL ) {
634+ Py_DECREF (values );
635+ return NULL ;
636+ }
592637
593638 for (int i = 0 ; i < co -> co_nlocalsplus ; i ++ ) {
594639 PyObject * value = framelocalsproxy_getval (frame -> f_frame , co , i );
595640 if (value ) {
641+ PyObject * name = PyTuple_GET_ITEM (co -> co_localsplusnames , i );
642+ int include = framelocalsproxy_include_name (seen , name );
643+ if (include < 0 ) {
644+ Py_DECREF (value );
645+ goto error ;
646+ }
647+ if (!include ) {
648+ Py_DECREF (value );
649+ continue ;
650+ }
596651 if (PyList_Append (values , value ) < 0 ) {
597652 Py_DECREF (value );
598- Py_DECREF (values );
599- return NULL ;
653+ goto error ;
600654 }
601655 Py_DECREF (value );
602656 }
@@ -609,15 +663,21 @@ framelocalsproxy_values(PyObject *self, PyObject *Py_UNUSED(ignored))
609663 PyObject * value = NULL ;
610664 while (PyDict_Next (frame -> f_extra_locals , & j , & key , & value )) {
611665 if (PyList_Append (values , value ) < 0 ) {
612- Py_DECREF (values );
613- return NULL ;
666+ goto error ;
614667 }
615668 }
616669 }
617670
671+ Py_DECREF (seen );
618672 return values ;
673+
674+ error :
675+ Py_DECREF (seen );
676+ Py_DECREF (values );
677+ return NULL ;
619678}
620679
680+
621681static PyObject *
622682framelocalsproxy_items (PyObject * self , PyObject * Py_UNUSED (ignored ))
623683{
@@ -627,12 +687,26 @@ framelocalsproxy_items(PyObject *self, PyObject *Py_UNUSED(ignored))
627687 if (items == NULL ) {
628688 return NULL ;
629689 }
690+ PyObject * seen = PySet_New (NULL );
691+ if (seen == NULL ) {
692+ Py_DECREF (items );
693+ return NULL ;
694+ }
630695
631696 for (int i = 0 ; i < co -> co_nlocalsplus ; i ++ ) {
632697 PyObject * name = PyTuple_GET_ITEM (co -> co_localsplusnames , i );
633698 PyObject * value = framelocalsproxy_getval (frame -> f_frame , co , i );
634699
635700 if (value ) {
701+ int include = framelocalsproxy_include_name (seen , name );
702+ if (include < 0 ) {
703+ Py_DECREF (value );
704+ goto error ;
705+ }
706+ if (!include ) {
707+ Py_DECREF (value );
708+ continue ;
709+ }
636710 PyObject * pair = _PyTuple_FromPairSteal (Py_NewRef (name ), value );
637711 if (pair == NULL ) {
638712 goto error ;
@@ -660,13 +734,16 @@ framelocalsproxy_items(PyObject *self, PyObject *Py_UNUSED(ignored))
660734 }
661735 }
662736
737+ Py_DECREF (seen );
663738 return items ;
664739
665740error :
741+ Py_DECREF (seen );
666742 Py_DECREF (items );
667743 return NULL ;
668744}
669745
746+
670747static Py_ssize_t
671748framelocalsproxy_length (PyObject * self )
672749{
@@ -679,14 +756,28 @@ framelocalsproxy_length(PyObject *self)
679756 size += PyDict_Size (frame -> f_extra_locals );
680757 }
681758
759+ PyObject * seen = PySet_New (NULL );
760+ if (seen == NULL ) {
761+ return -1 ;
762+ }
682763 for (int i = 0 ; i < co -> co_nlocalsplus ; i ++ ) {
683764 if (framelocalsproxy_hasval (frame -> f_frame , co , i )) {
684- size ++ ;
765+ PyObject * name = PyTuple_GET_ITEM (co -> co_localsplusnames , i );
766+ int include = framelocalsproxy_include_name (seen , name );
767+ if (include < 0 ) {
768+ Py_DECREF (seen );
769+ return -1 ;
770+ }
771+ if (include ) {
772+ size ++ ;
773+ }
685774 }
686775 }
776+ Py_DECREF (seen );
687777 return size ;
688778}
689779
780+
690781static int
691782framelocalsproxy_contains (PyObject * self , PyObject * key )
692783{
0 commit comments