Fix runtime kwarg leaking in `tractor_test`

The `functools` rewrite forwarded all `kwargs`
through `_main(**kwargs)` to `wrapped(**kwargs)`
unchanged — the Windows `start_method` default
could leak to test fns that don't declare it.
The pre-wrapt code guarded against this with
named wrapper params.

Extract runtime settings (`reg_addr`, `loglevel`,
`debug_mode`, `start_method`) as closure locals
in `wrapper`; `_main` uses them directly for
`open_root_actor()` while `kwargs` passes to
`wrapped()` unmodified.

Review: PR #439 (Copilot)
https://github.com/goodboy/tractor/pull/439#pullrequestreview-4091005202

(this patch was generated in some part by [`claude-code`][claude-code-gh])
[claude-code-gh]: https://github.com/anthropics/claude-code
subint_spawner_backend
Gud Boi 2026-04-10 16:20:01 -04:00
parent 452a32fb23
commit 9af6adc181
1 changed files with 17 additions and 12 deletions

View File

@ -127,38 +127,43 @@ def tractor_test(
if kw in inspect.signature(wrapped).parameters: if kw in inspect.signature(wrapped).parameters:
assert kw in kwargs assert kw in kwargs
# Extract runtime settings as locals for
# `open_root_actor()`; these must NOT leak into
# `kwargs` when the test fn doesn't declare them
# (the original pre-wrapt code had the same guard).
reg_addr = kwargs.get('reg_addr')
loglevel = kwargs.get('loglevel')
debug_mode = kwargs.get('debug_mode', False)
start_method = kwargs.get('start_method') start_method = kwargs.get('start_method')
if platform.system() == 'Windows': if platform.system() == 'Windows':
if start_method is None: if start_method is None:
kwargs['start_method'] = 'trio' start_method = 'trio'
elif start_method != 'trio': elif start_method != 'trio':
raise ValueError( raise ValueError(
'ONLY the `start_method="trio"` is supported on Windows.' 'ONLY the `start_method="trio"` is supported on Windows.'
) )
# Open a root-actor, passing certain runtime-settings # Open a root-actor, passing runtime-settings
# extracted from the fixture kwargs, then invoke the # extracted above as closure locals, then invoke
# test-fn body as the root-most task. # the test-fn body as the root-most task.
# #
# NOTE: we use `kwargs.get()` (not named params) so that # NOTE: `kwargs` is forwarded as-is to
# the fixture values remain in `kwargs` and are forwarded # `wrapped()` — it only contains what pytest
# to `wrapped()` — the test fn may declare the same # injected based on the test fn's signature.
# fixtures in its own signature.
async def _main(**kwargs): async def _main(**kwargs):
__tracebackhide__: bool = hide_tb __tracebackhide__: bool = hide_tb
reg_addr = kwargs.get('reg_addr')
with trio.fail_after(timeout): with trio.fail_after(timeout):
async with tractor.open_root_actor( async with tractor.open_root_actor(
registry_addrs=( registry_addrs=(
[reg_addr] if reg_addr else None [reg_addr] if reg_addr else None
), ),
loglevel=kwargs.get('loglevel'), loglevel=loglevel,
start_method=kwargs.get('start_method'), start_method=start_method,
# TODO: only enable when pytest is passed # TODO: only enable when pytest is passed
# --pdb # --pdb
debug_mode=kwargs.get('debug_mode', False), debug_mode=debug_mode,
): ):
# invoke test-fn body IN THIS task # invoke test-fn body IN THIS task