diff --git a/.efrocachemap b/.efrocachemap
index 0f91c73b..8fb8c98b 100644
--- a/.efrocachemap
+++ b/.efrocachemap
@@ -420,7 +420,7 @@
"assets/build/ba_data/audio/zoeOw.ogg": "https://files.ballistica.net/cache/ba1/14/f1/4f2995d78fc20dd79dfb39c5d554",
"assets/build/ba_data/audio/zoePickup01.ogg": "https://files.ballistica.net/cache/ba1/57/ac/6ed0caecd25dc23688debed24c45",
"assets/build/ba_data/audio/zoeScream01.ogg": "https://files.ballistica.net/cache/ba1/32/08/38dac4a79ab2acee76a75d32a310",
- "assets/build/ba_data/data/langdata.json": "https://files.ballistica.net/cache/ba1/1a/7a/d5d4fbaedd83708b8891c82c1eac",
+ "assets/build/ba_data/data/langdata.json": "https://files.ballistica.net/cache/ba1/97/0d/5879ed6101537373f87deeccf860",
"assets/build/ba_data/data/languages/arabic.json": "https://files.ballistica.net/cache/ba1/0f/0e/7184059414320d32104463e41038",
"assets/build/ba_data/data/languages/belarussian.json": "https://files.ballistica.net/cache/ba1/e2/58/c2c5964370df118c51528dc4bfa2",
"assets/build/ba_data/data/languages/chinese.json": "https://files.ballistica.net/cache/ba1/0c/40/6222070dc95b29e42b77dd105357",
@@ -440,13 +440,13 @@
"assets/build/ba_data/data/languages/indonesian.json": "https://files.ballistica.net/cache/ba1/87/e5/a10ddd73cfb7996bbd576032db6a",
"assets/build/ba_data/data/languages/italian.json": "https://files.ballistica.net/cache/ba1/34/0f/dd2e311024ceb913b8489b823fdc",
"assets/build/ba_data/data/languages/korean.json": "https://files.ballistica.net/cache/ba1/26/8d/bf9cc8db2cc71b69e789898e1093",
- "assets/build/ba_data/data/languages/persian.json": "https://files.ballistica.net/cache/ba1/02/9a/1e376aaccff29520874e1127e1c7",
+ "assets/build/ba_data/data/languages/persian.json": "https://files.ballistica.net/cache/ba1/4f/23/b9692ca7f9407972254fb245ffb0",
"assets/build/ba_data/data/languages/polish.json": "https://files.ballistica.net/cache/ba1/2e/17/fb3e7ed77fa54427b434b1791793",
"assets/build/ba_data/data/languages/portuguese.json": "https://files.ballistica.net/cache/ba1/6c/04/a528a4df9364ad4f0261cbc83f0a",
"assets/build/ba_data/data/languages/romanian.json": "https://files.ballistica.net/cache/ba1/82/12/57bf144e12be229a9b70da9c45cb",
"assets/build/ba_data/data/languages/russian.json": "https://files.ballistica.net/cache/ba1/bb/67/bdf5e720897c0eb966acdbab4274",
"assets/build/ba_data/data/languages/serbian.json": "https://files.ballistica.net/cache/ba1/91/1e/2846b8fab5260d1949562e7979be",
- "assets/build/ba_data/data/languages/slovak.json": "https://files.ballistica.net/cache/ba1/02/f9/4a1e4b16c1c0db6e1a88fb7c2b1f",
+ "assets/build/ba_data/data/languages/slovak.json": "https://files.ballistica.net/cache/ba1/97/ec/384ca27db2fdc711514273726935",
"assets/build/ba_data/data/languages/spanish.json": "https://files.ballistica.net/cache/ba1/42/8a/31ee9bf7d90ad1c7a613ab91e4f3",
"assets/build/ba_data/data/languages/swedish.json": "https://files.ballistica.net/cache/ba1/50/9f/be006ba19be6a69a57837eb6dca0",
"assets/build/ba_data/data/languages/turkish.json": "https://files.ballistica.net/cache/ba1/a4/d9/93b754bae8c86791f6d8d3b600e1",
@@ -3936,50 +3936,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/14/65/e76d6db4acf8d4aa4911cc9d028a",
+ "build/prefab/full/linux_arm64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/ef/d7/495767e5ff1cb54f8f0460333e78",
"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/bf/7d/77c00ddc1c099b369ba2b8df28d9",
- "build/prefab/full/linux_arm64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/1b/7c/ce10fe8508a295a677560861c586",
- "build/prefab/full/linux_x86_64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/27/ce/334457db385ce15f76b33c42d382",
+ "build/prefab/full/linux_arm64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/6b/c1/dfc5ceac182cf05b8768e7ff13db",
+ "build/prefab/full/linux_arm64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/c0/d1/30001af821a0429937d8ffaa8e3c",
+ "build/prefab/full/linux_x86_64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/bf/23/321b8fe6eece0708ea3a68f6018b",
"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/f4/2b/d2610ee3521acca45dc010d96940",
- "build/prefab/full/linux_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/74/1d/142b429f823320491387ed97d877",
- "build/prefab/full/mac_arm64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/9e/d9/c7e3bd2adee613ca901238c26bb8",
- "build/prefab/full/mac_arm64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/86/a4/c4b4a6e6d5dda42199e4dac5184a",
- "build/prefab/full/mac_arm64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/b7/5a/60fdcd55840cebc736673ff32416",
- "build/prefab/full/mac_arm64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/4c/6f/1af3830bdcf69fba8078b2866fd1",
- "build/prefab/full/mac_x86_64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/0d/08/8c329bd71c2df09ead449bac480a",
- "build/prefab/full/mac_x86_64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/3b/2f/ef7a91e2b36e29f124febca5c62a",
- "build/prefab/full/mac_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/20/c0/942cd20ac2265769b1a1f14f0d17",
- "build/prefab/full/mac_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/3f/e0/f462366cd16674ebeea4e2463d69",
- "build/prefab/full/windows_x86_gui/debug/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/04/36/24006cd9c69d896fa09294f1c551",
- "build/prefab/full/windows_x86_gui/release/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/82/42/2f8d90b396536430bac4e8f9e1a6",
- "build/prefab/full/windows_x86_server/debug/dist/BallisticaCoreHeadless.exe": "https://files.ballistica.net/cache/ba1/64/bf/7f5e32cc0ccfa83fac033c7f6e3e",
- "build/prefab/full/windows_x86_server/release/dist/BallisticaCoreHeadless.exe": "https://files.ballistica.net/cache/ba1/e2/d7/2a22130c82126c06debcd2e07d9a",
- "build/prefab/lib/linux_arm64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/87/8c/8844f896ae2f2e23fc7de8ebf83e",
- "build/prefab/lib/linux_arm64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/b6/34/c5f471383506bec47851780c51fa",
- "build/prefab/lib/linux_arm64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/ef/05/a7b311b98f8eb082352f89c0467b",
- "build/prefab/lib/linux_arm64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/1c/be/12c89e97422620f979482dee9be2",
- "build/prefab/lib/linux_x86_64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/27/23/a0ca2d8114858f9d73e6fe1e3a49",
- "build/prefab/lib/linux_x86_64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/ab/ab/7773aa505ac380b7aa17e5a01d7d",
- "build/prefab/lib/linux_x86_64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/50/37/b3af9d6405c1dbe08b1ab9d0c5e0",
- "build/prefab/lib/linux_x86_64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/62/65/86ccbcdf8ca0f1aeb97607271f7b",
- "build/prefab/lib/mac_arm64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/22/74/b91237a1c27491892ac77448e684",
- "build/prefab/lib/mac_arm64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/23/9b/5b5b5d663cef540a9462ef2a37d0",
- "build/prefab/lib/mac_arm64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/6c/ff/1e06dd40fd61042e36e83cecdb38",
- "build/prefab/lib/mac_arm64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/20/55/39ac5c3257e9767512671bf80272",
- "build/prefab/lib/mac_x86_64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/b7/0c/848465c86d4933a025e45eaa1148",
- "build/prefab/lib/mac_x86_64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/30/7f/419770d849f568a170d956081452",
- "build/prefab/lib/mac_x86_64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/2d/6e/d4665bf704a16f4df850df6b3fa7",
- "build/prefab/lib/mac_x86_64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/04/d3/7cbe365469441b408e8b1b537e97",
- "build/prefab/lib/windows/Debug_Win32/BallisticaCoreGenericInternal.lib": "https://files.ballistica.net/cache/ba1/6e/f3/3084e35e36e378a31efcd5322475",
- "build/prefab/lib/windows/Debug_Win32/BallisticaCoreGenericInternal.pdb": "https://files.ballistica.net/cache/ba1/bc/68/170b706d3424ae63d087038c8132",
- "build/prefab/lib/windows/Debug_Win32/BallisticaCoreHeadlessInternal.lib": "https://files.ballistica.net/cache/ba1/ed/7e/efc93d28f9540a66998d3981d8d2",
- "build/prefab/lib/windows/Debug_Win32/BallisticaCoreHeadlessInternal.pdb": "https://files.ballistica.net/cache/ba1/bb/8d/212edd7de31aa73920ddad2a6c45",
- "build/prefab/lib/windows/Release_Win32/BallisticaCoreGenericInternal.lib": "https://files.ballistica.net/cache/ba1/e6/8a/df58e0d6ab23f9242b7ca806f084",
- "build/prefab/lib/windows/Release_Win32/BallisticaCoreGenericInternal.pdb": "https://files.ballistica.net/cache/ba1/b2/80/d6f4f629e96bc2ead3921c20e2cf",
- "build/prefab/lib/windows/Release_Win32/BallisticaCoreHeadlessInternal.lib": "https://files.ballistica.net/cache/ba1/5f/f8/f14410dbaeffbb1e8cb1ca24a293",
- "build/prefab/lib/windows/Release_Win32/BallisticaCoreHeadlessInternal.pdb": "https://files.ballistica.net/cache/ba1/a9/7a/2ec01f037c3a5ac14cf99c0bbe74",
+ "build/prefab/full/linux_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/90/0e/d5de3cbc4268e730ee4950096a32",
+ "build/prefab/full/linux_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/1e/15/efb752a8d30ccfc39086e9fe14c0",
+ "build/prefab/full/mac_arm64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/33/f3/44f878e84d481a1080fa0c607717",
+ "build/prefab/full/mac_arm64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/7f/b0/0e17879c0d6993a7141a1b51d7d6",
+ "build/prefab/full/mac_arm64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/a3/8a/80596a9cbf5d8b656419dddd3d92",
+ "build/prefab/full/mac_arm64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/65/74/25e2b6f16b8c69c2bdb5c8b3270e",
+ "build/prefab/full/mac_x86_64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/5d/b4/f0cf21a3bf04befc86f77eabf4e3",
+ "build/prefab/full/mac_x86_64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/48/fc/17631bee5d1928b50db91073db46",
+ "build/prefab/full/mac_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/76/f2/e4c5fe6f4e84f7da72d86d4bf1b3",
+ "build/prefab/full/mac_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/49/51/168461fca007d19f0ba3cae8cf45",
+ "build/prefab/full/windows_x86_gui/debug/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/30/13/847d10b164271ea17480555a6f60",
+ "build/prefab/full/windows_x86_gui/release/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/0f/f8/bbb7a81eb89a8d8080300c739d44",
+ "build/prefab/full/windows_x86_server/debug/dist/BallisticaCoreHeadless.exe": "https://files.ballistica.net/cache/ba1/de/ae/6e8a2eef2338e0ee5ab5a9c6a5a0",
+ "build/prefab/full/windows_x86_server/release/dist/BallisticaCoreHeadless.exe": "https://files.ballistica.net/cache/ba1/20/2a/09bc1afad5218914c2c392c01d0e",
+ "build/prefab/lib/linux_arm64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/d8/d4/1d4855188cca3e68d58396b1ffcd",
+ "build/prefab/lib/linux_arm64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/2f/b0/88755dcef661a87ca4cb78a7d071",
+ "build/prefab/lib/linux_arm64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/51/87/7e2bf06277db8bb5c8514d8ed4c6",
+ "build/prefab/lib/linux_arm64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/24/c6/1a927f22dcc2cc14b2751ce2fd2a",
+ "build/prefab/lib/linux_x86_64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/2e/a9/d9282a2d031f5ab788798f7f28f5",
+ "build/prefab/lib/linux_x86_64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/de/04/a8c21c315cbbaed3666a2cf52fad",
+ "build/prefab/lib/linux_x86_64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/3f/2e/6648547bf5291dc8c2d776f45c28",
+ "build/prefab/lib/linux_x86_64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/95/cc/54d0df06b07982427c45cee91e0c",
+ "build/prefab/lib/mac_arm64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/a3/a4/02ce13269b4c85643187e46f520c",
+ "build/prefab/lib/mac_arm64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/8c/97/ccec012d4634e0e8e63a04b7ad1c",
+ "build/prefab/lib/mac_arm64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/73/46/f45a8c661bf08fcf9c5f47f8fd9a",
+ "build/prefab/lib/mac_arm64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/23/19/20d3ce3220d91b9f6e8f7f2ece90",
+ "build/prefab/lib/mac_x86_64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/7f/47/740a8693d4f92b74ffcd1786c1d7",
+ "build/prefab/lib/mac_x86_64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/45/2c/b0fc8bcdee390b9fa5c6ab964ec4",
+ "build/prefab/lib/mac_x86_64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/d4/59/df59dbf3e9a6b643a3b71101bad5",
+ "build/prefab/lib/mac_x86_64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/e0/b3/2ca91ed3aaf0e94d2619f8affa07",
+ "build/prefab/lib/windows/Debug_Win32/BallisticaCoreGenericInternal.lib": "https://files.ballistica.net/cache/ba1/48/d4/3834b0060398af319ac450c3a608",
+ "build/prefab/lib/windows/Debug_Win32/BallisticaCoreGenericInternal.pdb": "https://files.ballistica.net/cache/ba1/5b/89/aa3d3dcec140c43aa26db9300f4b",
+ "build/prefab/lib/windows/Debug_Win32/BallisticaCoreHeadlessInternal.lib": "https://files.ballistica.net/cache/ba1/94/4b/fe9ead9348303dc8a76986e5d9f8",
+ "build/prefab/lib/windows/Debug_Win32/BallisticaCoreHeadlessInternal.pdb": "https://files.ballistica.net/cache/ba1/f9/65/367102b313ff6e5eb1008a5da08e",
+ "build/prefab/lib/windows/Release_Win32/BallisticaCoreGenericInternal.lib": "https://files.ballistica.net/cache/ba1/4f/aa/b2d7d605fb03b4bf7e7544206615",
+ "build/prefab/lib/windows/Release_Win32/BallisticaCoreGenericInternal.pdb": "https://files.ballistica.net/cache/ba1/37/fa/446af85bc9f2f0e220571b4ec5f9",
+ "build/prefab/lib/windows/Release_Win32/BallisticaCoreHeadlessInternal.lib": "https://files.ballistica.net/cache/ba1/03/98/926496f7267a2dafd53d1e9f2f2b",
+ "build/prefab/lib/windows/Release_Win32/BallisticaCoreHeadlessInternal.pdb": "https://files.ballistica.net/cache/ba1/11/1e/4af5d4405d646108dacdfd8d687f",
"src/ballistica/generated/python_embedded/binding.inc": "https://files.ballistica.net/cache/ba1/fc/b5/62133319c6df8567aecfd29b1204",
"src/ballistica/generated/python_embedded/bootstrap.inc": "https://files.ballistica.net/cache/ba1/8d/96/c1516dee7d458c9065597e0aede6"
}
\ No newline at end of file
diff --git a/assets/.asset_manifest_public.json b/assets/.asset_manifest_public.json
index 8b15328c..8da644d2 100644
--- a/assets/.asset_manifest_public.json
+++ b/assets/.asset_manifest_public.json
@@ -132,11 +132,9 @@
"ba_data/python/bacommon/__init__.py",
"ba_data/python/bacommon/__pycache__/__init__.cpython-38.opt-1.pyc",
"ba_data/python/bacommon/__pycache__/assets.cpython-38.opt-1.pyc",
- "ba_data/python/bacommon/__pycache__/err.cpython-38.opt-1.pyc",
"ba_data/python/bacommon/__pycache__/net.cpython-38.opt-1.pyc",
"ba_data/python/bacommon/__pycache__/servermanager.cpython-38.opt-1.pyc",
"ba_data/python/bacommon/assets.py",
- "ba_data/python/bacommon/err.py",
"ba_data/python/bacommon/net.py",
"ba_data/python/bacommon/servermanager.py",
"ba_data/python/bastd/__init__.py",
diff --git a/assets/Makefile b/assets/Makefile
index abbe812a..fc65156b 100644
--- a/assets/Makefile
+++ b/assets/Makefile
@@ -637,7 +637,6 @@ $(eval $(call make-opt-pyc-target,$(element))))
SCRIPT_TARGETS_PY_PUBLIC_TOOLS = \
build/ba_data/python/bacommon/__init__.py \
build/ba_data/python/bacommon/assets.py \
- build/ba_data/python/bacommon/err.py \
build/ba_data/python/bacommon/net.py \
build/ba_data/python/bacommon/servermanager.py \
build/ba_data/python/efro/__init__.py \
@@ -660,7 +659,6 @@ SCRIPT_TARGETS_PY_PUBLIC_TOOLS = \
SCRIPT_TARGETS_PYC_PUBLIC_TOOLS = \
build/ba_data/python/bacommon/__pycache__/__init__.cpython-38.opt-1.pyc \
build/ba_data/python/bacommon/__pycache__/assets.cpython-38.opt-1.pyc \
- build/ba_data/python/bacommon/__pycache__/err.cpython-38.opt-1.pyc \
build/ba_data/python/bacommon/__pycache__/net.cpython-38.opt-1.pyc \
build/ba_data/python/bacommon/__pycache__/servermanager.cpython-38.opt-1.pyc \
build/ba_data/python/efro/__pycache__/__init__.cpython-38.opt-1.pyc \
diff --git a/assets/src/ba_data/python/._ba_sources_hash b/assets/src/ba_data/python/._ba_sources_hash
index 21541fb7..fbbdf705 100644
--- a/assets/src/ba_data/python/._ba_sources_hash
+++ b/assets/src/ba_data/python/._ba_sources_hash
@@ -1 +1 @@
-119796669300700074719883839459258868842
\ No newline at end of file
+206707705873020170747420069909535530879
\ No newline at end of file
diff --git a/assets/src/ba_data/python/_ba.py b/assets/src/ba_data/python/_ba.py
index 62c91731..74a5f289 100644
--- a/assets/src/ba_data/python/_ba.py
+++ b/assets/src/ba_data/python/_ba.py
@@ -18,6 +18,8 @@ NOTE: This file was autogenerated by batools.dummymodule; do not edit by hand.
# I'm sorry Pylint. I know this file saddens you. Be strong.
# pylint: disable=useless-suppression
# pylint: disable=unnecessary-pass
+# pylint: disable=use-dict-literal
+# pylint: disable=use-list-literal
# pylint: disable=unused-argument
# pylint: disable=missing-docstring
# pylint: disable=too-many-locals
diff --git a/assets/src/ba_data/python/ba/_appconfig.py b/assets/src/ba_data/python/ba/_appconfig.py
index 7967e584..c60c01cd 100644
--- a/assets/src/ba_data/python/ba/_appconfig.py
+++ b/assets/src/ba_data/python/ba/_appconfig.py
@@ -107,7 +107,7 @@ def read_config() -> Tuple[AppConfig, bool]:
config_contents = ''
try:
if os.path.exists(config_file_path):
- with open(config_file_path) as infile:
+ with open(config_file_path, encoding='utf-8') as infile:
config_contents = infile.read()
config = AppConfig(json.loads(config_contents))
else:
@@ -140,7 +140,7 @@ def read_config() -> Tuple[AppConfig, bool]:
prev_path = config_file_path + '.prev'
try:
if os.path.exists(prev_path):
- with open(prev_path) as infile:
+ with open(prev_path, encoding='utf-8') as infile:
config_contents = infile.read()
config = AppConfig(json.loads(config_contents))
else:
diff --git a/assets/src/ba_data/python/ba/_apputils.py b/assets/src/ba_data/python/ba/_apputils.py
index 3a2f6441..9146b0f4 100644
--- a/assets/src/ba_data/python/ba/_apputils.py
+++ b/assets/src/ba_data/python/ba/_apputils.py
@@ -124,7 +124,7 @@ def handle_leftover_log_file() -> None:
from ba._net import master_server_post
if os.path.exists(_ba.get_log_file_path()):
- with open(_ba.get_log_file_path()) as infile:
+ with open(_ba.get_log_file_path(), encoding='utf-8') as infile:
info = json.loads(infile.read())
infile.close()
do_send = should_submit_debug_info()
diff --git a/assets/src/ba_data/python/ba/_assetmanager.py b/assets/src/ba_data/python/ba/_assetmanager.py
index c23624a4..204726f0 100644
--- a/assets/src/ba_data/python/ba/_assetmanager.py
+++ b/assets/src/ba_data/python/ba/_assetmanager.py
@@ -101,7 +101,7 @@ class AssetManager:
try:
state_path = self.state_path
if state_path.exists():
- with open(self.state_path) as infile:
+ with open(self.state_path, encoding='utf-8') as infile:
self._state = State.from_json_str(infile.read())
return
except Exception:
@@ -113,7 +113,7 @@ class AssetManager:
print('ASSET-MANAGER SAVING STATE')
try:
- with open(self.state_path, 'w') as outfile:
+ with open(self.state_path, 'w', encoding='utf-8') as outfile:
outfile.write(self._state.to_json_str())
except Exception:
logging.exception('Error writing AssetManager state')
diff --git a/assets/src/ba_data/python/ba/_language.py b/assets/src/ba_data/python/ba/_language.py
index 98f0da56..75487688 100644
--- a/assets/src/ba_data/python/ba/_language.py
+++ b/assets/src/ba_data/python/ba/_language.py
@@ -161,7 +161,8 @@ class LanguageSubsystem:
else:
switched = False
- with open('ba_data/data/languages/english.json') as infile:
+ with open('ba_data/data/languages/english.json',
+ encoding='utf-8') as infile:
lenglishvalues = json.loads(infile.read())
# None implies default.
@@ -173,7 +174,7 @@ class LanguageSubsystem:
else:
lmodfile = 'ba_data/data/languages/' + language.lower(
) + '.json'
- with open(lmodfile) as infile:
+ with open(lmodfile, encoding='utf-8') as infile:
lmodvalues = json.loads(infile.read())
except Exception:
from ba import _error
diff --git a/assets/src/ba_data/python/ba/modutils.py b/assets/src/ba_data/python/ba/modutils.py
index e269da60..cdd6ecaf 100644
--- a/assets/src/ba_data/python/ba/modutils.py
+++ b/assets/src/ba_data/python/ba/modutils.py
@@ -73,7 +73,7 @@ def show_user_scripts() -> None:
usd: Optional[str] = app.python_directory_user
if usd is not None and os.path.isdir(usd):
file_name = usd + '/about_this_folder.txt'
- with open(file_name, 'w') as outfile:
+ with open(file_name, 'w', encoding='utf-8') as outfile:
outfile.write('You can drop files in here to mod the game.'
' See settings/advanced'
' in the game for more info.')
diff --git a/assets/src/ba_data/python/bastd/stdmap.py b/assets/src/ba_data/python/bastd/stdmap.py
index 237ea4a5..2508fa8b 100644
--- a/assets/src/ba_data/python/bastd/stdmap.py
+++ b/assets/src/ba_data/python/bastd/stdmap.py
@@ -15,7 +15,8 @@ if TYPE_CHECKING:
def _get_map_data(name: str) -> Dict[str, Any]:
import json
print('Would get map data', name)
- with open('ba_data/data/maps/' + name + '.json') as infile:
+ with open('ba_data/data/maps/' + name + '.json',
+ encoding='utf-8') as infile:
mapdata = json.loads(infile.read())
assert isinstance(mapdata, dict)
return mapdata
diff --git a/assets/src/ba_data/python/bastd/ui/coop/browser.py b/assets/src/ba_data/python/bastd/ui/coop/browser.py
index a7f18ec7..a0a008b8 100644
--- a/assets/src/ba_data/python/bastd/ui/coop/browser.py
+++ b/assets/src/ba_data/python/bastd/ui/coop/browser.py
@@ -369,9 +369,9 @@ class CoopBrowserWindow(ba.Window):
# If the number of tournaments or challenges in the data differs from
# our current arrangement, refresh with the new number.
- if (((data is None and (self._tournament_button_count != 0))
- or (data is not None and
- (len(data) != self._tournament_button_count)))):
+ if ((data is None and self._tournament_button_count != 0)
+ or (data is not None and
+ (len(data) != self._tournament_button_count))):
self._tournament_button_count = len(
data) if data is not None else 0
ba.app.config['Tournament Rows'] = self._tournament_button_count
diff --git a/assets/src/ba_data/python/bastd/ui/creditslist.py b/assets/src/ba_data/python/bastd/ui/creditslist.py
index b5caebc4..161b77d2 100644
--- a/assets/src/ba_data/python/bastd/ui/creditslist.py
+++ b/assets/src/ba_data/python/bastd/ui/creditslist.py
@@ -156,7 +156,8 @@ class CreditsListWindow(ba.Window):
freesound_names = _format_names(names, 90)
try:
- with open('ba_data/data/langdata.json') as infile:
+ with open('ba_data/data/langdata.json',
+ encoding='utf-8') as infile:
translation_contributors = (json.loads(
infile.read())['translation_contributors'])
except Exception:
diff --git a/assets/src/ba_data/python/bastd/ui/settings/advanced.py b/assets/src/ba_data/python/bastd/ui/settings/advanced.py
index 1d786905..5d428cb7 100644
--- a/assets/src/ba_data/python/bastd/ui/settings/advanced.py
+++ b/assets/src/ba_data/python/bastd/ui/settings/advanced.py
@@ -258,7 +258,8 @@ class AdvancedSettingsWindow(ba.Window):
# so we don't have to go digging through each full language.
try:
import json
- with open('ba_data/data/langdata.json') as infile:
+ with open('ba_data/data/langdata.json',
+ encoding='utf-8') as infile:
lang_names_translated = (json.loads(
infile.read())['lang_names_translated'])
except Exception:
diff --git a/assets/src/server/ballisticacore_server.py b/assets/src/server/ballisticacore_server.py
index 6d170170..9267d190 100755
--- a/assets/src/server/ballisticacore_server.py
+++ b/assets/src/server/ballisticacore_server.py
@@ -513,7 +513,7 @@ class ServerManagerApp:
f"Config file not found: '{self._config_path}'.")
import yaml
- with open(self._config_path) as infile:
+ with open(self._config_path, encoding='utf-8') as infile:
user_config_raw = yaml.safe_load(infile.read())
# An empty config file will yield None, and that's ok.
@@ -646,7 +646,7 @@ class ServerManagerApp:
os.makedirs(self._ba_root_path, exist_ok=True)
cfgpath = os.path.join(self._ba_root_path, 'config.json')
if os.path.exists(cfgpath):
- with open(cfgpath) as infile:
+ with open(cfgpath, encoding='utf-8') as infile:
bincfg = json.loads(infile.read())
else:
bincfg = {}
@@ -668,7 +668,7 @@ class ServerManagerApp:
del bincfg['Custom Team Colors']
bincfg['Idle Exit Minutes'] = self._config.idle_exit_minutes
- with open(cfgpath, 'w') as outfile:
+ with open(cfgpath, 'w', encoding='utf-8') as outfile:
outfile.write(json.dumps(bincfg))
def _enqueue_server_command(self, command: ServerCommand) -> None:
diff --git a/docs/ba_module.md b/docs/ba_module.md
index 5a1e0322..96705057 100644
--- a/docs/ba_module.md
+++ b/docs/ba_module.md
@@ -1,5 +1,5 @@
-
last updated on 2021-08-16 for Ballistica version 1.6.5 build 20391
+last updated on 2021-08-23 for Ballistica version 1.6.5 build 20391
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/tools/bacloud b/tools/bacloud
index 0b7ea780..c9424b09 100755
--- a/tools/bacloud
+++ b/tools/bacloud
@@ -217,7 +217,7 @@ class App:
if not os.path.exists(self._state_data_path):
return
try:
- with open(self._state_data_path, 'r') as infile:
+ with open(self._state_data_path, 'r', encoding='utf-8') as infile:
self._state = StateData(**json.loads(infile.read()))
except Exception:
print(f'{Clr.RED}Error loading {TOOL_NAME} data;'
@@ -226,7 +226,7 @@ class App:
def _save_state(self) -> None:
if not self._state_dir.exists():
self._state_dir.mkdir(parents=True, exist_ok=True)
- with open(self._state_data_path, 'w') as outfile:
+ with open(self._state_data_path, 'w', encoding='utf-8') as outfile:
outfile.write(json.dumps(self._state.__dict__))
def _servercmd(self,
diff --git a/tools/bacommon/err.py b/tools/bacommon/err.py
deleted file mode 100644
index b1403cbc..00000000
--- a/tools/bacommon/err.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Released under the MIT License. See LICENSE for details.
-#
-"""Error related functionality."""
-
-from __future__ import annotations
-
-from typing import TYPE_CHECKING
-
-if TYPE_CHECKING:
- pass
-
-
-class RemoteError(Exception):
- """An error occurred on the other end of some connection."""
-
- def __str__(self) -> str:
- s = ''.join(str(arg) for arg in self.args)
- return f'Remote Exception Follows:\n{s}'
diff --git a/tools/batools/androidsdkutils.py b/tools/batools/androidsdkutils.py
index a2d5111b..eda44e5f 100755
--- a/tools/batools/androidsdkutils.py
+++ b/tools/batools/androidsdkutils.py
@@ -15,7 +15,7 @@ if TYPE_CHECKING:
def _parse_lprop_file(local_properties_path: str) -> str:
- with open(local_properties_path) as infile:
+ with open(local_properties_path, encoding='utf-8') as infile:
lines = infile.read().splitlines()
sdk_dir_lines = [l for l in lines if 'sdk.dir=' in l]
if len(sdk_dir_lines) != 1:
@@ -68,7 +68,7 @@ def _gen_lprop_file(local_properties_path: str) -> str:
' sdk elsewhere\n'
'\n'
'sdk.dir=' + sdk_dir + '\n')
- with open(local_properties_path, 'w') as outfile:
+ with open(local_properties_path, 'w', encoding='utf-8') as outfile:
outfile.write(config)
print('Generating local.properties file (found Android SDK at "' +
sdk_dir + '")',
diff --git a/tools/batools/assetsmakefile.py b/tools/batools/assetsmakefile.py
index 7102bf18..e64065e4 100755
--- a/tools/batools/assetsmakefile.py
+++ b/tools/batools/assetsmakefile.py
@@ -124,7 +124,8 @@ def _get_py_targets(src: str, dst: str, py_targets: List[str],
# lives under this dir.
meta_targets: List[str] = []
for mantype in ['public', 'private']:
- with open(f'src/meta/.meta_manifest_{mantype}.json') as infile:
+ with open(f'src/meta/.meta_manifest_{mantype}.json',
+ encoding='utf-8') as infile:
meta_targets += json.loads(infile.read())
meta_targets = [
t for t in meta_targets
@@ -282,7 +283,7 @@ def update_assets_makefile(projroot: str, check: bool) -> None:
assert isinstance(public, bool)
fname = 'assets/Makefile'
- with open(fname) as infile:
+ with open(fname, encoding='utf-8') as infile:
original = infile.read()
lines = original.splitlines()
@@ -367,7 +368,7 @@ def update_assets_makefile(projroot: str, check: bool) -> None:
f'END COMPARE ========================================')
sys.exit(255)
print(f'{Clr.SBLU}Updating: {fname}{Clr.RST}')
- with open(fname, 'w') as outfile:
+ with open(fname, 'w', encoding='utf-8') as outfile:
outfile.write(out)
# Lastly, write a simple manifest of the things we expect to have
@@ -387,7 +388,7 @@ def _write_manifest(manifest_path: str, all_targets: Set[str],
if not os.path.exists(manifest_path):
existing_manifest = None
else:
- with open(manifest_path) as infile:
+ with open(manifest_path, encoding='utf-8') as infile:
existing_manifest = json.loads(infile.read())
manifest = sorted(t[13:] for t in all_targets)
if manifest == existing_manifest:
@@ -398,5 +399,5 @@ def _write_manifest(manifest_path: str, all_targets: Set[str],
f" '{manifest_path}'.{Clr.RST}")
sys.exit(255)
print(f'{Clr.SBLU}Updating: {manifest_path}{Clr.RST}')
- with open(manifest_path, 'w') as outfile:
+ with open(manifest_path, 'w', encoding='utf-8') as outfile:
outfile.write(json.dumps(manifest, indent=1))
diff --git a/tools/batools/assetstaging.py b/tools/batools/assetstaging.py
index 000cf2c6..d5f18435 100755
--- a/tools/batools/assetstaging.py
+++ b/tools/batools/assetstaging.py
@@ -225,7 +225,7 @@ def _write_payload_file(assets_root: str, full: bool) -> None:
# the file list.
payload_str = (str(len(file_list)) + '\n' + ('1' if full else '0') +
'\n' + payload_str)
- with open(payload_path, 'w') as outfile:
+ with open(payload_path, 'w', encoding='utf-8') as outfile:
outfile.write(payload_str)
else:
# Remove the payload file; this will cause the game to completely
@@ -400,13 +400,13 @@ def _write_if_changed(path: str,
make_executable: bool = False) -> None:
changed: bool
try:
- with open(path) as infile:
+ with open(path, encoding='utf-8') as infile:
existing = infile.read()
changed = (contents != existing)
except FileNotFoundError:
changed = True
if changed:
- with open(path, 'w') as outfile:
+ with open(path, 'w', encoding='utf-8') as outfile:
outfile.write(contents)
if make_executable:
subprocess.run(['chmod', '+x', path], check=True)
@@ -434,7 +434,7 @@ def stage_server_file(projroot: str, mode: str, infilename: str,
elif basename == 'ballisticacore_server.py':
# Run Python in opt mode for release builds.
- with open(infilename) as infile:
+ with open(infilename, encoding='utf-8') as infile:
lines = infile.read().splitlines()
if mode == 'release':
lines[0] = replace_one(lines[0],
@@ -444,12 +444,12 @@ def stage_server_file(projroot: str, mode: str, infilename: str,
'\n'.join(lines) + '\n',
make_executable=True)
elif basename == 'README.txt':
- with open(infilename) as infile:
+ with open(infilename, encoding='utf-8') as infile:
readme = infile.read()
_write_if_changed(outfilename, readme)
elif basename == 'launch_ballisticacore_server.bat':
# Run Python in opt mode for release builds.
- with open(infilename) as infile:
+ with open(infilename, encoding='utf-8') as infile:
lines = infile.read().splitlines()
if mode == 'release':
lines[1] = replace_one(
diff --git a/tools/batools/build.py b/tools/batools/build.py
index d58db1b4..00551acb 100644
--- a/tools/batools/build.py
+++ b/tools/batools/build.py
@@ -31,7 +31,7 @@ class PipRequirement:
# Note: we look directly for modules when possible instead of just pip
# entries; this accounts for manual installations or other nonstandard setups.
PIP_REQUIREMENTS = [
- PipRequirement(modulename='pylint', minversion=[2, 9, 6]),
+ PipRequirement(modulename='pylint', minversion=[2, 10, 2]),
PipRequirement(modulename='mypy', minversion=[0, 910]),
PipRequirement(modulename='yapf', minversion=[0, 31, 0]),
PipRequirement(modulename='cpplint', minversion=[1, 5, 5]),
@@ -41,10 +41,10 @@ PIP_REQUIREMENTS = [
PipRequirement(modulename='yaml', pipname='PyYAML'),
PipRequirement(modulename='requests'),
PipRequirement(pipname='typing-extensions', minversion=[3, 10, 0, 0]),
- PipRequirement(pipname='types-filelock', minversion=[0, 1, 3]),
- PipRequirement(pipname='types-requests', minversion=[0, 1, 9]),
- PipRequirement(pipname='types-pytz', minversion=[0, 1, 0]),
- PipRequirement(pipname='types-PyYAML', minversion=[0, 1, 6]),
+ PipRequirement(pipname='types-filelock', minversion=[0, 1, 5]),
+ PipRequirement(pipname='types-requests', minversion=[2, 25, 6]),
+ PipRequirement(pipname='types-pytz', minversion=[2021, 1, 2]),
+ PipRequirement(pipname='types-PyYAML', minversion=[5, 4, 6]),
]
# Parts of full-tests suite we only run on particular days.
@@ -335,7 +335,7 @@ def gen_fulltest_buildfile_android() -> None:
else:
raise RuntimeError(f'Unknown extra: {extra}')
- with open('_fulltest_buildfile_android', 'w') as outfile:
+ with open('_fulltest_buildfile_android', 'w', encoding='utf-8') as outfile:
outfile.write('\n'.join(lines))
@@ -381,7 +381,7 @@ def gen_fulltest_buildfile_windows() -> None:
else:
raise RuntimeError(f'Unknown extra: {extra}')
- with open('_fulltest_buildfile_windows', 'w') as outfile:
+ with open('_fulltest_buildfile_windows', 'w', encoding='utf-8') as outfile:
outfile.write('\n'.join(lines))
@@ -448,7 +448,7 @@ def gen_fulltest_buildfile_apple() -> None:
else:
raise RuntimeError(f'Unknown extra: {extra}')
- with open('_fulltest_buildfile_apple', 'w') as outfile:
+ with open('_fulltest_buildfile_apple', 'w', encoding='utf-8') as outfile:
outfile.write('\n'.join(lines))
@@ -476,7 +476,7 @@ def gen_fulltest_buildfile_linux() -> None:
else:
raise RuntimeError(f'Unknown extra: {extra}')
- with open('_fulltest_buildfile_linux', 'w') as outfile:
+ with open('_fulltest_buildfile_linux', 'w', encoding='utf-8') as outfile:
outfile.write('\n'.join(lines))
@@ -685,8 +685,8 @@ def update_makebob() -> None:
def _get_server_config_raw_contents(projroot: str) -> str:
import textwrap
- with open(os.path.join(projroot,
- 'tools/bacommon/servermanager.py')) as infile:
+ with open(os.path.join(projroot, 'tools/bacommon/servermanager.py'),
+ encoding='utf-8') as infile:
lines = infile.read().splitlines()
firstline = lines.index('class ServerConfig:') + 1
lastline = firstline + 1
@@ -782,7 +782,7 @@ def _get_server_config_template_yaml(projroot: str) -> str:
def filter_server_config(projroot: str, infilepath: str) -> str:
"""Add commented-out config options to a server config."""
- with open(infilepath) as infile:
+ with open(infilepath, encoding='utf-8') as infile:
cfg = infile.read()
return cfg.replace('#__CONFIG_TEMPLATE_VALUES__',
_get_server_config_template_yaml(projroot))
@@ -821,7 +821,7 @@ def update_docs_md(check: bool) -> None:
# Extract the current embedded hash.
if os.path.exists(docs_hash_path):
- with open(docs_hash_path) as infile:
+ with open(docs_hash_path, encoding='utf-8') as infile:
storedhash = infile.read()
else:
storedhash = None
@@ -836,14 +836,14 @@ def update_docs_md(check: bool) -> None:
# Our docs markdown is just the docs html with a few added
# bits at the top.
- with open('build/docs.html') as infile:
+ with open('build/docs.html', encoding='utf-8') as infile:
docs = infile.read()
docs = ('\n'
) + docs
os.makedirs(os.path.dirname(docs_path), exist_ok=True)
- with open(docs_path, 'w') as outfile:
+ with open(docs_path, 'w', encoding='utf-8') as outfile:
outfile.write(docs)
- with open(docs_hash_path, 'w') as outfile:
+ with open(docs_hash_path, 'w', encoding='utf-8') as outfile:
outfile.write(curhash)
print(f'{docs_path} is up to date.')
@@ -909,7 +909,7 @@ def cmake_prep_dir(dirname: str, verbose: bool = False) -> None:
versions: Dict[str, str]
if os.path.isfile(verfilename):
- with open(verfilename) as infile:
+ with open(verfilename, encoding='utf-8') as infile:
versions = json.loads(infile.read())
assert isinstance(versions, dict)
assert all(isinstance(x, str) for x in versions.keys())
@@ -932,7 +932,7 @@ def cmake_prep_dir(dirname: str, verbose: bool = False) -> None:
f'{Clr.BLD}{title}:{Clr.RST} Blowing away existing build dir.')
subprocess.run(['rm', '-rf', dirname], check=True)
os.makedirs(dirname, exist_ok=True)
- with open(verfilename, 'w') as outfile:
+ with open(verfilename, 'w', encoding='utf-8') as outfile:
outfile.write(
json.dumps(
{entry.name: entry.current_value
diff --git a/tools/batools/changelog.py b/tools/batools/changelog.py
index a361885c..abf6bace 100755
--- a/tools/batools/changelog.py
+++ b/tools/batools/changelog.py
@@ -16,7 +16,7 @@ def generate(projroot: str) -> None:
out_path_tmp = out_path + '.md'
# Do some filtering of our raw changelog.
- with open('CHANGELOG.md') as infile:
+ with open('CHANGELOG.md', encoding='utf-8') as infile:
lines = infile.read().splitlines()
# Strip out anything marked internal.
@@ -24,7 +24,7 @@ def generate(projroot: str) -> None:
line for line in lines if not line.strip().startswith('- (internal)')
]
- with open(out_path_tmp, 'w') as outfile:
+ with open(out_path_tmp, 'w', encoding='utf-8') as outfile:
outfile.write('\n'.join(lines))
subprocess.run(f'pandoc -f markdown {out_path_tmp} > {out_path}',
diff --git a/tools/batools/docs.py b/tools/batools/docs.py
index 03b2051e..6beed9b6 100755
--- a/tools/batools/docs.py
+++ b/tools/batools/docs.py
@@ -1264,7 +1264,7 @@ class Generator:
raise Exception(
str(len(self._errors)) + ' docs generation issues.')
- with open(outfilename, 'w') as outfile:
+ with open(outfilename, 'w', encoding='utf-8') as outfile:
outfile.write(self._out)
print(f"Generated docs file: '{Clr.BLU}{outfilename}.{Clr.RST}'")
diff --git a/tools/batools/dummymodule.py b/tools/batools/dummymodule.py
index 0043f9be..d69bd75b 100755
--- a/tools/batools/dummymodule.py
+++ b/tools/batools/dummymodule.py
@@ -609,6 +609,8 @@ def generate(sources_hash: str, outfilename: str) -> None:
'# I\'m sorry Pylint. I know this file saddens you. Be strong.\n'
'# pylint: disable=useless-suppression\n'
'# pylint: disable=unnecessary-pass\n'
+ '# pylint: disable=use-dict-literal\n'
+ '# pylint: disable=use-list-literal\n'
'# pylint: disable=unused-argument\n'
'# pylint: disable=missing-docstring\n'
'# pylint: disable=too-many-locals\n'
@@ -650,10 +652,10 @@ def generate(sources_hash: str, outfilename: str) -> None:
outhashpath = os.path.join(os.path.dirname(outfilename),
'._ba_sources_hash')
- with open(outfilename, 'w') as outfile:
+ with open(outfilename, 'w', encoding='utf-8') as outfile:
outfile.write(out)
- with open(outhashpath, 'w') as outfile:
+ with open(outhashpath, 'w', encoding='utf-8') as outfile:
outfile.write(sources_hash)
# Lastly, format it.
@@ -679,7 +681,7 @@ def _dummy_module_dirty() -> Tuple[bool, str]:
if not os.path.exists(outpath):
existing_hash = ''
else:
- with open(outpath) as infile:
+ with open(outpath, encoding='utf-8') as infile:
existing_hash = infile.read()
# Important to keep this deterministic...
diff --git a/tools/batools/meta.py b/tools/batools/meta.py
index 1a77ddca..a4f1b1e8 100644
--- a/tools/batools/meta.py
+++ b/tools/batools/meta.py
@@ -44,7 +44,7 @@ def gen_flat_data_code(projroot: str, in_path: str, out_path: str,
if pretty_path.startswith(projroot + '/'):
pretty_path = pretty_path[len(projroot) + 1:]
print(f'Meta-building {Clr.BLD}{pretty_path}{Clr.RST}')
- with open(out_path, 'w') as outfile:
+ with open(out_path, 'w', encoding='utf-8') as outfile:
outfile.write(sval_out)
@@ -56,7 +56,7 @@ def gen_binding_code(projroot: str, in_path: str, out_path: str) -> None:
os.makedirs(out_dir, exist_ok=True)
# Pull all lines in the embedded list and split into py and c++ names.
- with open(in_path) as infile:
+ with open(in_path, encoding='utf-8') as infile:
pycode = infile.read()
# Double quotes cause errors.
@@ -102,5 +102,5 @@ def gen_binding_code(projroot: str, in_path: str, out_path: str) -> None:
if pretty_path.startswith(projroot + '/'):
pretty_path = pretty_path[len(projroot) + 1:]
print(f'Meta-building {Clr.BLD}{pretty_path}{Clr.RST}')
- with open(out_path, 'w') as outfile:
+ with open(out_path, 'w', encoding='utf-8') as outfile:
outfile.write(ccode)
diff --git a/tools/batools/metamakefile.py b/tools/batools/metamakefile.py
index 4ab32f71..168e9586 100755
--- a/tools/batools/metamakefile.py
+++ b/tools/batools/metamakefile.py
@@ -176,14 +176,14 @@ def update(projroot: str, check: bool) -> None:
fname_pub_man = 'src/meta/.meta_manifest_public.json'
fname_priv_man = 'src/meta/.meta_manifest_private.json'
- with open(fname) as infile:
+ with open(fname, encoding='utf-8') as infile:
original = infile.read()
lines = original.splitlines()
- with open(fname_pub_man) as infile:
+ with open(fname_pub_man, encoding='utf-8') as infile:
original_pub_man = infile.read()
- with open(fname_priv_man) as infile:
+ with open(fname_priv_man, encoding='utf-8') as infile:
original_priv_man = infile.read()
# We'll generate manifests of all public/private files we generate
@@ -263,7 +263,7 @@ def update(projroot: str, check: bool) -> None:
f'{Clr.RST}')
if out != original:
- with open(fname, 'w') as outfile:
+ with open(fname, 'w', encoding='utf-8') as outfile:
outfile.write(out)
# Also write our output file manifests every time we write the
@@ -271,10 +271,10 @@ def update(projroot: str, check: bool) -> None:
# they're out of date but the Makefile isn't, though that should not
# happen normally).
if out_pub_man != fname_pub_man:
- with open(fname_pub_man, 'w') as outfile:
+ with open(fname_pub_man, 'w', encoding='utf-8') as outfile:
outfile.write(out_pub_man)
if out_priv_man != fname_priv_man:
- with open(fname_priv_man, 'w') as outfile:
+ with open(fname_priv_man, 'w', encoding='utf-8') as outfile:
outfile.write(out_priv_man)
# Also clean existing meta output every time the Makefile changes;
diff --git a/tools/batools/pcommand.py b/tools/batools/pcommand.py
index d5190284..0b40519f 100644
--- a/tools/batools/pcommand.py
+++ b/tools/batools/pcommand.py
@@ -66,9 +66,11 @@ def clean_orphaned_assets() -> None:
os.chdir(PROJROOT)
# Our manifest is split into 2 files (public and private)
- with open('assets/.asset_manifest_public.json') as infile:
+ with open('assets/.asset_manifest_public.json',
+ encoding='utf-8') as infile:
manifest = set(json.loads(infile.read()))
- with open('assets/.asset_manifest_private.json') as infile:
+ with open('assets/.asset_manifest_private.json',
+ encoding='utf-8') as infile:
manifest.update(set(json.loads(infile.read())))
for root, _dirs, fnames in os.walk('assets/build'):
for fname in fnames:
@@ -159,7 +161,7 @@ def lazy_increment_build() -> None:
codehash = get_files_hash(codefiles)
hashfilename = '.cache/lazy_increment_build'
try:
- with open(hashfilename) as infile:
+ with open(hashfilename, encoding='utf-8') as infile:
lasthash = infile.read()
except FileNotFoundError:
lasthash = ''
@@ -176,7 +178,7 @@ def lazy_increment_build() -> None:
# We probably just changed code, so we need to re-calc the hash.
codehash = get_files_hash(codefiles)
os.makedirs(os.path.dirname(hashfilename), exist_ok=True)
- with open(hashfilename, 'w') as outfile:
+ with open(hashfilename, 'w', encoding='utf-8') as outfile:
outfile.write(codehash)
@@ -864,9 +866,11 @@ def win_ci_install_prereqs() -> None:
# Look through everything that gets generated by our meta builds
# and pick out anything we need for our basic builds/tests.
- with open('src/meta/.meta_manifest_public.json') as infile:
+ with open('src/meta/.meta_manifest_public.json',
+ encoding='utf-8') as infile:
meta_public: List[str] = json.loads(infile.read())
- with open('src/meta/.meta_manifest_private.json') as infile:
+ with open('src/meta/.meta_manifest_private.json',
+ encoding='utf-8') as infile:
meta_private: List[str] = json.loads(infile.read())
for target in meta_public + meta_private:
if (target.startswith('src/ballistica/generated/') or
@@ -957,7 +961,7 @@ def gen_python_init_module() -> None:
os.makedirs(os.path.dirname(outfilename), exist_ok=True)
prettypath = project_centric_path(projroot=str(PROJROOT), path=outfilename)
print(f'Meta-building {Clr.BLD}{prettypath}{Clr.RST}')
- with open(outfilename, 'w') as outfile:
+ with open(outfilename, 'w', encoding='utf-8') as outfile:
outfile.write('# Released under the MIT License.'
' See LICENSE for details.\n'
'#\n')
diff --git a/tools/batools/project.py b/tools/batools/project.py
index d00a56c4..6a7e0c2c 100755
--- a/tools/batools/project.py
+++ b/tools/batools/project.py
@@ -159,7 +159,7 @@ class Updater:
for fname, fcode in self._file_changes.items():
f_orig: Optional[str]
if os.path.exists(fname):
- with open(fname, 'r') as infile:
+ with open(fname, 'r', encoding='utf-8') as infile:
f_orig = infile.read()
else:
f_orig = None
@@ -172,7 +172,7 @@ class Updater:
sys.exit(255)
print(f'{Clr.BLU}Writing project file: {fname}{Clr.RST}')
- with open(fname, 'w') as outfile:
+ with open(fname, 'w', encoding='utf-8') as outfile:
outfile.write(fcode)
if unchanged_project_count > 0:
print(
@@ -209,7 +209,7 @@ class Updater:
print(f'{Clr.RED}#{i}: {change[0]}:{Clr.RST}')
print(
f'{Clr.RED} Expected "{change[1].expected}"{Clr.RST}')
- with open(change[0]) as infile:
+ with open(change[0], encoding='utf-8') as infile:
lines = infile.read().splitlines()
line = lines[change[1].line_number]
print(f'{Clr.RED} Found "{line}"{Clr.RST}')
@@ -220,10 +220,10 @@ class Updater:
else:
for i, change in enumerate(auto_changes):
print(f'{Clr.BLU}Correcting file: {change[0]}{Clr.RST}')
- with open(change[0]) as infile:
+ with open(change[0], encoding='utf-8') as infile:
lines = infile.read().splitlines()
lines[change[1].line_number] = change[1].expected
- with open(change[0], 'w') as outfile:
+ with open(change[0], 'w', encoding='utf-8') as outfile:
outfile.write('\n'.join(lines) + '\n')
# If there were no issues whatsoever, note that.
@@ -250,7 +250,7 @@ class Updater:
self._check_source_file(fname)
def _check_source_file(self, fname: str) -> None:
- with open(fname) as infile:
+ with open(fname, encoding='utf-8') as infile:
lines = infile.read().splitlines()
if self._license_line_checks:
@@ -299,7 +299,7 @@ class Updater:
# Make sure its define guard is correct.
guard = (fname[4:].upper().replace('/', '_').replace('.', '_') + '_')
- with open(fname) as fhdr:
+ with open(fname, encoding='utf-8') as fhdr:
lines = fhdr.read().splitlines()
if self._license_line_checks:
@@ -340,7 +340,7 @@ class Updater:
fnames = [n for n in fnames if '/build/' not in n]
for fname in fnames:
- with open(fname) as infile:
+ with open(fname, encoding='utf-8') as infile:
makefile = infile.read()
if self._public:
public_license = get_public_license('makefile')
@@ -354,7 +354,7 @@ class Updater:
def _check_python_file(self, fname: str) -> None:
from efrotools import get_public_license, PYVER
- with open(fname) as infile:
+ with open(fname, encoding='utf-8') as infile:
contents = infile.read()
lines = contents.splitlines()
@@ -451,7 +451,7 @@ class Updater:
if not os.path.exists(fname):
return
- with open(fname) as infile:
+ with open(fname, encoding='utf-8') as infile:
lines = infile.read().splitlines()
src_root = '..\\..\\src'
@@ -556,7 +556,7 @@ class Updater:
return filename not in self._get_internal_source_files()
def _update_cmake_file(self, fname: str) -> None:
- with open(fname) as infile:
+ with open(fname, encoding='utf-8') as infile:
lines = infile.read().splitlines()
for section in ['PUBLIC', 'PRIVATE']:
diff --git a/tools/batools/pythonenumsmodule.py b/tools/batools/pythonenumsmodule.py
index 79089698..08b02b77 100755
--- a/tools/batools/pythonenumsmodule.py
+++ b/tools/batools/pythonenumsmodule.py
@@ -27,7 +27,7 @@ def camel_case_convert(name: str) -> str:
def _gen_enums(infilename: str) -> str:
out = ''
enum_lnums: List[int] = []
- with open(infilename) as infile:
+ with open(infilename, encoding='utf-8') as infile:
lines = infile.read().splitlines()
# Tally up all places tagged for exporting python enums.
@@ -150,5 +150,5 @@ def generate(projroot: str, infilename: str, outfilename: str) -> None:
path = project_centric_path(projroot=projroot, path=outfilename)
print(f'Meta-building {Clr.BLD}{path}{Clr.RST}')
os.makedirs(os.path.dirname(outfilename), exist_ok=True)
- with open(outfilename, 'w') as outfile:
+ with open(outfilename, 'w', encoding='utf-8') as outfile:
outfile.write(out)
diff --git a/tools/batools/resourcesmakefile.py b/tools/batools/resourcesmakefile.py
index 8fac6a45..d18b5f0b 100755
--- a/tools/batools/resourcesmakefile.py
+++ b/tools/batools/resourcesmakefile.py
@@ -362,7 +362,7 @@ def update(projroot: str, check: bool) -> None:
assert isinstance(public, bool)
fname = 'resources/Makefile'
- with open(fname) as infile:
+ with open(fname, encoding='utf-8') as infile:
original = infile.read()
lines = original.splitlines()
@@ -432,5 +432,5 @@ def update(projroot: str, check: bool) -> None:
f'EXPECTED------\n{out}\nEND EXPECTED-------\n')
raise CleanError(f"ERROR: file is out of date: '{fname}'.")
print(f'{Clr.SBLU}Updating: {fname}{Clr.RST}')
- with open(fname, 'w') as outfile:
+ with open(fname, 'w', encoding='utf-8') as outfile:
outfile.write(out)
diff --git a/tools/batools/xcode.py b/tools/batools/xcode.py
index 4cc8a08c..e1f89338 100755
--- a/tools/batools/xcode.py
+++ b/tools/batools/xcode.py
@@ -32,7 +32,7 @@ def project_build_path(projroot: str, project_path: str,
try:
if os.path.exists(config_path):
- with open(config_path) as infile:
+ with open(config_path, encoding='utf-8') as infile:
config = json.loads(infile.read())
if (project_path in config
and configuration in config[project_path]):
@@ -84,7 +84,7 @@ def project_build_path(projroot: str, project_path: str,
'timestamp': time.time()
}
os.makedirs(os.path.dirname(config_path), exist_ok=True)
- with open(config_path, 'w') as outfile:
+ with open(config_path, 'w', encoding='utf-8') as outfile:
outfile.write(json.dumps(config))
assert build_dir is not None
diff --git a/tools/efro/error.py b/tools/efro/error.py
index 80bd3ce7..bee7e500 100644
--- a/tools/efro/error.py
+++ b/tools/efro/error.py
@@ -1,6 +1,6 @@
# Released under the MIT License. See LICENSE for details.
#
-"""Functionality for dealing with errors."""
+"""Common errors and related functionality."""
from __future__ import annotations
from typing import TYPE_CHECKING
@@ -33,3 +33,29 @@ class CleanError(Exception):
errstr = str(self)
if errstr:
print(f'{Clr.SRED}{errstr}{Clr.RST}', flush=flush)
+
+
+class TransportError(Exception):
+ """A transport-related communication error has occurred.
+
+ This covers anything network-related going wrong in the sending
+ of data or receiving of a response. This error does not imply
+ that data was not received on the other end; only that a full
+ response round trip was not completed.
+
+ These errors should be gracefully handled whenever possible, as
+ occasional network outages are generally unavoidable.
+ """
+
+
+class RemoteError(Exception):
+ """An error occurred on the other end of some connection.
+
+ This occurs when communication succeeds but another type of error
+ occurs remotely. The error string can consist of a remote stack
+ trace or a simple message depending on the context.
+ """
+
+ def __str__(self) -> str:
+ s = ''.join(str(arg) for arg in self.args)
+ return f'Remote Exception Follows:\n{s}'
diff --git a/tools/efro/message.py b/tools/efro/message.py
index fde338a6..af77fbdd 100644
--- a/tools/efro/message.py
+++ b/tools/efro/message.py
@@ -10,6 +10,7 @@ from typing import TYPE_CHECKING
if TYPE_CHECKING:
from typing import Dict, Type, Tuple, List, Any, Callable, Optional
+ from efro.error import TransportError
class MessageProtocol:
@@ -23,13 +24,24 @@ class MessageProtocol:
message_types: Dict[int, Tuple[Type, List[Type]]],
type_key: str = '_t',
) -> None:
- """Create a protocol with the given settings.
+ """Create a protocol with a given configuration.
Each entry for message_types should contain an ID, a message type,
and all possible response types.
"""
+ self._message_types = message_types
+ self._type_key = type_key
- @classmethod
- def create_sender_module(cls, classname: str) -> str:
+ def message_encode(self, message: Any) -> bytes:
+ """Encode a message to bytes for sending."""
+ print(f'WOULD ENCODE MSG: {message} TO RAW.')
+ return b''
+
+ def message_decode(self, data: bytes) -> Any:
+ """Decode a message from bytes."""
+ print(f'WOULD DECODE MSG FROM RAW: {str(data)}')
+ return 'foo'
+
+ def create_sender_module(self, classname: str) -> str:
""""Create a Python module defining a MessageSender subclass.
This class is primarily for type checking and will contain overrides
@@ -37,8 +49,7 @@ class MessageProtocol:
in the protocol.
"""
- @classmethod
- def create_receiver_module(cls, classname: str) -> str:
+ def create_receiver_module(self, classname: str) -> str:
""""Create a Python module defining a MessageReceiver subclass.
This class is primarily for type checking and will contain overrides
@@ -47,16 +58,6 @@ class MessageProtocol:
"""
-class TransportError(Exception):
- """Error occurring for all transport related errors in messaging.
-
- This covers anything network-related going wrong in the sending
- of data or receiving of the response. This error does not imply
- that the message was not received on the other end; only that a
- complete response round trip was not completed.
- """
-
-
class MessageSender:
"""Facilitates sending messages to a target and receiving responses.
This is instantiated at the class level and used to register unbound
@@ -78,10 +79,11 @@ class MessageSender:
"""
def __init__(
- self, protocol: MessageProtocol,
- send_raw_message: Optional[Callable[[Any, bytes], bytes]]) -> None:
+ self, protocol: MessageProtocol,
+ send_raw_message_call: Optional[Callable[[Any, bytes],
+ bytes]]) -> None:
self._protocol = protocol
- self._send_raw_message = send_raw_message
+ self._send_raw_message_call = send_raw_message_call
def __get__(self, obj: Any, type_in: Any = None) -> Any:
if obj is None:
@@ -93,10 +95,10 @@ class MessageSender:
"""Send a message and receive a response.
Will encode the message for transport and call dispatch_raw_message()
"""
- if self._send_raw_message is None:
+ if self._send_raw_message_call is None:
raise RuntimeError('Unimplemented!')
- print(f'WOULD CONVERT MSG TO RAW: {message}')
- return self._send_raw_message(None, b'')
+ encoded = self._protocol.message_encode(message)
+ return self._send_raw_message_call(None, encoded)
def send_bg(self, message: Any) -> Any:
"""Send a message asynchronously and receive a future.
@@ -112,19 +114,6 @@ class MessageSender:
"""
raise RuntimeError('Unimplemented!')
- # def send_raw_message(self, msg: bytes) -> bytes:
- # """Send a message and get a response synchronously.
- # Should be overridden by child classes if supported.
- # Gets called by send() or in a bg thread by send_bg().
- # """
- # raise TransportError('Unimplemented for this dispatch type.')
-
- # async def send_raw_message_async(self, msg: bytes) -> bytes:
- # """Send a message and get a response via asyncio.
- # Should be overridden by child classes if supported.
- # """
- # raise TransportError('Unimplemented for this dispatch type.')
-
class MessageReceiver:
"""Facilitates receiving & responding to messages from a remote source.
@@ -142,9 +131,12 @@ class MessageReceiver:
def handle_some_message_type(self, message: SomeType) -> AnotherType:
# Deal with this message type here.
- # This will be passed along to the registered handler.
+ # This will trigger the registered handler being called.
obj = MyClass()
obj.receiver.handle_raw_message(some_raw_data)
+
+ Any unhandled Exception occurring during message handling will result in
+ an Exception being raised on the sending end.
"""
def __init__(self, protocol: MessageProtocol) -> None:
@@ -162,23 +154,3 @@ class MessageReceiver:
"""Should be called when the receiver gets a message.
The return value is the raw response to the message.
"""
-
-
-# class SubTest(MessageSender):
-# """Test."""
-
-# if TYPE_CHECKING:
-
-# @overload
-# def send(self, message: str) -> str:
-# pass
-
-# @overload
-# def send(self, message: bool) -> bool:
-# pass
-
-# def send(self, message: Any) -> Any:
-# pass
-
-# blah = SubTest(protocol=MessageProtocol(message_types={}))
-# blah.send('foo')
diff --git a/tools/efrotools/__init__.py b/tools/efrotools/__init__.py
index 07d8d6da..db1546e3 100644
--- a/tools/efrotools/__init__.py
+++ b/tools/efrotools/__init__.py
@@ -37,7 +37,8 @@ def getlocalconfig(projroot: Path) -> Dict[str, Any]:
"""Return a project's localconfig contents (or default if missing)."""
localconfig: Dict[str, Any]
try:
- with open(Path(projroot, 'config/localconfig.json')) as infile:
+ with open(Path(projroot, 'config/localconfig.json'),
+ encoding='utf-8') as infile:
localconfig = json.loads(infile.read())
except FileNotFoundError:
localconfig = {}
@@ -48,7 +49,8 @@ def getconfig(projroot: Path) -> Dict[str, Any]:
"""Return a project's config contents (or default if missing)."""
config: Dict[str, Any]
try:
- with open(Path(projroot, 'config/config.json')) as infile:
+ with open(Path(projroot, 'config/config.json'),
+ encoding='utf-8') as infile:
config = json.loads(infile.read())
except FileNotFoundError:
config = {}
@@ -84,13 +86,13 @@ def get_public_license(style: str) -> str:
def readfile(path: Union[str, Path]) -> str:
"""Read a text file and return a str."""
- with open(path) as infile:
+ with open(path, encoding='utf-8') as infile:
return infile.read()
def writefile(path: Union[str, Path], txt: str) -> None:
"""Write a string to a file."""
- with open(path, 'w') as outfile:
+ with open(path, 'w', encoding='utf-8') as outfile:
outfile.write(txt)
@@ -157,7 +159,7 @@ def py_examine(projroot: Path, filename: Path, line: int, column: int,
# Pull in our pylint plugin which really just adds astroid filters.
# That way our introspection here will see the same thing as pylint's does.
- with open(filename) as infile:
+ with open(filename, encoding='utf-8') as infile:
fcontents = infile.read()
if '#@' in fcontents:
raise Exception('#@ marker found in file; this breaks examinations.')
diff --git a/tools/efrotools/android.py b/tools/efrotools/android.py
index 64738fbb..83b17631 100644
--- a/tools/efrotools/android.py
+++ b/tools/efrotools/android.py
@@ -23,7 +23,7 @@ def filter_gradle_file(buildfilename: str, enabled_tags: Set[str]) -> None:
sections: List[GradleFilterSection] = []
- with open(buildfilename) as infile:
+ with open(buildfilename, encoding='utf-8') as infile:
original = infile.read()
lines = original.splitlines()
@@ -61,5 +61,5 @@ def filter_gradle_file(buildfilename: str, enabled_tags: Set[str]) -> None:
# Only write if its not changed (potentially avoid triggering builds).
out = '\n'.join(lines) + '\n'
if out != original:
- with open(buildfilename, 'w') as outfile:
+ with open(buildfilename, 'w', encoding='utf-8') as outfile:
outfile.write(out)
diff --git a/tools/efrotools/code.py b/tools/efrotools/code.py
index c4bb26a9..6decf388 100644
--- a/tools/efrotools/code.py
+++ b/tools/efrotools/code.py
@@ -198,7 +198,7 @@ def _should_include_script(fnamefull: str) -> bool:
# Look for 'binary' scripts with no extensions too.
if not fname.startswith('.') and '.' not in fname:
try:
- with open(fnamefull) as infile:
+ with open(fnamefull, encoding='utf-8') as infile:
line = infile.readline()
if '/usr/bin/env python' in line or '/usr/bin/python' in line:
return True
@@ -726,7 +726,7 @@ def _run_idea_inspections_cached(cachepath: Path,
current_hash = md5.hexdigest()
existing_hash: Optional[str]
try:
- with open(cachepath) as infile2:
+ with open(cachepath, encoding='utf-8') as infile2:
existing_hash = json.loads(infile2.read())['hash']
except Exception:
existing_hash = None
@@ -738,7 +738,7 @@ def _run_idea_inspections_cached(cachepath: Path,
verbose=verbose,
inspectdir=inspectdir)
cachepath.parent.mkdir(parents=True, exist_ok=True)
- with open(cachepath, 'w') as outfile:
+ with open(cachepath, 'w', encoding='utf-8') as outfile:
outfile.write(json.dumps({'hash': current_hash}))
print(
f'{Clr.GRN}{displayname}: all {len(filenames)}'
diff --git a/tools/efrotools/efrocache.py b/tools/efrotools/efrocache.py
index 831976f5..27aabc14 100644
--- a/tools/efrotools/efrocache.py
+++ b/tools/efrotools/efrocache.py
@@ -66,7 +66,7 @@ def get_target(path: str) -> None:
path = _project_centric_path(path)
- with open(CACHE_MAP_NAME) as infile:
+ with open(CACHE_MAP_NAME, encoding='utf-8') as infile:
efrocachemap = json.loads(infile.read())
if path not in efrocachemap:
raise RuntimeError(f'Path not found in efrocache: {path}')
@@ -218,7 +218,7 @@ def update_cache(makefile_dirs: List[str]) -> None:
# is exactly the same as last time we can skip this step.
hashes = _gen_hashes(fnames1 + fnames2)
if os.path.isfile(UPLOAD_STATE_CACHE_FILE):
- with open(UPLOAD_STATE_CACHE_FILE) as infile:
+ with open(UPLOAD_STATE_CACHE_FILE, encoding='utf-8') as infile:
hashes_existing = infile.read()
else:
hashes_existing = ''
@@ -234,7 +234,7 @@ def update_cache(makefile_dirs: List[str]) -> None:
# Write the cache state so we can skip the next run if nothing changes.
os.makedirs(os.path.dirname(UPLOAD_STATE_CACHE_FILE), exist_ok=True)
- with open(UPLOAD_STATE_CACHE_FILE, 'w') as outfile:
+ with open(UPLOAD_STATE_CACHE_FILE, 'w', encoding='utf-8') as outfile:
outfile.write(hashes)
@@ -352,10 +352,11 @@ def _write_cache_files(fnames1: List[str], fnames2: List[str],
'subprocess.run(["rm", "-rf", "efrocache", "genstartercache.py"])\n'
'print("Starter cache generation complete!", flush=True)\n')
- with open('build/efrocache/genstartercache.py', 'w') as outfile:
+ with open('build/efrocache/genstartercache.py', 'w',
+ encoding='utf-8') as outfile:
outfile.write(script)
- with open(mapping_file, 'w') as outfile:
+ with open(mapping_file, 'w', encoding='utf-8') as outfile:
outfile.write(json.dumps(mapping, indent=2, sort_keys=True))
@@ -437,7 +438,7 @@ def warm_start_cache() -> None:
# each time the cache map changes. It is much more efficient to do
# it in one go here.
cachemap: Dict[str, str]
- with open(CACHE_MAP_NAME) as infile:
+ with open(CACHE_MAP_NAME, encoding='utf-8') as infile:
cachemap = json.loads(infile.read())
assert isinstance(cachemap, dict)
cachemap_mtime = os.path.getmtime(CACHE_MAP_NAME)
diff --git a/tools/efrotools/filecache.py b/tools/efrotools/filecache.py
index e43f5a04..bf3764d8 100644
--- a/tools/efrotools/filecache.py
+++ b/tools/efrotools/filecache.py
@@ -8,8 +8,13 @@ import json
import os
from typing import TYPE_CHECKING
-from efrotools import get_files_hash
+# Pylint's preferred import order here seems non-deterministic (as of 2.10.1).
+# pylint: disable=useless-suppression
+# pylint: disable=wrong-import-order
from efro.terminal import Clr
+from efrotools import get_files_hash
+# pylint: enable=wrong-import-order
+# pylint: enable=useless-suppression
if TYPE_CHECKING:
from typing import Dict, Optional, Sequence, Any
@@ -27,7 +32,7 @@ class FileCache:
if not os.path.exists(path):
self.entries = {}
else:
- with open(path, 'r') as infile:
+ with open(path, 'r', encoding='utf-8') as infile:
self.entries = json.loads(infile.read())
def update(self, filenames: Sequence[str], extrahash: str) -> None:
diff --git a/tools/efrotools/pcommand.py b/tools/efrotools/pcommand.py
index d85263e7..9df0497a 100644
--- a/tools/efrotools/pcommand.py
+++ b/tools/efrotools/pcommand.py
@@ -123,7 +123,7 @@ def _spelling(words: List[str]) -> None:
]:
if not os.path.exists(fname):
continue
- with open(fname) as infile:
+ with open(fname, encoding='utf-8') as infile:
lines = infile.read().splitlines()
if lines[2] != ' ':
raise RuntimeError('Unexpected dictionary format.')
@@ -134,7 +134,7 @@ def _spelling(words: List[str]) -> None:
lines.insert(3, line)
added_count += 1
- with open(fname, 'w') as outfile:
+ with open(fname, 'w', encoding='utf-8') as outfile:
# Sort lines in the words section.
assert all(l.startswith(' ') for l in lines[3:-3])
@@ -252,14 +252,14 @@ def formatscripts() -> None:
def formatmakefile() -> None:
"""Format the main makefile."""
from efrotools.makefile import Makefile
- with open('Makefile') as infile:
+ with open('Makefile', encoding='utf-8') as infile:
original = infile.read()
formatted = Makefile(original).get_output()
# Only write if it changed.
if formatted != original:
- with open('Makefile', 'w') as outfile:
+ with open('Makefile', 'w', encoding='utf-8') as outfile:
outfile.write(formatted)
@@ -582,7 +582,7 @@ def makefile_target_list() -> None:
if len(sys.argv) != 3:
raise RuntimeError('Expected exactly one filename arg.')
- with open(sys.argv[2]) as infile:
+ with open(sys.argv[2], encoding='utf-8') as infile:
lines = infile.readlines()
def _docstr(lines2: List[str], linenum: int) -> str:
diff --git a/tools/efrotools/pybuild.py b/tools/efrotools/pybuild.py
index 5c97309d..8b1b63d3 100644
--- a/tools/efrotools/pybuild.py
+++ b/tools/efrotools/pybuild.py
@@ -713,7 +713,8 @@ def gather() -> None:
# Write a master pyconfig header that reroutes to each
# platform's actual header.
- with open(header_dst + '/pyconfig.h', 'w') as hfile:
+ with open(header_dst + '/pyconfig.h', 'w',
+ encoding='utf-8') as hfile:
hfile.write(
'#if BA_OSTYPE_MACOS\n'
'#include "pyconfig-macos.h"\n\n'
diff --git a/tools/efrotools/pylintplugins.py b/tools/efrotools/pylintplugins.py
index 4b6c3c0c..d3fdca8f 100644
--- a/tools/efrotools/pylintplugins.py
+++ b/tools/efrotools/pylintplugins.py
@@ -9,7 +9,7 @@ from typing import TYPE_CHECKING
import astroid
if TYPE_CHECKING:
- from astroid import node_classes as nc
+ from astroid import nodes as nc
from typing import Set, Dict, Any, List
VERBOSE = False
diff --git a/tools/efrotools/statictest.py b/tools/efrotools/statictest.py
index 8a9456a0..0da0f0bb 100644
--- a/tools/efrotools/statictest.py
+++ b/tools/efrotools/statictest.py
@@ -46,7 +46,7 @@ class StaticTestFile:
self.linetypes_mypy: Dict[int, str] = {}
print(f'Running Mypy static testing on "{filename}"...')
- with open(filename, 'r') as infile:
+ with open(filename, 'r', encoding='utf-8') as infile:
fdata = infile.read()
# Make sure we're running where the config is..
@@ -63,7 +63,7 @@ class StaticTestFile:
# instances of static_type_equals(), and run mypy type checks
# in those places to get static types.
tempfilepath = os.path.join(_tempdir.name, self.modulename + '.py')
- with open(tempfilepath, 'w') as outfile:
+ with open(tempfilepath, 'w', encoding='utf-8') as outfile:
outfile.write(self.filter_file_contents(fdata))
results = subprocess.run(
[