efro.message cleanup to clarify internal vs user response types

This commit is contained in:
Eric 2022-09-21 19:27:19 -07:00
parent a8232f3d63
commit cf27d6ac6f
No known key found for this signature in database
GPG Key ID: 89C93F0F8D6D5A98
11 changed files with 158 additions and 120 deletions

View File

@ -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/__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", "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", "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/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/ea/d0/67462139da48d00f1aa2019f922f", "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/12/55/8fa04f69caad9c0a599d8930cebb", "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/ae/ec/9915b895baf73e1c4b611ae29124", "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/42/90/5a24c7916556b0e172d40988c374", "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/c9/f0/1699b86f1c9e7644df46e1743aeb", "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/01/59/8e456a69cb9cf215fe2767e095ca", "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/09/ee/ae52581f16b3b1a5d8033afd08d3", "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/15/22/9e2fa220b4aac26ee74f6ce1d1dc", "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/42/16/5ecb5eeaf2229530dcd5fa7a8b30", "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/f6/af/3ee6ead1ac4db2c8d610215a25c0", "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/74/bb/bfd9f53b8e9efa5fa1a3ecbc95e3", "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/e5/de/4c1d5e100f8cab1bde6564d0e98b", "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/a6/29/9a8e1a0e70888563a7721b4acb92", "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/00/e8/341aa12a06b2acbb9725d3d34dfc", "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/9e/cd/dec598eec665c634e1bdd253c6b4", "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/6d/bd/e8a96659500d6114205b95283b0f", "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/cf/d6/00fb3da261066c9aae7daa97b6a1", "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/14/ab/9953746fc7301c2696193e7f33c5", "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/b7/e8/90ff3dd46f6c6cca356f135c9221", "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/e4/52/87fe024c4770f8fcf1781263dde0", "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/74/c0/4c7bbd9444abdf80b0a9c4488601", "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/4d/ae/b4ae14f9a81de19df7c9bfcb4681", "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/52/4b/8a2e69af63cdb8b0c163f2d5d5fa", "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/f5/a8/7db7184cff719671094aa773982f", "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/cf/13/3ec025d1c71b72da334f1654a998", "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/57/8e/9036ee37957fb9f78c4da33d2528", "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/1f/b4/4cc655bd0d010205f87f2df38408", "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/02/f3/722eecf8b043bd3e6446867977e7", "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/04/36/41f5026b51077e2238c498bc0f4b", "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/b4/c8/6438c59b7ab41ae49149510c34fe", "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/ec/14/89e1f6829341d2fdf0564b426312", "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/d6/1a/fc37ff6a34b19948374fd527cf66", "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/3f/bb/99b4e9a9907cd40788ca957d9d4d", "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/e9/34/ebabccae377eee2cc8aadf557e7f", "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/b5/d2/5a8f9c751486ecf75c47d93ae8cd", "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/79/3e/c51bc16d2329d5589d1e618e5c71", "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/4f/aa/a29074431ef7e8ee41647a0081ef", "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/cd/81/eb4a9bda7b13f3533b4101266871", "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/28/18/042e311d7709771c68297bf4ef5a", "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/b6/1b/8f597eb70959a800dd77d0ebc8ed", "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/43/a5/45d00cff78ad7edceea76492923d", "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/6e/74/654cfd6c4a0f213225405c029855", "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/ef/62/b2d30a83a57880ab6e4805f21c51", "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/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.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" "src/ballistica/generated/python_embedded/bootstrap_monolithic.inc": "https://files.ballistica.net/cache/ba1/ef/c1/aa5f1aa10af89f5c0b1e616355fd"

View File

@ -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) ### 1.7.8 (build 20871, api 7, 2022-09-21)
- Fixed tournament scores submits which were broken in 1.7.7 (oops). - 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). - 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. - 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 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. - (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. - 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) ### 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. - 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. - Newly discovered plugins are now activated immediately instead of requiring a restart.

View File

