This commit is contained in:
Eric Froemling 2022-01-25 14:16:50 -06:00
parent 67649a0eae
commit 4d31d59467
No known key found for this signature in database
GPG Key ID: 89C93F0F8D6D5A98
29 changed files with 348 additions and 157 deletions

View File

@ -3966,50 +3966,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/72/82/86956fae909ac2fe2a1abd84a361",
"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/e0/06/60b8a1d7a19049a169c2b68cf89e",
"build/prefab/full/linux_arm64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/15/fe/006539c400523428bb45ce4da9b2",
"build/prefab/full/linux_arm64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/74/1d/fc9e33e565475daaac80da5252f0",
"build/prefab/full/linux_arm64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/ff/56/a8f14d13ba81f23800568720624d",
"build/prefab/full/linux_arm64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/39/11/72a9a331f96f4bca715374dbf242",
"build/prefab/full/linux_x86_64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/00/a8/92d6a00c4af3c43e0af4bbe7b143",
"build/prefab/full/linux_arm64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/51/6f/40639d4d24908fbd9c32dd781818",
"build/prefab/full/linux_arm64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/60/8f/996fdf4a1f1e26b566b5e0b4f54b",
"build/prefab/full/linux_x86_64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/bc/75/418515fb999d524564ba485e3643",
"build/prefab/full/linux_x86_64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/9c/7b/ac1a200be0f37078af0991faca3b",
"build/prefab/full/linux_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/1a/83/3884b71fc3c83a597114c661fc94",
"build/prefab/full/linux_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/be/98/c29dc5249a7d772f778de4ca4119",
"build/prefab/full/mac_arm64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/c8/2f/07bae56da30dd2a4eb549d21788a",
"build/prefab/full/mac_arm64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/7d/fd/cf88baa3715276b66ffc8e500c28",
"build/prefab/full/mac_arm64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/cd/86/75a814002acbd4e3e7ce72135a69",
"build/prefab/full/mac_arm64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/e5/9a/90efe486fbe7015fc45d71215350",
"build/prefab/full/mac_x86_64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/34/81/45129983739ba3ab4c3b42aa6039",
"build/prefab/full/mac_x86_64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/5e/10/d0a79286b205c0da36f0a05b289c",
"build/prefab/full/mac_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/20/9e/2798a0a74cf535e1cccb4e2feb00",
"build/prefab/full/mac_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/95/55/eefd6a0c57c7a1f5d84128d3c1de",
"build/prefab/full/windows_x86_gui/debug/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/f2/9f/d5b12184218d6f15743d22e34ecf",
"build/prefab/full/windows_x86_gui/release/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/34/b9/0da9f3ac3d89cf144130ed5d6032",
"build/prefab/full/windows_x86_server/debug/dist/BallisticaCoreHeadless.exe": "https://files.ballistica.net/cache/ba1/bb/a3/8ee9ae4cd5260306bb14a17792ea",
"build/prefab/full/windows_x86_server/release/dist/BallisticaCoreHeadless.exe": "https://files.ballistica.net/cache/ba1/79/79/5b30b573a258262cf7ab5247b05d",
"build/prefab/lib/linux_arm64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/a3/d7/80ac73303ca83f1768b9ce1c2078",
"build/prefab/lib/linux_arm64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/f5/2a/c618f4f733d32d8a583348042e20",
"build/prefab/lib/linux_arm64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/63/6a/d187d352844a3b7c1861e79d2c8f",
"build/prefab/lib/linux_arm64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/bb/52/ae0ed2e83a449865252d90c2ac9a",
"build/prefab/lib/linux_x86_64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/be/2e/4bc4bf1ec4db1b98c7d7081095d4",
"build/prefab/lib/linux_x86_64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/59/7e/253051aa3d105e94235f74a0b5ae",
"build/prefab/lib/linux_x86_64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/d3/65/63cf06b0f8bd7fbc15edaf8a842b",
"build/prefab/lib/linux_x86_64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/63/61/7ff8074fa76ae8b5010b07d4f1f2",
"build/prefab/lib/mac_arm64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/5a/31/9b542804ba449ce4c8c6eddf3971",
"build/prefab/lib/mac_arm64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/3c/38/6be5cd846d6bd38acea70e8ee667",
"build/prefab/lib/mac_arm64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/2b/d9/7c24f63dd8737ca4ecb77d103560",
"build/prefab/lib/mac_arm64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/70/2c/1d44685db575e201542685f41f32",
"build/prefab/lib/mac_x86_64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/82/97/2afbffeaf0989dfd5094a48ecac7",
"build/prefab/lib/mac_x86_64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/d4/2c/d5cdded5a9d1018dad9f8e8a812e",
"build/prefab/lib/mac_x86_64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/ac/47/16da91f514af302c5435d060d75c",
"build/prefab/lib/mac_x86_64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/67/5b/47db352ef527391482b42e1072e8",
"build/prefab/lib/windows/Debug_Win32/BallisticaCoreGenericInternal.lib": "https://files.ballistica.net/cache/ba1/9f/1d/3d2c2056c491fdab1055a6a5f114",
"build/prefab/lib/windows/Debug_Win32/BallisticaCoreGenericInternal.pdb": "https://files.ballistica.net/cache/ba1/1e/a2/ff5dd453f396fa58362e24b34b5f",
"build/prefab/lib/windows/Debug_Win32/BallisticaCoreHeadlessInternal.lib": "https://files.ballistica.net/cache/ba1/30/8b/3a547ac0ce167a0c43492dab5104",
"build/prefab/lib/windows/Debug_Win32/BallisticaCoreHeadlessInternal.pdb": "https://files.ballistica.net/cache/ba1/73/8d/d25e325791ac3e0aab48f204bea3",
"build/prefab/lib/windows/Release_Win32/BallisticaCoreGenericInternal.lib": "https://files.ballistica.net/cache/ba1/cd/a4/925f8c8ef4026b07b55ef841e8e8",
"build/prefab/lib/windows/Release_Win32/BallisticaCoreGenericInternal.pdb": "https://files.ballistica.net/cache/ba1/62/b9/3539b66b55f11851633a120b40a1",
"build/prefab/lib/windows/Release_Win32/BallisticaCoreHeadlessInternal.lib": "https://files.ballistica.net/cache/ba1/64/d6/57d647611e82b6b36b0c2decd826",
"build/prefab/lib/windows/Release_Win32/BallisticaCoreHeadlessInternal.pdb": "https://files.ballistica.net/cache/ba1/f7/2f/f34dd67c2d40a8988b999cb59d2e",
"src/ballistica/generated/python_embedded/binding.inc": "https://files.ballistica.net/cache/ba1/f2/6c/5a0a4695dcc2a11e7941b8777e80",
"build/prefab/full/linux_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/47/d4/1d346b91ebcaa1215954d9a2bb71",
"build/prefab/full/linux_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/b0/c3/7c1cb5a0f96212ceafc08f71369a",
"build/prefab/full/mac_arm64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/5b/c9/3397972da39e948b812bc809e0c1",
"build/prefab/full/mac_arm64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/32/51/ff535d9b0f91f0defc7f9aee2bc4",
"build/prefab/full/mac_arm64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/ca/01/ecda0f5771d5008da48e7328da34",
"build/prefab/full/mac_arm64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/6c/6d/93aa177977ed7d0c529f4bcaa212",
"build/prefab/full/mac_x86_64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/68/3d/d25cb717747c1f41a64c3dc2b353",
"build/prefab/full/mac_x86_64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/c4/3a/64a33f5f837c435fd61bc34621de",
"build/prefab/full/mac_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/20/0f/157782b569ef6dfbe23e5f435aeb",
"build/prefab/full/mac_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/a6/f6/f1a09d846273291efcdebc32384c",
"build/prefab/full/windows_x86_gui/debug/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/e1/23/154b39f6ce4017f694b9bc50b4e5",
"build/prefab/full/windows_x86_gui/release/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/19/42/ffe1fbc5277708393c96ae1556b2",
"build/prefab/full/windows_x86_server/debug/dist/BallisticaCoreHeadless.exe": "https://files.ballistica.net/cache/ba1/c2/81/1445b40155e83482fe8629fc4659",
"build/prefab/full/windows_x86_server/release/dist/BallisticaCoreHeadless.exe": "https://files.ballistica.net/cache/ba1/09/88/cc1f4f48e2c3f98bd9140507aca4",
"build/prefab/lib/linux_arm64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/95/51/acefa044dba77f3adada7115f487",
"build/prefab/lib/linux_arm64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/ca/18/846e182605d341b8225865c8646c",
"build/prefab/lib/linux_arm64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/71/61/f81984085eb141f9ea1e6abca272",
"build/prefab/lib/linux_arm64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/f0/3c/2820206454ed96ae7317eb3ff31f",
"build/prefab/lib/linux_x86_64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/52/68/e27ec9c0b2253213dd24cd49701c",
"build/prefab/lib/linux_x86_64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/83/f5/9c21b5431f1f22bad111b0872301",
"build/prefab/lib/linux_x86_64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/80/82/6af017a24c9de0e67d1702b538e6",
"build/prefab/lib/linux_x86_64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/ad/00/be95b2212c2b2555c4c5130a0cd0",
"build/prefab/lib/mac_arm64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/8c/e2/5c704673f29a89445eee67248f84",
"build/prefab/lib/mac_arm64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/cc/1c/91c9f1ddf9c159f63a45175596eb",
"build/prefab/lib/mac_arm64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/36/3b/250d4aaaccfcb8d60c4fbf3f083d",
"build/prefab/lib/mac_arm64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/3d/2b/59466f90e18c090c8a9154b38b7e",
"build/prefab/lib/mac_x86_64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/d7/6d/f22ddf7ee877b50c3010506bff49",
"build/prefab/lib/mac_x86_64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/d9/b2/34b5f247b3952323e4ffde3d4a2d",
"build/prefab/lib/mac_x86_64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/ad/fb/5ccdb9da44706867aa56cb0c6316",
"build/prefab/lib/mac_x86_64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/d9/02/36ab4592b9a994e9be386c79d2df",
"build/prefab/lib/windows/Debug_Win32/BallisticaCoreGenericInternal.lib": "https://files.ballistica.net/cache/ba1/af/1a/85c3b9a25c3cabe0cf05892b34ee",
"build/prefab/lib/windows/Debug_Win32/BallisticaCoreGenericInternal.pdb": "https://files.ballistica.net/cache/ba1/3e/6a/a12f9aad3dc9de538d7f99751709",
"build/prefab/lib/windows/Debug_Win32/BallisticaCoreHeadlessInternal.lib": "https://files.ballistica.net/cache/ba1/01/c5/fe63c070acfd6592a41a0a1f6480",
"build/prefab/lib/windows/Debug_Win32/BallisticaCoreHeadlessInternal.pdb": "https://files.ballistica.net/cache/ba1/2d/0a/9594864542e84204caba36de407c",
"build/prefab/lib/windows/Release_Win32/BallisticaCoreGenericInternal.lib": "https://files.ballistica.net/cache/ba1/ae/42/17489667000e866dfa319d84cb04",
"build/prefab/lib/windows/Release_Win32/BallisticaCoreGenericInternal.pdb": "https://files.ballistica.net/cache/ba1/1a/c4/a5ad68b0e99db6f802b7bdb3cb04",
"build/prefab/lib/windows/Release_Win32/BallisticaCoreHeadlessInternal.lib": "https://files.ballistica.net/cache/ba1/e2/70/bff2f5ab3526af5a39e6ac4a65cd",
"build/prefab/lib/windows/Release_Win32/BallisticaCoreHeadlessInternal.pdb": "https://files.ballistica.net/cache/ba1/3b/7c/d9e5ae045c3347812a30f75273e5",
"src/ballistica/generated/python_embedded/binding.inc": "https://files.ballistica.net/cache/ba1/c5/18/29d9fe8e483ce222d3263336f7e6",
"src/ballistica/generated/python_embedded/bootstrap.inc": "https://files.ballistica.net/cache/ba1/65/ac/d5c4162a71028c1bfa73ebc1f881"
}

