Search code examples
pythonpicklepython-datetimepython-3.9

Python 3.9: unpickling of IsoCalendarDate data returns a tuple


Consider the following discussion / feature in Python3.9: https://bugs.python.org/issue24416

In short, it was decided that the result of datetime.date.isocalendar would be changed to a namedtuple instead of tuple.

Now, I can see the benefit in doing that, but they also decided "to pickle" the new object (datetime.IsoCalendarDate) as a tuple:
https://github.com/python/cpython/commit/1b97b9b0ad9a2ff8eb5c8f2e2e7c2aec1d13a330#diff-2a8962dcecb109859cedd81ddc5729bea57d156e0947cb8413f99781a0860fd1R1214

So my question is, why did they make it so that creating the object directly, and "pickling-and-unpickling" the object require slightly different flows?

For example:

import datetime
from pathlib import Path
import pickle



RESULTS_CACHE_PICKLE = Path('cache.pickle')


if RESULTS_CACHE_PICKLE.is_file():
    with open(RESULTS_CACHE_PICKLE, 'rb') as f:
        icd = pickle.load(f)
else:
    icd = datetime.date(2019, 1, 1).isocalendar()
    with open(RESULTS_CACHE_PICKLE, 'wb') as f:
        pickle.dump(icd, f)
        
        
print(icd.year)

Results in:

$ python icd_test.py
2019
$ python icd_test.py
Traceback (most recent call last):
  File "icd_test.py", line 19, in <module>
    print(icd.year)
AttributeError: 'tuple' object has no attribute 'year'

This inconsistency looks unstable to me. Does it happen in other places in the language?


Solution

  • I guess, as suggested by Batman comment in this answer:

    One is that namedtuple isn't a class, it's a class factory that returns a class, which you in turn use to make instances. (...)

    And unfortunately, this is exactly the case (intentionally!) as we can read in the code of IsoCalendarDate(tuple) class:

    def __reduce__(self):
        # This code is intended to pickle the object without making the
        # class public. See https://bugs.python.org/msg352381
        return (tuple, (tuple(self),))
    

    So it seems that, for some reason, the inconsequential approach was taken intentionally but I am not aware of (m)any similar situations in Python code.

    I think you can raise it as a bug. Maybe the rationale to keep IsoCalendarDate private from the pickle perspective should get revisited.