コード例 #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)
コード例 #2
0
def test_validate() -> None:
    """Testing validation."""
    @ioprepped
    @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 (without coerce)
    tclass.fval = 1
    with pytest.raises(TypeError):
        dataclass_validate(tclass, coerce_to_float=False)

    # 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)
コード例 #3
0
def test_coerce() -> None:
    """Test value coercion."""
    @ioprepped
    @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)
コード例 #4
0
def test_datetime_limits() -> None:
    """Test limiting datetime values in various ways."""
    from efro.util import utc_today, utc_this_hour

    @ioprepped
    @dataclass
    class _TestClass:
        tval: Annotated[datetime.datetime, IOAttrs(whole_hours=True)]

    # Check whole-hour limit when validating/exporting.
    obj = _TestClass(tval=utc_this_hour() + datetime.timedelta(minutes=1))
    with pytest.raises(ValueError):
        dataclass_validate(obj)
    obj.tval = utc_this_hour()
    dataclass_validate(obj)

    # Check whole-days limit when importing.
    out = dataclass_to_dict(obj)
    out['tval'][-1] += 1
    with pytest.raises(ValueError):
        dataclass_from_dict(_TestClass, out)

    # Check whole-days limit when validating/exporting.
    @ioprepped
    @dataclass
    class _TestClass2:
        tval: Annotated[datetime.datetime, IOAttrs(whole_days=True)]

    obj2 = _TestClass2(tval=utc_today() + datetime.timedelta(hours=1))
    with pytest.raises(ValueError):
        dataclass_validate(obj2)
    obj2.tval = utc_today()
    dataclass_validate(obj2)

    # Check whole-days limit when importing.
    out = dataclass_to_dict(obj2)
    out['tval'][-1] += 1
    with pytest.raises(ValueError):
        dataclass_from_dict(_TestClass2, out)
コード例 #5
0
def test_any() -> None:
    """Test data included with type Any."""
    @ioprepped
    @dataclass
    class _TestClass:
        anyval: Any

    obj = _TestClass(anyval=b'bytes')

    # JSON output doesn't allow bytes or datetime objects
    # included in 'Any' data.
    with pytest.raises(TypeError):
        dataclass_validate(obj, codec=Codec.JSON)

    obj.anyval = datetime.datetime.now()
    with pytest.raises(TypeError):
        dataclass_validate(obj, codec=Codec.JSON)

    # Firestore, however, does.
    obj.anyval = b'bytes'
    dataclass_validate(obj, codec=Codec.FIRESTORE)
    obj.anyval = datetime.datetime.now()
    dataclass_validate(obj, codec=Codec.FIRESTORE)
コード例 #6
0
 def config(self, value: ServerConfig) -> None:
     dataclass_validate(value)
     self._config = value