@ -45,7 +45,7 @@ def bootstrap() -> None:
# Give a soft warning if we're being used with a different binary # Give a soft warning if we're being used with a different binary
# version than we expect. # version than we expect.
expected_build = 20873 expected_build = 20875
running_build: int = env['build_number'] running_build: int = env['build_number']
if running_build != expected_build: if running_build != expected_build:
print( print(

View File

@ -32,7 +32,7 @@
namespace ballistica { namespace ballistica {
// These are set automatically via script; don't modify them here. // 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"; const char* kAppVersion = "1.7.9";
// Our standalone globals. // Our standalone globals.

View File

@ -23,6 +23,8 @@ from efro.message import (Message, Response, MessageProtocol, MessageSender,
if TYPE_CHECKING: if TYPE_CHECKING:
from typing import Any, Callable, Awaitable from typing import Any, Callable, Awaitable
from efro.message import SysResponse
@ioprepped @ioprepped
@dataclass @dataclass
@ -31,7 +33,7 @@ class _TMsg1(Message):
ival: int ival: int
@classmethod @classmethod
def get_response_types(cls) -> list[type[Response]]: def get_response_types(cls) -> list[type[Response] | None]:
return [_TResp1] return [_TResp1]
@ -42,7 +44,7 @@ class _TMsg2(Message):
sval: str sval: str
@classmethod @classmethod
def get_response_types(cls) -> list[type[Response]]: def get_response_types(cls) -> list[type[Response] | None]:
return [_TResp1, _TResp2] return [_TResp1, _TResp2]
@ -808,7 +810,7 @@ def test_full_pipeline() -> None:
@msg.decode_filter_method @msg.decode_filter_method
def _decode_filter(self, message: Message, indata: dict, def _decode_filter(self, message: Message, indata: dict,
response: Response) -> None: response: Response | SysResponse) -> None:
"""Filter our incoming responses.""" """Filter our incoming responses."""
del message # Unused. del message # Unused.
if self.test_sidecar: if self.test_sidecar:
@ -852,7 +854,8 @@ def test_full_pipeline() -> None:
setattr(message, '_sidecar_data', indata['_sidecar_data']) setattr(message, '_sidecar_data', indata['_sidecar_data'])
@receiver.encode_filter_method @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: outdict: dict) -> None:
"""Filter our outgoing responses.""" """Filter our outgoing responses."""
del message # Unused. del message # Unused.

View File

@ -21,7 +21,7 @@ class LoginProxyRequestMessage(Message):
"""Request send to the cloud to ask for a login-proxy.""" """Request send to the cloud to ask for a login-proxy."""
@classmethod @classmethod
def get_response_types(cls) -> list[type[Response]]: def get_response_types(cls) -> list[type[Response] | None]:
return [LoginProxyRequestResponse] return [LoginProxyRequestResponse]
@ -48,7 +48,7 @@ class LoginProxyStateQueryMessage(Message):
proxykey: Annotated[str, IOAttrs('k')] proxykey: Annotated[str, IOAttrs('k')]
@classmethod @classmethod
def get_response_types(cls) -> list[type[Response]]: def get_response_types(cls) -> list[type[Response] | None]:
return [LoginProxyStateQueryResponse] return [LoginProxyStateQueryResponse]
@ -82,7 +82,7 @@ class PingMessage(Message):
"""Standard ping.""" """Standard ping."""
@classmethod @classmethod
def get_response_types(cls) -> list[type[Response]]: def get_response_types(cls) -> list[type[Response] | None]:
return [PingResponse] return [PingResponse]
@ -99,7 +99,7 @@ class TestMessage(Message):
testfoo: Annotated[int, IOAttrs('f')] testfoo: Annotated[int, IOAttrs('f')]
@classmethod @classmethod
def get_response_types(cls) -> list[type[Response]]: def get_response_types(cls) -> list[type[Response] | None]:
return [TestResponse] return [TestResponse]
@ -130,7 +130,7 @@ class WorkspaceFetchMessage(Message):
state: Annotated[WorkspaceFetchState, IOAttrs('s')] state: Annotated[WorkspaceFetchState, IOAttrs('s')]
@classmethod @classmethod
def get_response_types(cls) -> list[type[Response]]: def get_response_types(cls) -> list[type[Response] | None]:
return [WorkspaceFetchResponse] return [WorkspaceFetchResponse]

View File

@ -11,15 +11,16 @@ from efro.message._protocol import MessageProtocol
from efro.message._sender import (MessageSender, BoundMessageSender) from efro.message._sender import (MessageSender, BoundMessageSender)
from efro.message._receiver import (MessageReceiver, BoundMessageReceiver) from efro.message._receiver import (MessageReceiver, BoundMessageReceiver)
from efro.message._module import (create_sender_module, create_receiver_module) from efro.message._module import (create_sender_module, create_receiver_module)
from efro.message._message import (Message, Response, EmptySysResponse, from efro.message._message import (Message, Response, SysResponse,
ErrorSysResponse, StringResponse, EmptySysResponse, ErrorSysResponse,
BoolResponse, UnregisteredMessageIDError) StringResponse, BoolResponse,
UnregisteredMessageIDError)
__all__ = [ __all__ = [
'Message', 'Response', 'EmptySysResponse', 'ErrorSysResponse', 'Message', 'Response', 'SysResponse', 'EmptySysResponse',
'StringResponse', 'BoolResponse', 'MessageProtocol', 'MessageSender', 'ErrorSysResponse', 'StringResponse', 'BoolResponse', 'MessageProtocol',
'BoundMessageSender', 'MessageReceiver', 'BoundMessageReceiver', 'MessageSender', 'BoundMessageSender', 'MessageReceiver',
'create_sender_module', 'create_receiver_module', 'BoundMessageReceiver', 'create_sender_module', 'create_receiver_module',
'UnregisteredMessageIDError' 'UnregisteredMessageIDError'
] ]

View File

@ -24,7 +24,7 @@ class Message:
"""Base class for messages.""" """Base class for messages."""
@classmethod @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. """Return all message types this Message can result in when sent.
The default implementation specifies EmptySysResponse, so messages with 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 Note that ErrorMessage is handled as a special case and does not
need to be specified here. need to be specified here.
""" """
return [EmptySysResponse] return [None]
class Response: class Response:
"""Base class for responses to messages.""" """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: # Some standard response types:
@ioprepped @ioprepped
@dataclass @dataclass
class ErrorSysResponse(Response): class ErrorSysResponse(SysResponse):
"""Response saying some error has occurred for the send. """Response saying some error has occurred for the send.
This type is unique in that it is not returned to the user; it This type is unique in that it is not returned to the user; it
@ -64,7 +72,7 @@ class ErrorSysResponse(Response):
@ioprepped @ioprepped
@dataclass @dataclass
class EmptySysResponse(Response): class EmptySysResponse(SysResponse):
"""The response equivalent of None.""" """The response equivalent of None."""

View File

@ -14,8 +14,8 @@ import json
from efro.error import CleanError from efro.error import CleanError
from efro.dataclassio import (is_ioprepped_dataclass, dataclass_to_dict, from efro.dataclassio import (is_ioprepped_dataclass, dataclass_to_dict,
dataclass_from_dict) dataclass_from_dict)
from efro.message._message import (Message, Response, ErrorSysResponse, from efro.message._message import (Message, Response, SysResponse,
EmptySysResponse, ErrorSysResponse, EmptySysResponse,
UnregisteredMessageIDError) UnregisteredMessageIDError)
if TYPE_CHECKING: if TYPE_CHECKING:
@ -54,8 +54,10 @@ class MessageProtocol:
""" """
self.message_types_by_id: dict[int, type[Message]] = {} self.message_types_by_id: dict[int, type[Message]] = {}
self.message_ids_by_type: dict[type[Message], int] = {} self.message_ids_by_type: dict[type[Message], int] = {}
self.response_types_by_id: dict[int, type[Response]] = {} self.response_types_by_id: dict[int, type[Response]
self.response_ids_by_type: dict[type[Response], int] = {} | type[SysResponse]] = {}
self.response_ids_by_type: dict[type[Response] | type[SysResponse],
int] = {}
for m_id, m_type in message_types.items(): for m_id, m_type in message_types.items():
# Make sure only valid message types were passed and each # 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 # Go ahead and auto-register a few common response types
# if the user has not done so explicitly. Use unique negative # if the user has not done so explicitly. Use unique negative
# IDs which will never change or overlap with user ids. # 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 assert self.response_types_by_id.get(reg_id) is None
self.response_types_by_id[reg_id] = reg_tp self.response_types_by_id[reg_id] = reg_tp
self.response_ids_by_type[reg_tp] = reg_id self.response_ids_by_type[reg_tp] = reg_id
@ -92,15 +94,19 @@ class MessageProtocol:
if __debug__: if __debug__:
# Make sure all Message types' return types are valid # Make sure all Message types' return types are valid
# and have been assigned an ID as well. # 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(): for m_id, m_type in message_types.items():
m_rtypes = m_type.get_response_types() m_rtypes = m_type.get_response_types()
assert isinstance(m_rtypes, list) assert isinstance(m_rtypes, list)
assert m_rtypes, ( assert m_rtypes, (
f'Message type {m_type} specifies no return types.') f'Message type {m_type} specifies no return types.')
assert len(set(m_rtypes)) == len(m_rtypes) # check dups 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: for cls in all_response_types:
if cls is None:
continue
assert is_ioprepped_dataclass(cls) assert is_ioprepped_dataclass(cls)
assert issubclass(cls, Response) assert issubclass(cls, Response)
if cls not in self.response_ids_by_type: if cls not in self.response_ids_by_type:
@ -130,11 +136,11 @@ class MessageProtocol:
"""Encode a message to a json ready dict.""" """Encode a message to a json ready dict."""
return self._to_dict(message, self.message_ids_by_type, 'message') 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.""" """Encode a response to a json ready dict."""
return self._to_dict(response, self.response_ids_by_type, 'response') 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.""" """Translate an error to a response."""
# Log any errors we got during handling. # Log any errors we got during handling.
@ -176,10 +182,10 @@ class MessageProtocol:
assert isinstance(out, Message) assert isinstance(out, Message)
return out 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.""" """Decode a response from a json string."""
out = self._from_dict(data, self.response_types_by_id, 'response') out = self._from_dict(data, self.response_types_by_id, 'response')
assert isinstance(out, Response) assert isinstance(out, Response | SysResponse)
return out return out
# Weeeird; we get mypy errors returning dict[int, type] but # Weeeird; we get mypy errors returning dict[int, type] but
@ -332,10 +338,10 @@ class MessageProtocol:
f'class {ppre}Bound{basename}(BoundMessageSender):\n' f'class {ppre}Bound{basename}(BoundMessageSender):\n'
f' """Protocol-specific bound sender."""\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 # We accept None to equal EmptySysResponse so reflect that
# in the type annotation. # 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. # Define handler() overloads for all registered message types.
if msgtypes: if msgtypes:
@ -372,6 +378,7 @@ class MessageProtocol:
for msgtype in msgtypes: for msgtype in msgtypes:
msgtypevar = msgtype.__name__ msgtypevar = msgtype.__name__
# rtypes = msgtype.get_response_types()
rtypes = msgtype.get_response_types() rtypes = msgtype.get_response_types()
if len(rtypes) > 1: if len(rtypes) > 1:
rtypevar = ' | '.join( rtypevar = ' | '.join(
@ -429,10 +436,10 @@ class MessageProtocol:
# Define handler() overloads for all registered message types. # 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 # We accept None to equal EmptySysResponse so reflect that
# in the type annotation. # in the type annotation.
return 'None' if rtype is EmptySysResponse else rtype.__name__ return 'None' if rtype is None else rtype.__name__
if msgtypes: if msgtypes:
cbgn = 'Awaitable[' if is_async else '' cbgn = 'Awaitable[' if is_async else ''

View File

@ -12,13 +12,13 @@ import logging
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from efro.message._message import (Message, Response, EmptySysResponse, from efro.message._message import (Message, Response, EmptySysResponse,
ErrorSysResponse,
UnregisteredMessageIDError) UnregisteredMessageIDError)
if TYPE_CHECKING: if TYPE_CHECKING:
from typing import Any, Callable, Awaitable from typing import Any, Callable, Awaitable
from efro.message._protocol import MessageProtocol from efro.message._protocol import MessageProtocol
from efro.message._message import SysResponse
class MessageReceiver: class MessageReceiver:
@ -54,7 +54,8 @@ class MessageReceiver:
self._decode_filter_call: Callable[[Any, dict, Message], self._decode_filter_call: Callable[[Any, dict, Message],
None] | None = None None] | None = None
self._encode_filter_call: Callable[ 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 # TODO: don't currently have async encode equivalent
# or either for sender; can add as needed. # or either for sender; can add as needed.
@ -107,26 +108,31 @@ class MessageReceiver:
assert issubclass(msgtype, Message) assert issubclass(msgtype, Message)
ret = anns.get('return') 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. # Return types can be a single type or a union of types.
if isinstance(ret, (_GenericAlias, types.UnionType)): if isinstance(ret, (_GenericAlias, types.UnionType)):
targs = get_args(ret) 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;' raise TypeError(f'expected only types for "return" annotation;'
f' got {targs}.') f' got {targs}.')
responsetypes = targs responsetypes = targs
else: else:
if not isinstance(ret, type): if not isinstance(ret, (type, type(None))):
raise TypeError(f'expected one or more types for' raise TypeError(f'expected one or more types for'
f' "return" annotation; got a {type(ret)}.') f' "return" annotation; got a {type(ret)}.')
# This seems like maybe a mypy bug. Appeared after adding # This seems like maybe a mypy bug. Appeared after adding
# types.UnionType above. # 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. # Return type of None translates to EmptySysResponse.
responsetypes = tuple(EmptySysResponse if r is type(None) else r # responsetypes = tuple(EmptySysResponse if r is type(None) else r
for r in responsetypes) # noqa # for r in responsetypes) # noqa
# Make sure our protocol has this message type registered and our # Make sure our protocol has this message type registered and our
# return types exactly match. (Technically we could return a subset # return types exactly match. (Technically we could return a subset
@ -179,7 +185,9 @@ class MessageReceiver:
return call return call
def encode_filter_method( 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]: ) -> Callable[[Any, Message | None, Response, dict], None]:
"""Function decorator for defining an encode filter. """Function decorator for defining an encode filter.
@ -237,17 +245,21 @@ class MessageReceiver:
response: Response | None) -> str: response: Response | None) -> str:
"""Encode a response provided by the user for sending.""" """Encode a response provided by the user for sending."""
# A return value of None equals EmptySysResponse. assert isinstance(response, Response | None)
if response is None:
response = EmptySysResponse()
assert isinstance(response, Response)
# (user should never explicitly return error-responses) # (user should never explicitly return error-responses)
assert not isinstance(response, ErrorSysResponse) assert (response is None
assert type(response) in message.get_response_types() or type(response) in message.get_response_types())
response_dict = self.protocol.response_to_dict(response)
# 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: 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) response_dict)
return self.protocol.encode_dict(response_dict) return self.protocol.encode_dict(response_dict)
@ -281,7 +293,7 @@ class MessageReceiver:
if handler is None: if handler is None:
raise RuntimeError(f'Got unhandled message type: {msgtype}.') raise RuntimeError(f'Got unhandled message type: {msgtype}.')
response = handler(bound_obj, msg_decoded) 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) return self.encode_user_response(bound_obj, msg_decoded, response)
except Exception as exc: except Exception as exc:
@ -309,7 +321,7 @@ class MessageReceiver:
if handler is None: if handler is None:
raise RuntimeError(f'Got unhandled message type: {msgtype}.') raise RuntimeError(f'Got unhandled message type: {msgtype}.')
response = await handler(bound_obj, msg_decoded) 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) return self.encode_user_response(bound_obj, msg_decoded, response)
except Exception as exc: except Exception as exc:

View File

@ -10,12 +10,12 @@ import logging
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
from efro.error import CleanError, RemoteError, CommunicationError 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: if TYPE_CHECKING:
from typing import Any, Callable, Awaitable 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 from efro.message._protocol import MessageProtocol
@ -46,8 +46,8 @@ class MessageSender:
[Any, str], Awaitable[str]] | None = None [Any, str], Awaitable[str]] | None = None
self._encode_filter_call: Callable[[Any, Message, dict], self._encode_filter_call: Callable[[Any, Message, dict],
None] | None = None None] | None = None
self._decode_filter_call: Callable[[Any, Message, dict, Response], self._decode_filter_call: Callable[
None] | None = None [Any, Message, dict, Response | SysResponse], None] | None = None
def send_method( def send_method(
self, call: Callable[[Any, str], self, call: Callable[[Any, str],
@ -90,7 +90,8 @@ class MessageSender:
return call return call
def decode_filter_method( 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]: ) -> Callable[[Any, Message, dict, Response], None]:
"""Function decorator for defining a decode filter. """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. """Send a message synchronously.
Generally you can just call send(); these split versions are Generally you can just call send(); these split versions are
@ -147,8 +149,8 @@ class MessageSender:
ErrorSysResponse.ErrorType.LOCAL)) ErrorSysResponse.ErrorType.LOCAL))
return self._decode_raw_response(bound_obj, message, response_encoded) return self._decode_raw_response(bound_obj, message, response_encoded)
async def send_split_part_1_async(self, bound_obj: Any, async def send_split_part_1_async(
message: Message) -> Response: self, bound_obj: Any, message: Message) -> Response | SysResponse:
"""Send a message asynchronously. """Send a message asynchronously.
Generally you can just call send(); these split versions are Generally you can just call send(); these split versions are
@ -175,8 +177,9 @@ class MessageSender:
ErrorSysResponse.ErrorType.LOCAL)) ErrorSysResponse.ErrorType.LOCAL))
return self._decode_raw_response(bound_obj, message, response_encoded) return self._decode_raw_response(bound_obj, message, response_encoded)
def send_split_part_2(self, message: Message, def send_split_part_2(
raw_response: Response) -> Response | None: self, message: Message,
raw_response: Response | SysResponse) -> Response | None:
"""Complete message sending (both sync and async). """Complete message sending (both sync and async).
Generally you can just call send(); these split versions are Generally you can just call send(); these split versions are
@ -196,7 +199,7 @@ class MessageSender:
return self.protocol.encode_dict(msg_dict) return self.protocol.encode_dict(msg_dict)
def _decode_raw_response(self, bound_obj: Any, message: Message, 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. """Create a Response from returned data.
These Responses may encapsulate things like remote errors and 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 should be used to translate to special values like None or raise
Exceptions. This function itself should never raise Exceptions. Exceptions. This function itself should never raise Exceptions.
""" """
response: Response | SysResponse
try: try:
response_dict = self.protocol.decode_dict(response_encoded) response_dict = self.protocol.decode_dict(response_encoded)
response = self.protocol.response_from_dict(response_dict) response = self.protocol.response_from_dict(response_dict)
@ -223,7 +227,8 @@ class MessageSender:
error_type=ErrorSysResponse.ErrorType.LOCAL) error_type=ErrorSysResponse.ErrorType.LOCAL)
return response 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. """Given a raw Response, unpacks to special values or Exceptions.
The result of this call is what should be passed to users. 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. # Everything else gets lumped in as a remote error.
raise RemoteError(raw_response.error_message) raise RemoteError(raw_response.error_message)
assert isinstance(raw_response, Response)
return raw_response return raw_response
@ -292,15 +298,16 @@ class BoundMessageSender:
return await self._sender.send_async(bound_obj=self._obj, return await self._sender.send_async(bound_obj=self._obj,
message=message) message=message)
async def send_split_part_1_async_untyped(self, async def send_split_part_1_async_untyped(
message: Message) -> Response: self, message: Message) -> Response | SysResponse:
"""Split send (part 1 of 2).""" """Split send (part 1 of 2)."""
assert self._obj is not None assert self._obj is not None
return await self._sender.send_split_part_1_async(bound_obj=self._obj, return await self._sender.send_split_part_1_async(bound_obj=self._obj,
message=message) message=message)
def send_split_part_2_untyped(self, message: Message, def send_split_part_2_untyped(
raw_response: Response) -> Response | None: self, message: Message,
raw_response: Response | SysResponse) -> Response | None:
"""Split send (part 2 of 2).""" """Split send (part 2 of 2)."""
return self._sender.send_split_part_2(message=message, return self._sender.send_split_part_2(message=message,
raw_response=raw_response) raw_response=raw_response)