fixed cloud console crash error

This commit is contained in:
Eric 2022-09-19 18:46:08 -07:00
parent 86b931dfcc
commit 68ffe1f2bd
No known key found for this signature in database
GPG Key ID: 89C93F0F8D6D5A98
16 changed files with 151 additions and 150 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/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/bc/d7/f65513a0b3d5fa8b5f4cfe342052",
"build/prefab/full/linux_arm64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/06/03/381b1b4470868245225dd4e4bb67",
"build/prefab/full/linux_arm64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/52/b0/36011f65de64a547009bef7fe6ec",
"build/prefab/full/linux_arm64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/e2/46/384efcdbfb324b63889d567e7f14",
"build/prefab/full/linux_x86_64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/8b/27/7bab35742af8c101f4aab3ebbd3d",
"build/prefab/full/linux_x86_64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/3b/d0/c1ee5a02a09c1d676a22a20fe8a7",
"build/prefab/full/linux_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/e9/2e/3ae4fb2983bb36167d1fe3044358",
"build/prefab/full/linux_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/9e/93/12e8d5357db44c97549fe2fbd7ca",
"build/prefab/full/mac_arm64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/53/6d/06e5987cfcf47d740ead2e8c3b7f",
"build/prefab/full/mac_arm64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/1b/5b/fd0fa7104c3e2d3a22c180db47cf",
"build/prefab/full/mac_arm64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/ca/98/0e12c6aa44fc9e64492b6da67b02",
"build/prefab/full/mac_arm64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/e6/81/aacab39968075487c9f610e3036c",
"build/prefab/full/mac_x86_64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/94/bc/7a2fb10033217cac2fc04ccd0309",
"build/prefab/full/mac_x86_64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/a4/da/b7095762fbdef22aff5d77c5d45d",
"build/prefab/full/mac_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/8b/05/2051b310f7989aa9aa04339de2f3",
"build/prefab/full/mac_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/2d/6e/3befc00833dc482ef55b70a915fe",
"build/prefab/full/windows_x86_gui/debug/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/82/ce/77695599588b675fac1e10febeae",
"build/prefab/full/windows_x86_gui/release/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/3a/c3/d042e59d36f71c885036ed0e73d4",
"build/prefab/full/windows_x86_server/debug/dist/BallisticaCoreHeadless.exe": "https://files.ballistica.net/cache/ba1/87/48/b1551cd7b5cf44c06a3e3a1a66a4",
"build/prefab/full/windows_x86_server/release/dist/BallisticaCoreHeadless.exe": "https://files.ballistica.net/cache/ba1/6d/e2/3366be4f442e6ef48b5ef5babe87",
"build/prefab/lib/linux_arm64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/d6/9a/5e36fb51a797da74db8f366a8e6d",
"build/prefab/lib/linux_arm64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/98/d7/e827cc644d4dde0c1bddb17121b0",
"build/prefab/lib/linux_arm64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/9b/e0/a1ae5fdadff116de78727e2a8d30",
"build/prefab/lib/linux_arm64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/49/76/fd27d293fd73d324ecc433b1e253",
"build/prefab/lib/linux_x86_64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/1e/93/b85c7585218b5c86d766a23373c6",
"build/prefab/lib/linux_x86_64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/34/0a/4547b5a8a00a35874cfbab25fc11",
"build/prefab/lib/linux_x86_64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/68/5a/a4a030424d8d5900433bdb70bd83",
"build/prefab/lib/linux_x86_64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/e1/4b/278433e2332245313d4fa3390aee",
"build/prefab/lib/mac_arm64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/22/c1/fc9726d56ac62c757e7f7eeb56e2",
"build/prefab/lib/mac_arm64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/23/4f/ee03bed8d42b8e12832efbd17968",
"build/prefab/lib/mac_arm64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/dc/7a/f5bae1a284d2b51e149f12da9e18",
"build/prefab/lib/mac_arm64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/2e/75/254680a01a85fcdc19db275cd2ff",
"build/prefab/lib/mac_x86_64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/79/fb/65e1f1851f2dc663eb5d9dd27ae9",
"build/prefab/lib/mac_x86_64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/24/5d/33ca757aa471e81ee2efe83b5c32",
"build/prefab/lib/mac_x86_64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/83/3e/35613478ab4887b7f77126c1d910",
"build/prefab/lib/mac_x86_64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/ba/a7/b925b7eb7b2da10fdc9c88e59576",
"build/prefab/lib/windows/Debug_Win32/BallisticaCoreGenericInternal.lib": "https://files.ballistica.net/cache/ba1/0a/ae/daac4e04ef473d0da419ebe2ddf2",
"build/prefab/lib/windows/Debug_Win32/BallisticaCoreGenericInternal.pdb": "https://files.ballistica.net/cache/ba1/52/9d/d6c5366d82b56d1c79553ae3258b",
"build/prefab/lib/windows/Debug_Win32/BallisticaCoreHeadlessInternal.lib": "https://files.ballistica.net/cache/ba1/35/db/9f6868dde4184cb3b33153981548",
"build/prefab/lib/windows/Debug_Win32/BallisticaCoreHeadlessInternal.pdb": "https://files.ballistica.net/cache/ba1/0c/e0/a869977d8ee1dcf4bcbb0853fe3c",
"build/prefab/lib/windows/Release_Win32/BallisticaCoreGenericInternal.lib": "https://files.ballistica.net/cache/ba1/31/c1/e51379d7cf51aec768855e6e7eb7",
"build/prefab/lib/windows/Release_Win32/BallisticaCoreGenericInternal.pdb": "https://files.ballistica.net/cache/ba1/0f/48/52683efe93f7311f2a81a280a87e",
"build/prefab/lib/windows/Release_Win32/BallisticaCoreHeadlessInternal.lib": "https://files.ballistica.net/cache/ba1/7c/05/660a71d76685232d9bd91d979ef8",
"build/prefab/lib/windows/Release_Win32/BallisticaCoreHeadlessInternal.pdb": "https://files.ballistica.net/cache/ba1/30/5a/b2e0ce3986936938c65e4bef2d0e",
"build/prefab/full/linux_arm64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/b3/94/954559711625a0bb6a29d41c35ff",
"build/prefab/full/linux_arm64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/44/1e/7eec4d37c0c2afb581927efbb2dc",
"build/prefab/full/linux_arm64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/92/75/2d6b53a09ce1fe7cbe3b50d832de",
"build/prefab/full/linux_arm64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/cf/eb/2f2119b7c86ecbfb2a8b725b21f6",
"build/prefab/full/linux_x86_64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/c2/95/be04f3219b078c92a787627b42b7",
"build/prefab/full/linux_x86_64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/53/f5/3bd3ba9c59b87ff7b8abc4233844",
"build/prefab/full/linux_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/5e/21/a913309416dde18bc4c9104c3ec5",
"build/prefab/full/linux_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/28/25/54314d17a5a12be51a3b340359bd",
"build/prefab/full/mac_arm64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/07/39/13923a2f10d8e2a8d1d12007cbed",
"build/prefab/full/mac_arm64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/a7/86/8cfb723071ad46355af9166ba59a",
"build/prefab/full/mac_arm64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/ad/73/6013ee6879fae3c3e06b15396fb0",
"build/prefab/full/mac_arm64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/da/8b/f33a546e2342591ab84ddcf4053e",
"build/prefab/full/mac_x86_64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/f8/b7/fffc4c52e906890b35c936441b60",
"build/prefab/full/mac_x86_64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/27/24/2ca5005df08ea5a7a78cd20b020a",
"build/prefab/full/mac_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/cd/8d/c59fcc270b637990d12cd8a758e0",
"build/prefab/full/mac_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/54/a9/78e4658fd4446898b11633191c01",
"build/prefab/full/windows_x86_gui/debug/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/53/30/cd915e748f31208239b90f5a7a2c",
"build/prefab/full/windows_x86_gui/release/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/a9/4e/c367a7a66eb17df7757dc85d59b6",
"build/prefab/full/windows_x86_server/debug/dist/BallisticaCoreHeadless.exe": "https://files.ballistica.net/cache/ba1/5d/13/38ab5bdd9fdb0abca878d0bbfbab",
"build/prefab/full/windows_x86_server/release/dist/BallisticaCoreHeadless.exe": "https://files.ballistica.net/cache/ba1/02/62/c3a8023aa43567204407325d6c4c",
"build/prefab/lib/linux_arm64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/23/17/f0d22a9a91a295ee4a9d94db2af9",
"build/prefab/lib/linux_arm64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/d5/4f/bfbcdd18679b4ba0df1b040e8c15",
"build/prefab/lib/linux_arm64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/96/e9/0f4aa3d10f2d50fe670bb6d89ba0",
"build/prefab/lib/linux_arm64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/26/ef/1b58d3c788441242ed9ca9352499",
"build/prefab/lib/linux_x86_64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/79/77/b35c5f93f62d92fe9cdb548f6e54",
"build/prefab/lib/linux_x86_64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/c6/53/8e678b75f456378e17e4f639fe35",
"build/prefab/lib/linux_x86_64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/ab/1d/b514c16cfda2f5840676a4c75ca3",
"build/prefab/lib/linux_x86_64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/d7/c4/b797fd5365d34fcfb60d6275fb3c",
"build/prefab/lib/mac_arm64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/b0/6c/b8f8c1d1d8e2f772208af12e085f",
"build/prefab/lib/mac_arm64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/c0/e1/f57102a8da2fe0ca7787c0a7924a",
"build/prefab/lib/mac_arm64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/8f/16/19494ad899b3f1990dd2b5cdfa99",
"build/prefab/lib/mac_arm64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/a9/d5/59ba30f21d5583bdc92516c0916e",
"build/prefab/lib/mac_x86_64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/e7/82/e29dbff494964b753a08eaa66b66",
"build/prefab/lib/mac_x86_64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/2f/06/ec124d5bfce4d3f268b1318755b8",
"build/prefab/lib/mac_x86_64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/3f/47/3322caea5f9a92735076c1017839",
"build/prefab/lib/mac_x86_64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/f5/9f/ce9a27aea9a4b7056800cf68afe9",
"build/prefab/lib/windows/Debug_Win32/BallisticaCoreGenericInternal.lib": "https://files.ballistica.net/cache/ba1/6c/56/6180e5d6db6868b7438751320faf",
"build/prefab/lib/windows/Debug_Win32/BallisticaCoreGenericInternal.pdb": "https://files.ballistica.net/cache/ba1/71/24/e873efab9c315ec907427bd22327",
"build/prefab/lib/windows/Debug_Win32/BallisticaCoreHeadlessInternal.lib": "https://files.ballistica.net/cache/ba1/3d/d6/b1a3c97a66d3f5ad38d5f5dda476",
"build/prefab/lib/windows/Debug_Win32/BallisticaCoreHeadlessInternal.pdb": "https://files.ballistica.net/cache/ba1/5a/e9/14593b84a3035a1bd7ce3980818d",
"build/prefab/lib/windows/Release_Win32/BallisticaCoreGenericInternal.lib": "https://files.ballistica.net/cache/ba1/87/5c/38b4d59ad04f6ba89dae6ca38fa5",
"build/prefab/lib/windows/Release_Win32/BallisticaCoreGenericInternal.pdb": "https://files.ballistica.net/cache/ba1/3c/0a/6b151c9054294db58cf98957a515",
"build/prefab/lib/windows/Release_Win32/BallisticaCoreHeadlessInternal.lib": "https://files.ballistica.net/cache/ba1/e3/fb/7be0f96c82816af61849aaf4357a",
"build/prefab/lib/windows/Release_Win32/BallisticaCoreHeadlessInternal.pdb": "https://files.ballistica.net/cache/ba1/23/dc/91a8e0f2123a75b4d72a0d26ab25",
"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"

