@@ -995,66 +995,119 @@ def test_text_real_file(self, tmp_path: Path):
995995 check_type (f , TextIO )
996996
997997
998+ @pytest .mark .parametrize (
999+ "instantiate, annotation" ,
1000+ [
1001+ pytest .param (True , RuntimeProtocol , id = "instance_runtime" ),
1002+ pytest .param (False , Type [RuntimeProtocol ], id = "class_runtime" ),
1003+ pytest .param (True , StaticProtocol , id = "instance_static" ),
1004+ pytest .param (False , Type [StaticProtocol ], id = "class_static" ),
1005+ ],
1006+ )
9981007class TestProtocol :
999- def test_protocol (self ):
1008+ def test_member_defaultval (self , instantiate , annotation ):
10001009 class Foo :
10011010 member = 1
10021011
1003- def meth (self ) -> None :
1012+ def meth (self , x : str ) -> None :
10041013 pass
10051014
1006- check_type (Foo (), RuntimeProtocol )
1007- check_type (Foo , Type [RuntimeProtocol ])
1015+ subject = Foo () if instantiate else Foo
1016+ for _ in range (2 ): # Makes sure that the cache is also exercised
1017+ check_type (subject , annotation )
10081018
1009- def test_protocol_warns_on_static (self ):
1019+ def test_member_annotation (self , instantiate , annotation ):
10101020 class Foo :
1011- member = 1
1021+ member : int
10121022
1013- def meth (self ) -> None :
1023+ def meth (self , x : str ) -> None :
10141024 pass
10151025
1016- with pytest .warns (
1017- UserWarning , match = r"Typeguard cannot check the StaticProtocol protocol.*"
1018- ) as warning :
1019- check_type (Foo (), StaticProtocol )
1026+ subject = Foo () if instantiate else Foo
1027+ for _ in range (2 ):
1028+ check_type (subject , annotation )
10201029
1021- assert warning .list [0 ].filename == __file__
1030+ def test_attribute_missing (self , instantiate , annotation ):
1031+ class Foo :
1032+ val = 1
10221033
1023- with pytest .warns (
1024- UserWarning , match = r"Typeguard cannot check the StaticProtocol protocol.*"
1025- ) as warning :
1026- check_type (Foo , Type [StaticProtocol ])
1034+ def meth (self , x : str ) -> None :
1035+ pass
10271036
1028- assert warning .list [0 ].filename == __file__
1037+ clsname = f"{ __name__ } .TestProtocol.test_attribute_missing.<locals>.Foo"
1038+ subject = Foo () if instantiate else Foo
1039+ for _ in range (2 ):
1040+ pytest .raises (TypeCheckError , check_type , subject , annotation ).match (
1041+ f"{ clsname } is not compatible with the (Runtime|Static)Protocol "
1042+ f"protocol because it has no attribute named 'member'"
1043+ )
10291044
1030- def test_fail_non_method_members (self ):
1045+ def test_method_missing (self , instantiate , annotation ):
10311046 class Foo :
1032- val = 1
1047+ member : int
10331048
1034- def meth (self ) -> None :
1035- pass
1049+ pattern = (
1050+ f"{ __name__ } .TestProtocol.test_method_missing.<locals>.Foo is not "
1051+ f"compatible with the (Runtime|Static)Protocol protocol because it has no "
1052+ f"method named 'meth'"
1053+ )
1054+ subject = Foo () if instantiate else Foo
1055+ for _ in range (2 ):
1056+ pytest .raises (TypeCheckError , check_type , subject , annotation ).match (
1057+ pattern
1058+ )
1059+
1060+ def test_attribute_is_not_method_1 (self , instantiate , annotation ):
1061+ class Foo :
1062+ member : int
1063+ meth : str
10361064
1037- clsname = f"{ __name__ } .TestProtocol.test_fail_non_method_members.<locals>.Foo"
1038- pytest .raises (TypeCheckError , check_type , Foo (), RuntimeProtocol ).match (
1039- f"{ clsname } is not compatible with the RuntimeProtocol protocol"
1065+ pattern = (
1066+ f"{ __name__ } .TestProtocol.test_attribute_is_not_method_1.<locals>.Foo is "
1067+ f"not compatible with the (Runtime|Static)Protocol protocol because its "
1068+ f"'meth' attribute is not a method"
10401069 )
1041- pytest .raises (TypeCheckError , check_type , Foo , Type [RuntimeProtocol ]).match (
1042- f"class { clsname } is not compatible with the RuntimeProtocol protocol"
1070+ subject = Foo () if instantiate else Foo
1071+ for _ in range (2 ):
1072+ pytest .raises (TypeCheckError , check_type , subject , annotation ).match (
1073+ pattern
1074+ )
1075+
1076+ def test_attribute_is_not_method_2 (self , instantiate , annotation ):
1077+ class Foo :
1078+ member : int
1079+ meth = "foo"
1080+
1081+ pattern = (
1082+ f"{ __name__ } .TestProtocol.test_attribute_is_not_method_2.<locals>.Foo is "
1083+ f"not compatible with the (Runtime|Static)Protocol protocol because its "
1084+ f"'meth' attribute is not a callable"
10431085 )
1086+ subject = Foo () if instantiate else Foo
1087+ for _ in range (2 ):
1088+ pytest .raises (TypeCheckError , check_type , subject , annotation ).match (
1089+ pattern
1090+ )
10441091
1045- def test_fail (self ):
1092+ def test_method_signature_mismatch (self , instantiate , annotation ):
10461093 class Foo :
1047- def meth2 (self ) -> None :
1094+ member : int
1095+
1096+ def meth (self , x : str , y : int ) -> None :
10481097 pass
10491098
10501099 pattern = (
1051- f"{ __name__ } .TestProtocol.test_fail.<locals>.Foo is not compatible with "
1052- f"the RuntimeProtocol protocol"
1053- )
1054- pytest .raises (TypeCheckError , check_type , Foo (), RuntimeProtocol ).match (pattern )
1055- pytest .raises (TypeCheckError , check_type , Foo , Type [RuntimeProtocol ]).match (
1056- pattern
1100+ rf"(class )?{ __name__ } .TestProtocol.test_method_signature_mismatch."
1101+ rf"<locals>.Foo is not compatible with the (Runtime|Static)Protocol "
1102+ rf"protocol because its 'meth' method has too many mandatory positional "
1103+ rf"arguments in its declaration; expected 2 but 3 mandatory positional "
1104+ rf"argument\(s\) declared"
10571105 )
1106+ subject = Foo () if instantiate else Foo
1107+ for _ in range (2 ):
1108+ pytest .raises (TypeCheckError , check_type , subject , annotation ).match (
1109+ pattern
1110+ )
10581111
10591112
10601113class TestRecursiveType :
0 commit comments