From b5343a36ff4c500905b537bbf297843578b7389e Mon Sep 17 00:00:00 2001 From: Doug Hoskisson Date: Fri, 29 Nov 2024 12:17:56 -0800 Subject: [PATCH] Core: fix settings API for removal of Python 3.8, 3.9 (#4280) * Core: fix settings API for removal of Python 3.8, 3.9 This is fixing 2 problems: - The `World` class has the annotation: `settings: ClassVar[Optional["Group"]]` so `MyWorld.settings` should not raise an exception like it does for some worlds. With the `Optional` there, it looks like it should return `None` for the worlds that don't use it. So that's what I changed it to. - `Group.update` had some code that required `typing.Union` instead of the Python 3.10 `|` for unions. added unit test for this fix added change in Zillion that I used to discover this problem and used it to test the test * fix copy-pasted stuff * tuple instead of set Co-authored-by: black-sliver <59490463+black-sliver@users.noreply.github.com> --------- Co-authored-by: black-sliver <59490463+black-sliver@users.noreply.github.com> --- settings.py | 10 ++++++++-- test/general/test_settings.py | 16 ++++++++++++++++ worlds/AutoWorld.py | 5 ++++- worlds/zillion/__init__.py | 2 +- 4 files changed, 29 insertions(+), 4 deletions(-) create mode 100644 test/general/test_settings.py diff --git a/settings.py b/settings.py index 79277052..ccd34580 100644 --- a/settings.py +++ b/settings.py @@ -7,6 +7,7 @@ import os import os.path import shutil import sys +import types import typing import warnings from enum import IntEnum @@ -162,8 +163,13 @@ class Group: else: # assign value, try to upcast to type hint annotation = self.get_type_hints().get(k, None) - candidates = [] if annotation is None else \ - typing.get_args(annotation) if typing.get_origin(annotation) is Union else [annotation] + candidates = ( + [] if annotation is None else ( + typing.get_args(annotation) + if typing.get_origin(annotation) in (Union, types.UnionType) + else [annotation] + ) + ) none_type = type(None) for cls in candidates: assert isinstance(cls, type), f"{self.__class__.__name__}.{k}: type {cls} not supported in settings" diff --git a/test/general/test_settings.py b/test/general/test_settings.py new file mode 100644 index 00000000..165d7982 --- /dev/null +++ b/test/general/test_settings.py @@ -0,0 +1,16 @@ +from unittest import TestCase + +from settings import Group +from worlds.AutoWorld import AutoWorldRegister + + +class TestSettings(TestCase): + def test_settings_can_update(self) -> None: + """ + Test that world settings can update. + """ + for game_name, world_type in AutoWorldRegister.world_types.items(): + with self.subTest(game=game_name): + if world_type.settings is not None: + assert isinstance(world_type.settings, Group) + world_type.settings.update({}) # a previous bug had a crash in this call to update diff --git a/worlds/AutoWorld.py b/worlds/AutoWorld.py index 3c4edc1b..ded8701d 100644 --- a/worlds/AutoWorld.py +++ b/worlds/AutoWorld.py @@ -33,7 +33,10 @@ class AutoWorldRegister(type): # lazy loading + caching to minimize runtime cost if cls.__settings is None: from settings import get_settings - cls.__settings = get_settings()[cls.settings_key] + try: + cls.__settings = get_settings()[cls.settings_key] + except AttributeError: + return None return cls.__settings def __new__(mcs, name: str, bases: Tuple[type, ...], dct: Dict[str, Any]) -> AutoWorldRegister: diff --git a/worlds/zillion/__init__.py b/worlds/zillion/__init__.py index d5e86bb3..e689f2f5 100644 --- a/worlds/zillion/__init__.py +++ b/worlds/zillion/__init__.py @@ -47,7 +47,7 @@ class ZillionSettings(settings.Group): """ rom_file: RomFile = RomFile(RomFile.copy_to) - rom_start: typing.Union[RomStart, bool] = RomStart("retroarch") + rom_start: RomStart | bool = RomStart("retroarch") class ZillionWebWorld(WebWorld):