View File

@ -1,4 +1,4 @@
### 1.7.7 (build 20862, api 7, 2022-09-17)
### 1.7.7 (build 20865, api 7, 2022-09-19)
- Added `ba.app.meta.load_exported_classes()` for loading classes discovered by the meta subsystem cleanly in a background thread.
- Improved logging of missing playlist game types.
- Some ba.Lstr functionality can now be used in background threads.
@ -39,6 +39,8 @@
- 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) Fixed an issue where the app could crash when connected to the cloud console while in a network game.
### 1.7.6 (build 20687, api 7, 2022-08-11)

View File

@ -1 +1 @@
84155513838284412598729205538639495871
137071025041513581787580065580079045765

View File

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

View File

@ -32,7 +32,7 @@
namespace ballistica {
// These are set automatically via script; don't modify them here.
const int kAppBuildNumber = 20862;
const int kAppBuildNumber = 20865;
const char* kAppVersion = "1.7.7";
// Our standalone globals.

View File

@ -27,7 +27,7 @@ auto FatalError::ReportFatalError(const std::string& message,
// error to the user and exiting the app cleanly (so we don't pollute our
// crash records with results of user tinkering).
// Give the platform the opportunity to completely override our handling.
// Give the platform the opportunity to augment or override our handling.
if (g_platform) {
auto handled =
g_platform->ReportFatalError(message, in_top_level_exception_handler);
@ -38,7 +38,8 @@ auto FatalError::ReportFatalError(const std::string& message,
std::string dialog_msg = message;
if (!dialog_msg.empty()) {
dialog_msg += "\n";
// Why was this here?
// dialog_msg += "\n";
}
auto starttime = time(nullptr);
@ -75,9 +76,10 @@ auto FatalError::ReportFatalError(const std::string& message,
g_early_v1_cloud_log_writes = 0;
// Add this to our V1CloudLog which we'll be attempting to send momentarily,
// and also try to present it directly to the user.
// and also go to platform-specific logging and good ol' stderr.
Logging::V1CloudLog(logmsg);
Logging::DisplayLog("root", LogLevel::kCritical, logmsg);
fprintf(stderr, "%s\n", logmsg.c_str());
std::string prefix = "FATAL-ERROR-LOG:";
std::string suffix;

View File

@ -1050,15 +1050,10 @@ void Logic::PushPythonRawCallable(PyObject* callable, bool fg_context) {
thread()->PushCall([this, callable, fg_context] {
assert(InLogicThread());
// Lets run this in the UI context.
// (can add other options if we need later)
// Run this in the UI context by default, or foreground if requested.
ScopedSetContext cp(fg_context ? GetForegroundContext() : GetUIContext());
// This event contains a raw python obj with an incremented ref-count.
auto call(Object::New<PythonContextCall>(callable));
Py_DECREF(callable); // now just held by call
call->Run();
PythonRef(callable, PythonRef::kSteal).Call();
});
}

View File

@ -83,8 +83,8 @@ class Logic {
auto PushPythonWeakCallArgs(const Object::WeakRef<PythonContextCall>& call,
const PythonRef& args) -> void;
// Push a raw Python call, decrements its refcount after running.
// Can be pushed from any thread.
// Push a raw Python call from any thread. Refcount should be incremented
// beforehand and will be decremented after running.
auto PushPythonRawCallable(PyObject* callable, bool fg_context = false)
-> void;
auto PushScreenMessage(const std::string& message, const Vector3f& color)

View File

@ -102,7 +102,7 @@ auto PythonClassContextCall::tp_new(PyTypeObject* type, PyObject* args,
if (!PyArg_ParseTuple(args, "O", &source_obj)) return nullptr;
if (!InLogicThread()) {
throw Exception(
"ERROR: " + std::string(type_obj.tp_name)
std::string(type_obj.tp_name)
+ " objects must only be created in the logic thread (current is ("
+ GetCurrentThreadName() + ").");
}

View File

@ -14,17 +14,17 @@ PythonContextCall* PythonContextCall::current_call_ = nullptr;
PythonContextCall::PythonContextCall(PyObject* obj_in) {
assert(InLogicThread());
// as a sanity test, store the current context ptr just to make sure it
// hasn't changed when we run
// As a sanity test, store the current context ptr just to make sure it
// hasn't changed when we run.
#if BA_DEBUG_BUILD
context_target_sanity_test_ = context_.target.get();
#endif // BA_DEBUG_BUILD
BA_PRECONDITION(PyCallable_Check(obj_in));
object_.Acquire(obj_in);
GetTrace();
// ok now we need to register this call with whatever the context is;
// Ok now we need to register this call with whatever the context is;
// it can be stored in a host-activity, a host-session, or the UI context.
// whoever it is registered with will explicitly release its contents on
// Whoever it is registered with will explicitly release its contents on
// shutdown and ensure that nothing gets run after that point.
if (HostActivity* ha = context_.GetHostActivity()) {
ha->RegisterCall(this);

View File

@ -18,7 +18,7 @@ from efro.dataclassio import ioprepped
from efro.message import (Message, Response, MessageProtocol, MessageSender,
BoundMessageSender, MessageReceiver,
BoundMessageReceiver, UnregisteredMessageIDError,
EmptyResponse)
EmptySysResponse)
if TYPE_CHECKING:
from typing import Any, Callable, Awaitable
@ -424,8 +424,8 @@ TEST_PROTOCOL = MessageProtocol(
0: _TResp1,
1: _TResp2,
},
receiver_returns_stack_traces=True,
receiver_logs_exceptions=False,
forward_clean_errors=True,
remote_errors_include_stack_traces=True,
)
# Represents an 'evolved' TEST_PROTOCOL (one extra message type added).
@ -441,8 +441,8 @@ TEST_PROTOCOL_B = MessageProtocol(
0: _TResp1,
1: _TResp2,
},
receiver_returns_stack_traces=True,
receiver_logs_exceptions=False,
forward_clean_errors=True,
remote_errors_include_stack_traces=True,
)
TEST_PROTOCOL_SINGLE = MessageProtocol(
@ -452,8 +452,7 @@ TEST_PROTOCOL_SINGLE = MessageProtocol(
response_types={
0: _TResp1,
},
receiver_returns_stack_traces=True,
receiver_logs_exceptions=False,
remote_errors_include_stack_traces=True,
)
@ -463,12 +462,16 @@ def test_protocol_creation() -> None:
# This should fail because _TMsg1 can return _TResp1 which
# is not given an id here.
with pytest.raises(ValueError):
_protocol = MessageProtocol(message_types={0: _TMsg1},
response_types={0: _TResp2})
_protocol = MessageProtocol(
message_types={0: _TMsg1},
response_types={0: _TResp2},
)
# Now it should work.
_protocol = MessageProtocol(message_types={0: _TMsg1},
response_types={0: _TResp1})
_protocol = MessageProtocol(
message_types={0: _TMsg1},
response_types={0: _TResp1},
)
def test_sender_module_single_emb() -> None:
@ -777,7 +780,7 @@ def test_full_pipeline() -> None:
# Emulate forwarding unregistered messages on to some
# other handler...
response_dict = self.msg.protocol.response_to_dict(
EmptyResponse())
EmptySysResponse())
return self.msg.protocol.encode_dict(response_dict)
raise
@ -909,7 +912,8 @@ def test_full_pipeline() -> None:
assert response3 is None
assert isinstance(response4, _TResp1)
# Remote CleanErrors should come across locally as the same.
# Remote CleanErrors should come across locally as the same
# (provided our protocol has enabled support for them).
try:
_response5 = obj.msg.send(_TMsg1(ival=1))
except Exception as exc:

View File

@ -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, EmptyResponse,
ErrorResponse, StringResponse, BoolResponse,
UnregisteredMessageIDError)
from efro.message._message import (Message, Response, EmptySysResponse,
ErrorSysResponse, StringResponse,
BoolResponse, UnregisteredMessageIDError)
__all__ = [
'Message', 'Response', 'EmptyResponse', 'ErrorResponse', 'StringResponse',
'BoolResponse', 'MessageProtocol', 'MessageSender', 'BoundMessageSender',
'MessageReceiver', 'BoundMessageReceiver', 'create_sender_module',
'create_receiver_module', 'UnregisteredMessageIDError'
'Message', 'Response', 'EmptySysResponse', 'ErrorSysResponse',
'StringResponse', 'BoolResponse', 'MessageProtocol', 'MessageSender',
'BoundMessageSender', 'MessageReceiver', 'BoundMessageReceiver',
'create_sender_module', 'create_receiver_module',
'UnregisteredMessageIDError'
]
# Have these things present themselves cleanly as 'thismodule.SomeClass'

View File

@ -27,12 +27,12 @@ class Message:
def get_response_types(cls) -> list[type[Response]]:
"""Return all message types this Message can result in when sent.
The default implementation specifies EmptyResponse, so messages with
The default implementation specifies EmptySysResponse, so messages with
no particular response needs can leave this untouched.
Note that ErrorMessage is handled as a special case and does not
need to be specified here.
"""
return [EmptyResponse]
return [EmptySysResponse]
class Response:
@ -44,7 +44,7 @@ class Response:
@ioprepped
@dataclass
class ErrorResponse(Response):
class ErrorSysResponse(Response):
"""Response saying some error has occurred for the send.
This type is unique in that it is not returned to the user; it
@ -64,12 +64,12 @@ class ErrorResponse(Response):
@ioprepped
@dataclass
class EmptyResponse(Response):
class EmptySysResponse(Response):
"""The response equivalent of None."""
# TODO: could allow handlers to deal in raw values for these
# types similar to how we allow None in place of EmptyResponse.
# types similar to how we allow None in place of EmptySysResponse.
# Though not sure if they are widely used enough to warrant the
# extra code complexity.
@ioprepped

View File

@ -14,8 +14,9 @@ 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, ErrorResponse,
EmptyResponse, UnregisteredMessageIDError)
from efro.message._message import (Message, Response, ErrorSysResponse,
EmptySysResponse,
UnregisteredMessageIDError)
if TYPE_CHECKING:
from typing import Any, Literal
@ -33,29 +34,20 @@ class MessageProtocol:
def __init__(self,
message_types: dict[int, type[Message]],
response_types: dict[int, type[Response]],
preserve_clean_errors: bool = True,
receiver_logs_exceptions: bool = True,
receiver_returns_stack_traces: bool = False) -> None:
forward_clean_errors: bool = False,
remote_errors_include_stack_traces: bool = False) -> None:
"""Create a protocol with a given configuration.
Note that common response types are automatically registered
with (unchanging negative ids) so they don't need to be passed
explicitly (but can be if a different id is desired).
If 'preserve_clean_errors' is True, efro.error.CleanError
If 'forward_clean_errors' is True, efro.error.CleanError
exceptions raised on the receiver end will result in a matching
CleanError raised back on the sender. All other Exception types
come across as efro.error.RemoteError.
When 'receiver_logs_exceptions' is True, any uncaught Exceptions
on the receiver end will be logged there via logging.exception()
(in addition to the usual behavior of returning an ErrorResponse
to the sender). This is good to leave enabled if your
intention is to never return ErrorResponses. Looser setups
making routine use of CleanErrors or whatnot may want to
disable this, however.
If 'receiver_returns_stack_traces' is True, stringified stack
If 'remote_errors_include_stack_traces' is True, stringified stack
traces will be returned to the sender for exceptions occurring
on the receiver end. This can make debugging easier but should
only be used when the client is trusted to see such info.
@ -88,15 +80,20 @@ 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_if_not(reg_tp: type[Response], reg_id: int) -> None:
def _reg_sys(reg_tp: type[Response], reg_id: int) -> None:
# If we have a positive id registered already, we still point
# negative sys id at this type but not the opposite.
if reg_tp in self.response_ids_by_type:
self.response_types_by_id[reg_id] = reg_tp
return
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
_reg_if_not(ErrorResponse, -1)
_reg_if_not(EmptyResponse, -2)
_reg_sys(ErrorSysResponse, -1)
_reg_sys(EmptySysResponse, -2)
# Some extra-thorough validation in debug mode.
if __debug__:
@ -127,9 +124,9 @@ class MessageProtocol:
'message_types contains duplicate __name__s;'
' all types are required to have unique names.')
self.preserve_clean_errors = preserve_clean_errors
self.receiver_logs_exceptions = receiver_logs_exceptions
self.receiver_returns_stack_traces = receiver_returns_stack_traces
self.forward_clean_errors = forward_clean_errors
self.remote_errors_include_stack_traces = (
remote_errors_include_stack_traces)
@staticmethod
def encode_dict(obj: dict) -> str:
@ -147,21 +144,20 @@ class MessageProtocol:
def error_to_response(self, exc: Exception) -> Response:
"""Translate an error to a response."""
# Log any errors we got during handling if so desired.
if self.receiver_logs_exceptions:
logging.exception('Error in efro.message handling.')
# Log any errors we got during handling.
logging.exception('Error in efro.message handling.')
# If anything goes wrong, return a ErrorResponse instead.
# If anything goes wrong, return a ErrorSysResponse instead.
# (either CLEAN or generic REMOTE)
if isinstance(exc, CleanError) and self.preserve_clean_errors:
return ErrorResponse(
if isinstance(exc, CleanError) and self.forward_clean_errors:
return ErrorSysResponse(
error_message=str(exc),
error_type=ErrorResponse.ErrorType.REMOTE_CLEAN)
return ErrorResponse(
error_type=ErrorSysResponse.ErrorType.REMOTE_CLEAN)
return ErrorSysResponse(
error_message=(traceback.format_exc()
if self.receiver_returns_stack_traces else
if self.remote_errors_include_stack_traces else
'An internal error has occurred.'),
error_type=ErrorResponse.ErrorType.REMOTE)
error_type=ErrorSysResponse.ErrorType.REMOTE)
def _to_dict(self, message: Any, ids_by_type: dict[type, int],
opname: str) -> dict:
@ -236,7 +232,7 @@ class MessageProtocol:
rsptypes.append(Response)
for rsp_tp in rsptypes:
# Skip these as they don't actually show up in code.
if rsp_tp is EmptyResponse or rsp_tp is ErrorResponse:
if rsp_tp is EmptySysResponse or rsp_tp is ErrorSysResponse:
continue
if (single_message_type and part == 'sender'
and rsp_tp is not Response):
@ -344,9 +340,9 @@ class MessageProtocol:
f' """Protocol-specific bound sender."""\n')
def _filt_tp_name(rtype: type[Response]) -> str:
# We accept None to equal EmptyResponse so reflect that
# We accept None to equal EmptySysResponse so reflect that
# in the type annotation.
return 'None' if rtype is EmptyResponse else rtype.__name__
return 'None' if rtype is EmptySysResponse else rtype.__name__
# Define handler() overloads for all registered message types.
if msgtypes:
@ -441,9 +437,9 @@ class MessageProtocol:
# Define handler() overloads for all registered message types.
def _filt_tp_name(rtype: type[Response]) -> str:
# We accept None to equal EmptyResponse so reflect that
# We accept None to equal EmptySysResponse so reflect that
# in the type annotation.
return 'None' if rtype is EmptyResponse else rtype.__name__
return 'None' if rtype is EmptySysResponse else rtype.__name__
if msgtypes:
cbgn = 'Awaitable[' if is_async else ''

View File

@ -11,8 +11,9 @@ import inspect
import logging
from typing import TYPE_CHECKING
from efro.message._message import (Message, Response, EmptyResponse,
ErrorResponse, UnregisteredMessageIDError)
from efro.message._message import (Message, Response, EmptySysResponse,
ErrorSysResponse,
UnregisteredMessageIDError)
if TYPE_CHECKING:
from typing import Any, Callable, Awaitable
@ -123,8 +124,8 @@ class MessageReceiver:
# types.UnionType above.
responsetypes = (ret, ) # type: ignore
# Return type of None translates to EmptyResponse.
responsetypes = tuple(EmptyResponse if r is type(None) else r
# Return type of None translates to EmptySysResponse.
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
@ -236,13 +237,13 @@ class MessageReceiver:
response: Response | None) -> str:
"""Encode a response provided by the user for sending."""
# A return value of None equals EmptyResponse.
# A return value of None equals EmptySysResponse.
if response is None:
response = EmptyResponse()
response = EmptySysResponse()
assert isinstance(response, Response)
# (user should never explicitly return error-responses)
assert not isinstance(response, ErrorResponse)
assert not isinstance(response, ErrorSysResponse)
assert type(response) in message.get_response_types()
response_dict = self.protocol.response_to_dict(response)
if self._encode_filter_call is not None:

View File

@ -10,7 +10,7 @@ import logging
from typing import TYPE_CHECKING
from efro.error import CleanError, RemoteError, CommunicationError
from efro.message._message import EmptyResponse, ErrorResponse
from efro.message._message import EmptySysResponse, ErrorSysResponse
if TYPE_CHECKING:
from typing import Any, Callable, Awaitable
@ -139,12 +139,12 @@ class MessageSender:
except Exception as exc:
# Any error in the raw send call gets recorded as either
# a local or communication error.
return ErrorResponse(
return ErrorSysResponse(
error_message=
f'Error in MessageSender @send_method ({type(exc)}): {exc}',
error_type=(ErrorResponse.ErrorType.COMMUNICATION
error_type=(ErrorSysResponse.ErrorType.COMMUNICATION
if isinstance(exc, CommunicationError) else
ErrorResponse.ErrorType.LOCAL))
ErrorSysResponse.ErrorType.LOCAL))
return self._decode_raw_response(bound_obj, message, response_encoded)
async def send_split_part_1_async(self, bound_obj: Any,
@ -166,13 +166,13 @@ class MessageSender:
except Exception as exc:
# Any error in the raw send call gets recorded as either
# a local or communication error.
return ErrorResponse(
return ErrorSysResponse(
error_message=
f'Error in MessageSender @send_async_method ({type(exc)}):'
f' {exc}',
error_type=(ErrorResponse.ErrorType.COMMUNICATION
error_type=(ErrorSysResponse.ErrorType.COMMUNICATION
if isinstance(exc, CommunicationError) else
ErrorResponse.ErrorType.LOCAL))
ErrorSysResponse.ErrorType.LOCAL))
return self._decode_raw_response(bound_obj, message, response_encoded)
def send_split_part_2(self, message: Message,
@ -214,13 +214,13 @@ class MessageSender:
# If we got to this point, we successfully communicated
# with the other end so errors represent protocol mismatches
# or other invalid data. For now let's just log it but perhaps
# we'd want to somehow embed it in the ErrorResponse to be
# we'd want to somehow embed it in the ErrorSysResponse to be
# available directly to the user later.
logging.exception('Error decoding raw response')
response = ErrorResponse(
response = ErrorSysResponse(
error_message=
'Error decoding raw response; see log for details.',
error_type=ErrorResponse.ErrorType.LOCAL)
error_type=ErrorSysResponse.ErrorType.LOCAL)
return response
def _unpack_raw_response(self, raw_response: Response) -> Response | None:
@ -232,25 +232,25 @@ class MessageSender:
run such that any raised Exception is active when the callback
fires; not on the thread where the message was sent.
"""
# EmptyResponse translates to None
if isinstance(raw_response, EmptyResponse):
# EmptySysResponse translates to None
if isinstance(raw_response, EmptySysResponse):
return None
# Some error occurred. Raise a local Exception for it.
if isinstance(raw_response, ErrorResponse):
if isinstance(raw_response, ErrorSysResponse):
if (raw_response.error_type is
ErrorResponse.ErrorType.COMMUNICATION):
ErrorSysResponse.ErrorType.COMMUNICATION):
raise CommunicationError(raw_response.error_message)
# If something went wrong on *our* end of the connection,
# don't say it was a remote error.
if raw_response.error_type is ErrorResponse.ErrorType.LOCAL:
if raw_response.error_type is ErrorSysResponse.ErrorType.LOCAL:
raise RuntimeError(raw_response.error_message)
# If they want to support clean errors, do those.
if (self.protocol.preserve_clean_errors and raw_response.error_type
is ErrorResponse.ErrorType.REMOTE_CLEAN):
if (self.protocol.forward_clean_errors and raw_response.error_type
is ErrorSysResponse.ErrorType.REMOTE_CLEAN):
raise CleanError(raw_response.error_message)
# Everything else gets lumped in as a remote error.