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

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

View File

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