fixes for playlist creation and game discovery

This commit is contained in:
Eric 2023-06-07 14:38:29 -07:00
parent c47068cb53
commit af5f518627
No known key found for this signature in database
GPG Key ID: 89C93F0F8D6D5A98
32 changed files with 124 additions and 115 deletions

40
.efrocachemap generated
View File

@ -4072,26 +4072,26 @@
"build/assets/workspace/ninjafightplug.py": "https://files.ballistica.net/cache/ba1/c5/09/4f10b8a21ba87aa5509cff7a164b",
"build/assets/workspace/onslaughtplug.py": "https://files.ballistica.net/cache/ba1/ff/0a/a354984f9c074dab0676ac7e4877",
"build/assets/workspace/runaroundplug.py": "https://files.ballistica.net/cache/ba1/2a/1c/9ee5db6d1bceca7fa6638fb8abde",
"build/prefab/full/linux_arm64_gui/debug/ballisticakit": "https://files.ballistica.net/cache/ba1/12/bf/7ec79c0aedc17a67b4dadfd08940",
"build/prefab/full/linux_arm64_gui/release/ballisticakit": "https://files.ballistica.net/cache/ba1/4f/66/dba5b2aa02a80e80dff750a59553",
"build/prefab/full/linux_arm64_server/debug/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/8d/06/c4cb56349781f97a3ff8081ce3ac",
"build/prefab/full/linux_arm64_server/release/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/c7/83/c7044a678a0bb28195d5a8c76972",
"build/prefab/full/linux_x86_64_gui/debug/ballisticakit": "https://files.ballistica.net/cache/ba1/08/90/96f0187beb141245d5ad0fdcd4f7",
"build/prefab/full/linux_x86_64_gui/release/ballisticakit": "https://files.ballistica.net/cache/ba1/4f/fe/9446e923a16f2b13690604ec0772",
"build/prefab/full/linux_x86_64_server/debug/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/c3/ab/2210004b5dc928aca3ed235d4bc3",
"build/prefab/full/linux_x86_64_server/release/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/c9/ff/904e8b4cdad471b5becd33800a53",
"build/prefab/full/mac_arm64_gui/debug/ballisticakit": "https://files.ballistica.net/cache/ba1/d5/2c/c580c5031d93138dce8e6513f44b",
"build/prefab/full/mac_arm64_gui/release/ballisticakit": "https://files.ballistica.net/cache/ba1/b9/12/a189b59b2bf373f4545dab5529b4",
"build/prefab/full/mac_arm64_server/debug/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/76/f0/f13c0fc030c17dd7b8ae70a14811",
"build/prefab/full/mac_arm64_server/release/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/59/25/a117150d6353eaa36fd14d98850a",
"build/prefab/full/mac_x86_64_gui/debug/ballisticakit": "https://files.ballistica.net/cache/ba1/bc/66/3644111b6407169b361d6aaf19e4",
"build/prefab/full/mac_x86_64_gui/release/ballisticakit": "https://files.ballistica.net/cache/ba1/b7/c1/d9c0f9a0eaf8545c7319325528c2",
"build/prefab/full/mac_x86_64_server/debug/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/7f/36/67da0a6bdd15c3683cd7f2f05e4c",
"build/prefab/full/mac_x86_64_server/release/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/9f/04/1094813b2f57b157669b7bb7fef4",
"build/prefab/full/windows_x86_gui/debug/BallisticaKit.exe": "https://files.ballistica.net/cache/ba1/64/a8/21bbfb168fce276fc37a2483131c",
"build/prefab/full/windows_x86_gui/release/BallisticaKit.exe": "https://files.ballistica.net/cache/ba1/20/0e/3da8e1dc080eb9f3f38072854084",
"build/prefab/full/windows_x86_server/debug/dist/BallisticaKitHeadless.exe": "https://files.ballistica.net/cache/ba1/8c/84/2ccb6892467dda04dccc3a588afb",
"build/prefab/full/windows_x86_server/release/dist/BallisticaKitHeadless.exe": "https://files.ballistica.net/cache/ba1/a4/99/cbb84a6fc6560fbfebad7c4b5914",
"build/prefab/full/linux_arm64_gui/debug/ballisticakit": "https://files.ballistica.net/cache/ba1/d4/26/b088ceb87917faecdf22fe036adc",
"build/prefab/full/linux_arm64_gui/release/ballisticakit": "https://files.ballistica.net/cache/ba1/6c/55/da248f57e63041de6bc576d8d5ac",
"build/prefab/full/linux_arm64_server/debug/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/dc/28/cb352e0ff313c45d453c840bcd7a",
"build/prefab/full/linux_arm64_server/release/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/63/0f/6c77db4f8703dda0f69426abd6e3",
"build/prefab/full/linux_x86_64_gui/debug/ballisticakit": "https://files.ballistica.net/cache/ba1/c6/6a/cc6ca14db5d55d49d3957d0ab078",
"build/prefab/full/linux_x86_64_gui/release/ballisticakit": "https://files.ballistica.net/cache/ba1/40/fd/de2558f278bf97ebde5db8675049",
"build/prefab/full/linux_x86_64_server/debug/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/8e/6e/e2d5d8da43a1e135577a9a36c90c",
"build/prefab/full/linux_x86_64_server/release/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/48/fd/56d0cc6137f0a1c864a0a018548b",
"build/prefab/full/mac_arm64_gui/debug/ballisticakit": "https://files.ballistica.net/cache/ba1/94/9b/5b05384233ebbfd18066d7b347ca",
"build/prefab/full/mac_arm64_gui/release/ballisticakit": "https://files.ballistica.net/cache/ba1/37/85/2b143d5c13c96604a86870b0351d",
"build/prefab/full/mac_arm64_server/debug/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/d8/c4/e5ef74124a10c006caf74bcd5e5d",
"build/prefab/full/mac_arm64_server/release/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/12/d9/16e443990547ec3a8c557bce1e9c",
"build/prefab/full/mac_x86_64_gui/debug/ballisticakit": "https://files.ballistica.net/cache/ba1/aa/00/adb9501b00a0c8166aa2e66e3cde",
"build/prefab/full/mac_x86_64_gui/release/ballisticakit": "https://files.ballistica.net/cache/ba1/d8/39/9cbef7ae2ddd3fed3d1bb362ac1e",
"build/prefab/full/mac_x86_64_server/debug/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/19/53/b99d48ebaf6f51ae34e5c758c16a",
"build/prefab/full/mac_x86_64_server/release/dist/ballisticakit_headless": "https://files.ballistica.net/cache/ba1/03/6d/30e34f0199482b7f52f263824197",
"build/prefab/full/windows_x86_gui/debug/BallisticaKit.exe": "https://files.ballistica.net/cache/ba1/12/ed/fb678499e58f562af6a89b241895",
"build/prefab/full/windows_x86_gui/release/BallisticaKit.exe": "https://files.ballistica.net/cache/ba1/ab/15/8fe60f35c75e3c52baf947ae09dd",
"build/prefab/full/windows_x86_server/debug/dist/BallisticaKitHeadless.exe": "https://files.ballistica.net/cache/ba1/82/17/3cb74613e589d88079d2fc51d0a6",
"build/prefab/full/windows_x86_server/release/dist/BallisticaKitHeadless.exe": "https://files.ballistica.net/cache/ba1/e4/b8/c101925870bb698b35ecf180762e",
"build/prefab/lib/linux_arm64_gui/debug/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/66/b7/4be2fcf57b9bc9cb12d97d6ac298",
"build/prefab/lib/linux_arm64_gui/release/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/4d/31/1654791c0ae8eb78b517b548f2f7",
"build/prefab/lib/linux_arm64_server/debug/libballistica_plus.a": "https://files.ballistica.net/cache/ba1/62/04/d4b18d10e866785b23756d48f91c",

View File

@ -1,4 +1,4 @@
### 1.7.20 (build 21058, api 8, 2023-06-07)
### 1.7.20 (build 21060, api 8, 2023-06-07)
- This seems like a good time for a `refactoring` release in anticipation of
changes coming in 1.8. Basically this means that a lot of things will be
@ -271,6 +271,13 @@
with `bascenev1lib`. That should mostly do it. Random tip: check out the
`tools/pcommand mypy_files` as a handy tool to help get your mods updated.
- (build 21057) Fixed an issue with news items erroring on the main menu (thanks for the heads up Rikko)
- (build 21059) Fixed an issue where trying to add a new playlist would error (thanks for the heads up SEBASTIAN2059)
- (build 21059) Fixed meta scanning which was coming up empty. Note that games
must now tag themselves via `ba_meta export bascenev1.GameActivity` instead of
`ba_meta export game` to be discovered. Warnings will be issued if the old tag
form is found. This is necessary because there will be totally different
concepts of game-activities/etc. in future scene versions so we need to use
exact class names instead of the 'game' shortcut.
### 1.7.19 (build 20997, api 7, 2023-01-19)

View File

@ -8,6 +8,8 @@ In some specific cases you may need to pull in individual submodules instead.
"""
# pylint: disable=redefined-builtin
from efro.util import set_canonical_module_names
import _babase
from _babase import (
SimpleSound,
@ -165,26 +167,8 @@ __all__ = [
'AppSubsystem',
]
# Have these things present themselves cleanly as 'ba.Foo'
# instead of 'ba._submodule.Foo'
def _simplify_module_names() -> None:
import os
# Though pdoc gets confused when we override __module__,
# so let's make an exception for it.
if os.environ.get('BA_DOCS_GENERATION', '0') != '1':
from efro.util import set_canonical_module
globs = globals()
set_canonical_module(
module_globals=globs,
names=[n for n in globs.keys() if not n.startswith('_')],
)
_simplify_module_names()
del _simplify_module_names
# We want stuff to show up as babase.Foo instead of babase._sub.Foo.
set_canonical_module_names(globals())
# Allow the native layer to wrap a few things up.
_babase.reached_end_of_babase()

View File

@ -28,9 +28,8 @@ CURRENT_API_VERSION = 8
# This is purely a convenience; it is possible to use full class paths
# instead of these or to make the meta system aware of arbitrary classes.
EXPORT_CLASS_NAME_SHORTCUTS: dict[str, str] = {
'plugin': 'ba.Plugin',
'keyboard': 'ba.Keyboard',
'game': 'ba.GameActivity',
'plugin': 'babase.Plugin',
'keyboard': 'babase.Keyboard',
}
T = TypeVar('T')
@ -54,7 +53,7 @@ class MetadataSubsystem:
Category: **App Classes**
Access the single shared instance of this class at 'ba.app.meta'.
Access the single shared instance of this class at 'babase.app.meta'.
"""
def __init__(self) -> None:
@ -159,7 +158,7 @@ class MetadataSubsystem:
if self.scanresults is None:
if _babase.in_logic_thread():
logging.warning(
'ba.meta._wait_for_scan_results()'
'babase.meta._wait_for_scan_results()'
' called in logic thread before scan completed;'
' this can cause hitches.'
)
@ -392,15 +391,27 @@ class DirectoryScan:
if export_class_name is not None:
classname = modulename + '.' + export_class_name
# If export type is one of our shortcuts, sub in the
# actual class path. Otherwise assume its a classpath
# itself.
exporttype = EXPORT_CLASS_NAME_SHORTCUTS.get(exporttypestr)
if exporttype is None:
exporttype = exporttypestr
self.results.exports.setdefault(exporttype, []).append(
classname
)
# Since we'll soon have multiple versions of 'game'
# classes we need to migrate people to using base
# class names for them.
if exporttypestr == 'game':
self.results.warnings.append(
f'{subpath}:'
" '# ba_meta export game' tag should be replaced by"
f" '# ba_meta export bascenev1.GameActivity'."
)
else:
# If export type is one of our shortcuts, sub in the
# actual class path. Otherwise assume its a classpath
# itself.
exporttype = EXPORT_CLASS_NAME_SHORTCUTS.get(
exporttypestr
)
if exporttype is None:
exporttype = exporttypestr
self.results.exports.setdefault(exporttype, []).append(
classname
)
def _get_export_class_name(
self, subpath: Path, lines: list[str], lindex: int

View File

@ -18,12 +18,6 @@ directly, it will most likely not work without classic present.
# ba_meta require api 8
# import traceback
# traceback.print_stack()
# sys.stderr.flush()
# sys.stdout.flush()
# Note: Code relying on classic should import things from here *only*
# for type-checking and use the versions in app.classic at runtime; that
# way type-checking will cleanly cover the classic-not-present case

View File

@ -850,3 +850,18 @@ class ClassicSubsystem(AppSubsystem):
ProfileBrowserWindow(
transition, in_main_menu, selected_profile, origin_widget
)
def preload_map_preview_media(self) -> None:
"""Preload media needed for map preview UIs.
Category: **Asset Functions**
"""
try:
_bauiv1.getmesh('level_select_button_opaque')
_bauiv1.getmesh('level_select_button_transparent')
for maptype in list(self.maps.values()):
map_tex_name = maptype.get_preview_texture_name()
if map_tex_name is not None:
_bauiv1.gettexture(map_tex_name)
except Exception:
logging.exception('Error preloading map preview media.')

View File

@ -28,7 +28,7 @@ if TYPE_CHECKING:
# Build number and version of the ballistica binary we expect to be
# using.
TARGET_BALLISTICA_BUILD = 21058
TARGET_BALLISTICA_BUILD = 21060
TARGET_BALLISTICA_VERSION = '1.7.20'
_g_env_config: EnvConfig | None = None

View File

@ -18,6 +18,8 @@ import logging
# other modules; the goal is to let most simple mods rely solely on this
# module to keep things simple.
from efro.util import set_canonical_module_names
from _babase import (
app,
ContextRef,
@ -394,6 +396,10 @@ __all__ = [
'Level',
]
# We want stuff here to show up as bascenev1.Foo instead of
# bascenev1._submodule.Foo.
set_canonical_module_names(globals())
# Sanity check: we want to keep ballistica's dependencies and
# bootstrapping order clearly defined; let's check a few particular
# modules to make sure they never directly or indirectly import us

View File

@ -18,20 +18,6 @@ if TYPE_CHECKING:
import bascenev1
def preload_map_preview_media() -> None:
"""Preload media needed for map preview UIs.
Category: **Asset Functions**
"""
assert _babase.app.classic is not None
_bascenev1.getmesh('level_select_button_opaque')
_bascenev1.getmesh('level_select_button_transparent')
for maptype in list(_babase.app.classic.maps.values()):
map_tex_name = maptype.get_preview_texture_name()
if map_tex_name is not None:
_bascenev1.gettexture(map_tex_name)
def get_filtered_map_name(name: str) -> str:
"""Filter a map name to account for name changes, etc.

View File

@ -13,7 +13,6 @@ from bascenev1._gameutils import get_trophy_string
from bascenev1._map import (
get_map_class,
register_map,
preload_map_preview_media,
get_map_display_string,
get_filtered_map_name,
)
@ -30,7 +29,6 @@ __all__ = [
'get_trophy_string',
'get_map_class',
'register_map',
'preload_map_preview_media',
'get_map_display_string',
'get_filtered_map_name',
'PlayerProfilesChangedMessage',

View File

@ -33,7 +33,7 @@ class Team(bs.Team[Player]):
self.score = 0
# ba_meta export game
# ba_meta export bascenev1.GameActivity
class AssaultGame(bs.TeamGameActivity[Player, Team]):
"""Game where you score by touching the other team's flag."""

View File

@ -95,7 +95,7 @@ class Team(bs.Team[Player]):
self.touch_return_timer_ticking: bs.NodeActor | None = None
# ba_meta export game
# ba_meta export bascenev1.GameActivity
class CaptureTheFlagGame(bs.TeamGameActivity[Player, Team]):
"""Game of stealing other team's flag and returning it to your base."""

View File

@ -34,7 +34,7 @@ class Team(bs.Team[Player]):
self.time_remaining = time_remaining
# ba_meta export game
# ba_meta export bascenev1.GameActivity
class ChosenOneGame(bs.TeamGameActivity[Player, Team]):
"""
Game involving trying to remain the one 'chosen one'

View File

@ -72,7 +72,7 @@ class Team(bs.Team[Player]):
self.flags_held = 0
# ba_meta export game
# ba_meta export bascenev1.GameActivity
class ConquestGame(bs.TeamGameActivity[Player, Team]):
"""A game where teams try to claim all flags on the map."""

View File

@ -28,7 +28,7 @@ class Team(bs.Team[Player]):
self.score = 0
# ba_meta export game
# ba_meta export bascenev1.GameActivity
class DeathMatchGame(bs.TeamGameActivity[Player, Team]):
"""A game type based on acquiring kills."""

View File

@ -38,7 +38,7 @@ class Team(bs.Team[Player]):
self.score = 0
# ba_meta export game
# ba_meta export bascenev1.GameActivity
class EasterEggHuntGame(bs.TeamGameActivity[Player, Team]):
"""A game where score is based on collecting eggs."""

View File

@ -180,7 +180,7 @@ class Team(bs.Team[Player]):
self.spawn_order: list[Player] = []
# ba_meta export game
# ba_meta export bascenev1.GameActivity
class EliminationGame(bs.TeamGameActivity[Player, Team]):
"""Game type where last player(s) left alive win."""

View File

@ -89,7 +89,7 @@ class Team(bs.Team[Player]):
self.score = 0
# ba_meta export game
# ba_meta export bascenev1.GameActivity
class FootballTeamGame(bs.TeamGameActivity[Player, Team]):
"""Football game for teams mode."""

View File

@ -113,7 +113,7 @@ class Team(bs.Team[Player]):
self.score = 0
# ba_meta export game
# ba_meta export bascenev1.GameActivity
class HockeyGame(bs.TeamGameActivity[Player, Team]):
"""Ice hockey game."""

View File

@ -46,7 +46,7 @@ class Team(bs.Team[Player]):
self.holdingflag = False
# ba_meta export game
# ba_meta export bascenev1.GameActivity
class KeepAwayGame(bs.TeamGameActivity[Player, Team]):
"""Game where you try to keep the flag away from your enemies."""

View File

@ -44,7 +44,7 @@ class Team(bs.Team[Player]):
self.time_remaining = time_remaining
# ba_meta export game
# ba_meta export bascenev1.GameActivity
class KingOfTheHillGame(bs.TeamGameActivity[Player, Team]):
"""Game where a team wins by holding a 'hill' for a set amount of time."""

View File

@ -30,7 +30,7 @@ class Team(bs.Team[Player]):
"""Our team type for this game."""
# ba_meta export game
# ba_meta export bascenev1.GameActivity
class MeteorShowerGame(bs.TeamGameActivity[Player, Team]):
"""Minigame involving dodging falling bombs."""

View File

@ -30,7 +30,7 @@ class Team(bs.Team[Player]):
"""Our team type for this game."""
# ba_meta export game
# ba_meta export bascenev1.GameActivity
class NinjaFightGame(bs.TeamGameActivity[Player, Team]):
"""
A co-op game where you try to defeat a group

View File

@ -74,7 +74,7 @@ class Team(bs.Team[Player]):
self.finished = False
# ba_meta export game
# ba_meta export bascenev1.GameActivity
class RaceGame(bs.TeamGameActivity[Player, Team]):
"""Game of racing around a track."""

View File

@ -36,7 +36,7 @@ class Team(bs.Team[Player]):
self.score = 0
# ba_meta export game
# ba_meta export bascenev1.GameActivity
class TargetPracticeGame(bs.TeamGameActivity[Player, Team]):
"""Game where players try to hit targets with bombs."""

View File

@ -16,6 +16,8 @@ from __future__ import annotations
import logging
from efro.util import set_canonical_module_names
from _babase import (
app,
ContextRef,
@ -209,6 +211,9 @@ __all__ = [
'AppMode',
]
# We want stuff to show up as bauiv1.Foo instead of bauiv1._sub.Foo.
set_canonical_module_names(globals())
# Sanity check: we want to keep ballistica's dependencies and
# bootstrapping order clearly defined; let's check a few particular
# modules to make sure they never directly or indirectly import us

View File

@ -182,8 +182,6 @@ class PlaylistAddGameWindow(bui.Window):
bui.pushcall(self._refresh, from_other_thread=True)
def _refresh(self, select_get_more_games_button: bool = False) -> None:
# from bui import get_game_types
if self._column is not None:
self._column.delete()

View File

@ -26,7 +26,6 @@ class PlaylistEditController:
playlist_name: str | None = None,
):
from bascenev1.internal import filter_playlist
from bascenev1.internal import preload_map_preview_media
from bauiv1lib.playlist import PlaylistTypeVars
from bauiv1lib.playlist.edit import PlaylistEditWindow
@ -34,7 +33,9 @@ class PlaylistEditController:
# Since we may be showing our map list momentarily,
# lets go ahead and preload all map preview textures.
preload_map_preview_media()
if bui.app.classic is not None:
bui.app.classic.preload_map_preview_media()
self._sessiontype = sessiontype
self._editing_game = False

View File

@ -39,7 +39,7 @@ auto main(int argc, char** argv) -> int {
namespace ballistica {
// These are set automatically via script; don't modify them here.
const int kEngineBuildNumber = 21058;
const int kEngineBuildNumber = 21060;
const char* kEngineVersion = "1.7.20";
auto MonolithicMain(const core::CoreConfig& core_config) -> int {

View File

@ -10,7 +10,7 @@ data formats in a nondestructive manner.
from __future__ import annotations
from efro.util import set_canonical_module
from efro.util import set_canonical_module_names
from efro.dataclassio._base import Codec, IOAttrs, IOExtendedData
from efro.dataclassio._prep import (
ioprep,
@ -47,4 +47,4 @@ __all__ = [
# Have these things present themselves cleanly as 'thismodule.SomeClass'
# instead of 'thismodule._internalmodule.SomeClass'
set_canonical_module(module_globals=globals(), names=__all__)
set_canonical_module_names(globals())

View File

@ -6,7 +6,7 @@ Supports static typing for message types and possible return types.
from __future__ import annotations
from efro.util import set_canonical_module
from efro.util import set_canonical_module_names
from efro.message._protocol import MessageProtocol
from efro.message._sender import MessageSender, BoundMessageSender
from efro.message._receiver import MessageReceiver, BoundMessageReceiver
@ -42,4 +42,4 @@ __all__ = [
# Have these things present themselves cleanly as 'thismodule.SomeClass'
# instead of 'thismodule._internalmodule.SomeClass'
set_canonical_module(module_globals=globals(), names=__all__)
set_canonical_module_names(globals())

View File

@ -706,29 +706,33 @@ def unchanging_hostname() -> str:
return os.uname().nodename
def set_canonical_module(
module_globals: dict[str, Any], names: list[str]
) -> None:
"""Override any __module__ attrs on passed classes/etc.
This allows classes to present themselves using clean paths such as
mymodule.MyClass instead of possibly ugly internal ones such as
mymodule._internal._stuff.MyClass.
"""
def set_canonical_module_names(module_globals: dict[str, Any]) -> None:
"""Do the thing."""
modulename = module_globals.get('__name__')
if not isinstance(modulename, str):
raise RuntimeError('Unable to get module name.')
for name in names:
obj = module_globals[name]
assert not modulename.startswith('_')
modulename_prefix = f'{modulename}.'
modulename_prefix_2 = f'_{modulename}.'
for name, obj in module_globals.items():
if name.startswith('_'):
continue
existing = getattr(obj, '__module__', None)
try:
if existing is not None and existing != modulename:
# Override the module ONLY if it lives under us somewhere.
# So ourpackage._submodule.Foo becomes ourpackage.Foo
# but otherpackage._submodule.Foo remains untouched.
if existing is not None and (
existing.startswith(modulename_prefix)
or existing.startswith(modulename_prefix_2)
):
obj.__module__ = modulename
except Exception:
import logging
logging.warning(
'set_canonical_module: unable to change __module__'
'set_canonical_module_names: unable to change __module__'
" from '%s' to '%s' on %s object at '%s'.",
existing,
modulename,