Cleaning up python bootstrapping code

This commit is contained in:
Eric Froemling 2021-04-15 14:23:42 -07:00
parent ff1001e41e
commit 97c14e7e10
No known key found for this signature in database
GPG Key ID: 89C93F0F8D6D5A98
10 changed files with 203 additions and 206 deletions

View File

@ -3932,40 +3932,40 @@
"assets/build/windows/Win32/ucrtbased.dll": "https://files.ballistica.net/cache/ba1/b5/85/f8b6d0558ddb87267f34254b1450", "assets/build/windows/Win32/ucrtbased.dll": "https://files.ballistica.net/cache/ba1/b5/85/f8b6d0558ddb87267f34254b1450",
"assets/build/windows/Win32/vc_redist.x86.exe": "https://files.ballistica.net/cache/ba1/1c/e1/4a1a2eddda2f4aebd5f8b64ab08e", "assets/build/windows/Win32/vc_redist.x86.exe": "https://files.ballistica.net/cache/ba1/1c/e1/4a1a2eddda2f4aebd5f8b64ab08e",
"assets/build/windows/Win32/vcruntime140d.dll": "https://files.ballistica.net/cache/ba1/50/8d/bc2600ac9491f1b14d659709451f", "assets/build/windows/Win32/vcruntime140d.dll": "https://files.ballistica.net/cache/ba1/50/8d/bc2600ac9491f1b14d659709451f",
"build/prefab/full/linux_arm64/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/77/45/69456cef2c9fddd5bb6a3497972b", "build/prefab/full/linux_arm64/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/27/04/dcdd63a27fa11e8dc5bb02dc8209",
"build/prefab/full/linux_arm64/release/ballisticacore": "https://files.ballistica.net/cache/ba1/87/6d/1bc0e1c667216e278e7c95050955", "build/prefab/full/linux_arm64/release/ballisticacore": "https://files.ballistica.net/cache/ba1/ab/0d/18fc7018dd73403b6e6d8739808d",
"build/prefab/full/linux_arm64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/f6/b1/713661602c7580e060a5b2f5391c", "build/prefab/full/linux_arm64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/f8/74/457bda7850348dbcdd235cfa9dcb",
"build/prefab/full/linux_arm64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/a4/2c/526bd742d1a1763d500bbfdfd763", "build/prefab/full/linux_arm64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/f7/a3/26eaba91b4384e2838cadad11ff6",
"build/prefab/full/linux_x86_64/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/30/f1/57680bd744ac6825dc44641c7f3f", "build/prefab/full/linux_x86_64/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/ef/80/64406ad4b21b8c75c3cf7dba3c98",
"build/prefab/full/linux_x86_64/release/ballisticacore": "https://files.ballistica.net/cache/ba1/71/5a/738514f3af01ecf3c806239e22e6", "build/prefab/full/linux_x86_64/release/ballisticacore": "https://files.ballistica.net/cache/ba1/bc/d4/5efe586e439de930ccbba8869c06",
"build/prefab/full/linux_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/20/26/2145ff72ccd4f408a27da6b42b82", "build/prefab/full/linux_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/7c/65/b95d5b30731ed29e04cb10572fe5",
"build/prefab/full/linux_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/34/e1/9dc07498dd96f73540290c884287", "build/prefab/full/linux_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/8e/7f/e179cf95dd54e437bfd22961afd7",
"build/prefab/full/mac_arm64/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/e5/99/22214c0b91bcc3298568c87b6487", "build/prefab/full/mac_arm64/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/88/73/911011e52f90d326ddf9cea03c84",
"build/prefab/full/mac_arm64/release/ballisticacore": "https://files.ballistica.net/cache/ba1/75/ae/0d37c72901fdfb64e8104aa1406f", "build/prefab/full/mac_arm64/release/ballisticacore": "https://files.ballistica.net/cache/ba1/4b/21/08678761775f2207e2f62994f79b",
"build/prefab/full/mac_arm64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/cb/24/c5bb95114f793bc857504446a59e", "build/prefab/full/mac_arm64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/94/0a/baa2efe432186b7b3c2c20883aed",
"build/prefab/full/mac_arm64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/53/0e/6725c8e8fa2c25e431e10b40384d", "build/prefab/full/mac_arm64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/ff/95/6668eb64a8c30470c223f217baaf",
"build/prefab/full/mac_x86_64/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/06/5e/694a00eb244e4925a1c3f74b5f02", "build/prefab/full/mac_x86_64/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/64/34/675c88ad196cbebdb0c19b51af41",
"build/prefab/full/mac_x86_64/release/ballisticacore": "https://files.ballistica.net/cache/ba1/32/f1/3a137d806dff7845d7e2dc766452", "build/prefab/full/mac_x86_64/release/ballisticacore": "https://files.ballistica.net/cache/ba1/56/2d/e58c62aecbf09c9d68be9fb4ef39",
"build/prefab/full/mac_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/f2/c6/b97c5eeea5307d0c679a9d2fa869", "build/prefab/full/mac_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/d9/2f/f3faa8cc336fddf54c55b9b35e7d",
"build/prefab/full/mac_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/76/80/08157eac8b97c0d68802f90120f9", "build/prefab/full/mac_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/c0/b7/26d1c7b5a21cae8dc6911d02c5db",
"build/prefab/full/windows_x86/debug/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/c8/88/996fd4b6e10df4afbd23263da36f", "build/prefab/full/windows_x86/debug/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/1d/5e/ef2468327ef8ec8a0023219e251c",
"build/prefab/full/windows_x86/release/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/27/98/b2fff1ad82e5cbc25eac397e4ae6", "build/prefab/full/windows_x86/release/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/3b/2a/f7f7ba687d28f429e416472c0c85",
"build/prefab/full/windows_x86_server/debug/dist/ballisticacore_headless.exe": "https://files.ballistica.net/cache/ba1/c1/69/62108258025dea6f8e7488e0e802", "build/prefab/full/windows_x86_server/debug/dist/ballisticacore_headless.exe": "https://files.ballistica.net/cache/ba1/bc/a1/703f357d2248c8b5674fa8bc3b8a",
"build/prefab/full/windows_x86_server/release/dist/ballisticacore_headless.exe": "https://files.ballistica.net/cache/ba1/3d/4d/b6cee501623ef5d78fb15bc87215", "build/prefab/full/windows_x86_server/release/dist/ballisticacore_headless.exe": "https://files.ballistica.net/cache/ba1/82/c9/a5811e1a80394e227366778380c1",
"build/prefab/lib/linux_arm64/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/b8/55/25ff15308d89838699e158024f9c", "build/prefab/lib/linux_arm64/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/9f/e1/17ec8710e6f9354dec40b134d963",
"build/prefab/lib/linux_arm64/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/dd/84/13497d8139e3b6095a87940188af", "build/prefab/lib/linux_arm64/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/02/90/d56f5d876b4d128f4c52a2c2ab01",
"build/prefab/lib/linux_arm64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/b5/de/6e368b0f405c9fbed0e9e6ab0e6a", "build/prefab/lib/linux_arm64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/25/0e/d813586a741bf4d15623899d5f74",
"build/prefab/lib/linux_arm64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/86/f0/6296ed8275094c2c1c96bbe98b56", "build/prefab/lib/linux_arm64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/e2/dc/3164ed18198ff5021746a5e2e3a6",
"build/prefab/lib/linux_x86_64/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/d9/7c/f166ddf0d1720ccea1720a1c2965", "build/prefab/lib/linux_x86_64/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/0c/be/6c258d57bf30c94b3dc7a03281e1",
"build/prefab/lib/linux_x86_64/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/75/b3/5cad6cf47697b6f03bea6369ef5b", "build/prefab/lib/linux_x86_64/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/bd/f9/3b68d1abda8ba917f7084a675009",
"build/prefab/lib/linux_x86_64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/d5/3a/fa01e178b6da2879864b50252c98", "build/prefab/lib/linux_x86_64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/4e/26/0f15c8533717ffeeae0b388546c2",
"build/prefab/lib/linux_x86_64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/48/4d/a3d1385420e5866559c210c584f1", "build/prefab/lib/linux_x86_64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/fb/89/e6c72ccad50cc3c0a34c4764d72c",
"build/prefab/lib/mac_arm64/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/6e/3e/1f7b71a7639703bb13d71fb36ae9", "build/prefab/lib/mac_arm64/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/79/38/7fed4d2f7f8e158c44939ca6f09f",
"build/prefab/lib/mac_arm64/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/8e/c1/373ad978ad9807f64baf6215a230", "build/prefab/lib/mac_arm64/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/1b/cd/1643ee0514a1fc57071b673c283e",
"build/prefab/lib/mac_arm64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/8a/d6/17652e3a94201b26b228d2fc53df", "build/prefab/lib/mac_arm64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/a0/2d/413df7c810b283fa6d67b41e1fbe",
"build/prefab/lib/mac_arm64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/7a/36/3267309a349385e52af77a2738a1", "build/prefab/lib/mac_arm64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/ff/e9/8eba3a5244b95e17887568fa3540",
"build/prefab/lib/mac_x86_64/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/b8/d1/0318bfa712c7e6ef0d46c9b25033", "build/prefab/lib/mac_x86_64/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/f7/4e/e98d85cbc5eeb67d916493776e53",
"build/prefab/lib/mac_x86_64/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/58/5f/1f0121bd900ad5345e9a27cd6198", "build/prefab/lib/mac_x86_64/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/a7/53/c9f849fc8985b5dbad0044b3e967",
"build/prefab/lib/mac_x86_64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/be/53/9de41660d67305ac5669141e557c", "build/prefab/lib/mac_x86_64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/17/ea/1fd0bcc743cc452c898d04bf9da2",
"build/prefab/lib/mac_x86_64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/27/d6/b5d5e1a56aaa00acf174dc609b59" "build/prefab/lib/mac_x86_64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/7f/14/bfe45f3ce7a374810f5d91204c80"
} }

View File

@ -2048,6 +2048,7 @@
<w>stickman</w> <w>stickman</w>
<w>storable</w> <w>storable</w>
<w>storagename</w> <w>storagename</w>
<w>storecmd</w>
<w>storedhash</w> <w>storedhash</w>
<w>storeitemui</w> <w>storeitemui</w>
<w>storename</w> <w>storename</w>

View File

@ -895,6 +895,7 @@
<w>stephane</w> <w>stephane</w>
<w>stepnum</w> <w>stepnum</w>
<w>stepsize</w> <w>stepsize</w>
<w>storecmd</w>
<w>strcasecmp</w> <w>strcasecmp</w>
<w>strchr</w> <w>strchr</w>
<w>strcpy</w> <w>strcpy</w>

View File

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

View File

@ -21,7 +21,7 @@
namespace ballistica { namespace ballistica {
// These are set automatically via script; don't change here. // These are set automatically via script; don't change here.
const int kAppBuildNumber = 20339; const int kAppBuildNumber = 20340;
const char* kAppVersion = "1.6.0"; const char* kAppVersion = "1.6.0";
// Our standalone globals. // Our standalone globals.

View File

@ -947,7 +947,9 @@ void Python::Reset(bool do_init) {
+ std::string(ver)); + std::string(ver));
} }
StoreObj(ObjID::kEmptyTuple, PyTuple_New(0)); // Create a dict for execing our bootstrap code in so
// we don't pollute the __main__ namespace.
auto bootstrap_context{PythonRef(PyDict_New(), PythonRef::kSteal)};
// Get the app up and running. // Get the app up and running.
// Run a few core bootstrappy things first: // Run a few core bootstrappy things first:
@ -956,21 +958,17 @@ void Python::Reset(bool do_init) {
// - import and instantiate our app-state class // - import and instantiate our app-state class
#include "generated/ballistica/bootstrap.inc" #include "generated/ballistica/bootstrap.inc"
int result = PyRun_SimpleString(bootstrap_code); PyObject* result =
if (result != 0) { PyRun_String(bootstrap_code, Py_file_input, bootstrap_context.get(),
bootstrap_context.get());
if (result == nullptr) {
PyErr_PrintEx(0); PyErr_PrintEx(0);
// Throw a simple exception so we don't get a stack trace. // Throw a simple exception so we don't get a stack trace.
throw std::logic_error( throw std::logic_error(
"Error in ba Python bootstrapping. See log for details."); "Error in ba Python bootstrapping. See log for details.");
} }
PyObject* appstate = PythonCommand("app_state", "<AppStateFetch>") Py_DECREF(result);
.RunReturnObj(false, nullptr);
if (appstate == nullptr) {
throw Exception("Unable to get value: '" + std::string("app_state")
+ "'.");
}
StoreObj(ObjID::kApp, appstate);
// Import and grab all the Python stuff we use. // Import and grab all the Python stuff we use.
#include "generated/ballistica/binding.inc" #include "generated/ballistica/binding.inc"

View File

@ -4,25 +4,36 @@
# Run make update to update the project after editing this.. # Run make update to update the project after editing this..
# pylint: disable=missing-module-docstring, missing-function-docstring # pylint: disable=missing-module-docstring, missing-function-docstring
# pylint: disable=line-too-long # pylint: disable=line-too-long
def get_binding_values() -> object: from __future__ import annotations
from ba import _hooks
import _ba import json
import json import copy
import copy from typing import TYPE_CHECKING
import ba
from ba import _language import ba
from ba import _music from ba import _language
from ba import _input from ba import _music
from ba import _apputils from ba import _input
from ba import _account from ba import _apputils
from ba import _dependency from ba import _dependency
from ba import _enums from ba import _enums
from ba import _player from ba import _player
# FIXME: There should be no bastd in here; from ba import _hooks
# should pull in bases from ba which get overridden by bastd (or other). import _ba
from bastd.ui.onscreenkeyboard import OnScreenKeyboardWindow
from bastd.ui import party # FIXME: There should be no bastd in here;
# should pull in bases from ba which get overridden by bastd (or other).
from bastd.ui.onscreenkeyboard import OnScreenKeyboardWindow
from bastd.ui import party
if TYPE_CHECKING:
from typing import Tuple, Any
def get_binding_values() -> Tuple[Any, ...]:
return ( return (
ba.app, # kApp
tuple(), # kEmptyTuple
_ba.client_info_query_response, # kClientInfoQueryResponseCall _ba.client_info_query_response, # kClientInfoQueryResponseCall
_hooks.reset_to_main_menu, # kResetToMainMenuCall _hooks.reset_to_main_menu, # kResetToMainMenuCall
_hooks.set_config_fullscreen_on, # kSetConfigFullscreenOnCall _hooks.set_config_fullscreen_on, # kSetConfigFullscreenOnCall

View File

@ -3,145 +3,136 @@
from __future__ import annotations from __future__ import annotations
import sys
import signal
import threading
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
import _ba
if TYPE_CHECKING: if TYPE_CHECKING:
from typing import Any, TextIO, Callable, List from typing import Any, TextIO, Callable, List
def _ballistica_bootstrap() -> object: class _BAConsoleRedirect:
import sys
import signal
import _ba
import threading
class _BAConsoleRedirect: def __init__(self, original: TextIO, call: Callable[[str], None]) -> None:
self._lock = threading.Lock()
self._linebits: List[str] = []
self._original = original
self._call = call
self._pending_ship = False
def __init__(self, original: TextIO, call: Callable[[str], def write(self, sval: Any) -> None:
None]) -> None: """Override standard stdout write."""
self._lock = threading.Lock()
self._linebits: List[str] = []
self._original = original
self._call = call
self._pending_ship = False
def write(self, sval: Any) -> None: self._call(sval)
"""Override standard stdout write."""
self._call(sval) # Now do logging:
# Add it to our accumulated line.
# If the message ends in a newline, we can ship it
# immediately as a log entry. Otherwise, schedule a ship
# next cycle (if it hasn't yet at that point) so that we
# can accumulate subsequent prints.
# (so stuff like print('foo', 123, 'bar') will ship as one entry)
with self._lock:
self._linebits.append(sval)
if sval.endswith('\n'):
self._shiplog()
else:
_ba.pushcall(self._shiplog,
from_other_thread=True,
suppress_other_thread_warning=True)
# Now do logging: def _shiplog(self) -> None:
# Add it to our accumulated line. with self._lock:
# If the message ends in a newline, we can ship it line = ''.join(self._linebits)
# immediately as a log entry. Otherwise, schedule a ship if not line:
# next cycle (if it hasn't yet at that point) so that we return
# can accumulate subsequent prints. self._linebits = []
# (so stuff like print('foo', 123, 'bar') will ship as one entry)
with self._lock:
self._linebits.append(sval)
if sval.endswith('\n'):
self._shiplog()
else:
_ba.pushcall(self._shiplog,
from_other_thread=True,
suppress_other_thread_warning=True)
def _shiplog(self) -> None: # Log messages aren't expected to have trailing newlines.
with self._lock: if line.endswith('\n'):
line = ''.join(self._linebits) line = line[:-1]
if not line: _ba.log(line, to_stdout=False)
return
self._linebits = []
# Log messages aren't expected to have trailing newlines. def flush(self) -> None:
if line.endswith('\n'): """Flush the file."""
line = line[:-1] self._original.flush()
_ba.log(line, to_stdout=False)
def flush(self) -> None: def isatty(self) -> bool:
"""Flush the file.""" """Are we a terminal?"""
self._original.flush() return self._original.isatty()
def isatty(self) -> bool:
"""Are we a terminal?"""
return self._original.isatty()
sys.stdout = _BAConsoleRedirect( # type: ignore
sys.stdout, _ba.print_stdout)
sys.stderr = _BAConsoleRedirect( # type: ignore
sys.stderr, _ba.print_stderr)
# Let's lookup mods first (so users can do whatever they want).
# and then our bundled scripts last (don't want to encourage dists
# overriding proper python functionality)
sys.path.insert(0, _ba.env()['python_directory_user'])
sys.path.append(_ba.env()['python_directory_app'])
sys.path.append(_ba.env()['python_directory_app_site'])
# Tell Python to not handle SIGINT itself (it normally generates
# KeyboardInterrupts which make a mess; we want to do a simple
# clean exit). We capture interrupts per-platform in the C++ layer.
# I tried creating a handler in Python but it seemed to often have
# a delay of up to a second before getting called. (not a huge deal
# but I'm picky).
signal.signal(signal.SIGINT, signal.SIG_DFL) # Do default handling.
# ..though it turns out we need to set up our C signal handling AFTER
# we've told Python to disable its own; otherwise (on Mac at least) it
# wipes out our existing C handler.
_ba.setup_sigint()
# Sanity check: we should always be run in UTF-8 mode.
if sys.flags.utf8_mode != 1:
print('ERROR: Python\'s UTF-8 mode is not set.'
' This will likely result in errors.')
# FIXME: I think we should init Python in the main thread, which should
# also avoid these issues. (and also might help us play better with
# Python debuggers?)
# Gloriously hacky workaround here:
# Our 'main' Python thread is the game thread (not the app's main
# thread) which means it has a small stack compared to the main
# thread (at least on apple). Sadly it turns out this causes the
# debug build of Python to blow its stack immediately when doing
# some big imports.
# Normally we'd just give the game thread the same stack size as
# the main thread and that'd be the end of it. However
# we're using std::threads which it turns out have no way to set
# the stack size (as of fall '19). Grumble.
#
# However python threads *can* take custom stack sizes.
# (and it appears they might use the main thread's by default?..)
# ...so as a workaround in the debug version, we can run problematic
# heavy imports here in another thread and all is well.
# If we ever see stack overflows in our release build we'll have
# to take more drastic measures like switching from std::threads
# to pthreads.
if _ba.env()['debug_build']:
def _thread_func() -> None:
# pylint: disable=unused-import
import json
import urllib.request
testthread = threading.Thread(target=_thread_func)
testthread.start()
testthread.join()
# Now spin up our App instance, store it on both _ba and ba,
# and return it to the C++ layer.
# noinspection PyProtectedMember
from ba._app import App
import ba
app = App()
_ba.app = app
ba.app = app
return app
app_state = _ballistica_bootstrap() sys.stdout = _BAConsoleRedirect(sys.stdout, _ba.print_stdout) # type: ignore
sys.stderr = _BAConsoleRedirect(sys.stderr, _ba.print_stderr) # type: ignore
# This code runs in the main namespace, so clean up after ourself. # Let's lookup mods first (so users can do whatever they want).
del _ballistica_bootstrap, TYPE_CHECKING # and then our bundled scripts last (don't want to encourage dists
# overriding proper python functionality)
sys.path.insert(0, _ba.env()['python_directory_user'])
sys.path.append(_ba.env()['python_directory_app'])
sys.path.append(_ba.env()['python_directory_app_site'])
# Tell Python to not handle SIGINT itself (it normally generates
# KeyboardInterrupts which make a mess; we want to do a simple
# clean exit). We capture interrupts per-platform in the C++ layer.
# I tried creating a handler in Python but it seemed to often have
# a delay of up to a second before getting called. (not a huge deal
# but I'm picky).
signal.signal(signal.SIGINT, signal.SIG_DFL) # Do default handling.
# ..though it turns out we need to set up our C signal handling AFTER
# we've told Python to disable its own; otherwise (on Mac at least) it
# wipes out our existing C handler.
_ba.setup_sigint()
# Sanity check: we should always be run in UTF-8 mode.
if sys.flags.utf8_mode != 1:
print('ERROR: Python\'s UTF-8 mode is not set.'
' This will likely result in errors.')
# FIXME: I think we should init Python in the main thread, which should
# also avoid these issues. (and also might help us play better with
# Python debuggers?)
# Gloriously hacky workaround here:
# Our 'main' Python thread is the game thread (not the app's main
# thread) which means it has a small stack compared to the main
# thread (at least on apple). Sadly it turns out this causes the
# debug build of Python to blow its stack immediately when doing
# some big imports.
# Normally we'd just give the game thread the same stack size as
# the main thread and that'd be the end of it. However
# we're using std::threads which it turns out have no way to set
# the stack size (as of fall '19). Grumble.
#
# However python threads *can* take custom stack sizes.
# (and it appears they might use the main thread's by default?..)
# ...so as a workaround in the debug version, we can run problematic
# heavy imports here in another thread and all is well.
# If we ever see stack overflows in our release build we'll have
# to take more drastic measures like switching from std::threads
# to pthreads.
if _ba.env()['debug_build']:
def _thread_func() -> None:
# pylint: disable=unused-import
import json
import urllib.request
testthread = threading.Thread(target=_thread_func)
testthread.start()
testthread.join()
del testthread
# Now spin up our App instance, store it on both _ba and ba,
# and return it to the C++ layer.
# noinspection PyProtectedMember
# pylint: disable=wrong-import-position
from ba._app import App
import ba
_ba.app = ba.app = App()

View File

@ -96,7 +96,7 @@ def _lazybuild_check_paths(inpaths: List[str], category: SourceCategory,
def _testpath(path: str) -> bool: def _testpath(path: str) -> bool:
# Now see this path is newer than our target.. # Now see this path is newer than our target..
if mtime is None or os.path.getmtime(path) >= mtime: if mtime is None or os.path.getmtime(path) >= mtime:
print(f'{Clr.SMAG}Build of {tnamepretty} triggered by' print(f'{Clr.SMAG}Build of {tnamepretty} triggered by change in'
f" '{path}'{Clr.RST}") f" '{path}'{Clr.RST}")
return True return True
return False return False

View File

@ -69,8 +69,9 @@ def gen_binding_code(projroot: str, in_path: str, out_path: str) -> None:
# Our C++ code first execs our input as a string. # Our C++ code first execs our input as a string.
ccode = ('{const char* bindcode = ' + repr(pycode).replace("'", '"') + ';') ccode = ('{const char* bindcode = ' + repr(pycode).replace("'", '"') + ';')
ccode += ('\nint result = PyRun_SimpleString(bindcode);\n' ccode += ('\nPyObject* result = PyRun_String(bindcode, Py_file_input,'
'if (result != 0) {\n' ' bootstrap_context.get(), bootstrap_context.get());\n'
'if (result == nullptr) {\n'
' PyErr_PrintEx(0);\n' ' PyErr_PrintEx(0);\n'
' // Use a standard error to avoid a useless stack trace.\n' ' // Use a standard error to avoid a useless stack trace.\n'
' throw std::logic_error("Error fetching required Python' ' throw std::logic_error("Error fetching required Python'
@ -80,7 +81,7 @@ def gen_binding_code(projroot: str, in_path: str, out_path: str) -> None:
# Then it grabs the function that was defined and runs it. # Then it grabs the function that was defined and runs it.
ccode += ('PyObject* bindvals = PythonCommand("get_binding_values()",' ccode += ('PyObject* bindvals = PythonCommand("get_binding_values()",'
' "<get_binding_values>")' ' "<get_binding_values>")'
'.RunReturnObj(true, nullptr);\n' '.RunReturnObj(true, bootstrap_context.get());\n'
'if (bindvals == nullptr) {\n' 'if (bindvals == nullptr) {\n'
' // Use a standard error to avoid a useless stack trace.\n' ' // Use a standard error to avoid a useless stack trace.\n'
' throw std::logic_error("Error binding required Python' ' throw std::logic_error("Error binding required Python'
@ -89,18 +90,12 @@ def gen_binding_code(projroot: str, in_path: str, out_path: str) -> None:
# Then it pulls the individual values out of the returned tuple. # Then it pulls the individual values out of the returned tuple.
for i, line in enumerate(lines): for i, line in enumerate(lines):
ccode += ('StoreObjCallable(ObjID::' + line[1] + storecmd = ('StoreObjCallable' if line[1].endswith('Class')
', PyTuple_GET_ITEM(bindvals, ' + str(i) + '), true);\n') or line[1].endswith('Call') else 'StoreObj')
ccode += (f'{storecmd}(ObjID::{line[1]},'
f' PyTuple_GET_ITEM(bindvals, {i}), true);\n')
# Lastly it cleans up after itself. ccode += 'Py_DECREF(bindvals);\n}\n'
ccode += ('result = PyRun_SimpleString("del get_binding_values");\n'
'if (result != 0) {\n'
' PyErr_PrintEx(0);\n'
' // Use a standard error to avoid a useless stack trace.\n'
' throw std::logic_error("Error cleaning up after Python'
' binding.");\n'
'}\n'
'}\n')
pretty_path = os.path.abspath(out_path) pretty_path = os.path.abspath(out_path)
if pretty_path.startswith(projroot + '/'): if pretty_path.startswith(projroot + '/'):
pretty_path = pretty_path[len(projroot) + 1:] pretty_path = pretty_path[len(projroot) + 1:]