[3.14] gh-132775: Add _PyCode_GetScriptXIData() (gh-133676)

This converts functions, code, str, bytes, bytearray, and memoryview objects to PyCodeObject,
and ensure that the object looks like a script.  That means no args, no return, and no closure.
_PyCode_GetPureScriptXIData() takes it a step further and ensures there are no globals.

We also add _PyObject_SupportedAsScript() to the internal C-API.

(cherry picked from commit c81fa2b9cd, AKA gh-133480)

Co-authored-by: Eric Snow <ericsnowcurrently@gmail.com>
This commit is contained in:
Miss Islington (bot) 2025-05-08 18:05:34 +02:00 committed by GitHub
parent dc1a4dda88
commit 1059548686
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 366 additions and 0 deletions

View file

@ -758,6 +758,126 @@ class CodeTests(_GetXIDataTests):
])
class PureShareableScriptTests(_GetXIDataTests):
MODE = 'script-pure'
VALID_SCRIPTS = [
'',
'spam',
'# a comment',
'print("spam")',
'raise Exception("spam")',
"""if True:
do_something()
""",
"""if True:
def spam(x):
return x
class Spam:
def eggs(self):
return 42
x = Spam().eggs()
raise ValueError(spam(x))
""",
]
INVALID_SCRIPTS = [
' pass', # IndentationError
'----', # SyntaxError
"""if True:
def spam():
# no body
spam()
""", # IndentationError
]
def test_valid_str(self):
self.assert_roundtrip_not_equal([
*self.VALID_SCRIPTS,
], expecttype=types.CodeType)
def test_invalid_str(self):
self.assert_not_shareable([
*self.INVALID_SCRIPTS,
])
def test_valid_bytes(self):
self.assert_roundtrip_not_equal([
*(s.encode('utf8') for s in self.VALID_SCRIPTS),
], expecttype=types.CodeType)
def test_invalid_bytes(self):
self.assert_not_shareable([
*(s.encode('utf8') for s in self.INVALID_SCRIPTS),
])
def test_pure_script_code(self):
self.assert_roundtrip_equal_not_identical([
*(f.__code__ for f in defs.PURE_SCRIPT_FUNCTIONS),
])
def test_impure_script_code(self):
self.assert_not_shareable([
*(f.__code__ for f in defs.SCRIPT_FUNCTIONS
if f not in defs.PURE_SCRIPT_FUNCTIONS),
])
def test_other_code(self):
self.assert_not_shareable([
*(f.__code__ for f in defs.FUNCTIONS
if f not in defs.SCRIPT_FUNCTIONS),
*(f.__code__ for f in defs.FUNCTION_LIKE),
])
def test_pure_script_function(self):
self.assert_roundtrip_not_equal([
*defs.PURE_SCRIPT_FUNCTIONS,
], expecttype=types.CodeType)
def test_impure_script_function(self):
self.assert_not_shareable([
*(f for f in defs.SCRIPT_FUNCTIONS
if f not in defs.PURE_SCRIPT_FUNCTIONS),
])
def test_other_function(self):
self.assert_not_shareable([
*(f for f in defs.FUNCTIONS
if f not in defs.SCRIPT_FUNCTIONS),
*defs.FUNCTION_LIKE,
])
def test_other_objects(self):
self.assert_not_shareable([
None,
True,
False,
Ellipsis,
NotImplemented,
(),
[],
{},
object(),
])
class ShareableScriptTests(PureShareableScriptTests):
MODE = 'script'
def test_impure_script_code(self):
self.assert_roundtrip_equal_not_identical([
*(f.__code__ for f in defs.SCRIPT_FUNCTIONS
if f not in defs.PURE_SCRIPT_FUNCTIONS),
])
def test_impure_script_function(self):
self.assert_roundtrip_not_equal([
*(f for f in defs.SCRIPT_FUNCTIONS
if f not in defs.PURE_SCRIPT_FUNCTIONS),
], expecttype=types.CodeType)
class ShareableTypeTests(_GetXIDataTests):
MODE = 'xidata'