Skip to content

Commit db56e26

Browse files
authored
Core: Make .apworlds importable using importlib (without force-importing them first) (ArchipelagoMW#5734)
* Make apworlds importable in general * move it to a probably more appropriate place? * oops
1 parent 5a88641 commit db56e26

1 file changed

Lines changed: 22 additions & 18 deletions

File tree

worlds/__init__.py

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
import importlib
2+
import importlib.abc
3+
import importlib.machinery
24
import importlib.util
35
import logging
46
import os
57
import sys
6-
import warnings
78
import zipimport
89
import time
910
import dataclasses
1011
import json
11-
from typing import List
12+
from pathlib import Path
13+
from types import ModuleType
14+
from typing import List, Sequence
1215

1316
from NetUtils import DataPackage
1417
from Utils import local_path, user_path, Version, version_tuple, tuplize_version
@@ -53,21 +56,7 @@ def resolved_path(self) -> str:
5356
def load(self) -> bool:
5457
try:
5558
start = time.perf_counter()
56-
if self.is_zip:
57-
importer = zipimport.zipimporter(self.resolved_path)
58-
spec = importer.find_spec(os.path.basename(self.path).rsplit(".", 1)[0])
59-
assert spec, f"{self.path} is not a loadable module"
60-
mod = importlib.util.module_from_spec(spec)
61-
62-
mod.__package__ = f"worlds.{mod.__package__}"
63-
64-
mod.__name__ = f"worlds.{mod.__name__}"
65-
sys.modules[mod.__name__] = mod
66-
with warnings.catch_warnings():
67-
warnings.filterwarnings("ignore", message="__package__ != __spec__.parent")
68-
importer.exec_module(mod)
69-
else:
70-
importlib.import_module(f".{self.path}", "worlds")
59+
importlib.import_module(f".{Path(self.path).stem}", "worlds")
7160
self.time_taken = time.perf_counter()-start
7261
return True
7362

@@ -112,7 +101,6 @@ def load(self) -> bool:
112101
else:
113102
world_source.load()
114103

115-
116104
from .AutoWorld import AutoWorldRegister
117105

118106
for world_source in world_sources:
@@ -174,13 +162,29 @@ def fail_world(game_name: str, reason: str, add_as_failed_to_load: bool = True)
174162
core_compatible.sort(
175163
key=lambda element: element[1].world_version if element[1].world_version else Version(0, 0, 0),
176164
reverse=True)
165+
166+
apworld_module_specs = {}
167+
class APWorldModuleFinder(importlib.abc.MetaPathFinder):
168+
def find_spec(
169+
self, fullname: str, _path: Sequence[str] | None, _target: ModuleType = None
170+
) -> importlib.machinery.ModuleSpec | None:
171+
return apworld_module_specs.get(fullname)
172+
173+
sys.meta_path.insert(0, APWorldModuleFinder())
174+
177175
for apworld_source, apworld in core_compatible:
178176
if apworld.game and apworld.game in AutoWorldRegister.world_types:
179177
fail_world(apworld.game,
180178
f"Did not load {apworld_source.path} "
181179
f"as its game {apworld.game} is already loaded.",
182180
add_as_failed_to_load=False)
183181
else:
182+
importer = zipimport.zipimporter(apworld_source.resolved_path)
183+
world_name = Path(apworld.path).stem
184+
185+
spec = importer.find_spec(f"worlds.{world_name}")
186+
apworld_module_specs[f"worlds.{world_name}"] = spec
187+
184188
apworld_source.load()
185189
if apworld.game in AutoWorldRegister.world_types:
186190
# world could fail to load at this point

0 commit comments

Comments
 (0)