@@ -69,6 +69,10 @@ def __init__(self, b: EmptyClass):
6969 self .b = b
7070
7171
72+ City = NewType ('City' , str )
73+ Animal = Annotated [str , 'Animal' ]
74+
75+
7276def prepare_nested_injectors ():
7377 def configure (binder ):
7478 binder .bind (str , to = 'asd' )
@@ -578,6 +582,27 @@ def provide_name(self) -> str:
578582 assert injector .get (str ) == 'Bob'
579583
580584
585+ def test_module_provider_keeps_annotated_types_and_new_types_separate () -> None :
586+ class MyModule (Module ):
587+ @provider
588+ def provide_name (self ) -> str :
589+ return 'Bob'
590+
591+ @provider
592+ def provide_city (self ) -> City :
593+ return City ('Stockholm' )
594+
595+ @provider
596+ def provide_animal (self ) -> Animal :
597+ return 'Dog'
598+
599+ module = MyModule ()
600+ injector = Injector (module )
601+ assert injector .get (str ) == 'Bob'
602+ assert injector .get (City ) == City ('Stockholm' )
603+ assert injector .get (Animal ) == 'Dog'
604+
605+
581606def test_module_class_gets_instantiated ():
582607 name = 'Meg'
583608
@@ -609,7 +634,10 @@ def provide_description(self, age: int, weight: float) -> str:
609634
610635
611636Names = NewType ('Names' , List [str ])
612- Passwords = NewType ('Ages' , Dict [str , str ])
637+ Passwords = NewType ('Passwords' , Dict [str , str ])
638+
639+ Animals = Annotated [List [str ], 'Animals' ]
640+ AnimalFoods = Annotated [Dict [str , str ], 'AnimalFoods' ]
613641
614642
615643def test_multibind ():
@@ -623,6 +651,12 @@ def configure(binder):
623651 # To see that NewTypes are treated distinctly
624652 binder .multibind (Names , to = ['Bob' ])
625653 binder .multibind (Passwords , to = {'Bob' : 'password1' })
654+ # To see that Annotated collections are treated distinctly
655+ binder .multibind (Animals , to = ['Dog' ])
656+ binder .multibind (AnimalFoods , to = {'Dog' : 'meat' })
657+ # To see that collections of Annotated types are treated distinctly
658+ binder .multibind (List [City ], to = [City ('Stockholm' )])
659+ binder .multibind (Dict [str , City ], to = {'Sweden' : City ('Stockholm' )})
626660
627661 # Then @multiprovider-decorated Module methods
628662 class CustomModule (Module ):
@@ -644,11 +678,27 @@ def provide_str_to_int_mapping(self) -> Dict[str, int]:
644678
645679 @multiprovider
646680 def provide_names (self ) -> Names :
647- return ['Alice' , 'Clarice' ]
681+ return Names ( ['Alice' , 'Clarice' ])
648682
649683 @multiprovider
650684 def provide_passwords (self ) -> Passwords :
651- return {'Alice' : 'aojrioeg3' , 'Clarice' : 'clarice30' }
685+ return Passwords ({'Alice' : 'aojrioeg3' , 'Clarice' : 'clarice30' })
686+
687+ @multiprovider
688+ def provide_animals (self ) -> Animals :
689+ return ['Cat' , 'Fish' ]
690+
691+ @multiprovider
692+ def provide_animal_foods (self ) -> AnimalFoods :
693+ return {'Cat' : 'milk' , 'Fish' : 'flakes' }
694+
695+ @multiprovider
696+ def provide_cities (self ) -> List [City ]:
697+ return [City ('New York' ), City ('Tokyo' )]
698+
699+ @multiprovider
700+ def provide_city_mapping (self ) -> Dict [str , City ]:
701+ return {'USA' : City ('New York' ), 'Japan' : City ('Tokyo' )}
652702
653703 injector = Injector ([configure , CustomModule ])
654704 assert injector .get (List [str ]) == ['not a name' , 'not a name either' ]
@@ -657,6 +707,10 @@ def provide_passwords(self) -> Passwords:
657707 assert injector .get (Dict [str , int ]) == {'weight' : 12 , 'height' : 33 }
658708 assert injector .get (Names ) == ['Bob' , 'Alice' , 'Clarice' ]
659709 assert injector .get (Passwords ) == {'Bob' : 'password1' , 'Alice' : 'aojrioeg3' , 'Clarice' : 'clarice30' }
710+ assert injector .get (Animals ) == ['Dog' , 'Cat' , 'Fish' ]
711+ assert injector .get (AnimalFoods ) == {'Dog' : 'meat' , 'Cat' : 'milk' , 'Fish' : 'flakes' }
712+ assert injector .get (List [City ]) == ['Stockholm' , 'New York' , 'Tokyo' ]
713+ assert injector .get (Dict [str , City ]) == {'Sweden' : 'Stockholm' , 'USA' : 'New York' , 'Japan' : 'Tokyo' }
660714
661715
662716class Plugin (abc .ABC ):
@@ -679,7 +733,7 @@ class PluginD(Plugin):
679733 pass
680734
681735
682- def test__multibind_list_of_plugins ():
736+ def test_multibind_list_of_plugins ():
683737 def configure (binder : Binder ):
684738 binder .multibind (List [Plugin ], to = PluginA )
685739 binder .multibind (List [Plugin ], to = [PluginB , PluginC ()])
@@ -694,7 +748,7 @@ def configure(binder: Binder):
694748 assert isinstance (plugins [3 ], PluginD )
695749
696750
697- def test__multibind_dict_of_plugins ():
751+ def test_multibind_dict_of_plugins ():
698752 def configure (binder : Binder ):
699753 binder .multibind (Dict [str , Plugin ], to = {'a' : PluginA })
700754 binder .multibind (Dict [str , Plugin ], to = {'b' : PluginB , 'c' : PluginC ()})
@@ -709,7 +763,7 @@ def configure(binder: Binder):
709763 assert isinstance (plugins ['d' ], PluginD )
710764
711765
712- def test__multibinding_to_non_generic_type_raises_error ():
766+ def test_multibinding_to_non_generic_type_raises_error ():
713767 def configure_list (binder : Binder ):
714768 binder .multibind (List , to = [1 ])
715769
@@ -723,22 +777,49 @@ def configure_dict(binder: Binder):
723777 Injector ([configure_dict ])
724778
725779
726- def test_multibind_types_respect_the_bound_type_scope () -> None :
780+ def test_multibind_types_are_not_affected_by_the_bound_type_scope () -> None :
727781 def configure (binder : Binder ) -> None :
728782 binder .bind (PluginA , to = PluginA , scope = singleton )
729783 binder .multibind (List [Plugin ], to = PluginA )
730784
731785 injector = Injector ([configure ])
732786 first_list = injector .get (List [Plugin ])
733787 second_list = injector .get (List [Plugin ])
734- child_injector = injector .create_child_injector ()
735- third_list = child_injector .get (List [Plugin ])
736788
737- assert first_list [0 ] is second_list [0 ]
738- assert third_list [0 ] is second_list [0 ]
789+ assert injector .get (PluginA ) is injector .get (PluginA )
790+ assert first_list [0 ] is not injector .get (PluginA )
791+ assert first_list [0 ] is not second_list [0 ]
792+
793+
794+ def test_multibind_types_are_not_affected_by_the_bound_type_provider () -> None :
795+ def configure (binder : Binder ) -> None :
796+ binder .bind (PluginA , to = InstanceProvider (PluginA ()))
797+ binder .multibind (List [Plugin ], to = PluginA )
798+
799+ injector = Injector ([configure ])
800+ first_list = injector .get (List [Plugin ])
801+ second_list = injector .get (List [Plugin ])
802+
803+ assert injector .get (PluginA ) is injector .get (PluginA )
804+ assert first_list [0 ] is not injector .get (PluginA )
805+ assert first_list [0 ] is not second_list [0 ]
739806
740807
741- def test_multibind_list_scopes_applies_to_the_bound_items () -> None :
808+ def test_multibind_dict_types_use_their_own_bound_providers_and_scopes () -> None :
809+ def configure (binder : Binder ) -> None :
810+ binder .bind (PluginA , to = InstanceProvider (PluginA ()))
811+ binder .bind (PluginB , to = PluginB , scope = singleton )
812+ binder .multibind (Dict [str , Plugin ], to = {'a' : PluginA , 'b' : PluginB })
813+
814+ injector = Injector ([configure ])
815+
816+ dictionary = injector .get (Dict [str , Plugin ])
817+
818+ assert dictionary ['a' ] is not injector .get (PluginA )
819+ assert dictionary ['b' ] is not injector .get (PluginB )
820+
821+
822+ def test_multibind_list_scopes_apply_to_the_bound_items () -> None :
742823 def configure (binder : Binder ) -> None :
743824 binder .multibind (List [Plugin ], to = PluginA , scope = singleton )
744825 binder .multibind (List [Plugin ], to = PluginB )
@@ -754,7 +835,64 @@ def configure(binder: Binder) -> None:
754835 assert first_list [2 ] is second_list [2 ]
755836
756837
757- def test_multibind_dict_scopes_applies_to_the_bound_items () -> None :
838+ def test_multibind_list_scopes_apply_to_the_bound_items_not_types () -> None :
839+ def configure (binder : Binder ) -> None :
840+ binder .multibind (List [Plugin ], to = PluginA )
841+ binder .multibind (List [Plugin ], to = [PluginA , PluginB ], scope = singleton )
842+ binder .multibind (List [Plugin ], to = PluginA )
843+ binder .multibind (List [Plugin ], to = PluginA , scope = singleton )
844+
845+ injector = Injector ([configure ])
846+ first_list = injector .get (List [Plugin ])
847+ second_list = injector .get (List [Plugin ])
848+
849+ assert first_list is not second_list
850+ assert first_list [0 ] is not second_list [0 ]
851+ assert first_list [1 ] is second_list [1 ]
852+ assert first_list [2 ] is second_list [2 ]
853+ assert first_list [3 ] is not second_list [3 ]
854+ assert first_list [4 ] is second_list [4 ]
855+
856+
857+ def test_multibind_dict_scopes_apply_to_the_bound_items_in_the_multibound_dict () -> None :
858+ SingletonPlugins = Annotated [Plugin , "singleton" ]
859+ OtherPlugins = Annotated [Plugin , "other" ]
860+
861+ def configure (binder : Binder ) -> None :
862+ binder .multibind (Dict [str , SingletonPlugins ], to = {'a' : PluginA }, scope = singleton )
863+ binder .multibind (Dict [str , OtherPlugins ], to = {'a' : PluginA })
864+
865+ injector = Injector ([configure ])
866+ singletons_1 = injector .get (Dict [str , SingletonPlugins ])
867+ singletons_2 = injector .get (Dict [str , SingletonPlugins ])
868+ others_1 = injector .get (Dict [str , OtherPlugins ])
869+ others_2 = injector .get (Dict [str , OtherPlugins ])
870+
871+ assert singletons_1 ['a' ] is singletons_2 ['a' ]
872+ assert singletons_1 ['a' ] is not others_1 ['a' ]
873+ assert others_1 ['a' ] is not others_2 ['a' ]
874+
875+
876+ def test_multibind_list_scopes_apply_to_the_bound_items_in_the_multibound_list () -> None :
877+ SingletonPlugins = Annotated [Plugin , "singleton" ]
878+ OtherPlugins = Annotated [Plugin , "other" ]
879+
880+ def configure (binder : Binder ) -> None :
881+ binder .multibind (List [SingletonPlugins ], to = PluginA , scope = singleton )
882+ binder .multibind (List [OtherPlugins ], to = PluginA )
883+
884+ injector = Injector ([configure ])
885+ singletons_1 = injector .get (List [SingletonPlugins ])
886+ singletons_2 = injector .get (List [SingletonPlugins ])
887+ others_1 = injector .get (List [OtherPlugins ])
888+ others_2 = injector .get (List [OtherPlugins ])
889+
890+ assert singletons_1 [0 ] is singletons_2 [0 ]
891+ assert singletons_1 [0 ] is not others_1 [0 ]
892+ assert others_1 [0 ] is not others_2 [0 ]
893+
894+
895+ def test_multibind_dict_scopes_apply_to_the_bound_items () -> None :
758896 def configure (binder : Binder ) -> None :
759897 binder .multibind (Dict [str , Plugin ], to = {'a' : PluginA }, scope = singleton )
760898 binder .multibind (Dict [str , Plugin ], to = {'b' : PluginB })
@@ -773,6 +911,7 @@ def configure(binder: Binder) -> None:
773911def test_multibind_scopes_does_not_apply_to_the_type_globally () -> None :
774912 def configure (binder : Binder ) -> None :
775913 binder .multibind (List [Plugin ], to = PluginA , scope = singleton )
914+ binder .multibind (Dict [str , Plugin ], to = {'a' : PluginA }, scope = singleton )
776915
777916 injector = Injector ([configure ])
778917 plugins = injector .get (List [Plugin ])
0 commit comments