Core: Make .apworlds importable using importlib (without force-importing them first) (#5734)

* Make apworlds importable in general

* move it to a probably more appropriate place?

* oops
This commit is contained in:
NewSoupVi
2026-01-05 21:54:02 +00:00
committed by GitHub
parent 5a88641228
commit db56e26df9

View File

@@ -1,14 +1,17 @@
import importlib import importlib
import importlib.abc
import importlib.machinery
import importlib.util import importlib.util
import logging import logging
import os import os
import sys import sys
import warnings
import zipimport import zipimport
import time import time
import dataclasses import dataclasses
import json import json
from typing import List from pathlib import Path
from types import ModuleType
from typing import List, Sequence
from NetUtils import DataPackage from NetUtils import DataPackage
from Utils import local_path, user_path, Version, version_tuple, tuplize_version from Utils import local_path, user_path, Version, version_tuple, tuplize_version
@@ -53,21 +56,7 @@ class WorldSource:
def load(self) -> bool: def load(self) -> bool:
try: try:
start = time.perf_counter() start = time.perf_counter()
if self.is_zip: importlib.import_module(f".{Path(self.path).stem}", "worlds")
importer = zipimport.zipimporter(self.resolved_path)
spec = importer.find_spec(os.path.basename(self.path).rsplit(".", 1)[0])
assert spec, f"{self.path} is not a loadable module"
mod = importlib.util.module_from_spec(spec)
mod.__package__ = f"worlds.{mod.__package__}"
mod.__name__ = f"worlds.{mod.__name__}"
sys.modules[mod.__name__] = mod
with warnings.catch_warnings():
warnings.filterwarnings("ignore", message="__package__ != __spec__.parent")
importer.exec_module(mod)
else:
importlib.import_module(f".{self.path}", "worlds")
self.time_taken = time.perf_counter()-start self.time_taken = time.perf_counter()-start
return True return True
@@ -112,7 +101,6 @@ for world_source in world_sources:
else: else:
world_source.load() world_source.load()
from .AutoWorld import AutoWorldRegister from .AutoWorld import AutoWorldRegister
for world_source in world_sources: for world_source in world_sources:
@@ -174,6 +162,16 @@ if apworlds:
core_compatible.sort( core_compatible.sort(
key=lambda element: element[1].world_version if element[1].world_version else Version(0, 0, 0), key=lambda element: element[1].world_version if element[1].world_version else Version(0, 0, 0),
reverse=True) reverse=True)
apworld_module_specs = {}
class APWorldModuleFinder(importlib.abc.MetaPathFinder):
def find_spec(
self, fullname: str, _path: Sequence[str] | None, _target: ModuleType = None
) -> importlib.machinery.ModuleSpec | None:
return apworld_module_specs.get(fullname)
sys.meta_path.insert(0, APWorldModuleFinder())
for apworld_source, apworld in core_compatible: for apworld_source, apworld in core_compatible:
if apworld.game and apworld.game in AutoWorldRegister.world_types: if apworld.game and apworld.game in AutoWorldRegister.world_types:
fail_world(apworld.game, fail_world(apworld.game,
@@ -181,6 +179,12 @@ if apworlds:
f"as its game {apworld.game} is already loaded.", f"as its game {apworld.game} is already loaded.",
add_as_failed_to_load=False) add_as_failed_to_load=False)
else: else:
importer = zipimport.zipimporter(apworld_source.resolved_path)
world_name = Path(apworld.path).stem
spec = importer.find_spec(f"worlds.{world_name}")
apworld_module_specs[f"worlds.{world_name}"] = spec
apworld_source.load() apworld_source.load()
if apworld.game in AutoWorldRegister.world_types: if apworld.game in AutoWorldRegister.world_types:
# world could fail to load at this point # world could fail to load at this point