Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use ChainMap to improve representer/constructor inheritance #672

Closed
wants to merge 1 commit into from

Conversation

rhansen
Copy link

@rhansen rhansen commented Oct 14, 2022

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.

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.
@nitzmahone
Copy link
Member

Beyond the case you bring up (which is admittedly pretty obscure), there's a bigger problem with this approach at a glance: it makes it effectively impossible for objects later in the MRO to explicitly remove (not replace/re-define) inherited entries, which is actually more common than one might think. That case could probably be handled by convention with a surrogate sentinel value or something, but it kinda doesn't feel right...

The prototype in #700 addresses this problem in a different way, which is probably a little more aligned with where things are going with type/tag/schema support for 1.2. Each runtime type ultimately still has its own flat list, but they're explicitly configured from shared first-class definitions that are usage-specific (and support inheritance themselves).

I'm going to close this one- sorry it's taken so long for us to look at it...

@nitzmahone nitzmahone closed this Aug 29, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants