Skip to content

Commit c61cae0

Browse files
Berserker66black-sliver
authored andcommitted
Core: refactor some loading mechanisms (ArchipelagoMW#1753)
Co-authored-by: black-sliver <[email protected]>
1 parent 6fe2fba commit c61cae0

File tree

2 files changed

+64
-37
lines changed

2 files changed

+64
-37
lines changed

Launcher.py

+21-7
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
import argparse
1313
import itertools
14+
import logging
1415
import multiprocessing
1516
import shlex
1617
import subprocess
@@ -55,7 +56,7 @@ def open_patch():
5556
except Exception as e:
5657
messagebox('Error', str(e), error=True)
5758
else:
58-
file, _, component = identify(filename)
59+
file, component = identify(filename)
5960
if file and component:
6061
launch([*get_exe(component), file], component.cli)
6162

@@ -96,11 +97,13 @@ def open_folder(folder_path):
9697

9798
def identify(path: Union[None, str]):
9899
if path is None:
99-
return None, None, None
100+
return None, None
100101
for component in components:
101102
if component.handles_file(path):
102-
return path, component.script_name, component
103-
return (None, None, None) if '/' in path or '\\' in path else (None, path, None)
103+
return path, component
104+
elif path == component.display_name or path == component.script_name:
105+
return None, component
106+
return None, None
104107

105108

106109
def get_exe(component: Union[str, Component]) -> Optional[Sequence[str]]:
@@ -223,23 +226,34 @@ def component_action(button):
223226
Launcher().run()
224227

225228

229+
def run_component(component: Component, *args):
230+
if component.func:
231+
component.func(*args)
232+
elif component.script_name:
233+
subprocess.run([*get_exe(component.script_name), *args])
234+
else:
235+
logging.warning(f"Component {component} does not appear to be executable.")
236+
237+
226238
def main(args: Optional[Union[argparse.Namespace, dict]] = None):
227239
if isinstance(args, argparse.Namespace):
228240
args = {k: v for k, v in args._get_kwargs()}
229241
elif not args:
230242
args = {}
231243

232244
if "Patch|Game|Component" in args:
233-
file, component, _ = identify(args["Patch|Game|Component"])
245+
file, component = identify(args["Patch|Game|Component"])
234246
if file:
235247
args['file'] = file
236248
if component:
237249
args['component'] = component
250+
if not component:
251+
logging.warning(f"Could not identify Component responsible for {args['Patch|Game|Component']}")
238252

239253
if 'file' in args:
240-
subprocess.run([*get_exe(args['component']), args['file'], *args['args']])
254+
run_component(args["component"], args["file"], *args["args"])
241255
elif 'component' in args:
242-
subprocess.run([*get_exe(args['component']), *args['args']])
256+
run_component(args["component"], *args["args"])
243257
else:
244258
run_gui()
245259

worlds/__init__.py

+43-30
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,50 @@ class DataPackage(typing.TypedDict):
3939
class WorldSource(typing.NamedTuple):
4040
path: str # typically relative path from this module
4141
is_zip: bool = False
42+
relative: bool = True # relative to regular world import folder
4243

4344
def __repr__(self):
44-
return f"{self.__class__.__name__}({self.path}, is_zip={self.is_zip})"
45+
return f"{self.__class__.__name__}({self.path}, is_zip={self.is_zip}, relative={self.relative})"
46+
47+
@property
48+
def resolved_path(self) -> str:
49+
if self.relative:
50+
return os.path.join(folder, self.path)
51+
return self.path
52+
53+
def load(self) -> bool:
54+
try:
55+
if self.is_zip:
56+
importer = zipimport.zipimporter(self.resolved_path)
57+
if hasattr(importer, "find_spec"): # new in Python 3.10
58+
spec = importer.find_spec(os.path.basename(self.path).rsplit(".", 1)[0])
59+
mod = importlib.util.module_from_spec(spec)
60+
else: # TODO: remove with 3.8 support
61+
mod = importer.load_module(os.path.basename(self.path).rsplit(".", 1)[0])
62+
63+
mod.__package__ = f"worlds.{mod.__package__}"
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+
# Found no equivalent for < 3.10
69+
if hasattr(importer, "exec_module"):
70+
importer.exec_module(mod)
71+
else:
72+
importlib.import_module(f".{self.path}", "worlds")
73+
return True
74+
75+
except Exception as e:
76+
# A single world failing can still mean enough is working for the user, log and carry on
77+
import traceback
78+
import io
79+
file_like = io.StringIO()
80+
print(f"Could not load world {self}:", file=file_like)
81+
traceback.print_exc(file=file_like)
82+
file_like.seek(0)
83+
import logging
84+
logging.exception(file_like.read())
85+
return False
4586

4687

4788
# find potential world containers, currently folders and zip-importable .apworld's
@@ -58,35 +99,7 @@ def __repr__(self):
5899
# import all submodules to trigger AutoWorldRegister
59100
world_sources.sort()
60101
for world_source in world_sources:
61-
try:
62-
if world_source.is_zip:
63-
importer = zipimport.zipimporter(os.path.join(folder, world_source.path))
64-
if hasattr(importer, "find_spec"): # new in Python 3.10
65-
spec = importer.find_spec(world_source.path.split(".", 1)[0])
66-
mod = importlib.util.module_from_spec(spec)
67-
else: # TODO: remove with 3.8 support
68-
mod = importer.load_module(world_source.path.split(".", 1)[0])
69-
70-
mod.__package__ = f"worlds.{mod.__package__}"
71-
mod.__name__ = f"worlds.{mod.__name__}"
72-
sys.modules[mod.__name__] = mod
73-
with warnings.catch_warnings():
74-
warnings.filterwarnings("ignore", message="__package__ != __spec__.parent")
75-
# Found no equivalent for < 3.10
76-
if hasattr(importer, "exec_module"):
77-
importer.exec_module(mod)
78-
else:
79-
importlib.import_module(f".{world_source.path}", "worlds")
80-
except Exception as e:
81-
# A single world failing can still mean enough is working for the user, log and carry on
82-
import traceback
83-
import io
84-
file_like = io.StringIO()
85-
print(f"Could not load world {world_source}:", file=file_like)
86-
traceback.print_exc(file=file_like)
87-
file_like.seek(0)
88-
import logging
89-
logging.exception(file_like.read())
102+
world_source.load()
90103

91104
lookup_any_item_id_to_name = {}
92105
lookup_any_location_id_to_name = {}

0 commit comments

Comments
 (0)