2022-08-29 19:08:04 +00:00
|
|
|
"""
|
|
|
|
|
Verifying internal runtime state and undocumented extras.
|
|
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
import os
|
|
|
|
|
|
|
|
|
|
import pytest
|
|
|
|
|
import trio
|
|
|
|
|
import tractor
|
|
|
|
|
|
2024-03-12 19:48:20 +00:00
|
|
|
from tractor._testing import tractor_test
|
2022-08-29 19:08:04 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
_file_path: str = ''
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def unlink_file():
|
|
|
|
|
print('Removing tmp file!')
|
|
|
|
|
os.remove(_file_path)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async def crash_and_clean_tmpdir(
|
|
|
|
|
tmp_file_path: str,
|
|
|
|
|
error: bool = True,
|
2026-03-08 19:13:09 +00:00
|
|
|
rent_cancel: bool = True,
|
|
|
|
|
|
|
|
|
|
# XXX unused, but do we really need to test these cases?
|
|
|
|
|
self_cancel: bool = False,
|
2022-08-29 19:08:04 +00:00
|
|
|
):
|
|
|
|
|
global _file_path
|
|
|
|
|
_file_path = tmp_file_path
|
|
|
|
|
|
|
|
|
|
actor = tractor.current_actor()
|
|
|
|
|
actor.lifetime_stack.callback(unlink_file)
|
|
|
|
|
|
|
|
|
|
assert os.path.isfile(tmp_file_path)
|
|
|
|
|
await trio.sleep(0.1)
|
|
|
|
|
if error:
|
2026-03-08 19:13:09 +00:00
|
|
|
print('erroring in subactor!')
|
2022-08-29 19:08:04 +00:00
|
|
|
assert 0
|
2026-03-08 19:13:09 +00:00
|
|
|
|
|
|
|
|
elif self_cancel:
|
|
|
|
|
print('SELF-cancelling subactor!')
|
2022-08-29 19:08:04 +00:00
|
|
|
actor.cancel_soon()
|
|
|
|
|
|
2026-03-08 19:13:09 +00:00
|
|
|
elif rent_cancel:
|
|
|
|
|
await trio.sleep_forever()
|
|
|
|
|
|
|
|
|
|
print('subactor exiting task!')
|
|
|
|
|
|
2022-08-29 19:08:04 +00:00
|
|
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
|
|
|
'error_in_child',
|
|
|
|
|
[True, False],
|
2026-03-08 19:13:09 +00:00
|
|
|
ids='error_in_child={}'.format,
|
2022-08-29 19:08:04 +00:00
|
|
|
)
|
|
|
|
|
@tractor_test
|
|
|
|
|
async def test_lifetime_stack_wipes_tmpfile(
|
|
|
|
|
tmp_path,
|
|
|
|
|
error_in_child: bool,
|
2026-03-08 19:13:09 +00:00
|
|
|
loglevel: str,
|
|
|
|
|
# log: tractor.log.StackLevelAdapter,
|
|
|
|
|
# ^TODO, once landed via macos support!
|
2022-08-29 19:08:04 +00:00
|
|
|
):
|
|
|
|
|
child_tmp_file = tmp_path / "child.txt"
|
|
|
|
|
child_tmp_file.touch()
|
|
|
|
|
assert child_tmp_file.exists()
|
|
|
|
|
path = str(child_tmp_file)
|
|
|
|
|
|
2026-03-08 19:13:09 +00:00
|
|
|
# NOTE, this is expected to cancel the sub
|
|
|
|
|
# in the `error_in_child=False` case!
|
|
|
|
|
timeout: float = (
|
|
|
|
|
1.6 if error_in_child
|
|
|
|
|
else 1
|
|
|
|
|
)
|
2022-08-29 19:08:04 +00:00
|
|
|
try:
|
2026-03-08 19:13:09 +00:00
|
|
|
with trio.move_on_after(timeout) as cs:
|
|
|
|
|
async with tractor.open_nursery(
|
|
|
|
|
loglevel=loglevel,
|
|
|
|
|
) as an:
|
|
|
|
|
await ( # inlined `tractor.Portal`
|
|
|
|
|
await an.run_in_actor(
|
|
|
|
|
crash_and_clean_tmpdir,
|
|
|
|
|
tmp_file_path=path,
|
|
|
|
|
error=error_in_child,
|
|
|
|
|
)
|
|
|
|
|
).result()
|
2022-10-14 19:45:08 +00:00
|
|
|
except (
|
|
|
|
|
tractor.RemoteActorError,
|
2024-03-13 22:41:24 +00:00
|
|
|
BaseExceptionGroup,
|
2026-03-08 19:13:09 +00:00
|
|
|
) as _exc:
|
|
|
|
|
exc = _exc
|
|
|
|
|
from tractor.log import get_console_log
|
|
|
|
|
log = get_console_log(
|
|
|
|
|
level=loglevel,
|
|
|
|
|
name=__name__,
|
|
|
|
|
)
|
|
|
|
|
log.exception(
|
|
|
|
|
f'Subactor failed as expected with {type(exc)!r}\n'
|
|
|
|
|
)
|
2022-08-29 19:08:04 +00:00
|
|
|
|
|
|
|
|
# tmp file should have been wiped by
|
|
|
|
|
# teardown stack.
|
|
|
|
|
assert not child_tmp_file.exists()
|
2026-03-08 19:13:09 +00:00
|
|
|
|
|
|
|
|
if error_in_child:
|
|
|
|
|
assert not cs.cancel_called
|
|
|
|
|
else:
|
|
|
|
|
# expect timeout in some cases?
|
|
|
|
|
assert cs.cancel_called
|