@@ -995,66 +995,119 @@ def test_text_real_file(self, tmp_path: Path):
995
995
check_type (f , TextIO )
996
996
997
997
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
+ )
998
1007
class TestProtocol :
999
- def test_protocol (self ):
1008
+ def test_member_defaultval (self , instantiate , annotation ):
1000
1009
class Foo :
1001
1010
member = 1
1002
1011
1003
- def meth (self ) -> None :
1012
+ def meth (self , x : str ) -> None :
1004
1013
pass
1005
1014
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 )
1008
1018
1009
- def test_protocol_warns_on_static (self ):
1019
+ def test_member_annotation (self , instantiate , annotation ):
1010
1020
class Foo :
1011
- member = 1
1021
+ member : int
1012
1022
1013
- def meth (self ) -> None :
1023
+ def meth (self , x : str ) -> None :
1014
1024
pass
1015
1025
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 )
1020
1029
1021
- assert warning .list [0 ].filename == __file__
1030
+ def test_attribute_missing (self , instantiate , annotation ):
1031
+ class Foo :
1032
+ val = 1
1022
1033
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
1027
1036
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
+ )
1029
1044
1030
- def test_fail_non_method_members (self ):
1045
+ def test_method_missing (self , instantiate , annotation ):
1031
1046
class Foo :
1032
- val = 1
1047
+ member : int
1033
1048
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
1036
1064
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"
1040
1069
)
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"
1043
1085
)
1086
+ subject = Foo () if instantiate else Foo
1087
+ for _ in range (2 ):
1088
+ pytest .raises (TypeCheckError , check_type , subject , annotation ).match (
1089
+ pattern
1090
+ )
1044
1091
1045
- def test_fail (self ):
1092
+ def test_method_signature_mismatch (self , instantiate , annotation ):
1046
1093
class Foo :
1047
- def meth2 (self ) -> None :
1094
+ member : int
1095
+
1096
+ def meth (self , x : str , y : int ) -> None :
1048
1097
pass
1049
1098
1050
1099
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"
1057
1105
)
1106
+ subject = Foo () if instantiate else Foo
1107
+ for _ in range (2 ):
1108
+ pytest .raises (TypeCheckError , check_type , subject , annotation ).match (
1109
+ pattern
1110
+ )
1058
1111
1059
1112
1060
1113
class TestRecursiveType :
0 commit comments