Skip to content

Commit 37a871e

Browse files
Jouramiebeauxq
andauthored
Core: Allow common collections in OptionSet and OptionList constructors (#2874)
* allow common collection in set and list option constructors * allow any iterable of strings * add return None --------- Co-authored-by: beauxq <[email protected]>
1 parent 113c54f commit 37a871e

File tree

3 files changed

+25
-12
lines changed

3 files changed

+25
-12
lines changed

Options.py

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,18 @@
11
from __future__ import annotations
22

33
import abc
4-
import logging
5-
from copy import deepcopy
6-
from dataclasses import dataclass
74
import functools
5+
import logging
86
import math
97
import numbers
108
import random
119
import typing
1210
from copy import deepcopy
11+
from dataclasses import dataclass
1312

1413
from schema import And, Optional, Or, Schema
1514

16-
from Utils import get_fuzzy_results
15+
from Utils import get_fuzzy_results, is_iterable_of_str
1716

1817
if typing.TYPE_CHECKING:
1918
from BaseClasses import PlandoOptions
@@ -59,6 +58,7 @@ def __new__(mcs, name, bases, attrs):
5958
def verify(self, *args, **kwargs) -> None:
6059
for f in verifiers:
6160
f(self, *args, **kwargs)
61+
6262
attrs["verify"] = verify
6363
else:
6464
assert verifiers, "class Option is supposed to implement def verify"
@@ -183,6 +183,7 @@ def get_option_name(cls, value: str) -> str:
183183

184184
class NumericOption(Option[int], numbers.Integral, abc.ABC):
185185
default = 0
186+
186187
# note: some of the `typing.Any`` here is a result of unresolved issue in python standards
187188
# `int` is not a `numbers.Integral` according to the official typestubs
188189
# (even though isinstance(5, numbers.Integral) == True)
@@ -598,7 +599,7 @@ def verify(self, world: typing.Type[World], player_name: str, plando_options: "P
598599
if isinstance(self.value, int):
599600
return
600601
from BaseClasses import PlandoOptions
601-
if not(PlandoOptions.bosses & plando_options):
602+
if not (PlandoOptions.bosses & plando_options):
602603
# plando is disabled but plando options were given so pull the option and change it to an int
603604
option = self.value.split(";")[-1]
604605
self.value = self.options[option]
@@ -765,7 +766,7 @@ class VerifyKeys(metaclass=FreezeValidKeys):
765766
value: typing.Any
766767

767768
@classmethod
768-
def verify_keys(cls, data: typing.List[str]):
769+
def verify_keys(cls, data: typing.Iterable[str]) -> None:
769770
if cls.valid_keys:
770771
data = set(data)
771772
dataset = set(word.casefold() for word in data) if cls.valid_keys_casefold else set(data)
@@ -843,11 +844,11 @@ class OptionList(Option[typing.List[typing.Any]], VerifyKeys):
843844
# If only unique entries are needed and input order of elements does not matter, OptionSet should be used instead.
844845
# Not a docstring so it doesn't get grabbed by the options system.
845846

846-
default: typing.List[typing.Any] = []
847+
default: typing.Union[typing.List[typing.Any], typing.Tuple[typing.Any, ...]] = ()
847848
supports_weighting = False
848849

849-
def __init__(self, value: typing.List[typing.Any]):
850-
self.value = deepcopy(value)
850+
def __init__(self, value: typing.Iterable[str]):
851+
self.value = list(deepcopy(value))
851852
super(OptionList, self).__init__()
852853

853854
@classmethod
@@ -856,7 +857,7 @@ def from_text(cls, text: str):
856857

857858
@classmethod
858859
def from_any(cls, data: typing.Any):
859-
if type(data) == list:
860+
if is_iterable_of_str(data):
860861
cls.verify_keys(data)
861862
return cls(data)
862863
return cls.from_text(str(data))
@@ -882,7 +883,7 @@ def from_text(cls, text: str):
882883

883884
@classmethod
884885
def from_any(cls, data: typing.Any):
885-
if isinstance(data, (list, set, frozenset)):
886+
if is_iterable_of_str(data):
886887
cls.verify_keys(data)
887888
return cls(data)
888889
return cls.from_text(str(data))
@@ -932,7 +933,7 @@ def __new__(mcs,
932933
bases: typing.Tuple[type, ...],
933934
attrs: typing.Dict[str, typing.Any]) -> "OptionsMetaProperty":
934935
for attr_type in attrs.values():
935-
assert not isinstance(attr_type, AssembleOptions),\
936+
assert not isinstance(attr_type, AssembleOptions), \
936937
f"Options for {name} should be type hinted on the class, not assigned"
937938
return super().__new__(mcs, name, bases, attrs)
938939

Utils.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
from argparse import Namespace
2020
from settings import Settings, get_settings
2121
from typing import BinaryIO, Coroutine, Optional, Set, Dict, Any, Union
22+
from typing_extensions import TypeGuard
2223
from yaml import load, load_all, dump
2324

2425
try:
@@ -966,3 +967,13 @@ def __bool__(self):
966967

967968
def __len__(self):
968969
return sum(len(iterable) for iterable in self.iterable)
970+
971+
972+
def is_iterable_of_str(obj: object) -> TypeGuard[typing.Iterable[str]]:
973+
""" but not a `str` (because technically, `str` is `Iterable[str]`) """
974+
if isinstance(obj, str):
975+
return False
976+
if not isinstance(obj, typing.Iterable):
977+
return False
978+
obj_it: typing.Iterable[object] = obj
979+
return all(isinstance(v, str) for v in obj_it)

requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,4 @@ certifi>=2023.11.17
1111
cython>=3.0.8
1212
cymem>=2.0.8
1313
orjson>=3.9.10
14+
typing-extensions>=4.7.0

0 commit comments

Comments
 (0)