Skip to content

Commit

Permalink
Core: typing for Option.default and a few other ClassVars (Archipel…
Browse files Browse the repository at this point in the history
…agoMW#2899)

* Core: typing for `Option.default` and a few other `Option` class variables

This is a replacement for ArchipelagoMW#2173

You can read discussion there for issues we found for why we can't have more specific typing on `default`

instead of setting a default in `Option` (where we don't know the type), we check in the metaclass to make sure they have a default.

* NumericOption doesn't need the type annotation that brings out the mypy bug

* SoE default ClassVar
  • Loading branch information
beauxq authored and EmilyV99 committed Apr 15, 2024
1 parent 13ffe97 commit 7909496
Show file tree
Hide file tree
Showing 2 changed files with 17 additions and 9 deletions.
20 changes: 14 additions & 6 deletions Options.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@ def __new__(mcs, name, bases, attrs):
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
Expand Down Expand Up @@ -96,7 +101,7 @@ def meta__init__(self, *args, **kwargs):

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()
Expand All @@ -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})"
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -811,7 +819,7 @@ def verify(self, world: typing.Type[World], player_name: str, plando_options: "P


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]):
Expand Down Expand Up @@ -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]):
Expand All @@ -878,7 +886,7 @@ def __contains__(self, item):


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]):
Expand Down
6 changes: 3 additions & 3 deletions worlds/soe/options.py
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -8,13 +8,13 @@
# 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


Expand Down

0 comments on commit 7909496

Please sign in to comment.