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:
parent
33f7f48ec5
commit
4492e00250
5 changed files with 118 additions and 71 deletions
|
@ -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
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue