6
6
from .compat import raise_from , with_metaclass
7
7
from .default import default , warn_if_defaults_use_non_interface_members
8
8
from .formatting import bulleted_list
9
- from .functional import complement , keyfilter , valfilter
9
+ from .functional import complement , keyfilter , merge , valfilter
10
10
from .typecheck import compatible
11
11
from .typed_signature import TypedSignature
12
12
from .utils import is_a , unique
@@ -21,8 +21,22 @@ class InvalidImplementation(TypeError):
21
21
"""
22
22
23
23
24
+ class InvalidSubInterface (TypeError ):
25
+ """
26
+ Raised when on attempt to define a subclass of an interface that's not
27
+ compatible with the parent definition.
28
+ """
29
+
30
+
24
31
CLASS_ATTRIBUTE_WHITELIST = frozenset (
25
- ["__doc__" , "__module__" , "__name__" , "__qualname__" , "__weakref__" ]
32
+ [
33
+ "__doc__" ,
34
+ "__module__" ,
35
+ "__name__" ,
36
+ "__qualname__" ,
37
+ "__weakref__" ,
38
+ "_INTERFACE_IGNORE_MEMBERS" ,
39
+ ]
26
40
)
27
41
28
42
is_interface_field_name = complement (CLASS_ATTRIBUTE_WHITELIST .__contains__ )
@@ -72,6 +86,14 @@ def _conflicting_defaults(typename, conflicts):
72
86
return InvalidImplementation (message )
73
87
74
88
89
+ def _merge_parent_signatures (bases ):
90
+ return merge (filter (None , (getattr (b , "_signatures" ) for b in bases )))
91
+
92
+
93
+ def _merge_parent_defaults (bases ):
94
+ return merge (filter (None , (getattr (b , "_defaults" ) for b in bases )))
95
+
96
+
75
97
class InterfaceMeta (type ):
76
98
"""
77
99
Metaclass for interfaces.
@@ -80,22 +102,43 @@ class InterfaceMeta(type):
80
102
"""
81
103
82
104
def __new__ (mcls , name , bases , clsdict ):
83
- signatures = {}
84
- defaults = {}
85
- for k , v in keyfilter (is_interface_field_name , clsdict ).items ():
105
+ signatures = _merge_parent_signatures (bases )
106
+ defaults = _merge_parent_defaults (bases )
107
+ ignored = clsdict .get ("_INTERFACE_IGNORE_MEMBERS" , set ())
108
+
109
+ for field , v in keyfilter (is_interface_field_name , clsdict ).items ():
110
+ if field in ignored :
111
+ continue
112
+
86
113
try :
87
- signatures [ k ] = TypedSignature (v )
114
+ signature = TypedSignature (v )
88
115
except TypeError as e :
89
116
errmsg = (
90
117
"Couldn't parse signature for field "
91
118
"{iface_name}.{fieldname} of type {attrtype}." .format (
92
- iface_name = name , fieldname = k , attrtype = getname (type (v )),
119
+ iface_name = name , fieldname = field , attrtype = getname (type (v )),
93
120
)
94
121
)
95
122
raise_from (TypeError (errmsg ), e )
96
123
124
+ # If we already have a signature for this field from a parent, then
125
+ # our new signature must be a subtype of the parent signature, so
126
+ # that any valid call to the new signature must also be a valid
127
+ # call to the parent signature.
128
+ if field in signatures and not compatible (signature , signatures [field ]):
129
+ conflicted = signatures [field ]
130
+ raise InvalidSubInterface (
131
+ "\n Interface field {new}.{field} conflicts with inherited field of "
132
+ "the same name.\n "
133
+ " - {field}{new_sig} != {field}{old_sig}" .format (
134
+ new = name , field = field , new_sig = signature , old_sig = conflicted ,
135
+ )
136
+ )
137
+ else :
138
+ signatures [field ] = signature
139
+
97
140
if isinstance (v , default ):
98
- defaults [k ] = v
141
+ defaults [field ] = v
99
142
100
143
warn_if_defaults_use_non_interface_members (
101
144
name , defaults , set (signatures .keys ())
@@ -139,7 +182,7 @@ def _diff_signatures(self, type_):
139
182
if not issubclass (impl_sig .type , iface_sig .type ):
140
183
mistyped [name ] = impl_sig .type
141
184
142
- if not compatible (impl_sig . signature , iface_sig . signature ):
185
+ if not compatible (impl_sig , iface_sig ):
143
186
mismatched [name ] = impl_sig
144
187
145
188
return missing , mistyped , mismatched
@@ -258,6 +301,9 @@ def _format_mismatched_methods(self, mismatched):
258
301
)
259
302
260
303
304
+ empty_set = frozenset ([])
305
+
306
+
261
307
class Interface (with_metaclass (InterfaceMeta )):
262
308
"""
263
309
Base class for interface definitions.
@@ -313,6 +359,10 @@ def delete(self, key):
313
359
:func:`implements`
314
360
"""
315
361
362
+ # Don't consider these members part of the interface definition for
363
+ # children of `Interface`.
364
+ _INTERFACE_IGNORE_MEMBERS = {"__new__" , "from_class" }
365
+
316
366
def __new__ (cls , * args , ** kwargs ):
317
367
raise TypeError ("Can't instantiate interface %s" % getname (cls ))
318
368
@@ -349,7 +399,10 @@ def from_class(cls, existing_class, subset=None, name=None):
349
399
)
350
400
351
401
352
- empty_set = frozenset ([])
402
+ # Signature requirements are inherited, so make sure the base interface doesn't
403
+ # require any methods of children.
404
+ assert Interface ._signatures == {}
405
+ assert Interface ._defaults == {}
353
406
354
407
355
408
class ImplementsMeta (type ):
0 commit comments