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>
This commit is contained in:
Doug Hoskisson 2024-11-29 12:17:56 -08:00 committed by GitHub
parent d7a0f4cb4c
commit b5343a36ff
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 29 additions and 4 deletions

View File

@ -7,6 +7,7 @@ import os
import os.path import os.path
import shutil import shutil
import sys import sys
import types
import typing import typing
import warnings import warnings
from enum import IntEnum from enum import IntEnum
@ -162,8 +163,13 @@ class Group:
else: else:
# assign value, try to upcast to type hint # assign value, try to upcast to type hint
annotation = self.get_type_hints().get(k, None) annotation = self.get_type_hints().get(k, None)
candidates = [] if annotation is None else \ candidates = (
typing.get_args(annotation) if typing.get_origin(annotation) is Union else [annotation] [] if annotation is None else (
typing.get_args(annotation)
if typing.get_origin(annotation) in (Union, types.UnionType)
else [annotation]
)
)
none_type = type(None) none_type = type(None)
for cls in candidates: for cls in candidates:
assert isinstance(cls, type), f"{self.__class__.__name__}.{k}: type {cls} not supported in settings" assert isinstance(cls, type), f"{self.__class__.__name__}.{k}: type {cls} not supported in settings"

View File

@ -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

View File

@ -33,7 +33,10 @@ class AutoWorldRegister(type):
# lazy loading + caching to minimize runtime cost # lazy loading + caching to minimize runtime cost
if cls.__settings is None: if cls.__settings is None:
from settings import get_settings 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 return cls.__settings
def __new__(mcs, name: str, bases: Tuple[type, ...], dct: Dict[str, Any]) -> AutoWorldRegister: def __new__(mcs, name: str, bases: Tuple[type, ...], dct: Dict[str, Any]) -> AutoWorldRegister:

View File

@ -47,7 +47,7 @@ class ZillionSettings(settings.Group):
""" """
rom_file: RomFile = RomFile(RomFile.copy_to) rom_file: RomFile = RomFile(RomFile.copy_to)
rom_start: typing.Union[RomStart, bool] = RomStart("retroarch") rom_start: RomStart | bool = RomStart("retroarch")
class ZillionWebWorld(WebWorld): class ZillionWebWorld(WebWorld):