Skip to content

Preserve Box subclass for nested boxes after unpickling#309

Open
koriyoshi2041 wants to merge 1 commit into
cdgriffith:masterfrom
koriyoshi2041:fix/unpickle-preserve-subclass
Open

Preserve Box subclass for nested boxes after unpickling#309
koriyoshi2041 wants to merge 1 commit into
cdgriffith:masterfrom
koriyoshi2041:fix/unpickle-preserve-subclass

Conversation

@koriyoshi2041
Copy link
Copy Markdown

Fixes #308.

A Box subclass is lost on nested attribute access after unpickling: nested boxes come back as the base box.box.Box instead of the subclass.

Box.__init__ sets box_class from self.__class__:

# __init__
"box_class": box_class if box_class is not None else self.__class__,

but Box.__new__ hardcodes the base Box:

# __new__
"box_class": box_class if box_class is not None else Box,

Unpickling calls __new__ + __setstate__, not __init__, so a subclass instance is restored with box_class=Box. Nested dicts converted to boxes on access (conversion box) then use box_class, producing base Box objects. Using cls in __new__ (which is the actual subclass) matches __init__.

Added test_pickle_preserves_subclass_on_nested_access: pickles a Box subclass with a nested dict and asserts the nested box is still the subclass after a round-trip. It fails on master (nested box is base Box) and passes with the fix; existing pickle tests still pass. (The subclass is defined at module level so its instances are picklable.)

Box.__init__ derives box_class from self.__class__, but Box.__new__ hardcoded
the base Box. Unpickling calls __new__ + __setstate__ (not __init__), so a
subclass instance ended up with box_class=Box, and nested boxes created on
access after unpickling became base Box instead of the subclass. Use cls in
__new__ to match __init__.
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.

subclass of Box does not unpickle correctly

1 participant