From 616ffb457ef73ef0614445f217f4e335ed758cf1 Mon Sep 17 00:00:00 2001 From: Dima Gerasimov Date: Mon, 25 May 2020 00:22:29 +0100 Subject: [PATCH] core: user overloads to type @warn_if_empty properly.. --- my/core/common.py | 32 ++++++++++++++++++++++++-------- tests/misc.py | 11 +++++++++++ 2 files changed, 35 insertions(+), 8 deletions(-) diff --git a/my/core/common.py b/my/core/common.py index 30c2cf0..10599f5 100644 --- a/my/core/common.py +++ b/my/core/common.py @@ -282,9 +282,22 @@ def _warn_iterator(it): if not emitted: warnings.warn(f"Function hasn't emitted any data, make sure your config paths are correct") -# todo need to popretly restrict to a container... ugh -C = TypeVar('C') -def _warn_iterable(it: C) -> C: + +# TODO ugh, so I want to express something like: +# X = TypeVar('X') +# C = TypeVar('C', bound=Iterable[X]) +# _warn_iterable(it: C) -> C +# but apparently I can't??? ugh. +# https://github.com/python/typing/issues/548 +# I guess for now overloads are fine... + +from typing import overload +X = TypeVar('X') +@overload +def _warn_iterable(it: List[X] ) -> List[X] : ... +@overload +def _warn_iterable(it: Iterable[X]) -> Iterable[X]: ... +def _warn_iterable(it): if isinstance(it, Sized): sz = len(it) if sz == 0: @@ -294,11 +307,14 @@ def _warn_iterable(it: C) -> C: return _warn_iterator(it) -CC = TypeVar('CC') -def warn_if_empty(f: CC) -> CC: +@overload +def warn_if_empty(f: Callable[[], List[X]] ) -> Callable[[], List[X]] : ... +@overload +def warn_if_empty(f: Callable[[], Iterable[X]]) -> Callable[[], Iterable[X]]: ... +def warn_if_empty(f): from functools import wraps - @wraps(f) # type: ignore[arg-type] + @wraps(f) def wrapped(*args, **kwargs): - res = f(*args, **kwargs) # type: ignore[call-arg, operator] + res = f(*args, **kwargs) return _warn_iterable(res) - return wrapped # type: ignore[return-value] + return wrapped diff --git a/tests/misc.py b/tests/misc.py index d425196..e503eb6 100644 --- a/tests/misc.py +++ b/tests/misc.py @@ -63,6 +63,15 @@ def test_warn_if_empty() -> None: def empty() -> List[str]: return [] + # should be rejected by mypy! + # todo how to actually test it? + # @warn_if_empty + # def baad() -> float: + # return 0.00 + + # reveal_type(nonempty) + # reveal_type(empty) + with warnings.catch_warnings(record=True) as w: assert list(nonempty()) == ['a', 'aba'] assert len(w) == 0 @@ -80,6 +89,8 @@ def test_warn_iterable() -> None: # reveal_type(i2) x1 = _warn_iterable(i1) x2 = _warn_iterable(i2) + # vvvv this should be flagged by mypy + # _warn_iterable(123) # reveal_type(x1) # reveal_type(x2) with warnings.catch_warnings(record=True) as w: