last updated on 2021-11-26 for Ballistica version 1.6.6 build 20410
+
last updated on 2021-12-17 for Ballistica version 1.6.6 build 20414
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 let me know. Happy modding!
diff --git a/resources/Makefile b/resources/Makefile
index b6e772d3..39b4411b 100644
--- a/resources/Makefile
+++ b/resources/Makefile
@@ -3,12 +3,12 @@
all: resources
# This section is autogenerated; do not edit by hand.
-#__AUTOGENERATED_PUBLIC_BEGIN__
-#__AUTOGENERATED_PUBLIC_END__
+# __AUTOGENERATED_PUBLIC_BEGIN__
+# __AUTOGENERATED_PUBLIC_END__
# This section is autogenerated; do not edit by hand.
-#__AUTOGENERATED_PRIVATE_BEGIN__
+# __AUTOGENERATED_PRIVATE_BEGIN__
resources: \
../ballisticacore-windows/Generic/BallisticaCore.ico
@@ -26,4 +26,4 @@ efrocache-list:
efrocache-build: resources
-#__AUTOGENERATED_PRIVATE_END__
+# __AUTOGENERATED_PRIVATE_END__
diff --git a/src/ballistica/app/app_config.cc b/src/ballistica/app/app_config.cc
index 8e8c7fa6..b529ec9f 100644
--- a/src/ballistica/app/app_config.cc
+++ b/src/ballistica/app/app_config.cc
@@ -144,6 +144,10 @@ AppConfig::AppConfig() {
SetupEntries();
}
+// Clion think all calls of this are unreachable.
+#pragma clang diagnostic push
+#pragma ide diagnostic ignored "UnreachableCallsOfFunction"
+
template
void AppConfig::CompleteMap(const T& entry_map) {
for (auto&& i : entry_map) {
@@ -165,6 +169,8 @@ void AppConfig::CompleteMap(const T& entry_map) {
#endif
}
+#pragma clang diagnostic pop
+
void AppConfig::SetupEntries() {
// Register all our typed entries.
float_entries_[FloatID::kScreenGamma] = FloatEntry("Screen Gamma", 1.0F);
diff --git a/src/ballistica/ballistica.cc b/src/ballistica/ballistica.cc
index f261b69c..752be6ac 100644
--- a/src/ballistica/ballistica.cc
+++ b/src/ballistica/ballistica.cc
@@ -21,7 +21,7 @@
namespace ballistica {
// These are set automatically via script; don't modify them here.
-const int kAppBuildNumber = 20410;
+const int kAppBuildNumber = 20414;
const char* kAppVersion = "1.6.6";
// Our standalone globals.
diff --git a/src/ballistica/game/player.cc b/src/ballistica/game/player.cc
index d3a5843c..42f044d9 100644
--- a/src/ballistica/game/player.cc
+++ b/src/ballistica/game/player.cc
@@ -291,12 +291,12 @@ void Player::RunInput(InputType type, float value) {
}
} else {
if (arg >= threshold) {
- if (!left_held_ && !up_held_ && !down_held_) {
+ if (!up_held_ && !down_held_) {
right_held_ = true;
RunInput(InputType::kRightPress);
}
} else if (arg <= -threshold) {
- if (!right_held_ && !up_held_ && !down_held_) {
+ if (!up_held_ && !down_held_) {
left_held_ = true;
RunInput(InputType::kLeftPress);
}
@@ -319,12 +319,12 @@ void Player::RunInput(InputType type, float value) {
}
} else {
if (arg <= -threshold) {
- if (!left_held_ && !right_held_ && !up_held_) {
+ if (!left_held_ && !right_held_) {
down_held_ = true;
RunInput(InputType::kDownPress);
}
} else if (arg >= threshold) {
- if (!left_held_ && !up_held_ && !right_held_) {
+ if (!left_held_ && !right_held_) {
up_held_ = true;
RunInput(InputType::kUpPress);
}
diff --git a/src/ballistica/generic/timer_list.cc b/src/ballistica/generic/timer_list.cc
index 964b36e3..533b6c0b 100644
--- a/src/ballistica/generic/timer_list.cc
+++ b/src/ballistica/generic/timer_list.cc
@@ -176,8 +176,14 @@ auto TimerList::NewTimer(TimerMedium current_time, TimerMedium length,
auto* t = new Timer(this, next_timer_id_++, current_time, length, offset,
repeat_count);
t->runnable_ = runnable;
+
+ // Clion (correctly) points out that t may get deallocated in this call,
+ // but the call returns nullptr in that case.
+#pragma clang diagnostic push
+#pragma ide diagnostic ignored "DanglingPointer"
t = SubmitTimer(t);
return t;
+#pragma clang diagnostic pop
}
auto TimerList::GetTimeToNextExpire(TimerMedium current_time) -> TimerMedium {
@@ -229,7 +235,9 @@ auto TimerList::SubmitTimer(Timer* t) -> Timer* {
return nullptr;
} else {
// Its still alive. Shove it back in line and tell it to keep working.
- if (!t->initial_ && t->repeat_count_ > 0) t->repeat_count_--;
+ if (!t->initial_ && t->repeat_count_ > 0) {
+ t->repeat_count_--;
+ }
t->initial_ = false;
// No drift.
diff --git a/src/ballistica/graphics/graphics_server.cc b/src/ballistica/graphics/graphics_server.cc
index 25c0cf22..91ad9dd5 100644
--- a/src/ballistica/graphics/graphics_server.cc
+++ b/src/ballistica/graphics/graphics_server.cc
@@ -127,21 +127,31 @@ void GraphicsServer::PreprocessRenderFrameDef(FrameDef* frame_def) {
assert(InGraphicsThread());
// Now let the renderer do any preprocess passes (shadows, etc).
- renderer_->PreprocessFrameDef(frame_def);
+ assert(renderer_);
+ if (renderer_ != nullptr) {
+ renderer_->PreprocessFrameDef(frame_def);
+ }
}
// Does the default drawing to the screen, either from the left or right stereo
// eye or in mono.
void GraphicsServer::DrawRenderFrameDef(FrameDef* frame_def, int eye) {
- renderer_->RenderFrameDef(frame_def);
+ assert(renderer_);
+ if (renderer_) {
+ renderer_->RenderFrameDef(frame_def);
+ }
}
// Clean up the frame_def once done drawing it.
void GraphicsServer::FinishRenderFrameDef(FrameDef* frame_def) {
- renderer_->FinishFrameDef(frame_def);
+ assert(renderer_);
+ if (renderer_) {
+ renderer_->FinishFrameDef(frame_def);
- // Let the app know a frame render is complete (it may need to do a swap/etc).
- g_app->DidFinishRenderingFrame(frame_def);
+ // Let the app know a frame render is complete (it may need to do a
+ // swap/etc).
+ g_app->DidFinishRenderingFrame(frame_def);
+ }
}
void GraphicsServer::TryRender() {
@@ -514,6 +524,10 @@ void GraphicsServer::VideoResize(float h, float v) {
// FIXME: Shouldn't have android-specific code in here.
void GraphicsServer::HandlePushAndroidRes(const std::string& android_res) {
if (g_buildconfig.ostype_android()) {
+ assert(renderer_);
+ if (renderer_ == nullptr) {
+ return;
+ }
// We push android res to the java layer here. We don't actually worry
// about screen-size-changed callbacks and whatnot, since those will happen
// automatically once things actually change. We just want to be sure that
diff --git a/src/ballistica/python/class/python_class_collide_model.cc b/src/ballistica/python/class/python_class_collide_model.cc
index ece54383..ca73aa7a 100644
--- a/src/ballistica/python/class/python_class_collide_model.cc
+++ b/src/ballistica/python/class/python_class_collide_model.cc
@@ -54,6 +54,12 @@ auto PythonClassCollideModel::GetCollideModel(bool doraise) const
return collide_model;
}
+// Clion makes some incorrect inferences here.
+#pragma clang diagnostic push
+#pragma ide diagnostic ignored "UnreachableCode"
+#pragma ide diagnostic ignored "ConstantConditionsOC"
+#pragma ide diagnostic ignored "ConstantFunctionResult"
+
auto PythonClassCollideModel::tp_new(PyTypeObject* type, PyObject* args,
PyObject* kwds) -> PyObject* {
auto* self =
@@ -67,10 +73,6 @@ auto PythonClassCollideModel::tp_new(PyTypeObject* type, PyObject* args,
+ GetCurrentThreadName() + ").");
}
-// Clion incorrectly things s_create_empty will always be false.
-#pragma clang diagnostic push
-#pragma ide diagnostic ignored "UnreachableCode"
-#pragma ide diagnostic ignored "ConstantConditionsOC"
if (!s_create_empty_) {
throw Exception(
"Can't instantiate CollideModels directly; use "
@@ -78,11 +80,12 @@ auto PythonClassCollideModel::tp_new(PyTypeObject* type, PyObject* args,
}
self->collide_model_ = new Object::Ref();
BA_PYTHON_NEW_CATCH;
-#pragma clang diagnostic pop
}
return reinterpret_cast(self);
}
+#pragma clang diagnostic pop
+
void PythonClassCollideModel::Delete(Object::Ref* ref) {
assert(InGameThread());
// if we're the py-object for a collide_model, clear them out
diff --git a/src/ballistica/python/class/python_class_data.cc b/src/ballistica/python/class/python_class_data.cc
index ef11bc49..5dbee6b9 100644
--- a/src/ballistica/python/class/python_class_data.cc
+++ b/src/ballistica/python/class/python_class_data.cc
@@ -52,6 +52,11 @@ auto PythonClassData::GetData(bool doraise) const -> Data* {
}
return data;
}
+// Clion makes some incorrect inferences here.
+#pragma clang diagnostic push
+#pragma ide diagnostic ignored "UnreachableCode"
+#pragma ide diagnostic ignored "ConstantConditionsOC"
+#pragma ide diagnostic ignored "ConstantFunctionResult"
auto PythonClassData::tp_new(PyTypeObject* type, PyObject* args, PyObject* kwds)
-> PyObject* {
@@ -65,10 +70,6 @@ auto PythonClassData::tp_new(PyTypeObject* type, PyObject* args, PyObject* kwds)
+ GetCurrentThreadName() + ").");
}
- // Clion incorrectly things s_create_empty will always be false.
-#pragma clang diagnostic push
-#pragma ide diagnostic ignored "UnreachableCode"
-#pragma ide diagnostic ignored "ConstantConditionsOC"
if (!s_create_empty_) {
throw Exception(
"Can't instantiate Datas directly; use ba.getdata() to get "
@@ -76,10 +77,10 @@ auto PythonClassData::tp_new(PyTypeObject* type, PyObject* args, PyObject* kwds)
}
self->data_ = new Object::Ref();
BA_PYTHON_NEW_CATCH;
-#pragma clang diagnostic pop
}
return reinterpret_cast(self);
}
+#pragma clang diagnostic pop
void PythonClassData::Delete(Object::Ref* ref) {
assert(InGameThread());
diff --git a/src/ballistica/python/class/python_class_model.cc b/src/ballistica/python/class/python_class_model.cc
index fc2c7406..5680716c 100644
--- a/src/ballistica/python/class/python_class_model.cc
+++ b/src/ballistica/python/class/python_class_model.cc
@@ -53,6 +53,12 @@ auto PythonClassModel::GetModel(bool doraise) const -> Model* {
return model;
}
+// Clion makes some incorrect inferences here.
+#pragma clang diagnostic push
+#pragma ide diagnostic ignored "UnreachableCode"
+#pragma ide diagnostic ignored "ConstantConditionsOC"
+#pragma ide diagnostic ignored "ConstantFunctionResult"
+
auto PythonClassModel::tp_new(PyTypeObject* type, PyObject* args,
PyObject* kwds) -> PyObject* {
auto* self = reinterpret_cast(type->tp_alloc(type, 0));
@@ -64,10 +70,6 @@ auto PythonClassModel::tp_new(PyTypeObject* type, PyObject* args,
+ " objects must only be created in the game thread (current is ("
+ GetCurrentThreadName() + ").");
}
- // Clion incorrectly things s_create_empty will always be false.
-#pragma clang diagnostic push
-#pragma ide diagnostic ignored "UnreachableCode"
-#pragma ide diagnostic ignored "ConstantConditionsOC"
if (!s_create_empty_) {
throw Exception(
"Can't instantiate Models directly; use ba.getmodel() to get "
@@ -75,11 +77,12 @@ auto PythonClassModel::tp_new(PyTypeObject* type, PyObject* args,
}
self->model_ = new Object::Ref();
BA_PYTHON_NEW_CATCH;
-#pragma clang diagnostic pop
}
return reinterpret_cast(self);
}
+#pragma clang diagnostic pop
+
void PythonClassModel::Delete(Object::Ref* ref) {
assert(InGameThread());
diff --git a/src/ballistica/python/class/python_class_sound.cc b/src/ballistica/python/class/python_class_sound.cc
index 74e4e098..171dce90 100644
--- a/src/ballistica/python/class/python_class_sound.cc
+++ b/src/ballistica/python/class/python_class_sound.cc
@@ -52,6 +52,12 @@ auto PythonClassSound::GetSound(bool doraise) const -> Sound* {
return sound;
}
+// Clion makes some incorrect inferences here.
+#pragma clang diagnostic push
+#pragma ide diagnostic ignored "UnreachableCode"
+#pragma ide diagnostic ignored "ConstantConditionsOC"
+#pragma ide diagnostic ignored "ConstantFunctionResult"
+
auto PythonClassSound::tp_new(PyTypeObject* type, PyObject* args,
PyObject* kwds) -> PyObject* {
auto* self = reinterpret_cast(type->tp_alloc(type, 0));
@@ -63,10 +69,6 @@ auto PythonClassSound::tp_new(PyTypeObject* type, PyObject* args,
+ " objects must only be created in the game thread (current is ("
+ GetCurrentThreadName() + ").");
}
- // Clion incorrectly things s_create_empty will always be false.
-#pragma clang diagnostic push
-#pragma ide diagnostic ignored "UnreachableCode"
-#pragma ide diagnostic ignored "ConstantConditionsOC"
if (!s_create_empty_) {
throw Exception(
"Can't instantiate Sounds directly; use ba.getsound() to get "
@@ -74,11 +76,12 @@ auto PythonClassSound::tp_new(PyTypeObject* type, PyObject* args,
}
self->sound_ = new Object::Ref();
BA_PYTHON_NEW_CATCH;
-#pragma clang diagnostic pop
}
return reinterpret_cast(self);
}
+#pragma clang diagnostic pop
+
void PythonClassSound::Delete(Object::Ref* ref) {
assert(InGameThread());
diff --git a/src/ballistica/python/class/python_class_widget.cc b/src/ballistica/python/class/python_class_widget.cc
index 11d35d6b..5f834091 100644
--- a/src/ballistica/python/class/python_class_widget.cc
+++ b/src/ballistica/python/class/python_class_widget.cc
@@ -139,7 +139,13 @@ auto PythonClassWidget::GetChildren(PythonClassWidget* self) -> PyObject* {
}
PyObject* py_list = PyList_New(0);
auto* cw = dynamic_cast(w);
+
+ // Clion seems to think dynamic_casting a Widget* to a ContainerWidget*
+ // will always succeed. Go home Clion; you're drunk.
+#pragma clang diagnostic push
+#pragma ide diagnostic ignored "ConstantConditionsOC"
if (cw) {
+#pragma clang diagnostic pop
for (auto&& i : cw->widgets()) {
assert(i.exists());
PyList_Append(py_list, i->BorrowPyRef());
@@ -156,7 +162,13 @@ auto PythonClassWidget::GetSelectedChild(PythonClassWidget* self) -> PyObject* {
throw Exception(PyExcType::kWidgetNotFound);
}
auto* cw = dynamic_cast(w);
+
+ // Clion seems to think dynamic_casting a Widget* to a ContainerWidget*
+ // will always succeed. Go home Clion; you're drunk.
+#pragma clang diagnostic push
+#pragma ide diagnostic ignored "ConstantConditionsOC"
if (cw) {
+#pragma clang diagnostic pop
Widget* selected_widget = cw->selected_widget();
if (selected_widget) return selected_widget->NewPyRef();
}
diff --git a/src/meta/Makefile b/src/meta/Makefile
index 3d806b43..7336a97c 100644
--- a/src/meta/Makefile
+++ b/src/meta/Makefile
@@ -9,7 +9,7 @@ clean:
# This section is autogenerated; do not edit by hand.
-#__AUTOGENERATED_PUBLIC_BEGIN__
+# __AUTOGENERATED_PUBLIC_BEGIN__
sources: \
../../assets/src/ba_data/python/ba/_generated/__init__.py \
@@ -29,10 +29,10 @@ sources: \
../../assets/src/ba_data/python/ba/_generated/enums.py : ../ballistica/core/types.h ../../tools/batools/pythonenumsmodule.py
@$(PCOMMAND) gen_python_enums_module $< $@
-#__AUTOGENERATED_PUBLIC_END__
+# __AUTOGENERATED_PUBLIC_END__
# This section is autogenerated; do not edit by hand.
-#__AUTOGENERATED_PRIVATE_BEGIN__
+# __AUTOGENERATED_PRIVATE_BEGIN__
# Note: we include our public targets in efrocache even
# though they are buildable in public. This allows us to
@@ -47,4 +47,4 @@ efrocache-list:
efrocache-build: sources
-#__AUTOGENERATED_PRIVATE_END__
+# __AUTOGENERATED_PRIVATE_END__
diff --git a/tests/test_efro/test_message.py b/tests/test_efro/test_message.py
index cf8eb7aa..4153fb5e 100644
--- a/tests/test_efro/test_message.py
+++ b/tests/test_efro/test_message.py
@@ -429,7 +429,7 @@ def test_protocol_creation() -> None:
response_types={0: _TResp1})
-def test_sender_module_single_embedded() -> None:
+def test_sender_module_single_emb() -> None:
"""Test generation of protocol-specific sender modules for typing/etc."""
# NOTE: Ideally we should be testing efro.message.create_sender_module()
# here, but it requires us to pass code which imports this test module
@@ -462,7 +462,7 @@ def test_sender_module_single_embedded() -> None:
' See test stdout for new code.')
-def test_sender_module_sync_embedded() -> None:
+def test_sender_module_sync_emb() -> None:
"""Test generation of protocol-specific sender modules for typing/etc."""
# NOTE: Ideally we should be testing efro.message.create_sender_module()
# here, but it requires us to pass code which imports this test module
@@ -495,7 +495,7 @@ def test_sender_module_sync_embedded() -> None:
' See test stdout for new code.')
-def test_sender_module_async_embedded() -> None:
+def test_sender_module_async_emb() -> None:
"""Test generation of protocol-specific sender modules for typing/etc."""
# NOTE: Ideally we should be testing efro.message.create_sender_module()
# here, but it requires us to pass code which imports this test module
@@ -528,7 +528,7 @@ def test_sender_module_async_embedded() -> None:
' See test stdout for new code.')
-def test_sender_module_both_embedded() -> None:
+def test_sender_module_both_emb() -> None:
"""Test generation of protocol-specific sender modules for typing/etc."""
# NOTE: Ideally we should be testing efro.message.create_sender_module()
# here, but it requires us to pass code which imports this test module
@@ -561,7 +561,7 @@ def test_sender_module_both_embedded() -> None:
' See test stdout for new code.')
-def test_receiver_module_single_embedded() -> None:
+def test_receiver_module_single_emb() -> None:
"""Test generation of protocol-specific sender modules for typing/etc."""
# NOTE: Ideally we should be testing efro.message.create_receiver_module()
# here, but it requires us to pass code which imports this test module
@@ -595,7 +595,7 @@ def test_receiver_module_single_embedded() -> None:
' See test stdout for new code.')
-def test_receiver_module_sync_embedded() -> None:
+def test_receiver_module_sync_emb() -> None:
"""Test generation of protocol-specific sender modules for typing/etc."""
# NOTE: Ideally we should be testing efro.message.create_receiver_module()
# here, but it requires us to pass code which imports this test module
@@ -628,7 +628,7 @@ def test_receiver_module_sync_embedded() -> None:
' See test stdout for new code.')
-def test_receiver_module_async_embedded() -> None:
+def test_receiver_module_async_emb() -> None:
"""Test generation of protocol-specific sender modules for typing/etc."""
# NOTE: Ideally we should be testing efro.message.create_receiver_module()
# here, but it requires us to pass code which imports this test module
diff --git a/tools/batools/assetsmakefile.py b/tools/batools/assetsmakefile.py
index f6604ac9..c65029c7 100755
--- a/tools/batools/assetsmakefile.py
+++ b/tools/batools/assetsmakefile.py
@@ -173,7 +173,7 @@ def _get_py_targets_subset(all_targets: set[str], subset: str,
' \\\n '.join(pyc_targets) + '\n')
# We transform all non-public targets into efrocache-fetches in public.
- efc = '' if subset.startswith('public') else '#__EFROCACHE_TARGET__\n'
+ efc = '' if subset.startswith('public') else '# __EFROCACHE_TARGET__\n'
out += ('\n# Rule to copy src asset scripts to dst.\n'
'# (and make non-writable so I\'m less likely to '
@@ -259,7 +259,7 @@ def _get_extras_targets_win(all_targets: set[str], platform: str) -> str:
# We transform all these targets into efrocache-fetches in public.
out += ('\n# Rule to copy src extras to build.\n'
- f'#__EFROCACHE_TARGET__\n'
+ f'# __EFROCACHE_TARGET__\n'
f'$(EXTRAS_TARGETS_WIN_{p_up}) : build/% :'
' src/%\n'
'\t@echo Copying file: $@\n'
@@ -287,10 +287,10 @@ def update_assets_makefile(projroot: str, check: bool) -> None:
original = infile.read()
lines = original.splitlines()
- auto_start_public = lines.index('#__AUTOGENERATED_PUBLIC_BEGIN__')
- auto_end_public = lines.index('#__AUTOGENERATED_PUBLIC_END__')
- auto_start_private = lines.index('#__AUTOGENERATED_PRIVATE_BEGIN__')
- auto_end_private = lines.index('#__AUTOGENERATED_PRIVATE_END__')
+ auto_start_public = lines.index('# __AUTOGENERATED_PUBLIC_BEGIN__')
+ auto_end_public = lines.index('# __AUTOGENERATED_PUBLIC_END__')
+ auto_start_private = lines.index('# __AUTOGENERATED_PRIVATE_BEGIN__')
+ auto_end_private = lines.index('# __AUTOGENERATED_PRIVATE_END__')
all_targets_public: set[str] = set()
all_targets_private: set[str] = set()
diff --git a/tools/batools/build.py b/tools/batools/build.py
index 0508bd22..edbefb6a 100644
--- a/tools/batools/build.py
+++ b/tools/batools/build.py
@@ -32,7 +32,7 @@ class PipRequirement:
# entries; this accounts for manual installations or other nonstandard setups.
PIP_REQUIREMENTS = [
PipRequirement(modulename='pylint', minversion=[2, 12, 1]),
- PipRequirement(modulename='mypy', minversion=[0, 910]),
+ PipRequirement(modulename='mypy', minversion=[0, 920]),
PipRequirement(modulename='yapf', minversion=[0, 31, 0]),
PipRequirement(modulename='cpplint', minversion=[1, 5, 5]),
PipRequirement(modulename='pytest', minversion=[6, 2, 4]),
@@ -788,7 +788,7 @@ def filter_server_config(projroot: str, infilepath: str) -> str:
"""Add commented-out config options to a server config."""
with open(infilepath, encoding='utf-8') as infile:
cfg = infile.read()
- return cfg.replace('#__CONFIG_TEMPLATE_VALUES__',
+ return cfg.replace('# __CONFIG_TEMPLATE_VALUES__',
_get_server_config_template_yaml(projroot))
diff --git a/tools/batools/metamakefile.py b/tools/batools/metamakefile.py
index fd157ebb..7a7c240d 100755
--- a/tools/batools/metamakefile.py
+++ b/tools/batools/metamakefile.py
@@ -191,10 +191,10 @@ def update(projroot: str, check: bool) -> None:
all_dsts_public: set[str] = set()
all_dsts_private: set[str] = set()
- auto_start_public = lines.index('#__AUTOGENERATED_PUBLIC_BEGIN__')
- auto_end_public = lines.index('#__AUTOGENERATED_PUBLIC_END__')
- auto_start_private = lines.index('#__AUTOGENERATED_PRIVATE_BEGIN__')
- auto_end_private = lines.index('#__AUTOGENERATED_PRIVATE_END__')
+ auto_start_public = lines.index('# __AUTOGENERATED_PUBLIC_BEGIN__')
+ auto_end_public = lines.index('# __AUTOGENERATED_PUBLIC_END__')
+ auto_start_private = lines.index('# __AUTOGENERATED_PRIVATE_BEGIN__')
+ auto_end_private = lines.index('# __AUTOGENERATED_PRIVATE_END__')
# Public targets (full sources available in public)
targets: list[Target] = []
@@ -219,7 +219,7 @@ def update(projroot: str, check: bool) -> None:
our_lines_private_1 = (
_empty_line_if(bool(targets)) +
_emit_group_build_lines(targets, basename) +
- ['#__EFROCACHE_TARGET__\n' + t.emit() for t in targets] + [
+ ['# __EFROCACHE_TARGET__\n' + t.emit() for t in targets] + [
'\n# Note: we include our public targets in efrocache even\n'
'# though they are buildable in public. This allows us to\n'
'# fetch them on Windows to bootstrap binary CI builds in\n'
@@ -231,11 +231,11 @@ def update(projroot: str, check: bool) -> None:
targets = []
basename = 'private-internal'
_add_python_embedded_targets_internal(targets)
- our_lines_private_2 = (['#__PUBSYNC_STRIP_BEGIN__'] +
+ our_lines_private_2 = (['# __PUBSYNC_STRIP_BEGIN__'] +
_empty_line_if(bool(targets)) +
_emit_group_build_lines(targets, basename) +
- [t.emit()
- for t in targets] + ['#__PUBSYNC_STRIP_END__'])
+ [t.emit() for t in targets] +
+ ['# __PUBSYNC_STRIP_END__'])
our_lines_private = our_lines_private_1 + our_lines_private_2
filtered = (lines[:auto_start_public + 1] + our_lines_public +
diff --git a/tools/batools/resourcesmakefile.py b/tools/batools/resourcesmakefile.py
index 31048bdd..ab59bebd 100755
--- a/tools/batools/resourcesmakefile.py
+++ b/tools/batools/resourcesmakefile.py
@@ -366,10 +366,10 @@ def update(projroot: str, check: bool) -> None:
original = infile.read()
lines = original.splitlines()
- auto_start_public = lines.index('#__AUTOGENERATED_PUBLIC_BEGIN__')
- auto_end_public = lines.index('#__AUTOGENERATED_PUBLIC_END__')
- auto_start_private = lines.index('#__AUTOGENERATED_PRIVATE_BEGIN__')
- auto_end_private = lines.index('#__AUTOGENERATED_PRIVATE_END__')
+ auto_start_public = lines.index('# __AUTOGENERATED_PUBLIC_BEGIN__')
+ auto_end_public = lines.index('# __AUTOGENERATED_PUBLIC_END__')
+ auto_start_private = lines.index('# __AUTOGENERATED_PRIVATE_BEGIN__')
+ auto_end_private = lines.index('# __AUTOGENERATED_PRIVATE_END__')
# Public targets (full sources available in public)
targets: list[Target] = []
@@ -392,7 +392,7 @@ def update(projroot: str, check: bool) -> None:
_empty_line_if(bool(targets)) +
_emit_group_build_lines(targets, basename) +
_emit_group_clean_lines(targets, basename) +
- ['#__EFROCACHE_TARGET__\n' + t.emit()
+ ['# __EFROCACHE_TARGET__\n' + t.emit()
for t in targets] + _emit_group_efrocache_lines(targets))
# Private-internal targets (not available at all in public)
@@ -410,12 +410,12 @@ def update(projroot: str, check: bool) -> None:
_add_apple_tv_3d_icon(targets)
_add_apple_tv_store_icon(targets)
_add_google_vr_icon(targets)
- our_lines_private_2 = (['#__PUBSYNC_STRIP_BEGIN__'] +
+ our_lines_private_2 = (['# __PUBSYNC_STRIP_BEGIN__'] +
_empty_line_if(bool(targets)) +
_emit_group_build_lines(targets, basename) +
_emit_group_clean_lines(targets, basename) +
- [t.emit()
- for t in targets] + ['#__PUBSYNC_STRIP_END__'])
+ [t.emit() for t in targets] +
+ ['# __PUBSYNC_STRIP_END__'])
our_lines_private = our_lines_private_1 + our_lines_private_2
filtered = (lines[:auto_start_public + 1] + our_lines_public +
diff --git a/tools/efro/dataclassio/__init__.py b/tools/efro/dataclassio/__init__.py
index 82e46ade..896d65a1 100644
--- a/tools/efro/dataclassio/__init__.py
+++ b/tools/efro/dataclassio/__init__.py
@@ -45,7 +45,7 @@ def dataclass_to_dict(obj: Any,
the ability to do a lossless round-trip with data).
If coerce_to_float is True, integer values present on float typed fields
- will be converted to floats in the dict output. If False, a TypeError
+ will be converted to float in the dict output. If False, a TypeError
will be triggered.
"""
@@ -94,10 +94,10 @@ def dataclass_from_dict(cls: type[T],
(as this would break the ability to do a lossless round-trip with data).
If coerce_to_float is True, int values passed for float typed fields
- will be converted to float values. Otherwise a TypeError is raised.
+ will be converted to float values. Otherwise, a TypeError is raised.
If allow_unknown_attrs is False, AttributeErrors will be raised for
- attributes present in the dict but not on the data class. Otherwise they
+ attributes present in the dict but not on the data class. Otherwise, they
will be preserved as part of the instance and included if it is
exported back to a dict, unless discard_unknown_attrs is True, in which
case they will simply be discarded.
diff --git a/tools/efro/dataclassio/_base.py b/tools/efro/dataclassio/_base.py
index 492a8d46..6240918b 100644
--- a/tools/efro/dataclassio/_base.py
+++ b/tools/efro/dataclassio/_base.py
@@ -14,7 +14,7 @@ from typing import _AnnotatedAlias # type: ignore
_pytz_utc: Any
-# We don't *require* pytz but we want to support it for tzinfos if available.
+# We don't *require* pytz, but we want to support it for tzinfos if available.
try:
import pytz
_pytz_utc = pytz.utc
@@ -126,7 +126,7 @@ class IOAttrs:
# Turning off store_default requires the field to have either
# a default_factory or a default
if not self.store_default:
- default_factory: Any = field.default_factory # type: ignore
+ default_factory: Any = field.default_factory
if (default_factory is dataclasses.MISSING
and field.default is dataclasses.MISSING):
raise TypeError(f'Field {field.name} of {cls} has'
@@ -163,7 +163,7 @@ def _get_origin(anntype: Any) -> Any:
def _parse_annotated(anntype: Any) -> tuple[Any, Optional[IOAttrs]]:
"""Parse Annotated() constructs, returning annotated type & IOAttrs."""
# If we get an Annotated[foo, bar, eep] we take
- # foo as the actual type and we look for IOAttrs instances in
+ # foo as the actual type, and we look for IOAttrs instances in
# bar/eep to affect our behavior.
ioattrs: Optional[IOAttrs] = None
if isinstance(anntype, _AnnotatedAlias):
diff --git a/tools/efro/dataclassio/_inputter.py b/tools/efro/dataclassio/_inputter.py
index 911fe4f0..dfa0c9b0 100644
--- a/tools/efro/dataclassio/_inputter.py
+++ b/tools/efro/dataclassio/_inputter.py
@@ -70,14 +70,14 @@ class _Inputter(Generic[T]):
return value
if origin is typing.Union:
- # Currently the only unions we support are None/Value
+ # Currently, the only unions we support are None/Value
# (translated from Optional), which we verified on prep.
# So let's treat this as a simple optional case.
if value is None:
return None
childanntypes_l = [
c for c in typing.get_args(anntype) if c is not type(None)
- ]
+ ] # noqa (pycodestyle complains about *is* with type)
assert len(childanntypes_l) == 1
return self._value_from_input(cls, fieldpath, childanntypes_l[0],
value, ioattrs)
@@ -127,7 +127,7 @@ class _Inputter(Generic[T]):
"""Given input data, returns bytes."""
import base64
- # For firestore, bytes are passed as-is. Otherwise they're encoded
+ # For firestore, bytes are passed as-is. Otherwise, they're encoded
# as base64.
if self._codec is Codec.FIRESTORE:
if not isinstance(value, bytes):
@@ -268,7 +268,7 @@ class _Inputter(Generic[T]):
cls, fieldpath, valanntype, val, ioattrs)
elif issubclass(keyanntype, Enum):
- # In prep we verified that all these enums' values have
+ # In prep, we verified that all these enums' values have
# the same type, so we can just look at the first to see if
# this is a string enum or an int enum.
enumvaltype = type(next(iter(keyanntype)).value)
diff --git a/tools/efro/dataclassio/_outputter.py b/tools/efro/dataclassio/_outputter.py
index 9dc193e8..9069bc08 100644
--- a/tools/efro/dataclassio/_outputter.py
+++ b/tools/efro/dataclassio/_outputter.py
@@ -60,7 +60,7 @@ class _Outputter:
# If we're not storing default values for this fella,
# we can skip all output processing if we've got a default value.
if ioattrs is not None and not ioattrs.store_default:
- default_factory: Any = field.default_factory # type: ignore
+ default_factory: Any = field.default_factory
if default_factory is not dataclasses.MISSING:
if default_factory() == value:
continue
@@ -113,14 +113,14 @@ class _Outputter:
return value if self._create else None
if origin is typing.Union:
- # Currently the only unions we support are None/Value
+ # Currently, the only unions we support are None/Value
# (translated from Optional), which we verified on prep.
# So let's treat this as a simple optional case.
if value is None:
return None
childanntypes_l = [
c for c in typing.get_args(anntype) if c is not type(None)
- ]
+ ] # noqa (pycodestyle complains about *is* with type)
assert len(childanntypes_l) == 1
return self._process_value(cls, fieldpath, childanntypes_l[0],
value, ioattrs)
diff --git a/tools/efro/dataclassio/_pathcapture.py b/tools/efro/dataclassio/_pathcapture.py
index d325b6a9..3d90647b 100644
--- a/tools/efro/dataclassio/_pathcapture.py
+++ b/tools/efro/dataclassio/_pathcapture.py
@@ -75,7 +75,7 @@ class DataclassFieldLookup(Generic[T]):
# We tell the type system that we are returning an instance
# of our class, which allows it to perform type checking on
# member lookups. In reality, however, we are providing a
- # special object which captures path lookups so we can build
+ # special object which captures path lookups, so we can build
# a string from them.
if not TYPE_CHECKING:
out = callback(_PathCapture(self.cls))
diff --git a/tools/efro/dataclassio/_prep.py b/tools/efro/dataclassio/_prep.py
index 55cc814f..7cc73512 100644
--- a/tools/efro/dataclassio/_prep.py
+++ b/tools/efro/dataclassio/_prep.py
@@ -303,7 +303,7 @@ class PrepSession:
"""Run prep on a Union type."""
typeargs = typing.get_args(anntype)
if (len(typeargs) != 2
- or len([c for c in typeargs if c is type(None)]) != 1):
+ or len([c for c in typeargs if c is type(None)]) != 1): # noqa
raise TypeError(f'Union {anntype} for attr \'{attrname}\' on'
f' {cls.__name__} is not supported by dataclassio;'
f' only 2 member Unions with one type being None'
diff --git a/tools/efro/message.py b/tools/efro/message.py
index 8d9bbfcc..f6f48b71 100644
--- a/tools/efro/message.py
+++ b/tools/efro/message.py
@@ -766,7 +766,7 @@ class MessageReceiver:
# Return type of None translates to EmptyResponse.
responsetypes = tuple(EmptyResponse if r is type(None) else r
- for r in responsetypes)
+ for r in responsetypes) # noqa
# Make sure our protocol has this message type registered and our
# return types exactly match. (Technically we could return a subset
diff --git a/tools/efro/util.py b/tools/efro/util.py
index 147b18c8..c4c54c2e 100644
--- a/tools/efro/util.py
+++ b/tools/efro/util.py
@@ -579,6 +579,8 @@ def human_readable_compact_id(num: int) -> str:
'o' is excluded due to similarity to '0'.
'z' is excluded due to similarity to '2'.
+ Therefore for n chars this can store values of 21^n.
+
When reading human input consisting of these IDs, it may be desirable
to map the disallowed chars to their corresponding allowed ones
('o' -> '0', etc).
@@ -599,6 +601,8 @@ def compact_id(num: int) -> str:
friendly to humans due to using both capital and lowercase letters,
both 'O' and '0', etc.
+ Therefore for n chars this can store values of 62^n.
+
Sort order for these ids is the same as the original numbers.
"""
return _compact_id(
@@ -612,3 +616,24 @@ def assert_never(value: NoReturn) -> NoReturn:
See https://github.com/python/typing/issues/735
"""
assert False, f'Unhandled value: {value} ({type(value).__name__})'
+
+
+def unchanging_hostname() -> str:
+ """Return an unchanging name for the local device.
+
+ Similar to the `hostname` call (or os.uname().nodename in Python)
+ except attempts to give a name that doesn't change depending on
+ network conditions. (A Mac will tend to go from Foo to Foo.local,
+ Foo.lan etc. throughout its various adventures)
+ """
+ import os
+ import platform
+ import subprocess
+
+ # On Mac, this should give the computer name assigned in System Prefs.
+ if platform.system() == 'Darwin':
+ return subprocess.run(
+ ['scutil', '--get', 'ComputerName'],
+ check=True,
+ capture_output=True).stdout.decode().strip().replace(' ', '-')
+ return os.uname().nodename
diff --git a/tools/efrotools/__init__.py b/tools/efrotools/__init__.py
index d39baec3..6f1e964d 100644
--- a/tools/efrotools/__init__.py
+++ b/tools/efrotools/__init__.py
@@ -172,8 +172,8 @@ def py_examine(projroot: Path, filename: Path, line: int, column: int,
symbol = (selection if selection is not None else _py_symbol_at_column(
flines[line - 1], column))
- # Insert a line after the provided one which is just the symbol so we
- # can ask for its value alone.
+ # Insert a line after the provided one which is just the symbol so
+ # that we can ask for its value alone.
match = re.match(r'\s*', flines[line - 1])
whitespace = match.group() if match is not None else ''
sline = whitespace + symbol + ' #@'
@@ -187,8 +187,8 @@ def py_examine(projroot: Path, filename: Path, line: int, column: int,
symbol = (selection if selection is not None else _py_symbol_at_column(
flines[line - 1], column))
- # Insert a line after the provided one which is just the symbol so we
- # can ask for its value alone.
+ # Insert a line after the provided one which is just the symbol so
+ # that we can ask for its value alone.
match = re.match(r'\s*', flines[line - 1])
whitespace = match.group() if match is not None else ''
if operation == 'mypy_infer':
diff --git a/tools/efrotools/efrocache.py b/tools/efrotools/efrocache.py
index 11458210..01b52da8 100644
--- a/tools/efrotools/efrocache.py
+++ b/tools/efrotools/efrocache.py
@@ -24,7 +24,7 @@ if TYPE_CHECKING:
BASE_URL = 'https://files.ballistica.net/cache/ba1/'
-TARGET_TAG = '#__EFROCACHE_TARGET__'
+TARGET_TAG = '# __EFROCACHE_TARGET__'
CACHE_DIR_NAME = '.efrocache'
CACHE_MAP_NAME = '.efrocachemap'
diff --git a/tools/efrotools/filecommand.py b/tools/efrotools/filecommand.py
index 2cd3385a..136f2f7d 100644
--- a/tools/efrotools/filecommand.py
+++ b/tools/efrotools/filecommand.py
@@ -10,7 +10,7 @@ from threading import Condition, Thread
import os
if TYPE_CHECKING:
- from typing import Generator, Optional, Callable
+ from typing import Iterable, Optional, Callable
class _FileBatchesRun:
@@ -107,7 +107,7 @@ def file_batches(
batch_size: int = 1,
file_filter: Optional[Callable[[str], bool]] = None,
include_mac_packages: bool = False,
-) -> Generator[list[str], None, None]:
+) -> Iterable[list[str]]:
"""Efficiently yield batches of files to operate on.
Accepts a list of paths which can be files or directories to be recursed.