Skip to content

Commit 24ebb9c

Browse files
gh-132775: Unrevert "Add _PyCode_GetVarCounts()" (gh-133265)
This reverts commit 811edcf (gh-133232), which itself reverted the original commit 811edcf (gh-133128). We reverted the original change due to failing s390 builds (a big-endian architecture). It ended up that I had not accommodated op caches.
1 parent b275b8f commit 24ebb9c

File tree

5 files changed

+732
-3
lines changed

5 files changed

+732
-3
lines changed

Include/cpython/funcobject.h

+5
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,11 @@ static inline PyObject* PyFunction_GET_GLOBALS(PyObject *func) {
9797
}
9898
#define PyFunction_GET_GLOBALS(func) PyFunction_GET_GLOBALS(_PyObject_CAST(func))
9999

100+
static inline PyObject* PyFunction_GET_BUILTINS(PyObject *func) {
101+
return _PyFunction_CAST(func)->func_builtins;
102+
}
103+
#define PyFunction_GET_BUILTINS(func) PyFunction_GET_BUILTINS(_PyObject_CAST(func))
104+
100105
static inline PyObject* PyFunction_GET_MODULE(PyObject *func) {
101106
return _PyFunction_CAST(func)->func_module;
102107
}

Include/internal/pycore_code.h

+51
Original file line numberDiff line numberDiff line change
@@ -570,6 +570,57 @@ extern int _Py_ClearUnusedTLBC(PyInterpreterState *interp);
570570
#endif
571571

572572

573+
typedef struct {
574+
int total;
575+
struct co_locals_counts {
576+
int total;
577+
struct {
578+
int total;
579+
int numposonly;
580+
int numposorkw;
581+
int numkwonly;
582+
int varargs;
583+
int varkwargs;
584+
} args;
585+
int numpure;
586+
struct {
587+
int total;
588+
// numargs does not contribute to locals.total.
589+
int numargs;
590+
int numothers;
591+
} cells;
592+
struct {
593+
int total;
594+
int numpure;
595+
int numcells;
596+
} hidden;
597+
} locals;
598+
int numfree; // nonlocal
599+
struct co_unbound_counts {
600+
int total;
601+
struct {
602+
int total;
603+
int numglobal;
604+
int numbuiltin;
605+
int numunknown;
606+
} globals;
607+
int numattrs;
608+
int numunknown;
609+
} unbound;
610+
} _PyCode_var_counts_t;
611+
612+
PyAPI_FUNC(void) _PyCode_GetVarCounts(
613+
PyCodeObject *,
614+
_PyCode_var_counts_t *);
615+
PyAPI_FUNC(int) _PyCode_SetUnboundVarCounts(
616+
PyThreadState *,
617+
PyCodeObject *,
618+
_PyCode_var_counts_t *,
619+
PyObject *globalnames,
620+
PyObject *attrnames,
621+
PyObject *globalsns,
622+
PyObject *builtinsns);
623+
573624
PyAPI_FUNC(int) _PyCode_ReturnsOnlyNone(PyCodeObject *);
574625

575626

Lib/test/test_code.py

+230
Original file line numberDiff line numberDiff line change
@@ -777,6 +777,236 @@ def test_local_kinds(self):
777777
kinds = _testinternalcapi.get_co_localskinds(func.__code__)
778778
self.assertEqual(kinds, expected)
779779

