-
Notifications
You must be signed in to change notification settings - Fork 519
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Use
ChainMap
to improve representer/constructor inheritance
Before this change, a child Dumper "inherited" its parent Dumper's representers by simply copying the parent's representers the first time a new representer was registered with the child Dumper. This approach works as users expect only if representers are never added to a parent (ancestor) Dumper after representers are added to a child (descendant) Dumper. Same goes for Loaders and their registered constructors. This commit uses a `collections.ChainMap` built from ancestor `dict` objects in method resolution order (MRO) to provide true inheritance of representers (for Dumpers) and constructors (for Loaders). This is technically a backwards-incompatible change: This change breaks any code that intentionally subverts the expected inheritance behavior by registering a function in an ancestor class *after* registering a function in a descendant class.
- Loading branch information
Showing
5 changed files
with
198 additions
and
18 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
import collections | ||
|
||
|
||
class InheritMapMixin: | ||
"""Adds :py:class:`collections.ChainMap` class attributes based on MRO. | ||
The added class attributes provide each subclass with its own mapping that | ||
works just like method resolution: each ancestor type in ``__mro__`` is | ||
visited until an entry is found in the ancestor-owned map. | ||
For example, for an inheritance DAG of ``InheritMapMixin`` <- ``Foo`` <- | ||
``Bar`` <- ``Baz`` and a desired attribute name of ``"_m"``: | ||
1. ``Foo._m`` is set to ``ChainMap({})``. | ||
2. ``Bar._m`` is set to ``ChainMap({}, Foo._m.maps[0])``. | ||
3. ``Baz._m`` is set to ``ChainMap({}, Bar._m.maps[0], Foo._m.maps[0])``. | ||
""" | ||
|
||
@classmethod | ||
def __init_subclass__(cls, *, inherit_map_attrs=None, **kwargs): | ||
"""Adds :py:class:`collections.ChainMap` class attributes based on MRO. | ||
:param inherit_map_attrs: | ||
Optional iterable of names of class attributes that will be set to a | ||
:py:class:`collections.ChainMap` containing the MRO-based list of | ||
ancestor maps. | ||
""" | ||
super().__init_subclass__(**kwargs) | ||
attrs = getattr(cls, "_inherit_map_attrs", set()) | ||
if inherit_map_attrs: | ||
attrs = {*attrs, *inherit_map_attrs} | ||
cls._inherit_map_attrs = attrs | ||
for attr in attrs: | ||
maps = [{}] # maps[0] is for cls itself. | ||
for c in cls.__mro__[1:]: # cls.__mro__[0] is cls itself. | ||
if ( | ||
issubclass(c, InheritMapMixin) and | ||
c is not InheritMapMixin and | ||
attr in getattr(c, "_inherit_map_attrs", set()) | ||
): | ||
maps.append(getattr(c, attr).maps[0]) | ||
setattr(cls, attr, collections.ChainMap(*maps)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters