gh-132775: Unrevert "Add _PyCode_VerifyStateless()" by ericsnowcurrently · Pull Request #133528 · python/cpython · GitHub
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions Include/internal/pycore_code.h
7 changes: 7 additions & 0 deletions Include/internal/pycore_function.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,13 @@ PyFunctionObject *_PyFunction_LookupByVersion(uint32_t version, PyObject **p_cod
extern PyObject *_Py_set_function_type_params(
PyThreadState* unused, PyObject *func, PyObject *type_params);


/* See pycore_code.h for explanation about what "stateless" means. */

PyAPI_FUNC(int)
_PyFunction_VerifyStateless(PyThreadState *, PyObject *);


#ifdef __cplusplus
}
#endif
Expand Down
2 changes: 2 additions & 0 deletions Include/internal/pycore_opcode_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ extern "C" {

#define IS_RETURN_OPCODE(opcode) \
(opcode == RETURN_VALUE)
#define IS_RAISE_OPCODE(opcode) \
(opcode == RAISE_VARARGS || opcode == RERAISE)


/* Flags used in the oparg for MAKE_FUNCTION */
Expand Down
26 changes: 26 additions & 0 deletions Lib/test/_code_definitions.py
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,32 @@ def ham_C_closure(z):
*NESTED_FUNCTIONS,
]

STATELESS_FUNCTIONS = [
spam,
spam_minimal,
spam_with_builtins,
spam_args_attrs_and_builtins,
spam_returns_arg,
spam_annotated,
spam_with_inner_not_closure,
spam_with_inner_closure,
spam_N,
spam_C,
spam_NN,
spam_NC,
spam_CN,
spam_CC,
eggs_nested,
eggs_nested_N,
ham_nested,
ham_C_nested
]
STATELESS_CODE = [
*STATELESS_FUNCTIONS,
spam_with_globals_and_builtins,
spam_full,
]


# generators

Expand Down
54 changes: 34 additions & 20 deletions Lib/test/test_code.py
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,7 @@
import _testinternalcapi
except ModuleNotFoundError:
_testinternalcapi = None
import test._code_definitions as defs

COPY_FREE_VARS = opmap['COPY_FREE_VARS']

Expand Down Expand Up @@ -671,7 +672,6 @@ def test_local_kinds(self):
VARARGS = CO_FAST_LOCAL | CO_FAST_ARG_VAR | CO_FAST_ARG_POS
VARKWARGS = CO_FAST_LOCAL | CO_FAST_ARG_VAR | CO_FAST_ARG_KW

import test._code_definitions as defs
funcs = {
defs.spam_minimal: {},
defs.spam_with_builtins: {
Expand Down Expand Up @@ -897,7 +897,6 @@ def new_var_counts(*,
},
}

import test._code_definitions as defs
funcs = {
defs.spam_minimal: new_var_counts(),
defs.spam_with_builtins: new_var_counts(
Expand Down Expand Up @@ -1025,55 +1024,70 @@ def new_var_counts(*,
counts = _testinternalcapi.get_code_var_counts(func.__code__)
self.assertEqual(counts, expected)

def func_with_globals_and_builtins():
mod1 = _testinternalcapi
mod2 = dis
mods = (mod1, mod2)
checks = tuple(callable(m) for m in mods)
return callable(mod2), tuple(mods), list(mods), checks

func = func_with_globals_and_builtins
func = defs.spam_with_globals_and_builtins
with self.subTest(f'{func} code'):
expected = new_var_counts(
purelocals=4,
globalvars=5,
purelocals=5,
globalvars=6,
)
counts = _testinternalcapi.get_code_var_counts(func.__code__)
self.assertEqual(counts, expected)

with self.subTest(f'{func} with own globals and builtins'):
expected = new_var_counts(
purelocals=4,
globalvars=(2, 3),
purelocals=5,
globalvars=(2, 4),
)
counts = _testinternalcapi.get_code_var_counts(func)
self.assertEqual(counts, expected)

with self.subTest(f'{func} without globals'):
expected = new_var_counts(
purelocals=4,
globalvars=(0, 3, 2),
purelocals=5,
globalvars=(0, 4, 2),
)
counts = _testinternalcapi.get_code_var_counts(func, globalsns={})
self.assertEqual(counts, expected)

with self.subTest(f'{func} without both'):
expected = new_var_counts(
purelocals=4,
globalvars=5,
purelocals=5,
globalvars=6,
)
counts = _testinternalcapi.get_code_var_counts(func, globalsns={},
builtinsns={})
self.assertEqual(counts, expected)

with self.subTest(f'{func} without builtins'):
expected = new_var_counts(
purelocals=4,
globalvars=(2, 0, 3),
purelocals=5,
globalvars=(2, 0, 4),
)
counts = _testinternalcapi.get_code_var_counts(func, builtinsns={})
self.assertEqual(counts, expected)

@unittest.skipIf(_testinternalcapi is None, "missing _testinternalcapi")
def test_stateless(self):
self.maxDiff = None

for func in defs.STATELESS_CODE:
with self.subTest((func, '(code)')):
_testinternalcapi.verify_stateless_code(func.__code__)
for func in defs.STATELESS_FUNCTIONS:
with self.subTest((func, '(func)')):
_testinternalcapi.verify_stateless_code(func)

for func in defs.FUNCTIONS:
if func not in defs.STATELESS_CODE:
with self.subTest((func, '(code)')):
with self.assertRaises(Exception):
_testinternalcapi.verify_stateless_code(func.__code__)

if func not in defs.STATELESS_FUNCTIONS:
with self.subTest((func, '(func)')):
with self.assertRaises(Exception):
_testinternalcapi.verify_stateless_code(func)


def isinterned(s):
return s is sys.intern(('_' + s + '_')[1:-1])
Expand Down
43 changes: 43 additions & 0 deletions Modules/_testinternalcapi.c
Loading