View File

@ -524,6 +524,7 @@
<w>dbpath</w>
<w>dcioexattrs</w>
<w>dcioprep</w>
<w>dcioprepsession</w>
<w>dcls</w>
<w>dcmake</w>
<w>deathmatch</w>
@ -1086,6 +1087,7 @@
<w>incmd</w>
<w>incr</w>
<w>incrementbuild</w>
<w>indata</w>
<w>indentfilter</w>
<w>indentstr</w>
<w>indexfile</w>
@ -1966,6 +1968,7 @@
<w>rsdr</w>
<w>rsms</w>
<w>rstr</w>
<w>rtest</w>
<w>rtnetlink</w>
<w>rtxt</w>
<w>rtypes</w>
@ -2284,6 +2287,7 @@
<w>tcall</w>
<w>tchar</w>
<w>tclass</w>
<w>tcls</w>
<w>tcombine</w>
<w>tdelay</w>
<w>tdval</w>

View File

@ -1,3 +1,6 @@
### 1.6.7 (20394)
- Fixed a vulnerability which could expose device-account uuids.
### 1.6.6 (20394)
- Beginning work on moving to new asset system.
- Added Tamil language (Thanks Ryan!)

View File

@ -1 +1 @@
128243803030405974762870876563323013389
142084453862763162635228312125641718455

