Skip to content

Commit

Permalink
Core: Utils.py typing (ArchipelagoMW#3064)
Browse files Browse the repository at this point in the history
* Core: Utils.py typing

`get_fuzzy_results` typing

There are places that this is called with a `word_list` that is not a `Sequence`, and it is valid (e.g., `set` or `dict`).

To decide the right type, we look at how `word_list` is used:
- the parameter to `len` - requires `__len__`
- the 2nd parameter to `map` - requires `__iter__`

Then we look at https://docs.python.org/3/library/collections.abc.html#collections-abstract-base-classes and ask what is the simplest type that includes both `__len__` and `__iter__`: `Collection`

(Python 3.8 requires using the alias in `typing`, instead of `collections.abc`)

* a bit more typing and cleaning

* fine, take away my fun for something that no one is ever going to see anyway...

Co-authored-by: black-sliver <[email protected]>

---------

Co-authored-by: black-sliver <[email protected]>
  • Loading branch information
2 people authored and James Schurig committed Jun 13, 2024
1 parent f7a03a4 commit f0e327e
Showing 1 changed file with 29 additions and 27 deletions.
56 changes: 29 additions & 27 deletions Utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,7 @@ def cache_self1(function: typing.Callable[[S, T], RetType]) -> typing.Callable[[

@functools.wraps(function)
def wrap(self: S, arg: T) -> RetType:
cache: Optional[Dict[T, RetType]] = typing.cast(Optional[Dict[T, RetType]],
getattr(self, cache_name, None))
cache: Optional[Dict[T, RetType]] = getattr(self, cache_name, None)
if cache is None:
res = function(self, arg)
setattr(self, cache_name, {arg: res})
Expand Down Expand Up @@ -209,10 +208,11 @@ def output_path(*path: str) -> str:

def open_file(filename: typing.Union[str, "pathlib.Path"]) -> None:
if is_windows:
os.startfile(filename)
os.startfile(filename) # type: ignore
else:
from shutil import which
open_command = which("open") if is_macos else (which("xdg-open") or which("gnome-open") or which("kde-open"))
assert open_command, "Didn't find program for open_file! Please report this together with system details."
subprocess.call([open_command, filename])


Expand Down Expand Up @@ -300,21 +300,21 @@ def get_options() -> Settings:
return get_settings()


def persistent_store(category: str, key: typing.Any, value: typing.Any):
def persistent_store(category: str, key: str, value: typing.Any):
path = user_path("_persistent_storage.yaml")
storage: dict = persistent_load()
category = storage.setdefault(category, {})
category[key] = value
storage = persistent_load()
category_dict = storage.setdefault(category, {})
category_dict[key] = value
with open(path, "wt") as f:
f.write(dump(storage, Dumper=Dumper))


def persistent_load() -> typing.Dict[str, dict]:
storage = getattr(persistent_load, "storage", None)
def persistent_load() -> Dict[str, Dict[str, Any]]:
storage: Union[Dict[str, Dict[str, Any]], None] = getattr(persistent_load, "storage", None)
if storage:
return storage
path = user_path("_persistent_storage.yaml")
storage: dict = {}
storage = {}
if os.path.exists(path):
try:
with open(path, "r") as f:
Expand All @@ -323,7 +323,7 @@ def persistent_load() -> typing.Dict[str, dict]:
logging.debug(f"Could not read store: {e}")
if storage is None:
storage = {}
persistent_load.storage = storage
setattr(persistent_load, "storage", storage)
return storage


Expand Down Expand Up @@ -365,6 +365,7 @@ def store_data_package_for_checksum(game: str, data: typing.Dict[str, Any]) -> N
except Exception as e:
logging.debug(f"Could not store data package: {e}")


def get_default_adjuster_settings(game_name: str) -> Namespace:
import LttPAdjuster
adjuster_settings = Namespace()
Expand All @@ -383,7 +384,9 @@ def get_adjuster_settings(game_name: str) -> Namespace:
default_settings = get_default_adjuster_settings(game_name)

# Fill in any arguments from the argparser that we haven't seen before
return Namespace(**vars(adjuster_settings), **{k:v for k,v in vars(default_settings).items() if k not in vars(adjuster_settings)})
return Namespace(**vars(adjuster_settings), **{
k: v for k, v in vars(default_settings).items() if k not in vars(adjuster_settings)
})


@cache_argsless
Expand All @@ -407,13 +410,13 @@ def get_unique_identifier():
class RestrictedUnpickler(pickle.Unpickler):
generic_properties_module: Optional[object]

def __init__(self, *args, **kwargs):
def __init__(self, *args: Any, **kwargs: Any) -> None:
super(RestrictedUnpickler, self).__init__(*args, **kwargs)
self.options_module = importlib.import_module("Options")
self.net_utils_module = importlib.import_module("NetUtils")
self.generic_properties_module = None

def find_class(self, module, name):
def find_class(self, module: str, name: str) -> type:
if module == "builtins" and name in safe_builtins:
return getattr(builtins, name)
# used by MultiServer -> savegame/multidata
Expand All @@ -437,7 +440,7 @@ def find_class(self, module, name):
raise pickle.UnpicklingError(f"global '{module}.{name}' is forbidden")


def restricted_loads(s):
def restricted_loads(s: bytes) -> Any:
"""Helper function analogous to pickle.loads()."""
return RestrictedUnpickler(io.BytesIO(s)).load()

Expand Down Expand Up @@ -493,7 +496,7 @@ def init_logging(name: str, loglevel: typing.Union[str, int] = logging.INFO, wri
file_handler.setFormatter(logging.Formatter(log_format))

class Filter(logging.Filter):
def __init__(self, filter_name, condition):
def __init__(self, filter_name: str, condition: typing.Callable[[logging.LogRecord], bool]) -> None:
super().__init__(filter_name)
self.condition = condition

Expand Down Expand Up @@ -544,7 +547,7 @@ def _cleanup():
)


def stream_input(stream, queue):
def stream_input(stream: typing.TextIO, queue: "asyncio.Queue[str]"):
def queuer():
while 1:
try:
Expand Down Expand Up @@ -572,7 +575,7 @@ class VersionException(Exception):
pass


def chaining_prefix(index: int, labels: typing.Tuple[str]) -> str:
def chaining_prefix(index: int, labels: typing.Sequence[str]) -> str:
text = ""
max_label = len(labels) - 1
while index > max_label:
Expand All @@ -595,29 +598,28 @@ def format_SI_prefix(value, power=1000, power_labels=("", "k", "M", "G", "T", "P
return f"{value.quantize(decimal.Decimal('1.00'))} {chaining_prefix(n, power_labels)}"


def get_fuzzy_results(input_word: str, wordlist: typing.Sequence[str], limit: typing.Optional[int] = None) \
def get_fuzzy_results(input_word: str, word_list: typing.Collection[str], limit: typing.Optional[int] = None) \
-> typing.List[typing.Tuple[str, int]]:
import jellyfish

def get_fuzzy_ratio(word1: str, word2: str) -> float:
return (1 - jellyfish.damerau_levenshtein_distance(word1.lower(), word2.lower())
/ max(len(word1), len(word2)))

limit: int = limit if limit else len(wordlist)
limit = limit if limit else len(word_list)
return list(
map(
lambda container: (container[0], int(container[1]*100)), # convert up to limit to int %
sorted(
map(lambda candidate:
(candidate, get_fuzzy_ratio(input_word, candidate)),
wordlist),
map(lambda candidate: (candidate, get_fuzzy_ratio(input_word, candidate)), word_list),
key=lambda element: element[1],
reverse=True)[0:limit]
reverse=True
)[0:limit]
)
)


def open_filename(title: str, filetypes: typing.Sequence[typing.Tuple[str, typing.Sequence[str]]], suggest: str = "") \
def open_filename(title: str, filetypes: typing.Iterable[typing.Tuple[str, typing.Iterable[str]]], suggest: str = "") \
-> typing.Optional[str]:
logging.info(f"Opening file input dialog for {title}.")

Expand Down Expand Up @@ -734,7 +736,7 @@ def is_kivy_running():
root.update()


def title_sorted(data: typing.Sequence, key=None, ignore: typing.Set = frozenset(("a", "the"))):
def title_sorted(data: typing.Iterable, key=None, ignore: typing.AbstractSet[str] = frozenset(("a", "the"))):
"""Sorts a sequence of text ignoring typical articles like "a" or "the" in the beginning."""
def sorter(element: Union[str, Dict[str, Any]]) -> str:
if (not isinstance(element, str)):
Expand Down Expand Up @@ -788,7 +790,7 @@ class DeprecateDict(dict):
log_message: str
should_error: bool

def __init__(self, message, error: bool = False) -> None:
def __init__(self, message: str, error: bool = False) -> None:
self.log_message = message
self.should_error = error
super().__init__()
Expand Down

0 comments on commit f0e327e

Please sign in to comment.