gh-87859: Track Code Object Local Kinds For Arguments (gh-132980)

Doing this was always the intention. I was finally motivated to find the time to do it.

See #87859 (comment).
This commit is contained in:
Eric Snow 2025-04-28 20:21:47 -06:00 committed by GitHub
parent 96a7fb93a8
commit 219d8d24b5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 372 additions and 194 deletions

View file

@ -246,6 +246,7 @@ def dump(co):
def external_getitem(self, i):
return f"Foreign getitem: {super().__getitem__(i)}"
class CodeTest(unittest.TestCase):
@cpython_only
@ -654,6 +655,128 @@ class CodeTest(unittest.TestCase):
self.assertNotEqual(code1, code2)
sys.settrace(None)
@unittest.skipIf(_testinternalcapi is None, "missing _testinternalcapi")
def test_local_kinds(self):
CO_FAST_ARG_POS = 0x02
CO_FAST_ARG_KW = 0x04
CO_FAST_ARG_VAR = 0x08
CO_FAST_HIDDEN = 0x10
CO_FAST_LOCAL = 0x20
CO_FAST_CELL = 0x40
CO_FAST_FREE = 0x80
POSONLY = CO_FAST_LOCAL | CO_FAST_ARG_POS
POSORKW = CO_FAST_LOCAL | CO_FAST_ARG_POS | CO_FAST_ARG_KW
KWONLY = CO_FAST_LOCAL | CO_FAST_ARG_KW
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_full: {
'a': POSONLY,
'b': POSONLY,
'c': POSORKW,
'd': POSORKW,
'e': KWONLY,
'f': KWONLY,
'args': VARARGS,
'kwargs': VARKWARGS,
'x': CO_FAST_LOCAL,
'y': CO_FAST_LOCAL,
'z': CO_FAST_LOCAL,
'extras': CO_FAST_LOCAL,
},
defs.spam: {
'x': POSORKW,
},
defs.spam_N: {
'x': POSORKW,
'eggs_nested': CO_FAST_LOCAL,
},
defs.spam_C: {
'x': POSORKW | CO_FAST_CELL,
'a': CO_FAST_CELL,
'eggs_closure': CO_FAST_LOCAL,
},
defs.spam_NN: {
'x': POSORKW,
'eggs_nested_N': CO_FAST_LOCAL,
},
defs.spam_NC: {
'x': POSORKW | CO_FAST_CELL,
'a': CO_FAST_CELL,
'eggs_nested_C': CO_FAST_LOCAL,
},
defs.spam_CN: {
'x': POSORKW | CO_FAST_CELL,
'a': CO_FAST_CELL,
'eggs_closure_N': CO_FAST_LOCAL,
},
defs.spam_CC: {
'x': POSORKW | CO_FAST_CELL,
'a': CO_FAST_CELL,
'eggs_closure_C': CO_FAST_LOCAL,
},
defs.eggs_nested: {
'y': POSORKW,
},
defs.eggs_closure: {
'y': POSORKW,
'x': CO_FAST_FREE,
'a': CO_FAST_FREE,
},
defs.eggs_nested_N: {
'y': POSORKW,
'ham_nested': CO_FAST_LOCAL,
},
defs.eggs_nested_C: {
'y': POSORKW | CO_FAST_CELL,
'x': CO_FAST_FREE,
'a': CO_FAST_FREE,
'ham_closure': CO_FAST_LOCAL,
},
defs.eggs_closure_N: {
'y': POSORKW,
'x': CO_FAST_FREE,
'a': CO_FAST_FREE,
'ham_C_nested': CO_FAST_LOCAL,
},
defs.eggs_closure_C: {
'y': POSORKW | CO_FAST_CELL,
'b': CO_FAST_CELL,
'x': CO_FAST_FREE,
'a': CO_FAST_FREE,
'ham_C_closure': CO_FAST_LOCAL,
},
defs.ham_nested: {
'z': POSORKW,
},
defs.ham_closure: {
'z': POSORKW,
'y': CO_FAST_FREE,
'x': CO_FAST_FREE,
'a': CO_FAST_FREE,
},
defs.ham_C_nested: {
'z': POSORKW,
},
defs.ham_C_closure: {
'z': POSORKW,
'y': CO_FAST_FREE,
'b': CO_FAST_FREE,
'x': CO_FAST_FREE,
'a': CO_FAST_FREE,
},
}
assert len(funcs) == len(defs.FUNCTIONS)
for func in defs.FUNCTIONS:
with self.subTest(func):
expected = funcs[func]
kinds = _testinternalcapi.get_co_localskinds(func.__code__)
self.assertEqual(kinds, expected)
def isinterned(s):
return s is sys.intern(('_' + s + '_')[1:-1])