コード例 #1
0
async def test_Process_object_wait_for_pid():
    async def return7():
        return 7

    with trio.fail_after(2):
        async with open_in_process(return7) as proc:
            await proc.wait_pid()
            assert isinstance(proc.pid, int)
コード例 #2
0
async def test_Process_object_wait_for_returncode():
    async def system_exit_123():
        raise SystemExit(123)

    with trio.fail_after(2):
        async with open_in_process(system_exit_123) as proc:
            await proc.wait_returncode()
            assert proc.returncode == 123
コード例 #3
0
async def test_Process_object_wait_for_error():
    async def raise_error():
        raise ValueError("child-error")

    with trio.fail_after(2):
        async with open_in_process(raise_error) as proc:
            await proc.wait_error()
            assert isinstance(proc.error, ValueError)
コード例 #4
0
async def test_Process_object_wait_for_result_when_error():
    async def raise_error():
        raise ValueError("child-error")

    with trio.fail_after(2):
        async with open_in_process(raise_error) as proc:
            with pytest.raises(ValueError, match="child-error"):
                await proc.wait_result_or_raise()
コード例 #5
0
async def test_Process_object_wait_for_return_value():
    async def return7():
        return 7

    with trio.fail_after(2):
        async with open_in_process(return7) as proc:
            await proc.wait_return_value()
            assert proc.return_value == 7
コード例 #6
0
async def test_open_proc_invalid_function_call():
    async def takes_no_args():
        pass

    with trio.fail_after(2):
        async with open_in_process(takes_no_args, 1, 2, 3) as proc:
            pass
        assert proc.returncode == 1
        assert isinstance(proc.error, TypeError)
コード例 #7
0
async def test_Process_object_wait_for_result_when_return_value():
    async def return7():
        return 7

    with trio.fail_after(2):
        async with open_in_process(return7) as proc:
            result = await proc.wait_result_or_raise()
            assert result == 7
            assert proc.error is None
コード例 #8
0
async def test_timeout_waiting_for_pid(monkeypatch):
    async def wait_pid(self):
        await trio.sleep(constants.STARTUP_TIMEOUT_SECONDS + 0.1)

    monkeypatch.setattr(Process, "wait_pid", wait_pid)
    monkeypatch.setattr(constants, "STARTUP_TIMEOUT_SECONDS", 1)

    with pytest.raises(trio.TooSlowError):
        async with open_in_process(trio.sleep_forever):
            pass
コード例 #9
0
async def test_open_in_proc_termination_while_running():
    async def do_sleep_forever():
        import trio

        await trio.sleep_forever()

    with trio.fail_after(2):
        async with open_in_process(do_sleep_forever) as proc:
            proc.terminate()
    assert proc.returncode == 15
コード例 #10
0
async def test_open_proc_unpickleable_params(touch_path):
    async def takes_open_file(f):
        pass

    with trio.fail_after(2):
        with pytest.raises(pickle.PickleError):
            with open(touch_path, "w") as touch_file:
                async with open_in_process(takes_open_file, touch_file):
                    # this code block shouldn't get executed
                    assert False
コード例 #11
0
async def test_open_proc_interrupt_while_running():
    async def monitor_for_interrupt():
        import trio

        await trio.sleep_forever()

    with trio.fail_after(2):
        async with open_in_process(monitor_for_interrupt) as proc:
            proc.send_signal(signal.SIGINT)
        assert proc.returncode == 2
コード例 #12
0
async def test_Process_object_state_api():
    async def return7():
        return 7

    with trio.fail_after(2):
        async with open_in_process(return7) as proc:
            assert proc.state.is_on_or_after(State.STARTED)

            await proc.wait_for_state(State.FINISHED)
            assert proc.state is State.FINISHED
            assert proc.return_value == 7
コード例 #13
0
async def test_open_proc_outer_KeyboardInterrupt():
    async def sleep_forever():
        import trio

        await trio.sleep_forever()

    with trio.fail_after(2):
        with pytest.raises(KeyboardInterrupt):
            async with open_in_process(sleep_forever) as proc:
                raise KeyboardInterrupt
        assert proc.returncode == 2
コード例 #14
0
async def test_timeout_waiting_for_executing_state(monkeypatch):
    async def wait_for_state(self, state):
        if state is State.EXECUTING:
            await trio.sleep(constants.STARTUP_TIMEOUT_SECONDS + 0.1)

    monkeypatch.setattr(Process, "wait_for_state", wait_for_state)
    monkeypatch.setattr(constants, "STARTUP_TIMEOUT_SECONDS", 1)

    with pytest.raises(trio.TooSlowError):
        async with open_in_process(trio.sleep_forever):
            pass
コード例 #15
0
async def test_open_in_proc_kill_while_running():
    async def do_sleep_forever():
        import trio

        await trio.sleep_forever()

    with trio.fail_after(2):
        async with open_in_process(do_sleep_forever) as proc:
            proc.kill()
    assert proc.returncode == -9
    assert isinstance(proc.error, ProcessKilled)
コード例 #16
0
async def test_unpickleable_exc():
    sleep = trio.sleep

    # Custom exception classes requiring multiple arguments cannot be pickled:
    # https://bugs.python.org/issue32696
    class CustomException(BaseException):
        def __init__(self, msg, arg2):
            super().__init__(msg)
            self.arg2 = arg2

    async def raise_():
        await sleep(0.01)
        raise CustomException("msg", "arg2")

    with trio.fail_after(2):
        with pytest.raises(InvalidDataFromChild):
            async with open_in_process(raise_) as proc:
                await proc.wait_result_or_raise()
コード例 #17
0
async def test_proc_ignores_KeyboardInterrupt(monkeypatch):
    # If we get a KeyboardInterrupt and the child process does not terminate after being sent a
    # SIGINT, we send a SIGKILL to avoid open_in_process() from hanging indefinitely.
    async def sleep_forever():
        import trio

        while True:
            try:
                await trio.lowlevel.checkpoint()
            except KeyboardInterrupt:
                pass

    monkeypatch.setattr(constants, "SIGINT_TIMEOUT_SECONDS", 0.2)
    with trio.fail_after(constants.SIGINT_TIMEOUT_SECONDS + 1):
        with pytest.raises(KeyboardInterrupt):
            async with open_in_process(sleep_forever) as proc:
                raise KeyboardInterrupt
        assert proc.returncode == -9
        assert isinstance(proc.error, ProcessKilled)
コード例 #18
0
async def test_open_proc_interrupt_while_running():
    with trio.fail_after(2):
        async with open_in_process(trio.sleep_forever) as proc:
            proc.send_signal(signal.SIGINT)
        assert proc.returncode == 2
コード例 #19
0
ファイル: _spawn.py プロジェクト: guilledk/tractor
async def new_proc(
        name: str,
        actor_nursery: 'ActorNursery',  # type: ignore
        subactor: Actor,
        errors: Dict[Tuple[str, str], Exception],
        # passed through to actor main
        bind_addr: Tuple[str, int],
        parent_addr: Tuple[str, int],
        use_trio_run_in_process: bool = False,
        task_status: TaskStatus[Portal] = trio.TASK_STATUS_IGNORED) -> None:
    """Create a new ``multiprocessing.Process`` using the
    spawn method as configured using ``try_set_start_method()``.
    """
    cancel_scope = None

    # mark the new actor with the global spawn method
    subactor._spawn_method = _spawn_method

    async with trio.open_nursery() as nursery:
        if use_trio_run_in_process or _spawn_method == 'trio_run_in_process':
            # trio_run_in_process
            async with trio_run_in_process.open_in_process(
                    subactor._trip_main,
                    bind_addr,
                    parent_addr,
            ) as proc:
                log.info(f"Started {proc}")

                # wait for actor to spawn and connect back to us
                # channel should have handshake completed by the
                # local actor by the time we get a ref to it
                event, chan = await actor_nursery._actor.wait_for_peer(
                    subactor.uid)
                portal = Portal(chan)
                actor_nursery._children[subactor.uid] = (subactor, proc,
                                                         portal)
                task_status.started(portal)

                # wait for ActorNursery.wait() to be called
                await actor_nursery._join_procs.wait()

                if portal in actor_nursery._cancel_after_result_on_exit:
                    cancel_scope = await nursery.start(cancel_on_completion,
                                                       portal, subactor,
                                                       errors)

                # TRIP blocks here until process is complete
        else:
            # `multiprocessing`
            assert _ctx
            start_method = _ctx.get_start_method()
            if start_method == 'forkserver':
                # XXX do our hackery on the stdlib to avoid multiple
                # forkservers (one at each subproc layer).
                fs = forkserver._forkserver
                curr_actor = current_actor()
                if is_main_process() and not curr_actor._forkserver_info:
                    # if we're the "main" process start the forkserver
                    # only once and pass its ipc info to downstream
                    # children
                    # forkserver.set_forkserver_preload(rpc_module_paths)
                    forkserver.ensure_running()
                    fs_info = (
                        fs._forkserver_address,
                        fs._forkserver_alive_fd,
                        getattr(fs, '_forkserver_pid', None),
                        getattr(resource_tracker._resource_tracker, '_pid',
                                None),
                        resource_tracker._resource_tracker._fd,
                    )
                else:
                    assert curr_actor._forkserver_info
                    fs_info = (
                        fs._forkserver_address,
                        fs._forkserver_alive_fd,
                        fs._forkserver_pid,
                        resource_tracker._resource_tracker._pid,
                        resource_tracker._resource_tracker._fd,
                    ) = curr_actor._forkserver_info
            else:
                fs_info = (None, None, None, None, None)

            proc = _ctx.Process(  # type: ignore
                target=subactor._mp_main,
                args=(bind_addr, fs_info, start_method, parent_addr),
                # daemon=True,
                name=name,
            )
            # `multiprocessing` only (since no async interface):
            # register the process before start in case we get a cancel
            # request before the actor has fully spawned - then we can wait
            # for it to fully come up before sending a cancel request
            actor_nursery._children[subactor.uid] = (subactor, proc, None)

            proc.start()
            if not proc.is_alive():
                raise ActorFailure("Couldn't start sub-actor?")

            log.info(f"Started {proc}")

            # wait for actor to spawn and connect back to us
            # channel should have handshake completed by the
            # local actor by the time we get a ref to it
            event, chan = await actor_nursery._actor.wait_for_peer(subactor.uid
                                                                   )
            portal = Portal(chan)
            actor_nursery._children[subactor.uid] = (subactor, proc, portal)

            # unblock parent task
            task_status.started(portal)

            # wait for ``ActorNursery`` block to signal that
            # subprocesses can be waited upon.
            # This is required to ensure synchronization
            # with user code that may want to manually await results
            # from nursery spawned sub-actors. We don't want the
            # containing nurseries here to collect results or error
            # while user code is still doing it's thing. Only after the
            # nursery block closes do we allow subactor results to be
            # awaited and reported upwards to the supervisor.
            await actor_nursery._join_procs.wait()

            if portal in actor_nursery._cancel_after_result_on_exit:
                cancel_scope = await nursery.start(cancel_on_completion,
                                                   portal, subactor, errors)

            # TODO: timeout block here?
            if proc.is_alive():
                await proc_waiter(proc)
            proc.join()

        log.debug(f"Joined {proc}")
        # pop child entry to indicate we are no longer managing this subactor
        subactor, proc, portal = actor_nursery._children.pop(subactor.uid)
        # cancel result waiter that may have been spawned in
        # tandem if not done already
        if cancel_scope:
            log.warning(
                f"Cancelling existing result waiter task for {subactor.uid}")
            cancel_scope.cancel()