22interface
33---------
44"""
5+ from collections import defaultdict
56from functools import wraps
67import inspect
78from operator import attrgetter , itemgetter
89from textwrap import dedent
910from weakref import WeakKeyDictionary
1011
11- from .compat import raise_from , with_metaclass
12- from .functional import complement , keyfilter
12+ from .compat import raise_from , viewkeys , with_metaclass
13+ from .default import default # noqa reexport
14+ from .functional import complement , keyfilter , valfilter
1315from .typecheck import compatible
1416from .typed_signature import TypedSignature
1517from .utils import is_a , unique
1820getname = attrgetter ('__name__' )
1921
2022
21- class IncompleteImplementation (TypeError ):
23+ class InvalidImplementation (TypeError ):
2224 """
2325 Raised when a class intending to implement an interface fails to do so.
2426 """
@@ -47,6 +49,39 @@ def static_get_type_attr(t, name):
4749 raise AttributeError (name )
4850
4951
52+ def _conflicting_defaults (typename , conflicts ):
53+ """Format an error message for conflicting default implementations.
54+
55+ Parameters
56+ ----------
57+ typename : str
58+ Name of the type for which we're producing an error.
59+ conflicts : dict[str -> list[Interface]]
60+ Map from strings to interfaces providing a default with that name.
61+
62+ Returns
63+ -------
64+ message : str
65+ User-facing error message.
66+ """
67+ message = "\n class {C} received conflicting default implementations:" .format (
68+ C = typename ,
69+ )
70+ for attrname , interfaces in conflicts .items ():
71+ message += dedent (
72+ """
73+
74+ The following interfaces provided default implementations for {attr!r}:
75+ {interfaces}"""
76+ ).format (
77+ attr = attrname ,
78+ interfaces = "\n " .join (sorted ([
79+ " - {name}" .format (name = iface .__name__ ) for iface in interfaces
80+ ]))
81+ )
82+ return InvalidImplementation (message )
83+
84+
5085class InterfaceMeta (type ):
5186 """
5287 Metaclass for interfaces.
@@ -55,6 +90,7 @@ class InterfaceMeta(type):
5590 """
5691 def __new__ (mcls , name , bases , clsdict ):
5792 signatures = {}
93+ defaults = {}
5894 for k , v in keyfilter (is_interface_field_name , clsdict ).items ():
5995 try :
6096 signatures [k ] = TypedSignature (v )
@@ -69,7 +105,11 @@ def __new__(mcls, name, bases, clsdict):
69105 )
70106 raise_from (TypeError (errmsg ), e )
71107
108+ if isinstance (v , default ):
109+ defaults [k ] = v
110+
72111 clsdict ['_signatures' ] = signatures
112+ clsdict ['_defaults' ] = defaults
73113 return super (InterfaceMeta , mcls ).__new__ (mcls , name , bases , clsdict )
74114
75115 def _diff_signatures (self , type_ ):
@@ -129,9 +169,20 @@ def verify(self, type_):
129169 -------
130170 None
131171 """
132- missing , mistyped , mismatched = self ._diff_signatures (type_ )
172+ raw_missing , mistyped , mismatched = self ._diff_signatures (type_ )
173+
174+ # See if we have defaults for missing methods.
175+ missing = []
176+ defaults_to_use = {}
177+ for name in raw_missing :
178+ try :
179+ defaults_to_use [name ] = self ._defaults [name ].implementation
180+ except KeyError :
181+ missing .append (name )
182+
133183 if not any ((missing , mistyped , mismatched )):
134- return
184+ return defaults_to_use
185+
135186 raise self ._invalid_implementation (type_ , missing , mistyped , mismatched )
136187
137188 def _invalid_implementation (self , t , missing , mistyped , mismatched ):
@@ -176,7 +227,7 @@ def _invalid_implementation(self, t, missing, mistyped, mismatched):
176227 I = getname (self ),
177228 mismatched_methods = self ._format_mismatched_methods (mismatched ),
178229 )
179- return IncompleteImplementation (message )
230+ return InvalidImplementation (message )
180231
181232 def _format_missing_methods (self , missing ):
182233 return "\n " .join (sorted ([
@@ -231,18 +282,31 @@ def __new__(mcls, name, bases, clsdict, interfaces=empty_set):
231282 return newtype
232283
233284 errors = []
285+ default_impls = {}
286+ default_providers = defaultdict (list )
234287 for iface in newtype .interfaces ():
235288 try :
236- iface .verify (newtype )
237- except IncompleteImplementation as e :
289+ defaults_from_iface = iface .verify (newtype )
290+ for name , impl in defaults_from_iface .items ():
291+ default_impls [name ] = impl
292+ default_providers [name ].append (iface )
293+ except InvalidImplementation as e :
238294 errors .append (e )
239295
296+ # The list of providers for `name`, if there's more than one.
297+ duplicate_defaults = valfilter (lambda ifaces : len (ifaces ) > 1 , default_providers )
298+ if duplicate_defaults :
299+ errors .append (_conflicting_defaults (newtype .__name__ , duplicate_defaults ))
300+ else :
301+ for name , impl in default_impls .items ():
302+ setattr (newtype , name , impl )
303+
240304 if not errors :
241305 return newtype
242306 elif len (errors ) == 1 :
243307 raise errors [0 ]
244308 else :
245- raise IncompleteImplementation ("\n \n " .join (map (str , errors )))
309+ raise InvalidImplementation ("\n \n " .join (map (str , errors )))
246310
247311 def __init__ (mcls , name , bases , clsdict , interfaces = empty_set ):
248312 mcls ._interfaces = interfaces
@@ -336,5 +400,6 @@ def implements(*interfaces):
336400 return result
337401 return implements
338402
403+
339404implements = _make_implements ()
340405del _make_implements
0 commit comments