This commit is contained in:
Eric Froemling 2020-04-30 00:37:31 -07:00
parent 48f72ec123
commit f26954f330
2 changed files with 20 additions and 15 deletions

View File

@ -27,9 +27,9 @@ from typing import TYPE_CHECKING
if TYPE_CHECKING:
from typing import Any, Dict, Type, Tuple
# For fields with these type strings, we require a passed value's type
# For fields with these string types, we require a passed value's type
# to exactly match one of the tuple values to consider the assignment valid.
_ASSIGN_TYPES: Dict[str, Tuple[Type, ...]] = {
_SIMPLE_ASSIGN_TYPES: Dict[str, Tuple[Type, ...]] = {
'bool': (bool, ),
'str': (str, ),
'int': (int, ),
@ -55,9 +55,9 @@ def dataclass_assign(instance: Any, values: Dict[str, Any]) -> None:
An AttributeError will be raised if attributes are passed which are
not present on the dataclass as fields.
This function may be significantly slower than simply passing dict
This function may add significant overhead compared to passing dict
values to a dataclass' constructor or other more direct methods, but
the increased safety checks may be worth the extra overhead in some
the increased safety checks may be worth the speed tradeoff in some
cases.
"""
if not dataclasses.is_dataclass(instance):
@ -72,8 +72,8 @@ def dataclass_assign(instance: Any, values: Dict[str, Any]) -> None:
f" no '{key}' field.")
field = fieldsdict[key]
# We expect to be operating with 'from __future__ import annotations'
# so this should always be a string for us; not an actual type.
# We expect to be operating under 'from __future__ import annotations'
# so field types should always be strings for us; not an actual types.
# Complain if we come across an actual type.
fieldtype: str = field.type # type: ignore
if not isinstance(fieldtype, str):
@ -82,11 +82,10 @@ def dataclass_assign(instance: Any, values: Dict[str, Any]) -> None:
f' been created without "from __future__ import annotations";'
f' those dataclasses are unsupported here.')
reqtypes = _ASSIGN_TYPES.get(fieldtype)
reqtypes = _SIMPLE_ASSIGN_TYPES.get(fieldtype)
if reqtypes is not None:
# pylint: disable=unidiomatic-typecheck
if not any(type(value) is t for t in reqtypes):
# if not isinstance(value, reqtype):
if len(reqtypes) == 1:
expected = reqtypes[0].__name__
else:
@ -105,8 +104,8 @@ def dataclass_assign(instance: Any, values: Dict[str, Any]) -> None:
def dataclass_validate(instance: Any) -> None:
"""Ensure values in a dataclass are correct types.
Note that this will always fail if a dataclass has value types
unsupported by this module.
Note that this will always fail if a dataclass contains field types
not supported by this module.
"""
# We currently simply operate by grabbing dataclass values as a dict
# and passing them through dataclass_assign().

View File

@ -38,9 +38,9 @@ sys.path += [
str(Path(os.getcwd(), 'dist', 'ba_data', 'python-site-packages'))
]
from efro.dataclassutils import dataclass_assign, dataclass_validate
from bacommon.servermanager import (ServerConfig, ServerCommand,
make_server_command)
from efro.dataclassutils import dataclass_assign
if TYPE_CHECKING:
from typing import Optional, List, Dict
@ -50,8 +50,8 @@ if TYPE_CHECKING:
class ServerManagerApp:
"""An app which manages BallisticaCore server execution.
Handles configuring, launching, re-launching, and controlling
BallisticaCore binaries operating in server mode.
Handles configuring, launching, re-launching, and otherwise
managing a BallisticaCore binary operating as a server.
"""
def __init__(self) -> None:
@ -82,11 +82,16 @@ class ServerManagerApp:
"""The current config settings for the app."""
return self._config
@config.setter
def config(self, value: ServerConfig) -> None:
dataclass_validate(value)
self._config = value
def _load_config(self) -> ServerConfig:
user_config_path = 'config.yaml'
# Start with a default config, and if there is a config.yaml,
# override parts of it.
# assign whatever is contained within.
config = ServerConfig()
if os.path.exists(user_config_path):
import yaml
@ -99,7 +104,6 @@ class ServerManagerApp:
if not isinstance(user_config, dict):
raise RuntimeError(f'Invalid config format; expected dict,'
f' got {type(user_config)}.')
return config
def _get_binary_path(self) -> str:
@ -256,6 +260,8 @@ class ServerManagerApp:
assert self._process 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)
cmd = make_server_command(ServerCommand.CONFIG, self._config)
assert self._process.stdin is not None
self._process.stdin.write(cmd)