Honor `TRACTOR_LOGLEVEL`+`TRACTOR_SPAWN_METHOD` env-vars
Add env-var overrides inside `._root.open_root_actor()` so devs/test-runs can swap the actor-spawn backend or crank console verbosity *without* touching application code. In `._root.open_root_actor()`, - read `TRACTOR_LOGLEVEL` early, overriding any caller-passed `loglevel` and stashing an `env_ll_report` to emit once the console log is set up. - pull the `loglevel` fallback (`or _default_loglevel`) and `log.get_console_log()` init *up* so the env-var report routes through tractor's own logger. - read `TRACTOR_SPAWN_METHOD`, overriding any caller-passed `start_method` and warn-logging when the env-var clobbers an explicit caller value. Wire the same vars through `tests/devx/conftest.py::spawn`, - request the `loglevel` fixture, set both `TRACTOR_LOGLEVEL` and `TRACTOR_SPAWN_METHOD` in `os.environ` before each `pexpect.spawn()` (inherited by the example subproc). - expand `supported_spawners` to include `main_thread_forkserver` and `subint_forkserver` bc example scripts no longer need per-script CLI plumbing. - pop both vars in fixture teardown so a leaked value can't re-route a later in-process tractor test's spawn-backend or loglevel. (this patch was generated in some part by [`claude-code`][claude-code-gh]) [claude-code-gh]: https://github.com/anthropics/claude-codesubint_forkserver_backend
parent
22cdf15b73
commit
208e7c0926
|
|
@ -56,6 +56,7 @@ type PexpectSpawner = Callable[
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def spawn(
|
def spawn(
|
||||||
start_method: str,
|
start_method: str,
|
||||||
|
loglevel: str,
|
||||||
testdir: pytest.Pytester,
|
testdir: pytest.Pytester,
|
||||||
reg_addr: tuple[str, int],
|
reg_addr: tuple[str, int],
|
||||||
|
|
||||||
|
|
@ -67,11 +68,12 @@ def spawn(
|
||||||
'''
|
'''
|
||||||
supported_spawners: set[str] = {
|
supported_spawners: set[str] = {
|
||||||
'trio',
|
'trio',
|
||||||
# ?TODO, other spawners that will work?
|
# `examples/debugging/<script>.py` picks up the spawn
|
||||||
# - [ ] need to pass `start_method={spawner}` to underlying
|
# backend via the `TRACTOR_SPAWN_METHOD` env-var which
|
||||||
# `examples/debugging/<script>.py` somehow?
|
# is honored inside `tractor._root.open_root_actor()`,
|
||||||
# 'main_thread_forkserver',
|
# so no per-script edits are required.
|
||||||
# 'subint_forkserver',
|
'main_thread_forkserver',
|
||||||
|
'subint_forkserver',
|
||||||
}
|
}
|
||||||
if start_method not in supported_spawners:
|
if start_method not in supported_spawners:
|
||||||
pytest.skip(
|
pytest.skip(
|
||||||
|
|
@ -94,6 +96,32 @@ def spawn(
|
||||||
# disable all ANSI color output
|
# disable all ANSI color output
|
||||||
# os.environ['NO_COLOR'] = '1'
|
# os.environ['NO_COLOR'] = '1'
|
||||||
|
|
||||||
|
def set_spawn_method():
|
||||||
|
'''
|
||||||
|
Drive the actor-spawn backend inside the spawned
|
||||||
|
`examples/debugging/<script>.py` subproc via env-var
|
||||||
|
(consumed by `tractor._root.open_root_actor()`),
|
||||||
|
without requiring per-script CLI plumbing.
|
||||||
|
|
||||||
|
'''
|
||||||
|
import os
|
||||||
|
os.environ['TRACTOR_SPAWN_METHOD'] = start_method
|
||||||
|
|
||||||
|
def set_loglevel():
|
||||||
|
'''
|
||||||
|
Forward the test-suite parametrized `loglevel` into the
|
||||||
|
spawned `examples/debugging/<script>.py` subproc via
|
||||||
|
env-var (consumed by `tractor._root.open_root_actor()`),
|
||||||
|
so console verbosity can be cranked or silenced from
|
||||||
|
the test harness without per-script edits.
|
||||||
|
|
||||||
|
'''
|
||||||
|
import os
|
||||||
|
if loglevel:
|
||||||
|
os.environ['TRACTOR_LOGLEVEL'] = loglevel
|
||||||
|
else:
|
||||||
|
os.environ.pop('TRACTOR_LOGLEVEL', None)
|
||||||
|
|
||||||
spawned: PexpectSpawner|None = None
|
spawned: PexpectSpawner|None = None
|
||||||
|
|
||||||
def _spawn(
|
def _spawn(
|
||||||
|
|
@ -103,6 +131,8 @@ def spawn(
|
||||||
) -> pty_spawn.spawn:
|
) -> pty_spawn.spawn:
|
||||||
nonlocal spawned
|
nonlocal spawned
|
||||||
unset_colors()
|
unset_colors()
|
||||||
|
set_spawn_method()
|
||||||
|
set_loglevel()
|
||||||
spawned = testdir.spawn(
|
spawned = testdir.spawn(
|
||||||
cmd=mk_cmd(
|
cmd=mk_cmd(
|
||||||
cmd,
|
cmd,
|
||||||
|
|
@ -146,6 +176,16 @@ def spawn(
|
||||||
if ptyproc.isalive():
|
if ptyproc.isalive():
|
||||||
ptyproc.kill(signal.SIGKILL)
|
ptyproc.kill(signal.SIGKILL)
|
||||||
|
|
||||||
|
# Scope our env-var mutations to this single fixture
|
||||||
|
# invocation — both `TRACTOR_SPAWN_METHOD` and
|
||||||
|
# `TRACTOR_LOGLEVEL` are honored by
|
||||||
|
# `tractor._root.open_root_actor()` so leaking them past
|
||||||
|
# this test could inadvertently re-route a later
|
||||||
|
# in-process tractor test's spawn-backend / loglevel.
|
||||||
|
import os
|
||||||
|
os.environ.pop('TRACTOR_SPAWN_METHOD', None)
|
||||||
|
os.environ.pop('TRACTOR_LOGLEVEL', None)
|
||||||
|
|
||||||
# TODO? ensure we've cleaned up any UDS-paths?
|
# TODO? ensure we've cleaned up any UDS-paths?
|
||||||
# breakpoint()
|
# breakpoint()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -241,6 +241,7 @@ async def open_root_actor(
|
||||||
f'_registry_addrs: {registry_addrs!r}\n'
|
f'_registry_addrs: {registry_addrs!r}\n'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# debug.mk_pdb().set_trace()
|
||||||
async with maybe_block_bp(
|
async with maybe_block_bp(
|
||||||
debug_mode=debug_mode,
|
debug_mode=debug_mode,
|
||||||
maybe_enable_greenback=maybe_enable_greenback,
|
maybe_enable_greenback=maybe_enable_greenback,
|
||||||
|
|
@ -284,6 +285,75 @@ async def open_root_actor(
|
||||||
)
|
)
|
||||||
enable_modules.extend(rpc_module_paths)
|
enable_modules.extend(rpc_module_paths)
|
||||||
|
|
||||||
|
# `TRACTOR_LOGLEVEL` env-var wins over any caller-passed
|
||||||
|
# `loglevel` so devs/test-runs can crank (or silence)
|
||||||
|
# console verbosity without touching application code.
|
||||||
|
env_ll_report: str = ''
|
||||||
|
if env_ll := os.environ.get('TRACTOR_LOGLEVEL'):
|
||||||
|
loglevel = env_ll
|
||||||
|
env_ll_report: str = (
|
||||||
|
f'Detected env-var setting,\n'
|
||||||
|
f'TRACTOR_LOGLEVEL={env_ll!r}\n'
|
||||||
|
f'\n'
|
||||||
|
f'Setting console loglevel per,\n'
|
||||||
|
f'loglevel={loglevel!r}\n'
|
||||||
|
)
|
||||||
|
if (
|
||||||
|
loglevel
|
||||||
|
and
|
||||||
|
loglevel.upper() != env_ll.upper()
|
||||||
|
):
|
||||||
|
env_ll_report += (
|
||||||
|
f'\n'
|
||||||
|
f'NOTE env-var OVERRIDES caller-passed,\n'
|
||||||
|
f'loglevel={loglevel!r}\n'
|
||||||
|
)
|
||||||
|
|
||||||
|
loglevel: str = (
|
||||||
|
loglevel
|
||||||
|
or
|
||||||
|
log._default_loglevel
|
||||||
|
)
|
||||||
|
loglevel: str = loglevel.upper()
|
||||||
|
|
||||||
|
assert loglevel
|
||||||
|
_log = log.get_console_log(
|
||||||
|
level=loglevel,
|
||||||
|
name='tractor',
|
||||||
|
logger=logger,
|
||||||
|
)
|
||||||
|
assert _log
|
||||||
|
if env_ll_report:
|
||||||
|
_log.info(env_ll_report)
|
||||||
|
|
||||||
|
# `TRACTOR_SPAWN_METHOD` env-var wins over any caller-passed
|
||||||
|
# `start_method` so devs/test-runs can swap the actor spawn
|
||||||
|
# backend without touching application code (e.g. driving
|
||||||
|
# the `examples/debugging/<script>.py` suite under each
|
||||||
|
# backend from `tests/devx/conftest.py::spawn`).
|
||||||
|
if env_sm := os.environ.get('TRACTOR_SPAWN_METHOD'):
|
||||||
|
start_method: str = env_sm
|
||||||
|
env_sm_report: str = (
|
||||||
|
f'Detected env-var setting,\n'
|
||||||
|
f'TRACTOR_SPAWN_METHOD={env_sm!r}\n'
|
||||||
|
f'\n'
|
||||||
|
f'Setting spawn backend as,\n'
|
||||||
|
f'start_method={env_sm!r}\n'
|
||||||
|
)
|
||||||
|
if (
|
||||||
|
start_method
|
||||||
|
and
|
||||||
|
start_method != env_sm
|
||||||
|
):
|
||||||
|
_log.warning(
|
||||||
|
env_sm_report
|
||||||
|
+
|
||||||
|
f'NOTE env-var OVERRIDES caller-passed,\n'
|
||||||
|
f'`start_method={start_method!r}`\n'
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
_log.info(env_sm_report)
|
||||||
|
|
||||||
if start_method is not None:
|
if start_method is not None:
|
||||||
_spawn.try_set_start_method(start_method)
|
_spawn.try_set_start_method(start_method)
|
||||||
|
|
||||||
|
|
@ -300,12 +370,6 @@ async def open_root_actor(
|
||||||
wrap_address(uw_addr)
|
wrap_address(uw_addr)
|
||||||
for uw_addr in uw_reg_addrs
|
for uw_addr in uw_reg_addrs
|
||||||
]
|
]
|
||||||
loglevel: str = (
|
|
||||||
loglevel
|
|
||||||
or
|
|
||||||
log._default_loglevel
|
|
||||||
)
|
|
||||||
loglevel: str = loglevel.upper()
|
|
||||||
|
|
||||||
# Debug-mode is currently only supported for backends whose
|
# Debug-mode is currently only supported for backends whose
|
||||||
# subactor root runtime is trio-native (so `tractor.devx.
|
# subactor root runtime is trio-native (so `tractor.devx.
|
||||||
|
|
@ -341,13 +405,6 @@ async def open_root_actor(
|
||||||
f'{_spawn._spawn_method!r}.'
|
f'{_spawn._spawn_method!r}.'
|
||||||
)
|
)
|
||||||
|
|
||||||
assert loglevel
|
|
||||||
_log = log.get_console_log(
|
|
||||||
level=loglevel,
|
|
||||||
name='tractor',
|
|
||||||
)
|
|
||||||
assert _log
|
|
||||||
|
|
||||||
# TODO: factor this into `.devx._stackscope`!!
|
# TODO: factor this into `.devx._stackscope`!!
|
||||||
if (
|
if (
|
||||||
debug_mode
|
debug_mode
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue