Esempio n. 1
0
    def _run_subprocess_until_exit(self) -> None:
        if self._subprocess is None:
            return

        assert current_thread() is self._subprocess_thread
        assert self._subprocess.stdin is not None

        # Send the initial server config which should kick things off.
        # (but make sure its values are still valid first)
        dataclass_validate(self._config)
        self._send_server_command(StartServerModeCommand(self._config))

        while True:

            # If the app is trying to shut down, nope out immediately.
            if self._done:
                break

            # Pass along any commands to our process.
            with self._subprocess_commands_lock:
                for incmd in self._subprocess_commands:
                    # If we're passing a raw string to exec, no need to wrap it
                    # in any proper structure.
                    if isinstance(incmd, str):
                        self._subprocess.stdin.write((incmd + '\n').encode())
                        self._subprocess.stdin.flush()
                    else:
                        self._send_server_command(incmd)
                self._subprocess_commands = []

            # Request restarts/shut-downs for various reasons.
            self._request_shutdowns_or_restarts()

            # If they want to force-kill our subprocess, simply exit this
            # loop; the cleanup code will kill the process if its still
            # alive.
            if (self._subprocess_force_kill_time is not None
                    and time.time() > self._subprocess_force_kill_time):
                print(
                    f'{Clr.CYN}Immediate shutdown time limit'
                    f' ({self.IMMEDIATE_SHUTDOWN_TIME_LIMIT:.1f} seconds)'
                    f' expired; force-killing subprocess...{Clr.RST}',
                    flush=True)
                break

            # Watch for the server process exiting..
            code: Optional[int] = self._subprocess.poll()
            if code is not None:

                clr = Clr.CYN if code == 0 else Clr.RED
                print(
                    f'{clr}Server subprocess exited'
                    f' with code {code}.{Clr.RST}',
                    flush=True)
                self._subprocess_exited_cleanly = (code == 0)
                break

            time.sleep(0.25)
    def _run_process_until_exit(self) -> None:
        assert self._process is not None
        assert self._process.stdin is not None

        # Send the initial server config which should kick things off.
        # (but make sure its values are still valid first)
        dataclass_validate(self._config)
        self._send_server_command(StartServerModeCommand(self._config))

        while True:

            # If the app is trying to shut down, nope out immediately.
            if self._done:
                break

            # Pass along any commands to our process.
            with self._process_commands_lock:
                for incmd in self._process_commands:
                    # If we're passing a raw string to exec, no need to wrap it
                    # in any proper structure.
                    if isinstance(incmd, str):
                        self._process.stdin.write((incmd + '\n').encode())
                        self._process.stdin.flush()
                    else:
                        self._send_server_command(incmd)
                self._process_commands = []

            # Request a soft restart after a while.
            assert self._process_launch_time is not None
            sincelaunch = time.time() - self._process_launch_time
            if (self._restart_minutes is not None and sincelaunch >
                (self._restart_minutes * 60.0)
                    and not self._process_sent_auto_restart):
                print(f'{Clr.CYN}restart_minutes ({self._restart_minutes})'
                      f' elapsed; requesting child-process'
                      f' soft restart...{Clr.RST}')
                self.restart()
                self._process_sent_auto_restart = True

            # Watch for the process exiting.
            code: Optional[int] = self._process.poll()
            if code is not None:
                if code == 0:
                    clr = Clr.CYN
                    slp = 0.0
                else:
                    clr = Clr.SRED
                    slp = 5.0  # Avoid super fast death loops.
                print(f'{clr}Server child-process exited'
                      f' with code {code}.{Clr.RST}')
                self._reset_process_vars()
                time.sleep(slp)
                break

            time.sleep(0.25)
