From f26954f3305f23950f95bd27dc767d229ee2532a Mon Sep 17 00:00:00 2001 From: Eric Froemling Date: Thu, 30 Apr 2020 00:37:31 -0700 Subject: [PATCH] Tidying --- .../src/ba_data/python/efro/dataclassutils.py | 19 +++++++++---------- assets/src/server/ballisticacore_server.py | 16 +++++++++++----- 2 files changed, 20 insertions(+), 15 deletions(-) diff --git a/assets/src/ba_data/python/efro/dataclassutils.py b/assets/src/ba_data/python/efro/dataclassutils.py index a709cea0..9d2f0f9d 100644 --- a/assets/src/ba_data/python/efro/dataclassutils.py +++ b/assets/src/ba_data/python/efro/dataclassutils.py @@ -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(). diff --git a/assets/src/server/ballisticacore_server.py b/assets/src/server/ballisticacore_server.py index 974bac12..4120a365 100755 --- a/assets/src/server/ballisticacore_server.py +++ b/assets/src/server/ballisticacore_server.py @@ -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)