diff --git a/.efrocachemap b/.efrocachemap index 595a636e..2ecdd6d8 100644 --- a/.efrocachemap +++ b/.efrocachemap @@ -3995,50 +3995,50 @@ "assets/src/ba_data/python/ba/_generated/__init__.py": "https://files.ballistica.net/cache/ba1/ee/e8/cad05aa531c7faf7ff7b96db7f6e", "assets/src/ba_data/python/ba/_generated/enums.py": "https://files.ballistica.net/cache/ba1/b2/e5/0ee0561e16257a32830645239f34", "ballisticacore-windows/Generic/BallisticaCore.ico": "https://files.ballistica.net/cache/ba1/89/c0/e32c7d2a35dc9aef57cc73b0911a", - "build/prefab/full/linux_arm64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/51/59/cc39973010b0acd529c15c2dd45f", - "build/prefab/full/linux_arm64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/ea/d0/67462139da48d00f1aa2019f922f", - "build/prefab/full/linux_arm64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/12/55/8fa04f69caad9c0a599d8930cebb", - "build/prefab/full/linux_arm64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/ae/ec/9915b895baf73e1c4b611ae29124", - "build/prefab/full/linux_x86_64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/42/90/5a24c7916556b0e172d40988c374", - "build/prefab/full/linux_x86_64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/c9/f0/1699b86f1c9e7644df46e1743aeb", - "build/prefab/full/linux_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/01/59/8e456a69cb9cf215fe2767e095ca", - "build/prefab/full/linux_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/09/ee/ae52581f16b3b1a5d8033afd08d3", - "build/prefab/full/mac_arm64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/15/22/9e2fa220b4aac26ee74f6ce1d1dc", - "build/prefab/full/mac_arm64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/42/16/5ecb5eeaf2229530dcd5fa7a8b30", - "build/prefab/full/mac_arm64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/f6/af/3ee6ead1ac4db2c8d610215a25c0", - "build/prefab/full/mac_arm64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/74/bb/bfd9f53b8e9efa5fa1a3ecbc95e3", - "build/prefab/full/mac_x86_64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/e5/de/4c1d5e100f8cab1bde6564d0e98b", - "build/prefab/full/mac_x86_64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/a6/29/9a8e1a0e70888563a7721b4acb92", - "build/prefab/full/mac_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/00/e8/341aa12a06b2acbb9725d3d34dfc", - "build/prefab/full/mac_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/9e/cd/dec598eec665c634e1bdd253c6b4", - "build/prefab/full/windows_x86_gui/debug/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/6d/bd/e8a96659500d6114205b95283b0f", - "build/prefab/full/windows_x86_gui/release/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/cf/d6/00fb3da261066c9aae7daa97b6a1", - "build/prefab/full/windows_x86_server/debug/dist/BallisticaCoreHeadless.exe": "https://files.ballistica.net/cache/ba1/14/ab/9953746fc7301c2696193e7f33c5", - "build/prefab/full/windows_x86_server/release/dist/BallisticaCoreHeadless.exe": "https://files.ballistica.net/cache/ba1/b7/e8/90ff3dd46f6c6cca356f135c9221", - "build/prefab/lib/linux_arm64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/e4/52/87fe024c4770f8fcf1781263dde0", - "build/prefab/lib/linux_arm64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/74/c0/4c7bbd9444abdf80b0a9c4488601", - "build/prefab/lib/linux_arm64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/4d/ae/b4ae14f9a81de19df7c9bfcb4681", - "build/prefab/lib/linux_arm64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/52/4b/8a2e69af63cdb8b0c163f2d5d5fa", - "build/prefab/lib/linux_x86_64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/f5/a8/7db7184cff719671094aa773982f", - "build/prefab/lib/linux_x86_64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/cf/13/3ec025d1c71b72da334f1654a998", - "build/prefab/lib/linux_x86_64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/57/8e/9036ee37957fb9f78c4da33d2528", - "build/prefab/lib/linux_x86_64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/1f/b4/4cc655bd0d010205f87f2df38408", - "build/prefab/lib/mac_arm64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/02/f3/722eecf8b043bd3e6446867977e7", - "build/prefab/lib/mac_arm64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/04/36/41f5026b51077e2238c498bc0f4b", - "build/prefab/lib/mac_arm64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/b4/c8/6438c59b7ab41ae49149510c34fe", - "build/prefab/lib/mac_arm64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/ec/14/89e1f6829341d2fdf0564b426312", - "build/prefab/lib/mac_x86_64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/d6/1a/fc37ff6a34b19948374fd527cf66", - "build/prefab/lib/mac_x86_64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/3f/bb/99b4e9a9907cd40788ca957d9d4d", - "build/prefab/lib/mac_x86_64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/e9/34/ebabccae377eee2cc8aadf557e7f", - "build/prefab/lib/mac_x86_64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/b5/d2/5a8f9c751486ecf75c47d93ae8cd", - "build/prefab/lib/windows/Debug_Win32/BallisticaCoreGenericInternal.lib": "https://files.ballistica.net/cache/ba1/79/3e/c51bc16d2329d5589d1e618e5c71", - "build/prefab/lib/windows/Debug_Win32/BallisticaCoreGenericInternal.pdb": "https://files.ballistica.net/cache/ba1/4f/aa/a29074431ef7e8ee41647a0081ef", - "build/prefab/lib/windows/Debug_Win32/BallisticaCoreHeadlessInternal.lib": "https://files.ballistica.net/cache/ba1/cd/81/eb4a9bda7b13f3533b4101266871", - "build/prefab/lib/windows/Debug_Win32/BallisticaCoreHeadlessInternal.pdb": "https://files.ballistica.net/cache/ba1/28/18/042e311d7709771c68297bf4ef5a", - "build/prefab/lib/windows/Release_Win32/BallisticaCoreGenericInternal.lib": "https://files.ballistica.net/cache/ba1/b6/1b/8f597eb70959a800dd77d0ebc8ed", - "build/prefab/lib/windows/Release_Win32/BallisticaCoreGenericInternal.pdb": "https://files.ballistica.net/cache/ba1/43/a5/45d00cff78ad7edceea76492923d", - "build/prefab/lib/windows/Release_Win32/BallisticaCoreHeadlessInternal.lib": "https://files.ballistica.net/cache/ba1/6e/74/654cfd6c4a0f213225405c029855", - "build/prefab/lib/windows/Release_Win32/BallisticaCoreHeadlessInternal.pdb": "https://files.ballistica.net/cache/ba1/ef/62/b2d30a83a57880ab6e4805f21c51", + "build/prefab/full/linux_arm64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/06/a9/6af2420bb4c2472e9ee8eb4adbbb", + "build/prefab/full/linux_arm64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/5a/1a/06cb8a31b90d5f6b4687f0dd900e", + "build/prefab/full/linux_arm64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/ec/5a/228a7a4533f47476d7feb66eb753", + "build/prefab/full/linux_arm64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/34/db/161b4aff5dce8ce73dc4a4124ea6", + "build/prefab/full/linux_x86_64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/d8/71/572c6ae3b24dab621444cb9cb4b9", + "build/prefab/full/linux_x86_64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/82/ea/742386e234f74bd27618341c8b1c", + "build/prefab/full/linux_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/3d/3c/f41c01c6d97baad1a2b0a5c8fe34", + "build/prefab/full/linux_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/59/ad/360fb7b46011c5bb22015e8d88e7", + "build/prefab/full/mac_arm64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/7c/1d/807ae39cd64308fb2cc4ee3821f0", + "build/prefab/full/mac_arm64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/e9/f3/ec8376ed0c6f53f009dd6f9c14ab", + "build/prefab/full/mac_arm64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/c4/d9/bd6d53b98c90e8ea9ba9a38390ba", + "build/prefab/full/mac_arm64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/c1/22/e76cf27c846b4bbf630d0058d244", + "build/prefab/full/mac_x86_64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/f2/ed/59aa13c1dbd45f574d50903d7b84", + "build/prefab/full/mac_x86_64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/d0/85/e527ac19480e8a168e0d4da742a4", + "build/prefab/full/mac_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/e8/9b/c35aa8a2690173004954345c879a", + "build/prefab/full/mac_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/56/8d/25475ac95c66ab90193bd835a5d1", + "build/prefab/full/windows_x86_gui/debug/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/7f/f4/460bff13d91b0d61f41ecc9f9128", + "build/prefab/full/windows_x86_gui/release/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/53/84/0439b2c18a02c33e71027d18f853", + "build/prefab/full/windows_x86_server/debug/dist/BallisticaCoreHeadless.exe": "https://files.ballistica.net/cache/ba1/a4/c0/7cf3abfa3224860e96c4d4c44184", + "build/prefab/full/windows_x86_server/release/dist/BallisticaCoreHeadless.exe": "https://files.ballistica.net/cache/ba1/5b/e5/f2858d831cad4162dc4d6529f28e", + "build/prefab/lib/linux_arm64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/41/b3/5a5813294b9281ca6bbb1f5caebe", + "build/prefab/lib/linux_arm64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/28/05/b6cc01e7b6762a3c8d47851cd30c", + "build/prefab/lib/linux_arm64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/f7/8c/36acf6d02d7c322aed086cc4ec3f", + "build/prefab/lib/linux_arm64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/ca/65/de52706de6aa624f01c5907e5367", + "build/prefab/lib/linux_x86_64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/2d/3d/3e3e65846f42810c032cb5efd997", + "build/prefab/lib/linux_x86_64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/da/bf/c4a1f6e6d2495723acbd93e1b22c", + "build/prefab/lib/linux_x86_64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/ac/34/6605f152207515d50174eca420e7", + "build/prefab/lib/linux_x86_64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/8c/b6/770c3d26d70edec763335c38de73", + "build/prefab/lib/mac_arm64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/33/10/a753a671240f243b2351b5730917", + "build/prefab/lib/mac_arm64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/1c/e4/05dc037f8669fc5ddc993dc8defc", + "build/prefab/lib/mac_arm64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/7c/c9/879778a21f2403346adf8db17318", + "build/prefab/lib/mac_arm64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/86/36/6d77a933a8126df4fde064fe881f", + "build/prefab/lib/mac_x86_64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/80/e8/1c9c69d9fd5e47d0fd6e3c9b1134", + "build/prefab/lib/mac_x86_64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/ac/e2/4208f2fc902ed0526bf4cb5bbc54", + "build/prefab/lib/mac_x86_64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/e2/95/5fd6cede6b0e88753bb50b437110", + "build/prefab/lib/mac_x86_64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/87/0b/442839bc7a21c12ed6e173abf7ca", + "build/prefab/lib/windows/Debug_Win32/BallisticaCoreGenericInternal.lib": "https://files.ballistica.net/cache/ba1/b6/aa/0666c00d1751f934cc1b0907fa40", + "build/prefab/lib/windows/Debug_Win32/BallisticaCoreGenericInternal.pdb": "https://files.ballistica.net/cache/ba1/aa/22/3afc115395a7909eac78d98cf856", + "build/prefab/lib/windows/Debug_Win32/BallisticaCoreHeadlessInternal.lib": "https://files.ballistica.net/cache/ba1/9f/e6/5097b01991ce22e1d3fd594808b2", + "build/prefab/lib/windows/Debug_Win32/BallisticaCoreHeadlessInternal.pdb": "https://files.ballistica.net/cache/ba1/d9/4f/7f2a34294f53378990487655f4ab", + "build/prefab/lib/windows/Release_Win32/BallisticaCoreGenericInternal.lib": "https://files.ballistica.net/cache/ba1/18/d3/50bbe34de1862d8224dafa8792e7", + "build/prefab/lib/windows/Release_Win32/BallisticaCoreGenericInternal.pdb": "https://files.ballistica.net/cache/ba1/82/93/f80194878bfed43f58809a7e9823", + "build/prefab/lib/windows/Release_Win32/BallisticaCoreHeadlessInternal.lib": "https://files.ballistica.net/cache/ba1/73/54/88f193633990884b61719a481dec", + "build/prefab/lib/windows/Release_Win32/BallisticaCoreHeadlessInternal.pdb": "https://files.ballistica.net/cache/ba1/f4/23/2a4e21dc99e6135c1cb102f6ca37", "src/ballistica/generated/python_embedded/binding.inc": "https://files.ballistica.net/cache/ba1/c0/32/b7907e3859a5c5013a3d97b6b523", "src/ballistica/generated/python_embedded/bootstrap.inc": "https://files.ballistica.net/cache/ba1/2d/4f/f4fe67827f36cd59cd5193333a02", "src/ballistica/generated/python_embedded/bootstrap_monolithic.inc": "https://files.ballistica.net/cache/ba1/ef/c1/aa5f1aa10af89f5c0b1e616355fd" diff --git a/CHANGELOG.md b/CHANGELOG.md index eea1d06f..42740e8f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,4 @@ -### 1.7.9 (build 20873, api 7, 2022-09-21) +### 1.7.9 (build 20875, api 7, 2022-09-21) ### 1.7.8 (build 20871, api 7, 2022-09-21) - Fixed tournament scores submits which were broken in 1.7.7 (oops). @@ -45,13 +45,13 @@ - If you want to grab recent logs, you can now use `ba.app.log_handler.get_cached()`. This will give you everything that has gone through Python logging, Python stdout/stderr, and the C++ Log() call (up to the max cache size that is). - LogHandler output now ALWAYS goes to stderr. Previously it only would if an interactive terminal was detected. This should make the binary easier to debug if run from scripts/etc. We can add a `--quiet` option if needed or whatnot. - (build 20859) Fixed an error setting up asyncio loops under Windows related to the fact that Python is now inited in the main thread. -- (build 20864) Fatal-error message/traceback now properly prints to stderr again (I think the reject logging rejiggering caused it to stop). +- (build 20864) Fatal-error message/traceback now properly prints to stderr again (I think the recent logging rejiggering caused it to stop). - (build 20864) Fixed an issue where the app could crash when connected to the cloud console while in a network game. - Added a simplified help() command which behaves reasonably under the in-game console or cloud-console. ### 1.7.6 (build 20687, api 7, 2022-08-11) -- Cleaned up da MetaSubsystem code. +- Cleaned up the MetaSubsystem code. - It is now possible to tell the meta system about arbitrary classes (ba_meta export foo.bar.Class) instead of just the preset types 'plugin', 'game', etc. - Newly discovered plugins are now activated immediately instead of requiring a restart. diff --git a/assets/src/ba_data/python/ba/_bootstrap.py b/assets/src/ba_data/python/ba/_bootstrap.py index 44daa02d..6ec84868 100644 --- a/assets/src/ba_data/python/ba/_bootstrap.py +++ b/assets/src/ba_data/python/ba/_bootstrap.py @@ -45,7 +45,7 @@ def bootstrap() -> None: # Give a soft warning if we're being used with a different binary # version than we expect. - expected_build = 20873 + expected_build = 20875 running_build: int = env['build_number'] if running_build != expected_build: print( diff --git a/src/ballistica/ballistica.cc b/src/ballistica/ballistica.cc index a8adeef0..c9f39df7 100644 --- a/src/ballistica/ballistica.cc +++ b/src/ballistica/ballistica.cc @@ -32,7 +32,7 @@ namespace ballistica { // These are set automatically via script; don't modify them here. -const int kAppBuildNumber = 20873; +const int kAppBuildNumber = 20875; const char* kAppVersion = "1.7.9"; // Our standalone globals. diff --git a/tests/test_efro/test_message.py b/tests/test_efro/test_message.py index 3d04624b..a88e90bb 100644 --- a/tests/test_efro/test_message.py +++ b/tests/test_efro/test_message.py @@ -23,6 +23,8 @@ from efro.message import (Message, Response, MessageProtocol, MessageSender, if TYPE_CHECKING: from typing import Any, Callable, Awaitable + from efro.message import SysResponse + @ioprepped @dataclass @@ -31,7 +33,7 @@ class _TMsg1(Message): ival: int @classmethod - def get_response_types(cls) -> list[type[Response]]: + def get_response_types(cls) -> list[type[Response] | None]: return [_TResp1] @@ -42,7 +44,7 @@ class _TMsg2(Message): sval: str @classmethod - def get_response_types(cls) -> list[type[Response]]: + def get_response_types(cls) -> list[type[Response] | None]: return [_TResp1, _TResp2] @@ -808,7 +810,7 @@ def test_full_pipeline() -> None: @msg.decode_filter_method def _decode_filter(self, message: Message, indata: dict, - response: Response) -> None: + response: Response | SysResponse) -> None: """Filter our incoming responses.""" del message # Unused. if self.test_sidecar: @@ -852,7 +854,8 @@ def test_full_pipeline() -> None: setattr(message, '_sidecar_data', indata['_sidecar_data']) @receiver.encode_filter_method - def _encode_filter(self, message: Message | None, response: Response, + def _encode_filter(self, message: Message | None, + response: Response | SysResponse, outdict: dict) -> None: """Filter our outgoing responses.""" del message # Unused. diff --git a/tools/bacommon/cloud.py b/tools/bacommon/cloud.py index 34e9ccaa..91512f92 100644 --- a/tools/bacommon/cloud.py +++ b/tools/bacommon/cloud.py @@ -21,7 +21,7 @@ class LoginProxyRequestMessage(Message): """Request send to the cloud to ask for a login-proxy.""" @classmethod - def get_response_types(cls) -> list[type[Response]]: + def get_response_types(cls) -> list[type[Response] | None]: return [LoginProxyRequestResponse] @@ -48,7 +48,7 @@ class LoginProxyStateQueryMessage(Message): proxykey: Annotated[str, IOAttrs('k')] @classmethod - def get_response_types(cls) -> list[type[Response]]: + def get_response_types(cls) -> list[type[Response] | None]: return [LoginProxyStateQueryResponse] @@ -82,7 +82,7 @@ class PingMessage(Message): """Standard ping.""" @classmethod - def get_response_types(cls) -> list[type[Response]]: + def get_response_types(cls) -> list[type[Response] | None]: return [PingResponse] @@ -99,7 +99,7 @@ class TestMessage(Message): testfoo: Annotated[int, IOAttrs('f')] @classmethod - def get_response_types(cls) -> list[type[Response]]: + def get_response_types(cls) -> list[type[Response] | None]: return [TestResponse] @@ -130,7 +130,7 @@ class WorkspaceFetchMessage(Message): state: Annotated[WorkspaceFetchState, IOAttrs('s')] @classmethod - def get_response_types(cls) -> list[type[Response]]: + def get_response_types(cls) -> list[type[Response] | None]: return [WorkspaceFetchResponse] diff --git a/tools/efro/message/__init__.py b/tools/efro/message/__init__.py index bce8da5b..326ba025 100644 --- a/tools/efro/message/__init__.py +++ b/tools/efro/message/__init__.py @@ -11,15 +11,16 @@ from efro.message._protocol import MessageProtocol from efro.message._sender import (MessageSender, BoundMessageSender) from efro.message._receiver import (MessageReceiver, BoundMessageReceiver) from efro.message._module import (create_sender_module, create_receiver_module) -from efro.message._message import (Message, Response, EmptySysResponse, - ErrorSysResponse, StringResponse, - BoolResponse, UnregisteredMessageIDError) +from efro.message._message import (Message, Response, SysResponse, + EmptySysResponse, ErrorSysResponse, + StringResponse, BoolResponse, + UnregisteredMessageIDError) __all__ = [ - 'Message', 'Response', 'EmptySysResponse', 'ErrorSysResponse', - 'StringResponse', 'BoolResponse', 'MessageProtocol', 'MessageSender', - 'BoundMessageSender', 'MessageReceiver', 'BoundMessageReceiver', - 'create_sender_module', 'create_receiver_module', + 'Message', 'Response', 'SysResponse', 'EmptySysResponse', + 'ErrorSysResponse', 'StringResponse', 'BoolResponse', 'MessageProtocol', + 'MessageSender', 'BoundMessageSender', 'MessageReceiver', + 'BoundMessageReceiver', 'create_sender_module', 'create_receiver_module', 'UnregisteredMessageIDError' ] diff --git a/tools/efro/message/_message.py b/tools/efro/message/_message.py index c2bc8c17..f74794eb 100644 --- a/tools/efro/message/_message.py +++ b/tools/efro/message/_message.py @@ -24,7 +24,7 @@ class Message: """Base class for messages.""" @classmethod - def get_response_types(cls) -> list[type[Response]]: + def get_response_types(cls) -> list[type[Response] | None]: """Return all message types this Message can result in when sent. The default implementation specifies EmptySysResponse, so messages with @@ -32,19 +32,27 @@ class Message: Note that ErrorMessage is handled as a special case and does not need to be specified here. """ - return [EmptySysResponse] + return [None] class Response: """Base class for responses to messages.""" +class SysResponse: + """Base class for system-responses to messages. + + These are only sent/handled by the messaging system itself; + users of the api never see them. + """ + + # Some standard response types: @ioprepped @dataclass -class ErrorSysResponse(Response): +class ErrorSysResponse(SysResponse): """Response saying some error has occurred for the send. This type is unique in that it is not returned to the user; it @@ -64,7 +72,7 @@ class ErrorSysResponse(Response): @ioprepped @dataclass -class EmptySysResponse(Response): +class EmptySysResponse(SysResponse): """The response equivalent of None.""" diff --git a/tools/efro/message/_protocol.py b/tools/efro/message/_protocol.py index b37f4284..7d7e572e 100644 --- a/tools/efro/message/_protocol.py +++ b/tools/efro/message/_protocol.py @@ -14,8 +14,8 @@ import json from efro.error import CleanError from efro.dataclassio import (is_ioprepped_dataclass, dataclass_to_dict, dataclass_from_dict) -from efro.message._message import (Message, Response, ErrorSysResponse, - EmptySysResponse, +from efro.message._message import (Message, Response, SysResponse, + ErrorSysResponse, EmptySysResponse, UnregisteredMessageIDError) if TYPE_CHECKING: @@ -54,8 +54,10 @@ class MessageProtocol: """ self.message_types_by_id: dict[int, type[Message]] = {} self.message_ids_by_type: dict[type[Message], int] = {} - self.response_types_by_id: dict[int, type[Response]] = {} - self.response_ids_by_type: dict[type[Response], int] = {} + self.response_types_by_id: dict[int, type[Response] + | type[SysResponse]] = {} + self.response_ids_by_type: dict[type[Response] | type[SysResponse], + int] = {} for m_id, m_type in message_types.items(): # Make sure only valid message types were passed and each @@ -80,7 +82,7 @@ class MessageProtocol: # Go ahead and auto-register a few common response types # if the user has not done so explicitly. Use unique negative # IDs which will never change or overlap with user ids. - def _reg_sys(reg_tp: type[Response], reg_id: int) -> None: + def _reg_sys(reg_tp: type[SysResponse], reg_id: int) -> None: assert self.response_types_by_id.get(reg_id) is None self.response_types_by_id[reg_id] = reg_tp self.response_ids_by_type[reg_tp] = reg_id @@ -92,15 +94,19 @@ class MessageProtocol: if __debug__: # Make sure all Message types' return types are valid # and have been assigned an ID as well. - all_response_types: set[type[Response]] = set() + all_response_types: set[type[Response] | None] = set() for m_id, m_type in message_types.items(): m_rtypes = m_type.get_response_types() + assert isinstance(m_rtypes, list) assert m_rtypes, ( f'Message type {m_type} specifies no return types.') assert len(set(m_rtypes)) == len(m_rtypes) # check dups - all_response_types.update(m_rtypes) + for m_rtype in m_rtypes: + all_response_types.add(m_rtype) for cls in all_response_types: + if cls is None: + continue assert is_ioprepped_dataclass(cls) assert issubclass(cls, Response) if cls not in self.response_ids_by_type: @@ -130,11 +136,11 @@ class MessageProtocol: """Encode a message to a json ready dict.""" return self._to_dict(message, self.message_ids_by_type, 'message') - def response_to_dict(self, response: Response) -> dict: + def response_to_dict(self, response: Response | SysResponse) -> dict: """Encode a response to a json ready dict.""" return self._to_dict(response, self.response_ids_by_type, 'response') - def error_to_response(self, exc: Exception) -> Response: + def error_to_response(self, exc: Exception) -> SysResponse: """Translate an error to a response.""" # Log any errors we got during handling. @@ -176,10 +182,10 @@ class MessageProtocol: assert isinstance(out, Message) return out - def response_from_dict(self, data: dict) -> Response: + def response_from_dict(self, data: dict) -> Response | SysResponse: """Decode a response from a json string.""" out = self._from_dict(data, self.response_types_by_id, 'response') - assert isinstance(out, Response) + assert isinstance(out, Response | SysResponse) return out # Weeeird; we get mypy errors returning dict[int, type] but @@ -332,10 +338,10 @@ class MessageProtocol: f'class {ppre}Bound{basename}(BoundMessageSender):\n' f' """Protocol-specific bound sender."""\n') - def _filt_tp_name(rtype: type[Response]) -> str: + def _filt_tp_name(rtype: type[Response] | None) -> str: # We accept None to equal EmptySysResponse so reflect that # in the type annotation. - return 'None' if rtype is EmptySysResponse else rtype.__name__ + return 'None' if rtype is None else rtype.__name__ # Define handler() overloads for all registered message types. if msgtypes: @@ -372,6 +378,7 @@ class MessageProtocol: for msgtype in msgtypes: msgtypevar = msgtype.__name__ + # rtypes = msgtype.get_response_types() rtypes = msgtype.get_response_types() if len(rtypes) > 1: rtypevar = ' | '.join( @@ -429,10 +436,10 @@ class MessageProtocol: # Define handler() overloads for all registered message types. - def _filt_tp_name(rtype: type[Response]) -> str: + def _filt_tp_name(rtype: type[Response] | None) -> str: # We accept None to equal EmptySysResponse so reflect that # in the type annotation. - return 'None' if rtype is EmptySysResponse else rtype.__name__ + return 'None' if rtype is None else rtype.__name__ if msgtypes: cbgn = 'Awaitable[' if is_async else '' diff --git a/tools/efro/message/_receiver.py b/tools/efro/message/_receiver.py index b277ff4c..5bf5d869 100644 --- a/tools/efro/message/_receiver.py +++ b/tools/efro/message/_receiver.py @@ -12,13 +12,13 @@ import logging from typing import TYPE_CHECKING from efro.message._message import (Message, Response, EmptySysResponse, - ErrorSysResponse, UnregisteredMessageIDError) if TYPE_CHECKING: from typing import Any, Callable, Awaitable from efro.message._protocol import MessageProtocol + from efro.message._message import SysResponse class MessageReceiver: @@ -54,7 +54,8 @@ class MessageReceiver: self._decode_filter_call: Callable[[Any, dict, Message], None] | None = None self._encode_filter_call: Callable[ - [Any, Message | None, Response, dict], None] | None = None + [Any, Message | None, Response | SysResponse, dict], + None] | None = None # TODO: don't currently have async encode equivalent # or either for sender; can add as needed. @@ -107,26 +108,31 @@ class MessageReceiver: assert issubclass(msgtype, Message) ret = anns.get('return') - responsetypes: tuple[type[Any] | type[None], ...] + responsetypes: tuple[type[Any] | None, ...] # Return types can be a single type or a union of types. if isinstance(ret, (_GenericAlias, types.UnionType)): targs = get_args(ret) - if not all(isinstance(a, type) for a in targs): + if not all(isinstance(a, (type, type(None))) for a in targs): raise TypeError(f'expected only types for "return" annotation;' f' got {targs}.') responsetypes = targs else: - if not isinstance(ret, type): + if not isinstance(ret, (type, type(None))): raise TypeError(f'expected one or more types for' f' "return" annotation; got a {type(ret)}.') # This seems like maybe a mypy bug. Appeared after adding # types.UnionType above. - responsetypes = (ret, ) # type: ignore + responsetypes = (ret, ) + + # This will contain NoneType for empty return cases, but + # we expect it to be None. + responsetypes = tuple(None if r is type(None) else r + for r in responsetypes) # Return type of None translates to EmptySysResponse. - responsetypes = tuple(EmptySysResponse if r is type(None) else r - for r in responsetypes) # noqa + # responsetypes = tuple(EmptySysResponse if r is type(None) else r + # for r in responsetypes) # noqa # Make sure our protocol has this message type registered and our # return types exactly match. (Technically we could return a subset @@ -179,7 +185,9 @@ class MessageReceiver: return call def encode_filter_method( - self, call: Callable[[Any, Message | None, Response, dict], None] + self, + call: Callable[[Any, Message | None, Response | SysResponse, dict], + None] ) -> Callable[[Any, Message | None, Response, dict], None]: """Function decorator for defining an encode filter. @@ -237,17 +245,21 @@ class MessageReceiver: response: Response | None) -> str: """Encode a response provided by the user for sending.""" - # A return value of None equals EmptySysResponse. - if response is None: - response = EmptySysResponse() - - assert isinstance(response, Response) + assert isinstance(response, Response | None) # (user should never explicitly return error-responses) - assert not isinstance(response, ErrorSysResponse) - assert type(response) in message.get_response_types() - response_dict = self.protocol.response_to_dict(response) + assert (response is None + or type(response) in message.get_response_types()) + + # A return value of None equals EmptySysResponse. + out_response: Response | SysResponse + if response is None: + out_response = EmptySysResponse() + else: + out_response = response + + response_dict = self.protocol.response_to_dict(out_response) if self._encode_filter_call is not None: - self._encode_filter_call(bound_obj, message, response, + self._encode_filter_call(bound_obj, message, out_response, response_dict) return self.protocol.encode_dict(response_dict) @@ -281,7 +293,7 @@ class MessageReceiver: if handler is None: raise RuntimeError(f'Got unhandled message type: {msgtype}.') response = handler(bound_obj, msg_decoded) - assert isinstance(response, (Response, type(None))) + assert isinstance(response, Response | None) return self.encode_user_response(bound_obj, msg_decoded, response) except Exception as exc: @@ -309,7 +321,7 @@ class MessageReceiver: if handler is None: raise RuntimeError(f'Got unhandled message type: {msgtype}.') response = await handler(bound_obj, msg_decoded) - assert isinstance(response, (Response, type(None))) + assert isinstance(response, Response | None) return self.encode_user_response(bound_obj, msg_decoded, response) except Exception as exc: diff --git a/tools/efro/message/_sender.py b/tools/efro/message/_sender.py index b176f7f7..48a32e9f 100644 --- a/tools/efro/message/_sender.py +++ b/tools/efro/message/_sender.py @@ -10,12 +10,12 @@ import logging from typing import TYPE_CHECKING from efro.error import CleanError, RemoteError, CommunicationError -from efro.message._message import EmptySysResponse, ErrorSysResponse +from efro.message._message import EmptySysResponse, ErrorSysResponse, Response if TYPE_CHECKING: from typing import Any, Callable, Awaitable - from efro.message._message import Message, Response + from efro.message._message import Message, SysResponse from efro.message._protocol import MessageProtocol @@ -46,8 +46,8 @@ class MessageSender: [Any, str], Awaitable[str]] | None = None self._encode_filter_call: Callable[[Any, Message, dict], None] | None = None - self._decode_filter_call: Callable[[Any, Message, dict, Response], - None] | None = None + self._decode_filter_call: Callable[ + [Any, Message, dict, Response | SysResponse], None] | None = None def send_method( self, call: Callable[[Any, str], @@ -90,7 +90,8 @@ class MessageSender: return call def decode_filter_method( - self, call: Callable[[Any, Message, dict, Response], None] + self, call: Callable[[Any, Message, dict, Response | SysResponse], + None] ) -> Callable[[Any, Message, dict, Response], None]: """Function decorator for defining a decode filter. @@ -122,7 +123,8 @@ class MessageSender: ), ) - def send_split_part_1(self, bound_obj: Any, message: Message) -> Response: + def send_split_part_1(self, bound_obj: Any, + message: Message) -> Response | SysResponse: """Send a message synchronously. Generally you can just call send(); these split versions are @@ -147,8 +149,8 @@ class MessageSender: ErrorSysResponse.ErrorType.LOCAL)) return self._decode_raw_response(bound_obj, message, response_encoded) - async def send_split_part_1_async(self, bound_obj: Any, - message: Message) -> Response: + async def send_split_part_1_async( + self, bound_obj: Any, message: Message) -> Response | SysResponse: """Send a message asynchronously. Generally you can just call send(); these split versions are @@ -175,8 +177,9 @@ class MessageSender: ErrorSysResponse.ErrorType.LOCAL)) return self._decode_raw_response(bound_obj, message, response_encoded) - def send_split_part_2(self, message: Message, - raw_response: Response) -> Response | None: + def send_split_part_2( + self, message: Message, + raw_response: Response | SysResponse) -> Response | None: """Complete message sending (both sync and async). Generally you can just call send(); these split versions are @@ -196,7 +199,7 @@ class MessageSender: return self.protocol.encode_dict(msg_dict) def _decode_raw_response(self, bound_obj: Any, message: Message, - response_encoded: str) -> Response: + response_encoded: str) -> Response | SysResponse: """Create a Response from returned data. These Responses may encapsulate things like remote errors and @@ -204,6 +207,7 @@ class MessageSender: should be used to translate to special values like None or raise Exceptions. This function itself should never raise Exceptions. """ + response: Response | SysResponse try: response_dict = self.protocol.decode_dict(response_encoded) response = self.protocol.response_from_dict(response_dict) @@ -223,7 +227,8 @@ class MessageSender: error_type=ErrorSysResponse.ErrorType.LOCAL) return response - def _unpack_raw_response(self, raw_response: Response) -> Response | None: + def _unpack_raw_response( + self, raw_response: Response | SysResponse) -> Response | None: """Given a raw Response, unpacks to special values or Exceptions. The result of this call is what should be passed to users. @@ -256,6 +261,7 @@ class MessageSender: # Everything else gets lumped in as a remote error. raise RemoteError(raw_response.error_message) + assert isinstance(raw_response, Response) return raw_response @@ -292,15 +298,16 @@ class BoundMessageSender: return await self._sender.send_async(bound_obj=self._obj, message=message) - async def send_split_part_1_async_untyped(self, - message: Message) -> Response: + async def send_split_part_1_async_untyped( + self, message: Message) -> Response | SysResponse: """Split send (part 1 of 2).""" assert self._obj is not None return await self._sender.send_split_part_1_async(bound_obj=self._obj, message=message) - def send_split_part_2_untyped(self, message: Message, - raw_response: Response) -> Response | None: + def send_split_part_2_untyped( + self, message: Message, + raw_response: Response | SysResponse) -> Response | None: """Split send (part 2 of 2).""" return self._sender.send_split_part_2(message=message, raw_response=raw_response)