Skip to content

Commit bcb1790

Browse files
committed
Closes #248
1 parent 5883858 commit bcb1790

File tree

3 files changed

+49
-1
lines changed

3 files changed

+49
-1
lines changed

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ We follow Semantic Versions since the `0.1.0` release.
77

88
### Features
99

10-
- Adds support for multiple type arguments in `Supports` type
10+
- Adds support for multiple type arguments in `Supports` type #244
11+
- Adds support for types that have `__instancecheck__` defined #248
1112

1213
### Bugfixes
1314

classes/_typeclass.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -520,6 +520,12 @@ def decorator(implementation):
520520
container = self._protocols if is_protocol else self._instances
521521
container[type_argument] = implementation # type: ignore
522522

523+
if getattr(type_argument, '__instancecheck__', None):
524+
# This means that this type has `__instancecheck__` defined,
525+
# which allows dynamic checks of what `isinstance` of this type.
526+
# That's why we also treat this type as a protocol.
527+
self._protocols[type_argument] = implementation
528+
523529
if self._cache_token is None: # pragma: no cover
524530
if getattr(type_argument, '__abstractmethods__', None):
525531
self._cache_token = get_cache_token()

docs/pages/concept.rst

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,47 @@ to be specified on ``.instance()`` call:
9595
>>> assert to_json([1, 'a', None]) == '[1, "a", null]'
9696
9797
98+
``__instancecheck__`` magic method
99+
----------------------------------
100+
101+
We also support types that have ``__instancecheck__`` magic method defined,
102+
like `phantom-types <https://github.com/antonagestam/phantom-types>`_.
103+
104+
We treat them similar to ``Protocol`` types, by checking passed values
105+
with ``isinstance`` for each type with ``__instancecheck__`` defined.
106+
First match wins.
107+
108+
Example:
109+
110+
.. code:: python
111+
112+
>>> from classes import typeclass
113+
114+
>>> class Meta(type):
115+
... def __instancecheck__(self, other) -> bool:
116+
... return other == 1
117+
118+
>>> class Some(object, metaclass=Meta):
119+
... ...
120+
121+
>>> @typeclass
122+
... def some(instance) -> int:
123+
... ...
124+
125+
>>> @some.instance(Some)
126+
... def _some_some(instance: Some) -> int:
127+
... return 2
128+
129+
>>> assert some(1) == 2
130+
131+
.. note::
132+
133+
It is impossible for ``mypy`` to understand that ``1`` has ``Some``
134+
type in this example. Be careful, it might break your code!
135+
136+
Consider that this feature is limited to ``phantom-types``.
137+
138+
98139
Type resolution order
99140
---------------------
100141

0 commit comments

Comments
 (0)