diff --git a/.efrocachemap b/.efrocachemap index 530fddc6..94ca4119 100644 --- a/.efrocachemap +++ b/.efrocachemap @@ -4072,18 +4072,18 @@ "build/prefab/full/linux_x86_64_gui/release/ballisticakit": "ab0cb59c874beb50e554d3d7c552565e", "build/prefab/full/linux_x86_64_server/debug/dist/ballisticakit_headless": "dec2c09bd136aa413b9b94729f7d6d2e", "build/prefab/full/linux_x86_64_server/release/dist/ballisticakit_headless": "a5c43eb8befc810d693ec9c9f83fcba4", - "build/prefab/full/mac_arm64_gui/debug/ballisticakit": "92bef80f4f6a5feda383035470ea2131", + "build/prefab/full/mac_arm64_gui/debug/ballisticakit": "56bf9b14517f0a579964b39218498fcf", "build/prefab/full/mac_arm64_gui/release/ballisticakit": "d6a10e3a6bc1a07609598f962439c6c8", - "build/prefab/full/mac_arm64_server/debug/dist/ballisticakit_headless": "0150dcf48cb6a34adecaaa27d8940d29", + "build/prefab/full/mac_arm64_server/debug/dist/ballisticakit_headless": "ab5cb01ed4a8a8db80ca65ab5cd0133b", "build/prefab/full/mac_arm64_server/release/dist/ballisticakit_headless": "231bd6e18145b06028e4f90fd7de106c", - "build/prefab/full/mac_x86_64_gui/debug/ballisticakit": "27c074268b5d1665ad45af2b5d62d03c", + "build/prefab/full/mac_x86_64_gui/debug/ballisticakit": "3cc88ed5471e9b7f2b84af4ee58a8061", "build/prefab/full/mac_x86_64_gui/release/ballisticakit": "4586629e8f114c1b5a22444ae26b5917", - "build/prefab/full/mac_x86_64_server/debug/dist/ballisticakit_headless": "2a776f145931be151cfc2f90907a1ddd", + "build/prefab/full/mac_x86_64_server/debug/dist/ballisticakit_headless": "e1177b377c3e48d60057a9719bf9d0e3", "build/prefab/full/mac_x86_64_server/release/dist/ballisticakit_headless": "518c529a7133d5ea41d730d12ae156c4", - "build/prefab/full/windows_x86_gui/debug/BallisticaKit.exe": "dc1424b526bd15a53a1f26346416b09e", - "build/prefab/full/windows_x86_gui/release/BallisticaKit.exe": "8ee060e63821fc3f0775ffaca476d3b5", - "build/prefab/full/windows_x86_server/debug/dist/BallisticaKitHeadless.exe": "33ca30ccfb51b1b047f5564edb505e79", - "build/prefab/full/windows_x86_server/release/dist/BallisticaKitHeadless.exe": "174581fbf846e3f14c3ef6f139ceefa1", + "build/prefab/full/windows_x86_gui/debug/BallisticaKit.exe": "41e7afae9395843c575c65593aab423c", + "build/prefab/full/windows_x86_gui/release/BallisticaKit.exe": "d95e4cc445b722f111e51b3d4b7bdde9", + "build/prefab/full/windows_x86_server/debug/dist/BallisticaKitHeadless.exe": "d75e3afcc1db962cd483f8b80a65c930", + "build/prefab/full/windows_x86_server/release/dist/BallisticaKitHeadless.exe": "2b1b27fc2ca79803d79796a7414db3a2", "build/prefab/lib/linux_arm64_gui/debug/libballisticaplus.a": "a3607fd941915ab11503f82acfc392b5", "build/prefab/lib/linux_arm64_gui/release/libballisticaplus.a": "b5a129d83796c9e7015ab5e319d2c22f", "build/prefab/lib/linux_arm64_server/debug/libballisticaplus.a": "a3607fd941915ab11503f82acfc392b5", @@ -4100,14 +4100,14 @@ "build/prefab/lib/mac_x86_64_gui/release/libballisticaplus.a": "c5c40967e63471c9c4abd6dfbef892df", "build/prefab/lib/mac_x86_64_server/debug/libballisticaplus.a": "d34c0a142e7d391a109a33ea3cc77c08", "build/prefab/lib/mac_x86_64_server/release/libballisticaplus.a": "c5c40967e63471c9c4abd6dfbef892df", - "build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.lib": "88b6da386b4044afa482da6ad85f704d", - "build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.pdb": "d2cbb43b7a5e4f7ddb7d70703f49ddf1", - "build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.lib": "997d010eeae0181b249f5936a891e9b1", - "build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.pdb": "0b8e42d5d61d953ea4e0df9b435a795a", - "build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.lib": "dd5e4e4b58e59054330a905b35337c67", - "build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.pdb": "8036025f45cb9c536dfe9fec83c6ef21", - "build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.lib": "ef0330100297333917d1b68b98c002b2", - "build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.pdb": "f3ca2b3dd2fd16bfe3e5253b087a6c28", + "build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.lib": "aa15bc38411a8a4194b3711c9c6ebf56", + "build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.pdb": "3539534ba129d95e99f89ddf79d14f28", + "build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.lib": "75e16f083a18621fe4502bc170e6a696", + "build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.pdb": "29b9a8543b1612c0451eaaf90647b905", + "build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.lib": "4fb61373afaa6ce350601b245fcb1b7a", + "build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.pdb": "d8e606cdfef72b6170721d87ee1d9b11", + "build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.lib": "240b54fa0d4334591c0876a2b72a4663", + "build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.pdb": "a7887447024ce5570da3f468bad5bbb4", "src/assets/ba_data/python/babase/_mgen/__init__.py": "f885fed7f2ed98ff2ba271f9dbe3391c", "src/assets/ba_data/python/babase/_mgen/enums.py": "f8cd3af311ac63147882590123b78318", "src/ballistica/base/mgen/pyembed/binding_base.inc": "ad347097a38e0d7ede9eb6dec6a80ee9", diff --git a/src/assets/ba_data/python/babase/_appcomponent.py b/src/assets/ba_data/python/babase/_appcomponent.py index d1c844fc..691325c8 100644 --- a/src/assets/ba_data/python/babase/_appcomponent.py +++ b/src/assets/ba_data/python/babase/_appcomponent.py @@ -50,7 +50,8 @@ class AppComponentSubsystem: # Currently limiting this to logic-thread use; can revisit if # needed (would need to guard access to our implementations # dict). - assert _babase.in_logic_thread() + if not _babase.in_logic_thread(): + raise RuntimeError('this must be called from the logic thread.') if not issubclass(implementation, baseclass): raise TypeError( @@ -73,7 +74,8 @@ class AppComponentSubsystem: If no custom implementation has been set, the provided base-class is returned. """ - assert _babase.in_logic_thread() + if not _babase.in_logic_thread(): + raise RuntimeError('this must be called from the logic thread.') del baseclass # Unused. return cast(T, None) @@ -87,7 +89,9 @@ class AppComponentSubsystem: loop. Note that any further setclass calls before the callback runs will not result in additional callbacks. """ - assert _babase.in_logic_thread() + if not _babase.in_logic_thread(): + raise RuntimeError('this must be called from the logic thread.') + self._change_callbacks.setdefault(baseclass, []).append(callback) def _run_change_callbacks(self) -> None: diff --git a/src/assets/ba_data/python/babase/_login.py b/src/assets/ba_data/python/babase/_login.py index 850a8288..f6c761c4 100644 --- a/src/assets/ba_data/python/babase/_login.py +++ b/src/assets/ba_data/python/babase/_login.py @@ -10,6 +10,7 @@ from dataclasses import dataclass from typing import TYPE_CHECKING, final from bacommon.login import LoginType + import _babase if TYPE_CHECKING: diff --git a/tools/batools/featureset.py b/tools/batools/featureset.py index fd5dc394..a2d2b776 100644 --- a/tools/batools/featureset.py +++ b/tools/batools/featureset.py @@ -208,6 +208,62 @@ class FeatureSet: ' not allowed.' ) + @property + def path_config_file(self) -> str: + """Project-relative path to the file defining this feature-set.""" + return f'config/featuresets/featureset_{self.name}.py' + + @property + def path_python_package(self) -> str: + """Project-relative path for this feature-set's Python package. + + Note that this does not mean that the package actually exists; + this just shows where it would. + """ + return f'src/assets/ba_data/python/{self.name_python_package}' + + @property + def path_python_package_meta(self) -> str: + """Project-relative path for this feature-set's Python meta package. + + Note that this does not mean that the package actually exists; + this just shows where it would. + """ + return f'src/meta/{self.name_python_package_meta}' + + @property + def path_python_package_tests(self) -> str: + """Project-relative path for this feature-set's Python tests package. + + Note that this does not mean that the package actually exists; + this just shows where it would. + """ + return f'tests/{self.name_python_package_tests}' + + @property + def path_native_source(self) -> str: + """Project-relative path for this feature-set's native source. + + Note that this does not mean that such source actually exists; + this just shows where it would. + """ + return f'src/ballistica/{self.name}' + + @property + def paths(self) -> list[str]: + """Return all file/dir paths associated with this feature-set. + + Paths are project relative and may not actually exist; this just + gives their theoretical locations. + """ + return [ + self.path_config_file, + self.path_python_package, + self.path_native_source, + self.path_python_package_meta, + self.path_python_package_tests, + ] + @classmethod def get_active(cls) -> FeatureSet: """Return the FeatureSet currently being defined. diff --git a/tools/batools/spinoff/_main.py b/tools/batools/spinoff/_main.py index ec394a4b..8313701e 100644 --- a/tools/batools/spinoff/_main.py +++ b/tools/batools/spinoff/_main.py @@ -33,9 +33,9 @@ class Command(Enum): OVERRIDE = 'override' DIFF = 'diff' BACKPORT = 'backport' - FEATURESETS = 'featuresets' CREATE = 'create' ADD_SUBMODULE_PARENT = 'add-submodule-parent' + FEATURE_SET_LIST = 'fset-list' FEATURE_SET_COPY = 'fset-copy' FEATURE_SET_DELETE = 'fset-delete' @@ -98,7 +98,7 @@ def _main() -> None: _do_override(src_root, dst_root) elif cmd is Command.BACKPORT: _do_backport(src_root, dst_root) - elif cmd is Command.FEATURESETS: + elif cmd is Command.FEATURE_SET_LIST: _do_featuresets(dst_root) elif cmd is Command.CREATE: _do_create(src_root, dst_root) @@ -166,7 +166,9 @@ def _do_create(src_root: str | None, dst_root: str) -> None: if len(args) != 2: raise CleanError(f'Expected a name and path arg; got {args}.') + # pylint: disable=useless-suppression name, path = args # pylint: disable=unbalanced-tuple-unpacking + # pylint: enable=useless-suppression if not name: raise CleanError('Name cannot be an empty string.') @@ -260,24 +262,6 @@ def _do_featuresets(dst_root: str) -> None: print(f' {Clr.BLU}{fset.name}{Clr.RST}') -def _fset_paths() -> list[str]: - """Given a feature-set, return all paths associated with it.""" - return [ - 'config/featuresets/featureset_$(NAME).py', - 'src/assets/ba_data/python/$(NAME_PY_PKG)', - 'src/ballistica/$(NAME)', - 'src/meta/$(NAME_PY_PKG_META)', - ] - - -def _filter_fset_path(path: str, fset: FeatureSet) -> str: - return ( - path.replace('$(NAME)', fset.name) - .replace('$(NAME_PY_PKG)', fset.name_python_package) - .replace('$(NAME_PY_PKG_META)', fset.name_python_package_meta) - ) - - def _do_featureset_delete() -> None: from batools.featureset import FeatureSet @@ -294,9 +278,7 @@ def _do_featureset_delete() -> None: if not os.path.exists('config/featuresets'): raise CleanError('Cannot run from this directory.') - paths_to_delete: list[str] = [] - for path in _fset_paths(): - paths_to_delete.append(_filter_fset_path(path, fset)) + paths_to_delete: list[str] = fset.paths print( '\n' + '⎯' * 80 + f'\n{Clr.BLD}Deleting feature-set{Clr.RST}' @@ -309,7 +291,7 @@ def _do_featureset_delete() -> None: for path in paths_to_delete: if os.path.exists(path): found_something = True - print(f' Deleting {Clr.MAG}{path}{Clr.RST}') + print(f' Deleting {Clr.RED}{path}{Clr.RST}') subprocess.run(['rm', '-rf', path], check=True) if not found_something: print( @@ -362,17 +344,15 @@ def _do_featureset_copy() -> None: raise CleanError(f"Src feature-set '{src}' not found.") # Make sure dst does *not* exist (unless we're forcing). - if os.path.exists(f'config/featuresets/featureset_{dst}.py') and not force: + if os.path.exists(dstfs.path_config_file) and not force: raise CleanError( f"Dst feature-set '{dst}' already exists." ' Use --force to blow it away.' ) paths_to_copy: list[tuple[str, str]] = [] - for path in _fset_paths(): - paths_to_copy.append( - (_filter_fset_path(path, srcfs), _filter_fset_path(path, dstfs)) - ) + for srcpath, dstpath in zip(srcfs.paths, dstfs.paths): + paths_to_copy.append((srcpath, dstpath)) # Replace variations of our name. Note that we don't have to include # stuff like name_python_package_meta here because that is covered @@ -387,10 +367,10 @@ def _do_featureset_copy() -> None: ] # Sanity check: we don't currently support renaming subdirs, so error - # if that would happen. + # if that would need to happen. for srcpath, _dstpath in paths_to_copy: - for root, dirs, _fnames in os.walk(srcpath): - for dname in dirs: + for root, dnames, _fnames in os.walk(srcpath): + for dname in dnames: if any(sub[0] in dname for sub in subs): raise CleanError( 'Directory name filtering is not supported' @@ -451,6 +431,10 @@ def _do_featureset_copy_dir( # Eww; reinventing the wheel here; should tap into existing # spinoff logic or something. cruft_names = ['.DS_Store', 'mgen', '_mgen'] + cruft_exts = ['.pyc'] + + def _is_cruft(name: str) -> bool: + return name in cruft_names or any(name.endswith(x) for x in cruft_exts) # We currently just copy the full dir and then rename/filter # individual files. If we need to filter subdir names at some point @@ -458,16 +442,19 @@ def _do_featureset_copy_dir( subprocess.run(['cp', '-r', srcpath, dstpath], check=True) for root, dnames, fnames in os.walk(dstpath, topdown=True): for dname in dnames: - if dname in cruft_names: - # Prevent us from recursing into it and blow it away. + if _is_cruft(dname): + # Blow away cruft dirs and don't recurse into them. dnames.remove(dname) subprocess.run( ['rm', '-rf', os.path.join(root, dname)], check=True ) for fname in fnames: - if fname in cruft_names: + if _is_cruft(fname): + # Blow away and ignore cruft files. os.unlink(os.path.join(root, fname)) continue + + # Rename files. fnamefilt = fname for subsrc, subdst in subs: fnamefilt = fnamefilt.replace(subsrc, subdst) @@ -480,7 +467,8 @@ def _do_featureset_copy_dir( ], check=True, ) - # Now filter contents. + + # Filter file contents. if not any(fname.endswith(ext) for ext in filtered_exts): print( f'{Clr.YLW}WARNING:' @@ -593,8 +581,6 @@ def _print_available_commands() -> None: 'Remove files from spinoff, leaving local copies in place.\n' f' {bgn}backport [file]{end} ' 'Help get changes to spinoff dst files back to src.\n' - f' {bgn}featuresets{end} ' - 'List featuresets present in the current project.\n' f' {bgn}create [name, path]{end} ' 'Create a new spinoff project based on this src one.\n' ' Name should be passed in CamelCase form.\n' @@ -620,13 +606,16 @@ def _print_available_commands() -> None: ' achieved by passing --submodule-parent to' ' the \'create\'\n' ' command.\n' - f' {bgn}fset-copy [src, dst]{end} Copy feature-set src to dst.' - ' Replaces variations of src\n' - ' feature-set name with dst equivalent,' - ' though may need\n' + f' {bgn}fset-list{end} ' + 'List featuresets present in the current project.\n' + f' {bgn}fset-copy [src, dst]{end} Copy feature-set src to dst' + ' in the current project.\n' + ' Replaces variations of src feature-set' + ' name with equivalent from dst, though may need\n' ' some manual correction afterwards to be' ' functional.\n' - f' {bgn}fset-delete [name]{end} Delete a feature-set.' + f' {bgn}fset-delete [name]{end} Delete a feature-set from the' + ' current project.' ), file=sys.stderr, )