72
72
We also support protocols. It has the same limitation as ``Generic`` types.
73
73
It is also dispatched after all regular instances are checked.
74
74
75
- To work with protocols, one needs to pass ``is_protocol`` flag to instance:
75
+ To work with protocols, one needs
76
+ to pass ``protocol`` named argument to instance:
76
77
77
78
.. code:: python
78
79
79
80
>>> from typing import Sequence
80
81
81
- >>> @example.instance(Sequence, is_protocol=True )
82
+ >>> @example.instance(protocol=Sequence )
82
83
... def _sequence_example(instance: Sequence) -> str:
83
84
... return ','.join(str(item) for item in instance)
84
85
99
100
>>> class CustomProtocol(Protocol):
100
101
... field: str
101
102
102
- >>> @example.instance(CustomProtocol, is_protocol=True )
103
+ >>> @example.instance(protocol=CustomProtocol )
103
104
... def _custom_protocol_example(instance: CustomProtocol) -> str:
104
105
... return instance.field
105
106
131
132
from typing_extensions import TypeGuard , final
132
133
133
134
from classes ._registry import (
135
+ DefaultValue ,
134
136
TypeRegistry ,
135
137
choose_registry ,
136
138
default_implementation ,
@@ -313,7 +315,7 @@ class _TypeClass( # noqa: WPS214
313
315
314
316
# Registry:
315
317
'_delegates' ,
316
- '_instances ' ,
318
+ '_exact_types ' ,
317
319
'_protocols' ,
318
320
319
321
# Cache:
@@ -362,7 +364,7 @@ def __init__(
362
364
363
365
# Registries:
364
366
self ._delegates : TypeRegistry = {}
365
- self ._instances : TypeRegistry = {}
367
+ self ._exact_types : TypeRegistry = {}
366
368
self ._protocols : TypeRegistry = {}
367
369
368
370
# Cache parts:
@@ -382,8 +384,9 @@ def __call__(
382
384
383
385
The resolution order is the following:
384
386
385
- 1. Exact types that are passed as ``.instance`` arguments
386
- 2. Protocols that are passed with ``is_protocol=True``
387
+ 1. Delegates passed with ``delegate=``
388
+ 2. Exact types that are passed as ``.instance`` arguments
389
+ 3. Protocols that are passed with ``protocol=``
387
390
388
391
We don't guarantee the order of types inside groups.
389
392
Use correct types, do not rely on our order.
@@ -480,7 +483,7 @@ def supports(
480
483
481
484
>>> from typing import Sized
482
485
483
- >>> @example.instance(Sized, is_protocol=True )
486
+ >>> @example.instance(protocol=Sized )
484
487
... def _example_sized(instance: Sized) -> str:
485
488
... return 'Size is {0}'.format(len(instance))
486
489
@@ -523,18 +526,16 @@ def supports(
523
526
524
527
def instance (
525
528
self ,
526
- type_argument : Optional [_NewInstanceType ],
529
+ exact_type : Optional [_NewInstanceType ] = DefaultValue , # type: ignore
527
530
* ,
528
- # TODO: at one point I would like to remove `is_protocol`
529
- # and make this function decide whether this type is protocol or not.
530
- is_protocol : bool = False ,
531
- delegate : Optional [type ] = None ,
531
+ protocol : type = DefaultValue ,
532
+ delegate : type = DefaultValue ,
532
533
) -> '_TypeClassInstanceDef[_NewInstanceType, _TypeClassType]' :
533
534
"""
534
535
We use this method to store implementation for each specific type.
535
536
536
537
Args:
537
- is_protocol : required when passing protocols.
538
+ protocol : required when passing protocols.
538
539
delegate: required when using delegate types, for example,
539
540
when working with concrete generics like ``List[str]``.
540
541
@@ -543,7 +544,8 @@ def instance(
543
544
544
545
.. note::
545
546
546
- ``is_protocol`` and ``delegate`` are mutually exclusive.
547
+ ``exact_type``, ``protocol``, and ``delegate``
548
+ are mutually exclusive. Only one argument can be passed.
547
549
548
550
We don't use ``@overload`` decorator here
549
551
(which makes our ``mypy`` plugin even more complex)
@@ -558,7 +560,14 @@ def instance(
558
560
# Then, we have a regular `type_argument`. It is used for most types.
559
561
# Lastly, we have `type(None)` to handle cases
560
562
# when we want to register `None` as a type / singleton value.
561
- typ = delegate or type_argument or type (None )
563
+ registry , typ = choose_registry (
564
+ exact_type = exact_type ,
565
+ protocol = protocol ,
566
+ delegate = delegate ,
567
+ exact_types = self ._exact_types ,
568
+ protocols = self ._protocols ,
569
+ delegates = self ._delegates ,
570
+ )
562
571
563
572
# That's how we check for generics,
564
573
# generics that look like `List[int]` or `set[T]` will fail this check,
@@ -567,19 +576,9 @@ def instance(
567
576
isinstance (object (), typ )
568
577
569
578
def decorator (implementation ):
570
- container = choose_registry (
571
- typ = typ ,
572
- is_protocol = is_protocol ,
573
- delegate = delegate ,
574
- delegates = self ._delegates ,
575
- instances = self ._instances ,
576
- protocols = self ._protocols ,
577
- )
578
- container [typ ] = implementation
579
-
579
+ registry [typ ] = implementation
580
580
self ._dispatch_cache .clear ()
581
581
return implementation
582
-
583
582
return decorator
584
583
585
584
def _dispatch (self , instance , instance_type : type ) -> Optional [Callable ]:
@@ -591,15 +590,15 @@ def _dispatch(self, instance, instance_type: type) -> Optional[Callable]:
591
590
2. By matching protocols
592
591
3. By its ``mro``
593
592
"""
594
- implementation = self ._instances .get (instance_type , None )
593
+ implementation = self ._exact_types .get (instance_type , None )
595
594
if implementation is not None :
596
595
return implementation
597
596
598
597
for protocol , callback in self ._protocols .items ():
599
598
if isinstance (instance , protocol ):
600
599
return callback
601
600
602
- return _find_impl (instance_type , self ._instances )
601
+ return _find_impl (instance_type , self ._exact_types )
603
602
604
603
def _dispatch_delegate (self , instance ) -> Optional [Callable ]:
605
604
for delegate , callback in self ._delegates .items ():
0 commit comments