@@ -365,46 +365,138 @@ def cache_clear():
365365### singledispatch() - single-dispatch generic function decorator
366366################################################################################
367367
368- def _compose_mro (cls , haystack ):
369- """Calculates the MRO for a given class `cls`, including relevant abstract
370- base classes from `haystack`.
368+ def _c3_merge (sequences ):
369+ """Merges MROs in *sequences* to a single MRO using the C3 algorithm.
370+
371+ Adapted from http://www.python.org/download/releases/2.3/mro/.
371372
372373 """
373- bases = set (cls .__mro__ )
374- mro = list (cls .__mro__ )
375- for needle in haystack :
376- if (needle in bases or not hasattr (needle , '__mro__' )
377- or not issubclass (cls , needle )):
378- continue # either present in the __mro__ already or unrelated
379- for index , base in enumerate (mro ):
380- if not issubclass (base , needle ):
374+ result = []
375+ while True :
376+ sequences = [s for s in sequences if s ] # purge empty sequences
377+ if not sequences :
378+ return result
379+ for s1 in sequences : # find merge candidates among seq heads
380+ candidate = s1 [0 ]
381+ for s2 in sequences :
382+ if candidate in s2 [1 :]:
383+ candidate = None
384+ break # reject the current head, it appears later
385+ else :
381386 break
382- if base in bases and not issubclass (needle , base ):
383- # Conflict resolution: put classes present in __mro__ and their
384- # subclasses first. See test_mro_conflicts() in test_functools.py
385- # for examples.
386- index += 1
387- mro .insert (index , needle )
388- return mro
387+ if not candidate :
388+ raise RuntimeError ("Inconsistent hierarchy" )
389+ result .append (candidate )
390+ # remove the chosen candidate
391+ for seq in sequences :
392+ if seq [0 ] == candidate :
393+ del seq [0 ]
394+
395+ def _c3_mro (cls , abcs = None ):
396+ """Computes the method resolution order using extended C3 linearization.
397+
398+ If no *abcs* are given, the algorithm works exactly like the built-in C3
399+ linearization used for method resolution.
400+
401+ If given, *abcs* is a list of abstract base classes that should be inserted
402+ into the resulting MRO. Unrelated ABCs are ignored and don't end up in the
403+ result. The algorithm inserts ABCs where their functionality is introduced,
404+ i.e. issubclass(cls, abc) returns True for the class itself but returns
405+ False for all its direct base classes. Implicit ABCs for a given class
406+ (either registered or inferred from the presence of a special method like
407+ __len__) are inserted directly after the last ABC explicitly listed in the
408+ MRO of said class. If two implicit ABCs end up next to each other in the
409+ resulting MRO, their ordering depends on the order of types in *abcs*.
410+
411+ """
412+ for i , base in enumerate (reversed (cls .__bases__ )):
413+ if hasattr (base , '__abstractmethods__' ):
414+ boundary = len (cls .__bases__ ) - i
415+ break # Bases up to the last explicit ABC are considered first.
416+ else :
417+ boundary = 0
418+ abcs = list (abcs ) if abcs else []
419+ explicit_bases = list (cls .__bases__ [:boundary ])
420+ abstract_bases = []
421+ other_bases = list (cls .__bases__ [boundary :])
422+ for base in abcs :
423+ if issubclass (cls , base ) and not any (
424+ issubclass (b , base ) for b in cls .__bases__
425+ ):
426+ # If *cls* is the class that introduces behaviour described by
427+ # an ABC *base*, insert said ABC to its MRO.
428+ abstract_bases .append (base )
429+ for base in abstract_bases :
430+ abcs .remove (base )
431+ explicit_c3_mros = [_c3_mro (base , abcs = abcs ) for base in explicit_bases ]
432+ abstract_c3_mros = [_c3_mro (base , abcs = abcs ) for base in abstract_bases ]
433+ other_c3_mros = [_c3_mro (base , abcs = abcs ) for base in other_bases ]
434+ return _c3_merge (
435+ [[cls ]] +
436+ explicit_c3_mros + abstract_c3_mros + other_c3_mros +
437+ [explicit_bases ] + [abstract_bases ] + [other_bases ]
438+ )
439+
440+ def _compose_mro (cls , types ):
441+ """Calculates the method resolution order for a given class *cls*.
442+
443+ Includes relevant abstract base classes (with their respective bases) from
444+ the *types* iterable. Uses a modified C3 linearization algorithm.
445+
446+ """
447+ bases = set (cls .__mro__ )
448+ # Remove entries which are already present in the __mro__ or unrelated.
449+ def is_related (typ ):
450+ return (typ not in bases and hasattr (typ , '__mro__' )
451+ and issubclass (cls , typ ))
452+ types = [n for n in types if is_related (n )]
453+ # Remove entries which are strict bases of other entries (they will end up
454+ # in the MRO anyway.
455+ def is_strict_base (typ ):
456+ for other in types :
457+ if typ != other and typ in other .__mro__ :
458+ return True
459+ return False
460+ types = [n for n in types if not is_strict_base (n )]
461+ # Subclasses of the ABCs in *types* which are also implemented by
462+ # *cls* can be used to stabilize ABC ordering.
463+ type_set = set (types )
464+ mro = []
465+ for typ in types :
466+ found = []
467+ for sub in typ .__subclasses__ ():
468+ if sub not in bases and issubclass (cls , sub ):
469+ found .append ([s for s in sub .__mro__ if s in type_set ])
470+ if not found :
471+ mro .append (typ )
472+ continue
473+ # Favor subclasses with the biggest number of useful bases
474+ found .sort (key = len , reverse = True )
475+ for sub in found :
476+ for subcls in sub :
477+ if subcls not in mro :
478+ mro .append (subcls )
479+ return _c3_mro (cls , abcs = mro )
389480
390481def _find_impl (cls , registry ):
391- """Returns the best matching implementation for the given class ` cls` in
392- `registry`. Where there is no registered implementation for a specific
393- type, its method resolution order is used to find a more generic
394- implementation.
482+ """Returns the best matching implementation from *registry* for type * cls*.
483+
484+ Where there is no registered implementation for a specific type, its method
485+ resolution order is used to find a more generic implementation.
395486
396- Note: if ` registry` does not contain an implementation for the base
397- ` object` type, this function may return None.
487+ Note: if * registry* does not contain an implementation for the base
488+ * object* type, this function may return None.
398489
399490 """
400491 mro = _compose_mro (cls , registry .keys ())
401492 match = None
402493 for t in mro :
403494 if match is not None :
404- # If `match` is an ABC but there is another unrelated, equally
405- # matching ABC. Refuse the temptation to guess.
406- if (t in registry and not issubclass (match , t )
407- and match not in cls .__mro__ ):
495+ # If *match* is an implicit ABC but there is another unrelated,
496+ # equally matching implicit ABC, refuse the temptation to guess.
497+ if (t in registry and t not in cls .__mro__
498+ and match not in cls .__mro__
499+ and not issubclass (match , t )):
408500 raise RuntimeError ("Ambiguous dispatch: {} or {}" .format (
409501 match , t ))
410502 break
@@ -418,19 +510,19 @@ def singledispatch(func):
418510 Transforms a function into a generic function, which can have different
419511 behaviours depending upon the type of its first argument. The decorated
420512 function acts as the default implementation, and additional
421- implementations can be registered using the ' register()' attribute of
422- the generic function.
513+ implementations can be registered using the register() attribute of the
514+ generic function.
423515
424516 """
425517 registry = {}
426518 dispatch_cache = WeakKeyDictionary ()
427519 cache_token = None
428520
429- def dispatch (typ ):
430- """generic_func.dispatch(type ) -> <function implementation>
521+ def dispatch (cls ):
522+ """generic_func.dispatch(cls ) -> <function implementation>
431523
432524 Runs the dispatch algorithm to return the best available implementation
433- for the given `type` registered on ` generic_func` .
525+ for the given *cls* registered on * generic_func* .
434526
435527 """
436528 nonlocal cache_token
@@ -440,26 +532,26 @@ def dispatch(typ):
440532 dispatch_cache .clear ()
441533 cache_token = current_token
442534 try :
443- impl = dispatch_cache [typ ]
535+ impl = dispatch_cache [cls ]
444536 except KeyError :
445537 try :
446- impl = registry [typ ]
538+ impl = registry [cls ]
447539 except KeyError :
448- impl = _find_impl (typ , registry )
449- dispatch_cache [typ ] = impl
540+ impl = _find_impl (cls , registry )
541+ dispatch_cache [cls ] = impl
450542 return impl
451543
452- def register (typ , func = None ):
453- """generic_func.register(type , func) -> func
544+ def register (cls , func = None ):
545+ """generic_func.register(cls , func) -> func
454546
455- Registers a new implementation for the given `type` on a ` generic_func` .
547+ Registers a new implementation for the given *cls* on a * generic_func* .
456548
457549 """
458550 nonlocal cache_token
459551 if func is None :
460- return lambda f : register (typ , f )
461- registry [typ ] = func
462- if cache_token is None and hasattr (typ , '__abstractmethods__' ):
552+ return lambda f : register (cls , f )
553+ registry [cls ] = func
554+ if cache_token is None and hasattr (cls , '__abstractmethods__' ):
463555 cache_token = get_cache_token ()
464556 dispatch_cache .clear ()
465557 return func
0 commit comments