780+
@unittest.skipIf(_testinternalcapi is None, "missing _testinternalcapi")
781+
def test_var_counts(self):
782+
self.maxDiff = None
783+
def new_var_counts(*,
784+
posonly=0,
785+
posorkw=0,
786+
kwonly=0,
787+
varargs=0,
788+
varkwargs=0,
789+
purelocals=0,
790+
argcells=0,
791+
othercells=0,
792+
freevars=0,
793+
globalvars=0,
794+
attrs=0,
795+
unknown=0,
796+
):
797+
nargvars = posonly + posorkw + kwonly + varargs + varkwargs
798+
nlocals = nargvars + purelocals + othercells
799+
if isinstance(globalvars, int):
800+
globalvars = {
801+
'total': globalvars,
802+
'numglobal': 0,
803+
'numbuiltin': 0,
804+
'numunknown': globalvars,
805+
}
806+
else:
807+
g_numunknown = 0
808+
if isinstance(globalvars, dict):
809+
numglobal = globalvars['numglobal']
810+
numbuiltin = globalvars['numbuiltin']
811+
size = 2
812+
if 'numunknown' in globalvars:
813+
g_numunknown = globalvars['numunknown']
814+
size += 1
815+
assert len(globalvars) == size, globalvars
816+
else:
817+
assert not isinstance(globalvars, str), repr(globalvars)
818+
try:
819+
numglobal, numbuiltin = globalvars
820+
except ValueError:
821+
numglobal, numbuiltin, g_numunknown = globalvars
822+
globalvars = {
823+
'total': numglobal + numbuiltin + g_numunknown,
824+
'numglobal': numglobal,
825+
'numbuiltin': numbuiltin,
826+
'numunknown': g_numunknown,
827+
}
828+
unbound = globalvars['total'] + attrs + unknown
829+
return {
830+
'total': nlocals + freevars + unbound,
831+
'locals': {
832+
'total': nlocals,
833+
'args': {
834+
'total': nargvars,
835+
'numposonly': posonly,
836+
'numposorkw': posorkw,
837+
'numkwonly': kwonly,
838+
'varargs': varargs,
839+
'varkwargs': varkwargs,
840+
},
841+
'numpure': purelocals,
842+
'cells': {
843+
'total': argcells + othercells,
844+
'numargs': argcells,
845+
'numothers': othercells,
846+
},
847+
'hidden': {
848+
'total': 0,
849+
'numpure': 0,
850+
'numcells': 0,
851+
},
852+
},
853+
'numfree': freevars,
854+
'unbound': {
855+
'total': unbound,
856+
'globals': globalvars,
857+
'numattrs': attrs,
858+
'numunknown': unknown,
859+
},
860+
}
861+
862+
import test._code_definitions as defs
863+
funcs = {
864+
defs.spam_minimal: new_var_counts(),
865+
defs.spam_full: new_var_counts(
866+
posonly=2,
867+
posorkw=2,
868+
kwonly=2,
869+
varargs=1,
870+
varkwargs=1,
871+
purelocals=4,
872+
globalvars=3,
873+
attrs=1,
874+
),
875+
defs.spam: new_var_counts(
876+
posorkw=1,
877+
),
878+
defs.spam_N: new_var_counts(
879+
posorkw=1,
880+
purelocals=1,
881+
),
882+
defs.spam_C: new_var_counts(
883+
posorkw=1,
884+
purelocals=1,
885+
argcells=1,
886+
othercells=1,
887+
),
888+
defs.spam_NN: new_var_counts(
889+
posorkw=1,
890+
purelocals=1,
891+
),
892+
defs.spam_NC: new_var_counts(
893+
posorkw=1,
894+
purelocals=1,
895+
argcells=1,
896+
othercells=1,
897+
),
898+
defs.spam_CN: new_var_counts(
899+
posorkw=1,
900+
purelocals=1,
901+
argcells=1,
902+
othercells=1,
903+
),
904+
defs.spam_CC: new_var_counts(
905+
posorkw=1,
906+
purelocals=1,
907+
argcells=1,
908+
othercells=1,
909+
),
910+
defs.eggs_nested: new_var_counts(
911+
posorkw=1,
912+
),
913+
defs.eggs_closure: new_var_counts(
914+
posorkw=1,
915+
freevars=2,
916+
),
917+
defs.eggs_nested_N: new_var_counts(
918+
posorkw=1,
919+
purelocals=1,
920+
),
921+
defs.eggs_nested_C: new_var_counts(
922+
posorkw=1,
923+
purelocals=1,
924+
argcells=1,
925+
freevars=2,
926+
),
927+
defs.eggs_closure_N: new_var_counts(
928+
posorkw=1,
929+
purelocals=1,
930+
freevars=2,
931+
),
932+
defs.eggs_closure_C: new_var_counts(
933+
posorkw=1,
934+
purelocals=1,
935+
argcells=1,
936+
othercells=1,
937+
freevars=2,
938+
),
939+
defs.ham_nested: new_var_counts(
940+
posorkw=1,
941+
),
942+
defs.ham_closure: new_var_counts(
943+
posorkw=1,
944+
freevars=3,
945+
),
946+
defs.ham_C_nested: new_var_counts(
947+
posorkw=1,
948+
),
949+
defs.ham_C_closure: new_var_counts(
950+
posorkw=1,
951+
freevars=4,
952+
),
953+
}
954+
assert len(funcs) == len(defs.FUNCTIONS), (len(funcs), len(defs.FUNCTIONS))
955+
for func in defs.FUNCTIONS:
956+
with self.subTest(func):
957+
expected = funcs[func]
958+
counts = _testinternalcapi.get_code_var_counts(func.__code__)
959+
self.assertEqual(counts, expected)
960+
961+
def func_with_globals_and_builtins():
962+
mod1 = _testinternalcapi
963+
mod2 = dis
964+
mods = (mod1, mod2)
965+
checks = tuple(callable(m) for m in mods)
966+
return callable(mod2), tuple(mods), list(mods), checks
967+
968+
func = func_with_globals_and_builtins
969+
with self.subTest(f'{func} code'):
970+
expected = new_var_counts(
971+
purelocals=4,
972+
globalvars=5,
973+
)
974+
counts = _testinternalcapi.get_code_var_counts(func.__code__)
975+
self.assertEqual(counts, expected)
976+
977+
with self.subTest(f'{func} with own globals and builtins'):
978+
expected = new_var_counts(
979+
purelocals=4,
980+
globalvars=(2, 3),
981+
)
982+
counts = _testinternalcapi.get_code_var_counts(func)
983+
self.assertEqual(counts, expected)
984+
985+
with self.subTest(f'{func} without globals'):
986+
expected = new_var_counts(
987+
purelocals=4,
988+
globalvars=(0, 3, 2),
989+
)
990+
counts = _testinternalcapi.get_code_var_counts(func, globalsns={})
991+
self.assertEqual(counts, expected)
992+
993+
with self.subTest(f'{func} without both'):
994+
expected = new_var_counts(
995+
purelocals=4,
996+
globalvars=5,
997+
)
998+
counts = _testinternalcapi.get_code_var_counts(func, globalsns={},
999+
builtinsns={})
1000+
self.assertEqual(counts, expected)
1001+
1002+
with self.subTest(f'{func} without builtins'):
1003+
expected = new_var_counts(
1004+
purelocals=4,
1005+
globalvars=(2, 0, 3),
1006+
)
1007+
counts = _testinternalcapi.get_code_var_counts(func, builtinsns={})
1008+
self.assertEqual(counts, expected)
1009+
7801010

7811011
def isinterned(s):
7821012
return s is sys.intern(('_' + s + '_')[1:-1])

0 commit comments

Comments
 (0)