Closed posita closed 2 years ago
@antonagestam, I still have no idea know why RealLike[IntegralLike[int]]
works for your use case. 😕 That's a head-scratcher. 😊
I tried the following with some success, although I'm not sure if this does what you want:
diff --git a/src/phantom/interval.py b/src/phantom/interval.py
index e795106..55db040 100644
--- a/src/phantom/interval.py
+++ b/src/phantom/interval.py
@@ -26,7 +26,6 @@ from __future__ import annotations
from typing import Any
from typing import TypeVar
-from numerary.types import IntegralLike
from numerary.types import RealLike
from typing_extensions import Final
from typing_extensions import Protocol
@@ -37,12 +36,12 @@ from .predicates import interval
from .schema import Schema
from .utils import resolve_class_attr
-N = TypeVar("N", bound=RealLike[IntegralLike[int]])
+N = TypeVar("N", covariant=True)
Derived = TypeVar("Derived", bound="Interval")
class IntervalCheck(Protocol):
- def __call__(self, a: N, b: N) -> Predicate[N]:
+ def __call__(self, a: RealLike[N], b: RealLike[N]) -> Predicate[RealLike[N]]:
...
diff --git a/src/phantom/predicates/base.py b/src/phantom/predicates/base.py
index d13a76e..10c01b7 100644
--- a/src/phantom/predicates/base.py
+++ b/src/phantom/predicates/base.py
@@ -1,6 +1,10 @@
-from typing import Callable
from typing import TypeVar
+from typing_extensions import Protocol
+
T = TypeVar("T", bound=object, contravariant=True)
-Predicate = Callable[[T], bool]
+
+class Predicate(Protocol[T]):
+ def __call__(self, __: T, /) -> bool:
+ ...
I also tried this:
diff --git a/src/phantom/interval.py b/src/phantom/interval.py
index e795106..a62fd3d 100644
--- a/src/phantom/interval.py
+++ b/src/phantom/interval.py
@@ -26,7 +26,6 @@ from __future__ import annotations
from typing import Any
from typing import TypeVar
-from numerary.types import IntegralLike
from numerary.types import RealLike
from typing_extensions import Final
from typing_extensions import Protocol
@@ -37,11 +36,11 @@ from .predicates import interval
from .schema import Schema
from .utils import resolve_class_attr
-N = TypeVar("N", bound=RealLike[IntegralLike[int]])
+N = TypeVar("N", bound=RealLike, contravariant=True)
Derived = TypeVar("Derived", bound="Interval")
-class IntervalCheck(Protocol):
+class IntervalCheck(Protocol[N]):
def __call__(self, a: N, b: N) -> Predicate[N]:
...
In both cases, I can eliminate the error you got, but I get a new one:
src/phantom/base.py:162: error: Too many arguments for "__init_subclass__" of "object" [call-arg]
super().__init_subclass__(**kwargs)
^
Again, I'm not sure I follow everything that code does, nor am I sure if any of the above is helpful. I'll continue to look into this, but if there's a way to reduce it, that might help?
Hmm, that change doesn't really make sense to me. The original signature def __call__(self, a: N, b: N) -> Predicate[N]
reads like, given two values that are of the same subtype of RealLike
, return a predicate that takes the same type as argument. So given a: float
the signature requires b: float
and Predicate[float]
. Returning Predicate[int]
would be a type error for that, and passing a: int, b: float
is also a type error.
But the new signature def __call__(self, a: RealLike[N], b: RealLike[N]) -> Predicate[RealLike[N]]
, reads something like given two values that are of some subtypes of RealLike
(not necessarily the same, this signature would accept a mix of float and ints), return a predicate that also takes some RealLike
(again, not necessarily the same). The only thing that's enforced to be shared between a
, b
and the return value here is the operations that are typed to return N
...
So I don't think I want to apply that change to the type var usage.
Is changing Predicate
from a Callable
to a Protocol
related?
In both cases, I can eliminate the error you got, but I get a new one:
That's a known mypy bug, I think it has been fixed recently: https://github.com/python/mypy/issues/4660 Which mypy version are you running with?
I finally have an update! I've since made some changes to numerary
, which comes with some caveats…
The bad news: numerary
has dropped support for Python 3.7. (Its support of 3.7 was illusory anyway, but now it no longer lies about it.) I know that phantom-types
signals compatibility with 3.7, so this may be a deal killer? I'm not sure.
Other news: numerary
's caching Protocol
implementation now resides in beartype
, which numerary
now requires.
The good news: It appears this is no longer an issue, at least not with Mypy 0.960 (and possibly other recent versions). Here's my diff to antonagestam/phantom-types@0461b69c5f38579183b47d40c139e914314e7cd9 :
diff --git a/src/phantom/interval.py b/src/phantom/interval.py
index 0abe9ba..31dbe6e 100644
--- a/src/phantom/interval.py
+++ b/src/phantom/interval.py
@@ -26,7 +26,6 @@ from __future__ import annotations
from typing import Any
from typing import TypeVar
-from numerary.types import IntegralLike
from numerary.types import RealLike
from typing_extensions import Final
from typing_extensions import Protocol
@@ -37,7 +36,7 @@ from .predicates import interval
from .schema import Schema
from .utils import resolve_class_attr
-N = TypeVar("N", bound=RealLike[IntegralLike[int]])
+N = TypeVar("N", bound=RealLike)
Derived = TypeVar("Derived", bound="Interval")
Here's how it plays (from the phantom-types
repo root):
$ pip list | grep numerary
numerary 0.4.0
$ mypy --version
mypy 0.960 (compiled: yes)
$ mypy src/phantom/interval.py
Success: no issues found in 1 source file
$ pre-commit run mypy --all-files
mypy.....................................................................Passed
I'm pretty sure this was the result of changes to Mypy rather than changes to numerary
, but I don't know for sure. Either way, I think we can mark this as closed. @antonagestam, if that doesn't feel right, let me know and we can reopen and discuss further.
First hinted at by @antonagestam in https://github.com/antonagestam/phantom-types/pull/179#issue-1111676009, which led to an interesting work-around that
numerary
probably shouldn't impose, if it can avoid it.