fix: restore CONSTANTS singleton identity across pickle/deepcopy#170
Conversation
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
#169) A default HumanName shares the module-level CONSTANTS singleton as its .C. pickle and copy.deepcopy cannot preserve object identity, so without custom hooks .C was serialized by value — causing has_own_config to flip to True and bloating every serialized default name with a full Constants copy. __getstate__ replaces .C with None as a sentinel when it is the shared singleton; __setstate__ restores the CONSTANTS reference from that sentinel. None is a safe sentinel because the constructor always replaces a None constants argument with a fresh Constants(). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…verage - Assert parsed fields (str, first, last) survive pickle/deepcopy round-trips alongside the existing singleton-identity checks - Add test_pickle_instance_config_name_preserves_own_config: pins that instance-config names are not collapsed onto CONSTANTS after pickle - Add test_shallow_copy_default_name_preserves_singleton_identity: documents that copy.copy shares the CONSTANTS reference without needing __getstate__ Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
Post-review follow-up (3c3c04f): strengthened the test suite based on a comprehensive review of this PR. What was added:
76 tests pass, no regressions. |
Summary
Closes #169.
A default
HumanNameshares the module-levelCONSTANTSsingleton as its.C.pickleandcopy.deepcopycannot preserve object identity, so without custom hooks.Cwas serialized by value — causinghas_own_configto flip toTrueand bloating every serialized default name with a fullConstantscopy (~1000+ entries).Fix
Add
__getstate__/__setstate__toHumanName:__getstate__replaces.CwithNoneas a sentinel when it is the sharedCONSTANTSsingleton.__setstate__restores theCONSTANTSreference from that sentinel.Noneis a safe sentinel becauseHumanName.__init__always replaces aNoneconstants argument with a freshConstants(), so.Cis never legitimatelyNoneat rest. Bothpickleandcopy.deepcopycall these hooks, so one implementation covers both.Instance-config names (where
.C is not CONSTANTS) are unaffected — their ownConstantscopy continues to round-trip through the existing__dict__path established in #168.Tests
test_pickle_default_name_preserves_singleton_identity—C is CONSTANTSandhas_own_config == Falseafter a pickle round-trip.test_deepcopy_default_name_preserves_singleton_identity— same assertions aftercopy.deepcopy.Also adds
assertIs/assertIsNotshims toHumanNameTestBase(the custom test base class that replacedunittest.TestCase) and updates three existingassertTrue(x is ...)callsites to use them for clearer failure messages.Full suite: 710 passed, 20 xfailed.
This was prepared with the assistance of AI, under my direction and review.