View File

@ -119,6 +119,11 @@ def gear_vr_controller_warning() -> None:
color=(1, 0, 0))
def uuid_str() -> str:
import uuid
return str(uuid.uuid4())
def orientation_reset_cb_message() -> None:
from ba._language import Lstr
_ba.screenmessage(

View File

@ -248,6 +248,7 @@
<w>dbias</w>
<w>dcioexattrs</w>
<w>dcioprep</w>
<w>dcioprepsession</w>
<w>dcol</w>
<w>ddcaps</w>
<w>ddpf</w>
@ -513,6 +514,7 @@
<w>imagewidget</w>
<w>importlines</w>
<w>incentivized</w>
<w>indata</w>
<w>inet</w>
<w>infotxt</w>
<w>inides</w>
@ -935,6 +937,7 @@
<w>rresult</w>
<w>rscode</w>
<w>rsgc</w>
<w>rtest</w>
<w>rtypes</w>
<w>rtypevar</w>
<w>runnables</w>
@ -1073,6 +1076,7 @@
<w>tabtypes</w>
<w>talloc</w>
<w>targs</w>
<w>tcls</w>
<w>tegra</w>
<w>telefonaktiebolaget</w>
<w>teleported</w>

View File

@ -1,5 +1,5 @@
<!-- THIS FILE IS AUTO GENERATED; DO NOT EDIT BY HAND -->
<h4><em>last updated on 2021-12-22 for Ballistica version 1.6.6 build 20416</em></h4>
<h4><em>last updated on 2022-01-25 for Ballistica version 1.6.7 build 20427</em></h4>
<p>This page documents the Python classes and functions in the 'ba' module,
which are the ones most relevant to modding in Ballistica. If you come across something you feel should be included here or could be better explained, please <a href="mailto:support@froemling.net">let me know</a>. Happy modding!</p>
<hr>

View File

@ -54,7 +54,7 @@ class AppGlobals {
bool reset_vr_orientation{};
bool user_ran_commands{};
UIScale ui_scale{UIScale::kLarge};
AccountType account_type{AccountType::kInvalid};
V1AccountType account_type{V1AccountType::kInvalid};
bool remote_server_accepting_connections{true};
std::string exec_command;
std::string user_agent_string{"BA_USER_AGENT_UNSET (" BA_PLATFORM_STRING ")"};

View File

@ -21,8 +21,8 @@
namespace ballistica {
// These are set automatically via script; don't modify them here.
const int kAppBuildNumber = 20416;
const char* kAppVersion = "1.6.6";
const int kAppBuildNumber = 20427;
const char* kAppVersion = "1.6.7";
// Our standalone globals.
// These are separated out for easy access.
@ -213,13 +213,24 @@ auto FatalError(const std::string& message) -> void {
auto GetUniqueSessionIdentifier() -> const std::string& {
static std::string session_id;
static bool have_session_id = false;
if (!have_session_id) {
srand(static_cast<unsigned int>(
Platform::GetCurrentMilliseconds())); // NOLINT
auto tval = static_cast<uint32_t>(rand()); // NOLINT
assert(g_platform);
session_id = g_platform->GetUniqueDeviceIdentifier() + std::to_string(tval);
have_session_id = true;
if (g_python) {
Python::ScopedInterpreterLock gil;
auto uuid = g_python->obj(Python::ObjID::kUUIDStrCall).Call();
if (uuid.exists()) {
session_id = uuid.ValueAsString().c_str();
have_session_id = true;
}
}
if (!have_session_id) {
// As an emergency fallback simply use a single random number.
Log("WARNING: GetUniqueSessionIdentifier() using rand fallback.");
srand(static_cast<unsigned int>(
Platform::GetCurrentMilliseconds())); // NOLINT
session_id = std::to_string(static_cast<uint32_t>(rand())); // NOLINT
have_session_id = true;
}
if (session_id.size() >= 100) {
Log("WARNING: session id longer than it should be.");
}

View File

@ -852,7 +852,7 @@ enum class NodeMessageType {
kFooting
};
enum class LoginState { kSignedOut, kSigningIn, kSignedIn };
enum class V1LoginState { kSignedOut, kSigningIn, kSignedIn };
enum class CameraMode { kFollow, kOrbit };
@ -1007,7 +1007,7 @@ enum class ThreadIdentifier {
kBGDynamics
};
enum class AccountType {
enum class V1AccountType {
kInvalid,
kTest,
kGameCenter,

View File

@ -16,9 +16,9 @@ namespace ballistica {
class Account {
public:
Account();
static auto AccountTypeFromString(const std::string& val) -> AccountType;
static auto AccountTypeToString(AccountType type) -> std::string;
static auto AccountTypeToIconString(AccountType type) -> std::string;
static auto AccountTypeFromString(const std::string& val) -> V1AccountType;
static auto AccountTypeToString(V1AccountType type) -> std::string;
static auto AccountTypeToIconString(V1AccountType type) -> std::string;
auto GetLoginName() -> std::string;
auto GetLoginID() -> std::string;
@ -28,7 +28,7 @@ class Account {
/// Return the current account state.
/// If an int pointer is passed, state-num will also be returned.
auto GetLoginState(int* state_num = nullptr) -> LoginState;
auto GetLoginState(int* state_num = nullptr) -> V1LoginState;
// An extra value included when passing our account info to the server
// ...(can be used for platform-specific install-signature stuff, etc.).
@ -37,7 +37,7 @@ class Account {
auto SetToken(const std::string& account_id, const std::string& token)
-> void;
auto SetLogin(AccountType account_type, LoginState login_state,
auto SetLogin(V1AccountType account_type, V1LoginState login_state,
const std::string& login_name, const std::string& login_id)
-> void;
@ -57,7 +57,7 @@ class Account {
std::string token_;
std::string extra_;
std::string extra_2_;
LoginState login_state_{LoginState::kSignedOut};
V1LoginState login_state_{V1LoginState::kSignedOut};
int login_state_num_{};
};

View File

@ -253,14 +253,10 @@ void Game::PushMediaPruneCall(int level) {
});
}
void Game::PushSetAccountTokenCall(const std::string& account_id,
const std::string& token) {
PushCall([account_id, token] { g_account->SetToken(account_id, token); });
}
void Game::PushSetLoginCall(AccountType account_type, LoginState account_state,
const std::string& account_name,
const std::string& account_id) {
void Game::PushSetV1LoginCall(V1AccountType account_type,
V1LoginState account_state,
const std::string& account_name,
const std::string& account_id) {
PushCall([this, account_type, account_state, account_name, account_id] {
g_account->SetLogin(account_type, account_state, account_name, account_id);
});
@ -964,7 +960,6 @@ void Game::PushInterruptSignalCall() {
return;
}
// Just go through _ba.quit()
// FIXME: Shouldn't need to go out to the Python layer here...
g_python->obj(Python::ObjID::kQuitCall).Call();
});
@ -979,11 +974,10 @@ void Game::PushAskUserForTelnetAccessCall() {
}
void Game::HandleThreadPause() {
// Give userspace python stuff a chance to pause.
ScopedSetContext cp(GetUIContextTarget());
g_python->obj(Python::ObjID::kOnAppPauseCall).Call();
// Tell our account client to commit any outstanding changes to disk.
// Let Python and internal layers do their thing.
g_python->obj(Python::ObjID::kOnAppPauseCall).Call();
AppInternalOnGameThreadPause();
}
@ -1831,23 +1825,24 @@ void Game::CleanUpBeforeConnectingToHost() {
SetPublicPartyEnabled(false);
}
void Game::PushPartyInviteCall(const std::string& name,
const std::string& invite_id) {
PushCall([this, name, invite_id] { PartyInvite(name, invite_id); });
void Game::PushV1PartyInviteCall(const std::string& name,
const std::string& invite_id) {
PushCall([this, name, invite_id] { V1PartyInvite(name, invite_id); });
}
void Game::PartyInvite(const std::string& name, const std::string& invite_id) {
void Game::V1PartyInvite(const std::string& name,
const std::string& invite_id) {
assert(InGameThread());
g_python->PartyInvite(name, invite_id);
g_python->V1PartyInvite(name, invite_id);
}
void Game::PushPartyInviteRevokeCall(const std::string& invite_id) {
PushCall([this, invite_id] { PartyInviteRevoke(invite_id); });
void Game::PushV1PartyInviteRevokeCall(const std::string& invite_id) {
PushCall([this, invite_id] { V1PartyInviteRevoke(invite_id); });
}
void Game::PartyInviteRevoke(const std::string& invite_id) {
void Game::V1PartyInviteRevoke(const std::string& invite_id) {
assert(InGameThread());
g_python->PartyInviteRevoke(invite_id);
g_python->V1PartyInviteRevoke(invite_id);
}
auto Game::GetPartySize() const -> int {

View File

@ -32,14 +32,13 @@ class Game : public Module {
auto LaunchClientSession() -> void;
auto LaunchReplaySession(const std::string& file_name) -> void;
auto PushSetLoginCall(AccountType account_type, LoginState account_state,
const std::string& account_name,
const std::string& account_id) -> void;
auto PushSetAccountTokenCall(const std::string& account_id,
const std::string& token) -> void;
auto PushPartyInviteCall(const std::string& name,
const std::string& invite_id) -> void;
auto PushPartyInviteRevokeCall(const std::string& invite_id) -> void;
auto PushSetV1LoginCall(V1AccountType account_type,
V1LoginState account_state,
const std::string& account_name,
const std::string& account_id) -> void;
auto PushV1PartyInviteCall(const std::string& name,
const std::string& invite_id) -> void;
auto PushV1PartyInviteRevokeCall(const std::string& invite_id) -> void;
auto PushInitialScreenCreatedCall() -> void;
auto PushApplyConfigCall() -> void;
auto PushRemoveGraphicsServerRenderHoldCall() -> void;
@ -253,9 +252,9 @@ class Game : public Module {
auto HandleQuitOnIdle() -> void;
auto InitSpecialChars() -> void;
auto Draw() -> void;
auto PartyInvite(const std::string& name, const std::string& invite_id)
auto V1PartyInvite(const std::string& name, const std::string& invite_id)
-> void;
auto PartyInviteRevoke(const std::string& invite_id) -> void;
auto V1PartyInviteRevoke(const std::string& invite_id) -> void;
auto InitialScreenCreated() -> void;
auto MainMenuPress(InputDevice* device) -> void;
auto ScreenResize(float virtual_width, float virtual_height,

View File

@ -34,7 +34,7 @@ PlayerSpec::PlayerSpec(const std::string& s) {
Log("Error creating PlayerSpec from string: '" + s + "'");
name_ = "<error>";
short_name_ = "";
account_type_ = AccountType::kInvalid;
account_type_ = V1AccountType::kInvalid;
}
}
@ -75,7 +75,7 @@ auto PlayerSpec::GetSpecString() const -> std::string {
auto PlayerSpec::GetAccountPlayerSpec() -> PlayerSpec {
PlayerSpec spec;
if (g_account->GetLoginState() == LoginState::kSignedIn) {
if (g_account->GetLoginState() == V1LoginState::kSignedIn) {
spec.account_type_ = g_app_globals->account_type;
spec.name_ =
Utils::GetValidUTF8(g_account->GetLoginName().c_str(), "bsgaps");

View File

@ -48,7 +48,7 @@ class PlayerSpec {
private:
std::string name_;
std::string short_name_;
AccountType account_type_{AccountType::kInvalid};
V1AccountType account_type_{V1AccountType::kInvalid};
};
} // namespace ballistica

View File

@ -153,10 +153,12 @@ class Platform {
// Return a string *reasonably* likely to be unique and consistent for this
// device. Do not assume this is globally unique and *do not* assume that it
// will never ever change (hardware upgrades may affect it, etc).
// IMPORTANT: This value should NEVER be sent over the wire to peers.
virtual auto GetUniqueDeviceIdentifier() -> const std::string&;
// Returns the ID to use for the device account
auto GetDeviceAccountID() -> std::string;
auto GetConfigDirectory() -> std::string;
auto GetConfigFilePath() -> std::string;
auto GetUserPythonDirectory() -> std::string;

View File

@ -2322,8 +2322,8 @@ auto Python::ObjToString(PyObject* obj) -> std::string {
}
}
void Python::PartyInvite(const std::string& player,
const std::string& invite_id) {
void Python::V1PartyInvite(const std::string& player,
const std::string& invite_id) {
ScopedSetContext cp(g_game->GetUIContext());
PythonRef args(
Py_BuildValue(
@ -2336,7 +2336,7 @@ void Python::PartyInvite(const std::string& player,
obj(ObjID::kHandlePartyInviteCall).Call(args);
}
void Python::PartyInviteRevoke(const std::string& invite_id) {
void Python::V1PartyInviteRevoke(const std::string& invite_id) {
ScopedSetContext cp(g_game->GetUIContext());
PythonRef args(
Py_BuildValue("(O)", PythonRef(PyUnicode_FromString(invite_id.c_str()),

View File

@ -142,8 +142,8 @@ class Python {
/// is useful as an object identifier/etc.
static auto GetPythonFileLocation(bool pretty = true) -> std::string;
void PartyInvite(const std::string& player, const std::string& invite_id);
void PartyInviteRevoke(const std::string& invite_id);
void V1PartyInvite(const std::string& player, const std::string& invite_id);
void V1PartyInviteRevoke(const std::string& invite_id);
void set_env_obj(PyObject* obj) { env_ = obj; }
auto env_obj() const -> PyObject* {
assert(env_);
@ -351,6 +351,7 @@ class Python {
kPlayerClass,
kGetPlayerIconCall,
kLstrFromJsonCall,
kUUIDStrCall,
kLast // Sentinel; must be at end.
};

View File

@ -134,4 +134,5 @@ def get_binding_values() -> tuple[Any, ...]:
_player.Player, # kPlayerClass
_hooks.get_player_icon, # kGetPlayerIconCall
_language.Lstr.from_json, # kLstrFromJsonCall
_hooks.uuid_str, # kUUIDStrCall
) # yapf: disable

View File

@ -13,8 +13,8 @@ import pytest
from efro.util import utc_now
from efro.dataclassio import (dataclass_validate, dataclass_from_dict,
dataclass_to_dict, ioprepped, IOAttrs, Codec,
DataclassFieldLookup)
dataclass_to_dict, ioprepped, ioprep, IOAttrs,
Codec, DataclassFieldLookup, IOExtendedData)
if TYPE_CHECKING:
pass
@ -220,11 +220,11 @@ def test_assign() -> None:
dataclass_from_dict(_TestClass, {'ssval': {}})
with pytest.raises(TypeError):
dataclass_from_dict(_TestClass, {'ssval': set()})
with pytest.raises(TypeError):
with pytest.raises(ValueError):
dataclass_from_dict(_TestClass, {'tupleval': []})
with pytest.raises(TypeError):
dataclass_from_dict(_TestClass, {'tupleval': [1, 1, 1]})
with pytest.raises(TypeError):
with pytest.raises(ValueError):
dataclass_from_dict(_TestClass, {'tupleval': [2, 'foof', True, True]})
# Fields with type Any should accept all types which are directly
@ -652,6 +652,36 @@ def test_name_clashes() -> None:
ival2: Annotated[int, IOAttrs('ival')] = 5
@dataclass
class _RecursiveTest:
val: int
child: Optional[_RecursiveTest] = None
def test_recursive() -> None:
"""Test recursive classes."""
# Can't use ioprepped on this since it refers to its own name which
# doesn't exist yet. Have to explicitly prep it after.
ioprep(_RecursiveTest)
rtest = _RecursiveTest(val=1)
rtest.child = _RecursiveTest(val=2)
rtest.child.child = _RecursiveTest(val=3)
expected_output = {
'val': 1,
'child': {
'val': 2,
'child': {
'val': 3,
'child': None
}
}
}
assert dataclass_to_dict(rtest) == expected_output
assert dataclass_from_dict(_RecursiveTest, expected_output) == rtest
def test_any() -> None:
"""Test data included with type Any."""
@ -795,3 +825,51 @@ def test_nested() -> None:
subval: _TestSubClass = field(default_factory=_TestSubClass)
enval: _TestEnum = _TestEnum.VAL1
def test_extended_data() -> None:
"""Test IOExtendedData functionality."""
@ioprepped
@dataclass
class _TestClass:
vals: tuple[int, int]
# This data lines up.
indata = {'vals': [0, 0]}
_obj = dataclass_from_dict(_TestClass, indata)
# This data doesn't.
indata = {'vals': [0, 0, 0]}
with pytest.raises(ValueError):
_obj = dataclass_from_dict(_TestClass, indata)
# Now define the same data but give it an adapter
# so it can work with our incorrectly-formatted data.
@ioprepped
@dataclass
class _TestClass2(IOExtendedData):
vals: tuple[int, int]
@classmethod
def will_input(cls, data: dict) -> None:
data['vals'] = data['vals'][:2]
def will_output(self) -> None:
self.vals = (0, 0)
# This data lines up.
indata = {'vals': [0, 0]}
_obj2 = dataclass_from_dict(_TestClass2, indata)
# Now this data will too via our custom input filter.
indata = {'vals': [0, 0, 0]}
_obj2 = dataclass_from_dict(_TestClass2, indata)
# Ok, now test output:
# Does the expected thing.
assert dataclass_to_dict(_TestClass(vals=(1, 2))) == {'vals': [1, 2]}
# Uses our output filter.
assert dataclass_to_dict(_TestClass2(vals=(1, 2))) == {'vals': [0, 0]}

View File

@ -38,8 +38,8 @@ class AssetType(Enum):
@dataclass
class AssetPackageFlavorManifest:
"""A manifest of asset info for a specific flavor of an asset package."""
assetfiles: Annotated[dict[str, str],
IOAttrs('assetfiles')] = field(default_factory=dict)
cloudfiles: Annotated[dict[str, str],
IOAttrs('cloudfiles')] = field(default_factory=dict)
@ioprepped

View File

@ -15,17 +15,19 @@ from typing import TYPE_CHECKING, TypeVar
from efro.dataclassio._outputter import _Outputter
from efro.dataclassio._inputter import _Inputter
from efro.dataclassio._base import Codec, IOAttrs
from efro.dataclassio._prep import ioprep, ioprepped, is_ioprepped_dataclass
from efro.dataclassio._base import Codec, IOAttrs, IOExtendedData
from efro.dataclassio._prep import (ioprep, ioprepped, will_ioprep,
is_ioprepped_dataclass)
from efro.dataclassio._pathcapture import DataclassFieldLookup
if TYPE_CHECKING:
from typing import Any, Optional
__all__ = [
'Codec', 'IOAttrs', 'ioprep', 'ioprepped', 'is_ioprepped_dataclass',
'DataclassFieldLookup', 'dataclass_to_dict', 'dataclass_to_json',
'dataclass_from_dict', 'dataclass_from_json', 'dataclass_validate'
'Codec', 'IOAttrs', 'IOExtendedData', 'ioprep', 'ioprepped', 'will_ioprep',
'is_ioprepped_dataclass', 'DataclassFieldLookup', 'dataclass_to_dict',
'dataclass_to_json', 'dataclass_from_dict', 'dataclass_from_json',
'dataclass_validate'
]
T = TypeVar('T')

View File

@ -12,15 +12,6 @@ from typing import TYPE_CHECKING, get_args
# noinspection PyProtectedMember
from typing import _AnnotatedAlias # type: ignore
_pytz_utc: Any
# We don't *require* pytz, but we want to support it for tzinfos if available.
try:
import pytz
_pytz_utc = pytz.utc
except ModuleNotFoundError:
_pytz_utc = None # pylint: disable=invalid-name
if TYPE_CHECKING:
from typing import Any, Optional
@ -32,14 +23,6 @@ SIMPLE_TYPES = {int, bool, str, float, type(None)}
EXTRA_ATTRS_ATTR = '_DCIOEXATTRS'
def _ensure_datetime_is_timezone_aware(value: datetime.datetime) -> None:
# We only support timezone-aware utc times.
if (value.tzinfo is not datetime.timezone.utc
and (_pytz_utc is None or value.tzinfo is not _pytz_utc)):
raise ValueError(
'datetime values must have timezone set as timezone.utc')
def _raise_type_error(fieldpath: str, valuetype: type,
expected: tuple[type, ...]) -> None:
"""Raise an error when a field value's type does not match expected."""
@ -67,6 +50,24 @@ class Codec(Enum):
FIRESTORE = 'firestore'
class IOExtendedData:
"""A class that data types can inherit from for extra functionality."""
def will_output(self) -> None:
"""Called before data is sent to an outputter.
Can be overridden to validate or filter data before
sending it on its way.
"""
@classmethod
def will_input(cls, data: dict) -> None:
"""Called on raw data before a class instance is created from it.
Can be overridden to migrate old data formats to new, etc.
"""
def _is_valid_for_codec(obj: Any, codec: Codec) -> bool:
"""Return whether a value consists solely of json-supported types.

View File

@ -14,11 +14,11 @@ import typing
import datetime
from typing import TYPE_CHECKING, Generic, TypeVar
from efro.util import enum_by_value
from efro.util import enum_by_value, check_utc
from efro.dataclassio._base import (Codec, _parse_annotated, EXTRA_ATTRS_ATTR,
_is_valid_for_codec, _get_origin,
SIMPLE_TYPES, _raise_type_error,
_ensure_datetime_is_timezone_aware)
IOExtendedData)
from efro.dataclassio._prep import PrepSession
if TYPE_CHECKING:
@ -48,6 +48,12 @@ class _Inputter(Generic[T]):
def run(self, values: dict) -> T:
"""Do the thing."""
# For special extended data types, call their 'will_output' callback.
tcls = self._cls
if issubclass(tcls, IOExtendedData):
tcls.will_input(values)
out = self._dataclass_from_input(self._cls, '', values)
assert isinstance(out, self._cls)
return out
@ -159,6 +165,7 @@ class _Inputter(Generic[T]):
prep = PrepSession(explicit=False).prep_dataclass(cls,
recursion_level=0)
assert prep is not None
extra_attrs = {}
@ -344,7 +351,7 @@ class _Inputter(Generic[T]):
f'Invalid input value for "{fieldpath}" on'
f' "{cls.__name__}";'
f' expected a datetime, got a {type(value).__name__}')
_ensure_datetime_is_timezone_aware(value)
check_utc(value)
return value
assert self._codec is Codec.JSON
@ -355,9 +362,9 @@ class _Inputter(Generic[T]):
f'Invalid input value for "{fieldpath}" on "{cls.__name__}";'
f' expected a list, got a {type(value).__name__}')
if len(value) != 7 or not all(isinstance(x, int) for x in value):
raise TypeError(
raise ValueError(
f'Invalid input value for "{fieldpath}" on "{cls.__name__}";'
f' expected a list of 7 ints.')
f' expected a list of 7 ints, got {[type(v) for v in value]}.')
out = datetime.datetime( # type: ignore
*value, tzinfo=datetime.timezone.utc)
if ioattrs is not None:
@ -380,9 +387,9 @@ class _Inputter(Generic[T]):
assert childanntypes
if len(value) != len(childanntypes):
raise TypeError(f'Invalid tuple input for "{fieldpath}";'
f' expected {len(childanntypes)} values,'
f' found {len(value)}.')
raise ValueError(f'Invalid tuple input for "{fieldpath}";'
f' expected {len(childanntypes)} values,'
f' found {len(value)}.')
for i, childanntype in enumerate(childanntypes):
childval = value[i]

View File

@ -14,10 +14,11 @@ import typing
import datetime
from typing import TYPE_CHECKING
from efro.util import check_utc
from efro.dataclassio._base import (Codec, _parse_annotated, EXTRA_ATTRS_ATTR,
_is_valid_for_codec, _get_origin,
SIMPLE_TYPES, _raise_type_error,
_ensure_datetime_is_timezone_aware)
IOExtendedData)
from efro.dataclassio._prep import PrepSession
if TYPE_CHECKING:
@ -37,6 +38,11 @@ class _Outputter:
def run(self) -> Any:
"""Do the thing."""
# For special extended data types, call their 'will_output' callback.
if isinstance(self._obj, IOExtendedData):
self._obj.will_output()
return self._process_dataclass(type(self._obj), self._obj, '')
def _process_dataclass(self, cls: type, obj: Any, fieldpath: str) -> Any:
@ -44,6 +50,7 @@ class _Outputter:
# pylint: disable=too-many-branches
prep = PrepSession(explicit=False).prep_dataclass(type(obj),
recursion_level=0)
assert prep is not None
fields = dataclasses.fields(obj)
out: Optional[dict[str, Any]] = {} if self._create else None
for field in fields:
@ -242,7 +249,7 @@ class _Outputter:
if not isinstance(value, origin):
raise TypeError(f'Expected a {origin} for {fieldpath};'
f' found a {type(value)}.')
_ensure_datetime_is_timezone_aware(value)
check_utc(value)
if ioattrs is not None:
ioattrs.validate_datetime(value, fieldpath)
if self._codec is Codec.FIRESTORE:

View File

@ -36,6 +36,7 @@ class _PathCapture:
prep = PrepSession(explicit=False).prep_dataclass(self._cls,
recursion_level=0)
assert prep is not None
try:
anntype = prep.annotations[name]
except KeyError as exc:

View File

@ -19,7 +19,7 @@ from typing import TYPE_CHECKING, TypeVar, get_type_hints
from efro.dataclassio._base import _parse_annotated, _get_origin, SIMPLE_TYPES
if TYPE_CHECKING:
from typing import Any
from typing import Any, Optional
T = TypeVar('T')
@ -27,9 +27,13 @@ T = TypeVar('T')
# (basically for detecting recursive types)
MAX_RECURSION = 10
# Attr name for data we store on dataclass types as part of prep.
# Attr name for data we store on dataclass types that have been prepped.
PREP_ATTR = '_DCIOPREP'
# We also store the prep-session while the prep is in progress.
# (necessary to support recursive types).
PREP_SESSION_ATTR = '_DCIOPREPSESSION'
def ioprep(cls: type) -> None:
"""Prep a dataclass type for use with this module's functionality.
@ -64,6 +68,23 @@ def ioprepped(cls: type[T]) -> type[T]:
return cls
def will_ioprep(cls: type[T]) -> type[T]:
"""Class decorator hinting that we will prep a class later.
In some cases (such as recursive types) we cannot use the @ioprepped
decorator and must instead call ioprep() explicitly later. However,
some of our custom pylint checking behaves differently when the
@ioprepped decorator is present, in that case requiring type annotations
to be present and not simply forward declared under an "if TYPE_CHECKING"
block. (since they are used at runtime).
The @will_ioprep decorator triggers the same pylint behavior
differences as @ioprepped (which are necessary for the later ioprep() call
to work correctly) but without actually running any prep itself.
"""
return cls
def is_ioprepped_dataclass(obj: Any) -> bool:
"""Return whether the obj is an ioprepped dataclass type or instance."""
cls = obj if isinstance(obj, type) else type(obj)
@ -90,8 +111,15 @@ class PrepSession:
def __init__(self, explicit: bool):
self.explicit = explicit
def prep_dataclass(self, cls: type, recursion_level: int) -> PrepData:
"""Run prep on a dataclass if necessary and return its prep data."""
def prep_dataclass(self, cls: type,
recursion_level: int) -> Optional[PrepData]:
"""Run prep on a dataclass if necessary and return its prep data.
The only case where this will return None is for recursive types
if the type is already being prepped higher in the call order.
"""
# pylint: disable=too-many-locals
# pylint: disable=too-many-branches
# We should only need to do this once per dataclass.
existing_data = getattr(cls, PREP_ATTR, None)
@ -99,8 +127,9 @@ class PrepSession:
assert isinstance(existing_data, PrepData)
return existing_data
# If we run into classes containing themselves, we may have
# to do something smarter to handle it.
# Sanity check.
# Note that we now support recursive types via the PREP_SESSION_ATTR,
# so we theoretically shouldn't run into this this.
if recursion_level > MAX_RECURSION:
raise RuntimeError('Max recursion exceeded.')
@ -108,6 +137,18 @@ class PrepSession:
if not isinstance(cls, type) or not dataclasses.is_dataclass(cls):
raise TypeError(f'Passed arg {cls} is not a dataclass type.')
# Add a pointer to the prep-session while doing the prep.
# This way we can ignore types that we're already in the process
# of prepping and can support recursive types.
existing_prep = getattr(cls, PREP_SESSION_ATTR, None)
if existing_prep is not None:
if existing_prep is self:
return None
# We shouldn't need to support failed preps
# or preps from multiple threads at once.
raise RuntimeError('Found existing in-progress prep.')
setattr(cls, PREP_SESSION_ATTR, self)
# Generate a warning on non-explicit preps; we prefer prep to
# happen explicitly at runtime so errors can be detected early on.
if not self.explicit:
@ -126,7 +167,6 @@ class PrepSession:
include_extras=True)
# pylint: enable=unexpected-keyword-arg
except Exception as exc:
print('GOT', cls.__dict__)
raise TypeError(
f'dataclassio prep for {cls} failed with error: {exc}.'
f' Make sure all types used in annotations are defined'
@ -175,6 +215,10 @@ class PrepSession:
annotations=resolved_annotations,
storage_names_to_attr_names=storage_names_to_attr_names)
setattr(cls, PREP_ATTR, prepdata)
# Clear our prep-session tag.
assert getattr(cls, PREP_SESSION_ATTR, None) is self
delattr(cls, PREP_SESSION_ATTR)
return prepdata
def prep_type(self, cls: type, attrname: str, anntype: Any,

View File

@ -11,6 +11,15 @@ import functools
from enum import Enum
from typing import TYPE_CHECKING, cast, TypeVar, Generic
_pytz_utc: Any
# We don't *require* pytz, but we want to support it for tzinfos if available.
try:
import pytz
_pytz_utc = pytz.utc
except ModuleNotFoundError:
_pytz_utc = None # pylint: disable=invalid-name
if TYPE_CHECKING:
import asyncio
from efro.call import Call as Call # 'as Call' so we re-export.
@ -62,6 +71,14 @@ def enum_by_value(cls: type[TENUM], value: Any) -> TENUM:
(value, cls.__name__)) from None
def check_utc(value: datetime.datetime) -> None:
"""Ensure a datetime value is timezone-aware utc."""
if (value.tzinfo is not datetime.timezone.utc
and (_pytz_utc is None or value.tzinfo is not _pytz_utc)):
raise ValueError('datetime value does not have timezone set as'
' datetime.timezone.utc')
def utc_now() -> datetime.datetime:
"""Get offset-aware current utc time.
@ -240,7 +257,8 @@ class DispatchMethodWrapper(Generic[TARG, TRET]):
pass
@staticmethod
def register(func: Callable[[Any, Any], TRET]) -> Callable:
def register(
func: Callable[[Any, Any], TRET]) -> Callable[[Any, Any], TRET]:
"""Register a new dispatch handler for this dispatch-method."""
registry: dict[Any, Callable]
@ -312,12 +330,16 @@ class ValueDispatcher(Generic[TVAL, TRET]):
return handler()
return self._base_call(value)
def _add_handler(self, value: TVAL, call: Callable[[], TRET]) -> None:
def _add_handler(self, value: TVAL,
call: Callable[[], TRET]) -> Callable[[], TRET]:
if value in self._handlers:
raise RuntimeError(f'Duplicate handlers added for {value}')
self._handlers[value] = call
return call
def register(self, value: TVAL) -> Callable[[Callable[[], TRET]], None]:
def register(
self,
value: TVAL) -> Callable[[Callable[[], TRET]], Callable[[], TRET]]:
"""Add a handler to the dispatcher."""
from functools import partial
return partial(self._add_handler, value)
@ -343,13 +365,16 @@ class ValueDispatcher1Arg(Generic[TVAL, TARG, TRET]):
return handler(arg)
return self._base_call(value, arg)
def _add_handler(self, value: TVAL, call: Callable[[TARG], TRET]) -> None:
def _add_handler(self, value: TVAL,
call: Callable[[TARG], TRET]) -> Callable[[TARG], TRET]:
if value in self._handlers:
raise RuntimeError(f'Duplicate handlers added for {value}')
self._handlers[value] = call
return call
def register(self,
value: TVAL) -> Callable[[Callable[[TARG], TRET]], None]:
def register(
self, value: TVAL
) -> Callable[[Callable[[TARG], TRET]], Callable[[TARG], TRET]]:
"""Add a handler to the dispatcher."""
from functools import partial
return partial(self._add_handler, value)
@ -363,8 +388,9 @@ if TYPE_CHECKING:
def __call__(self, value: TVAL) -> TRET:
...
def register(self,
value: TVAL) -> Callable[[Callable[[TSELF], TRET]], None]:
def register(
self, value: TVAL
) -> Callable[[Callable[[TSELF], TRET]], Callable[[TSELF], TRET]]:
"""Add a handler to the dispatcher."""
...

View File

@ -181,8 +181,8 @@ def var_annotations_filter(node: nc.NodeNG) -> nc.NodeNG:
for dec in fnode.decorators.nodes:
# Look for dataclassio.ioprepped.
if (isinstance(dec, astroid.nodes.Attribute)
and dec.attrname == 'ioprepped'
if (isinstance(dec, astroid.nodes.Attribute) and
dec.attrname in {'ioprepped', 'will_ioprep'}
and isinstance(dec.expr, astroid.nodes.Name)
and dec.expr.name == 'dataclassio'):
found_ioprepped = True
@ -190,7 +190,7 @@ def var_annotations_filter(node: nc.NodeNG) -> nc.NodeNG:
# Look for simply 'ioprepped'.
if (isinstance(dec, astroid.nodes.Name)
and dec.name == 'ioprepped'):
and dec.name in {'ioprepped', 'will_ioprep'}):
found_ioprepped = True
break