Esempio n. 3
0
def test_validate() -> None:
    """Testing validation."""
    @dataclass
    class _TestClass:
        ival: int = 0
        sval: str = ''
        bval: bool = True
        fval: float = 1.0
        oival: Optional[int] = None
        osval: Optional[str] = None
        obval: Optional[bool] = None
        ofval: Optional[float] = None

    # Should pass by default.
    tclass = _TestClass()
    dataclass_validate(tclass)

    # No longer valid.
    tclass.fval = 1
    with pytest.raises(TypeError):
        dataclass_validate(tclass)

    # Should pass by default.
    tclass = _TestClass()
    dataclass_validate(tclass)

    # No longer valid.
    # noinspection PyTypeHints
    tclass.ival = None  # type: ignore
    with pytest.raises(TypeError):
        dataclass_validate(tclass)
Esempio n. 4
0
def test_coerce() -> None:
    """Test value coercion."""
    @dataclass
    class _TestClass:
        ival: int = 0
        fval: float = 0.0

    # Float value present for int should never work.
    obj = _TestClass()
    # noinspection PyTypeHints
    obj.ival = 1.0  # type: ignore
    with pytest.raises(TypeError):
        dataclass_validate(obj, coerce_to_float=True)
    with pytest.raises(TypeError):
        dataclass_validate(obj, coerce_to_float=False)

    # Int value present for float should work only with coerce on.
    obj = _TestClass()
    obj.fval = 1
    dataclass_validate(obj, coerce_to_float=True)
    with pytest.raises(TypeError):
        dataclass_validate(obj, coerce_to_float=False)

    # Likewise, passing in an int for a float field should work only
    # with coerce on.
    dataclass_from_dict(_TestClass, {'fval': 1}, coerce_to_float=True)
    with pytest.raises(TypeError):
        dataclass_from_dict(_TestClass, {'fval': 1}, coerce_to_float=False)

    # Passing in floats for an int field should never work.
    with pytest.raises(TypeError):
        dataclass_from_dict(_TestClass, {'ival': 1.0}, coerce_to_float=True)
    with pytest.raises(TypeError):
        dataclass_from_dict(_TestClass, {'ival': 1.0}, coerce_to_float=False)
Esempio n. 5
0
 def config(self, value: ServerConfig) -> None:
     dataclass_validate(value)
     self._config = value
Esempio n. 6
0
    def _run_subprocess_until_exit(self) -> None:
        assert current_thread() is self._subprocess_thread
        assert self._subprocess is not None
        assert self._subprocess.stdin is not None

        # Send the initial server config which should kick things off.
        # (but make sure its values are still valid first)
        dataclass_validate(self._config)
        self._send_server_command(StartServerModeCommand(self._config))

        while True:

            # If the app is trying to shut down, nope out immediately.
            if self._done:
                break

            # Pass along any commands to our process.
            with self._subprocess_commands_lock:
                for incmd in self._subprocess_commands:
                    # If we're passing a raw string to exec, no need to wrap it
                    # in any proper structure.
                    if isinstance(incmd, str):
                        self._subprocess.stdin.write((incmd + '\n').encode())
                        self._subprocess.stdin.flush()
                    else:
                        self._send_server_command(incmd)
                self._subprocess_commands = []

            # Request restarts/shut-downs for various reasons.
            self._request_shutdowns_or_restarts()

            # If they want to force-kill our subprocess, simply exit this
            # loop; the cleanup code will kill the process.
            if (self._subprocess_force_kill_time is not None
                    and time.time() > self._subprocess_force_kill_time):
                print(f'{Clr.CYN}Force-killing subprocess...{Clr.RST}')
                break

            # Watch for the process exiting on its own..
            code: Optional[int] = self._subprocess.poll()
            if code is not None:
                if code == 0:
                    clr = Clr.CYN
                    slp = 0.0
                    desc = ''
                elif code == 154:
                    clr = Clr.CYN
                    slp = 0.0
                    desc = ' (idle_exit_minutes reached)'
                    self._wrapper_shutdown_desired = True
                else:
                    clr = Clr.SRED
                    slp = 5.0  # Avoid super fast death loops.
                    desc = ''
                print(f'{clr}Server subprocess exited'
                      f' with code {code}{desc}.{Clr.RST}')
                self._reset_subprocess_vars()
                time.sleep(slp)
                break

            time.sleep(0.25)