misc fixes

- convert import_source to a decorator which
  wraps the function call in a try block
- fix protocol class when not TYPE_CHECKING
- add id properties to Protocols, remove attributes
  since protocol expects them to be settable but
  NT is read-only
- use id to merge comments
- remove type: ignore's from reddit config
  and just store as 'Any'
This commit is contained in:
Sean Breckenridge 2021-10-28 20:29:50 -07:00
parent 33f7f48ec5
commit 4492e00250
5 changed files with 118 additions and 71 deletions

View file

@ -1,26 +1,52 @@
from typing import Any, Iterator, TypeVar, Callable, Optional, Iterable
"""
Decorator to gracefully handle importing a data source, or warning
and yielding nothing (or a default) when its not available
"""
from typing import Any, Iterator, TypeVar, Callable, Optional, Iterable, Any
from my.core.warnings import warn
from functools import wraps
T = TypeVar("T")
# The factory function may produce something that has data
# similar to the shared model, but not exactly, so not
# making this a TypeVar, is just to make reading the
# type signature below a bit easier...
T = Any
# this is probably more generic and results in less code, but is not mypy-friendly
def import_source(factory: Callable[[], Any], default: Any) -> Any:
try:
res = factory()
return res
except ModuleNotFoundError: # presumable means the user hasn't installed the module
warn(f"Module {factory.__qualname__} could not be imported, or isn't configured propertly")
return default
# https://mypy.readthedocs.io/en/latest/generics.html?highlight=decorators#decorator-factories
FactoryF = TypeVar("FactoryF", bound=Callable[..., Iterator[T]])
_DEFUALT_ITR = ()
# For an example of this, see the reddit.all file
def import_source_iter(factory: Callable[[], Iterator[T]], default: Optional[Iterable[T]] = None) -> Iterator[T]:
if default is None:
default = []
try:
res = factory()
yield from res
except ModuleNotFoundError: # presumable means the user hasn't installed the module
warn(f"Module {factory.__qualname__} could not be imported, or isn't configured propertly")
yield from default
# tried to use decorator module but it really doesn't work well
# with types and kw-arguments... :/
def import_source(
module_name: Optional[str] = None,
default: Optional[Iterable[T]] = _DEFUALT_ITR
) -> Callable[..., Callable[..., Iterator[T]]]:
"""
doesn't really play well with types, but is used to catch
ModuleNotFoundError's for when modules aren't installed in
all.py files, so the types don't particularly matter
this is meant to be used to wrap some function which imports
and then yields an iterator of objects
If the user doesn't have that module installed, it returns
nothing and warns instead
"""
def decorator(factory_func: FactoryF) -> Callable[..., Iterator[T]]:
@wraps(factory_func)
def wrapper(*args, **kwargs) -> Iterator[T]:
try:
res = factory_func(**kwargs)
yield from res
except ModuleNotFoundError:
# TODO: check if module_name is disabled and don't send warning
warn(f"Module {factory_func.__qualname__} could not be imported, or isn't configured propertly")
yield from default
return wrapper
return decorator