diff --git a/Options.py b/Options.py index 144220c8..e1ae3391 100644 --- a/Options.py +++ b/Options.py @@ -41,6 +41,11 @@ class AssembleOptions(abc.ABCMeta): aliases = {name[6:].lower(): option_id for name, option_id in attrs.items() if name.startswith("alias_")} + assert ( + name in {"Option", "VerifyKeys"} or # base abstract classes don't need default + "default" in attrs or + any(hasattr(base, "default") for base in bases) + ), f"Option class {name} needs default value" assert "random" not in aliases, "Choice option 'random' cannot be manually assigned." # auto-alias Off and On being parsed as True and False @@ -96,7 +101,7 @@ T = typing.TypeVar('T') class Option(typing.Generic[T], metaclass=AssembleOptions): value: T - default = 0 + default: typing.ClassVar[typing.Any] # something that __init__ will be able to convert to the correct type # convert option_name_long into Name Long as display_name, otherwise name_long is the result. # Handled in get_option_name() @@ -106,8 +111,9 @@ class Option(typing.Generic[T], metaclass=AssembleOptions): supports_weighting = True # filled by AssembleOptions: - name_lookup: typing.Dict[T, str] - options: typing.Dict[str, int] + name_lookup: typing.ClassVar[typing.Dict[T, str]] # type: ignore + # https://github.com/python/typing/discussions/1460 the reason for this type: ignore + options: typing.ClassVar[typing.Dict[str, int]] def __repr__(self) -> str: return f"{self.__class__.__name__}({self.current_option_name})" @@ -160,6 +166,8 @@ class FreeText(Option[str]): """Text option that allows users to enter strings. Needs to be validated by the world or option definition.""" + default = "" + def __init__(self, value: str): assert isinstance(value, str), "value of FreeText must be a string" self.value = value @@ -811,7 +819,7 @@ class VerifyKeys(metaclass=FreezeValidKeys): class OptionDict(Option[typing.Dict[str, typing.Any]], VerifyKeys, typing.Mapping[str, typing.Any]): - default: typing.Dict[str, typing.Any] = {} + default = {} supports_weighting = False def __init__(self, value: typing.Dict[str, typing.Any]): @@ -852,7 +860,7 @@ class OptionList(Option[typing.List[typing.Any]], VerifyKeys): # If only unique entries are needed and input order of elements does not matter, OptionSet should be used instead. # Not a docstring so it doesn't get grabbed by the options system. - default: typing.Union[typing.List[typing.Any], typing.Tuple[typing.Any, ...]] = () + default = () supports_weighting = False def __init__(self, value: typing.Iterable[typing.Any]): @@ -878,7 +886,7 @@ class OptionList(Option[typing.List[typing.Any]], VerifyKeys): class OptionSet(Option[typing.Set[str]], VerifyKeys): - default: typing.Union[typing.Set[str], typing.FrozenSet[str]] = frozenset() + default = frozenset() supports_weighting = False def __init__(self, value: typing.Iterable[str]): diff --git a/worlds/soe/options.py b/worlds/soe/options.py index cb9e9bb6..c5ac02c2 100644 --- a/worlds/soe/options.py +++ b/worlds/soe/options.py @@ -1,5 +1,5 @@ from dataclasses import dataclass, fields -from typing import Any, cast, Dict, Iterator, List, Tuple, Protocol +from typing import Any, ClassVar, cast, Dict, Iterator, List, Tuple, Protocol from Options import AssembleOptions, Choice, DeathLink, DefaultOnToggle, Option, PerGameCommonOptions, \ ProgressionBalancing, Range, Toggle @@ -8,13 +8,13 @@ from Options import AssembleOptions, Choice, DeathLink, DefaultOnToggle, Option, # typing boilerplate class FlagsProtocol(Protocol): value: int - default: int + default: ClassVar[int] flags: List[str] class FlagProtocol(Protocol): value: int - default: int + default: ClassVar[int] flag: str