Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Prev Previous commit
Next Next commit
Support full calls in interp_call().
  • Loading branch information
ericsnowcurrently committed May 22, 2025
commit 08307a0c29d4f8ed71c2c71afc1d5c3bb9a75c3b
20 changes: 20 additions & 0 deletions Lib/test/_code_definitions.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,15 @@ def spam_with_globals_and_builtins():
print(res)


def spam_full_args(a, b, /, c, d, *args, e, f, **kwargs):
return (a, b, c, d, e, f, args, kwargs)


def spam_full_args_with_defaults(a=-1, b=-2, /, c=-3, d=-4, *args,
e=-5, f=-6, **kwargs):
return (a, b, c, d, e, f, args, kwargs)


def spam_args_attrs_and_builtins(a, b, /, c, d, *args, e, f, **kwargs):
if args.__len__() > 2:
return None
Expand All @@ -67,6 +76,10 @@ def spam_returns_arg(x):
return x


def spam_raises():
raise Exception('spam!')


def spam_with_inner_not_closure():
def eggs():
pass
Expand Down Expand Up @@ -177,8 +190,11 @@ def ham_C_closure(z):
spam_minimal,
spam_with_builtins,
spam_with_globals_and_builtins,
spam_full_args,
spam_full_args_with_defaults,
spam_args_attrs_and_builtins,
spam_returns_arg,
spam_raises,
spam_with_inner_not_closure,
spam_with_inner_closure,
spam_annotated,
Expand Down Expand Up @@ -219,8 +235,10 @@ def ham_C_closure(z):
spam,
spam_minimal,
spam_with_builtins,
spam_full_args,
spam_args_attrs_and_builtins,
spam_returns_arg,
spam_raises,
spam_annotated,
spam_with_inner_not_closure,
spam_with_inner_closure,
Expand All @@ -238,6 +256,7 @@ def ham_C_closure(z):
STATELESS_CODE = [
*STATELESS_FUNCTIONS,
script_with_globals,
spam_full_args_with_defaults,
spam_with_globals_and_builtins,
spam_full,
]
Expand All @@ -248,6 +267,7 @@ def ham_C_closure(z):
script_with_explicit_empty_return,
spam_minimal,
spam_with_builtins,
spam_raises,
spam_with_inner_not_closure,
spam_with_inner_closure,
]
Expand Down
21 changes: 10 additions & 11 deletions Lib/test/support/interpreters/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,13 @@ def exec(self, code, /):
if excinfo is not None:
raise ExecutionFailed(excinfo)

def call(self, callable, /):
def _call(self, callable, args, kwargs):
res, excinfo = _interpreters.call(self._id, callable, args, kwargs, restrict=True)
if excinfo is not None:
raise ExecutionFailed(excinfo)
return res

def call(self, callable, /, *args, **kwargs):
"""Call the object in the interpreter with given args/kwargs.

Only functions that take no arguments and have no closure

@neonene neonene May 13, 2025 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The docstring looks partially outdated (here or in Lib/interpreters/__init__.py). The same would apply for _interpretersmodule.c?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

Expand All @@ -239,20 +245,13 @@ def call(self, callable, /):
and an ExecutionFailed exception is raised, much like what
happens with Interpreter.exec().
"""
# XXX Support args and kwargs.
# XXX Support arbitrary callables.
# XXX Support returning the return value (e.g. via pickle).
excinfo = _interpreters.call(self._id, callable, restrict=True)
if excinfo is not None:
raise ExecutionFailed(excinfo)
return self._call(callable, args, kwargs)

def call_in_thread(self, callable, /):
def call_in_thread(self, callable, /, *args, **kwargs):
"""Return a new thread that calls the object in the interpreter.

The return value and any raised exception are discarded.
"""
def task():
self.call(callable)
t = threading.Thread(target=task)
t = threading.Thread(target=self._call, args=(callable, args, kwargs))
t.start()
return t
48 changes: 46 additions & 2 deletions Lib/test/test_code.py
Original file line number Diff line number Diff line change
Expand Up @@ -701,6 +701,26 @@ def test_local_kinds(self):
'checks': CO_FAST_LOCAL,
'res': CO_FAST_LOCAL,
},
defs.spam_full_args: {
'a': POSONLY,
'b': POSONLY,
'c': POSORKW,
'd': POSORKW,
'e': KWONLY,
'f': KWONLY,
'args': VARARGS,
'kwargs': VARKWARGS,
},
defs.spam_full_args_with_defaults: {
'a': POSONLY,
'b': POSONLY,
'c': POSORKW,
'd': POSORKW,
'e': KWONLY,
'f': KWONLY,
'args': VARARGS,
'kwargs': VARKWARGS,
},
defs.spam_args_attrs_and_builtins: {
'a': POSONLY,
'b': POSONLY,
Expand All @@ -714,6 +734,7 @@ def test_local_kinds(self):
defs.spam_returns_arg: {
'x': POSORKW,
},
defs.spam_raises: {},
defs.spam_with_inner_not_closure: {
'eggs': CO_FAST_LOCAL,
},
Expand Down Expand Up @@ -934,6 +955,20 @@ def new_var_counts(*,
purelocals=5,
globalvars=6,
),
defs.spam_full_args: new_var_counts(
posonly=2,
posorkw=2,
kwonly=2,
varargs=1,
varkwargs=1,
),
defs.spam_full_args_with_defaults: new_var_counts(
posonly=2,
posorkw=2,
kwonly=2,
varargs=1,
varkwargs=1,
),
defs.spam_args_attrs_and_builtins: new_var_counts(
posonly=2,
posorkw=2,
Expand All @@ -945,6 +980,9 @@ def new_var_counts(*,
defs.spam_returns_arg: new_var_counts(
posorkw=1,
),
defs.spam_raises: new_var_counts(
globalvars=1,
),
defs.spam_with_inner_not_closure: new_var_counts(
purelocals=1,
),
Expand Down Expand Up @@ -1097,10 +1135,16 @@ def new_var_counts(*,
def test_stateless(self):
self.maxDiff = None

STATELESS_FUNCTIONS = [
*defs.STATELESS_FUNCTIONS,
# stateless with defaults
defs.spam_full_args_with_defaults,
]

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

Expand All @@ -1110,7 +1154,7 @@ def test_stateless(self):
with self.assertRaises(Exception):
_testinternalcapi.verify_stateless_code(func.__code__)

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