Repository: vacanza/python-holidays

Issue:
Can't un-pickle a `HolidayBase`
Seems that after a holidays class, e.g. `holidays.UnitedStates()` is used once, it can't be un-pickled.

For example, this snippet:

```python
import holidays
import pickle
from datetime import datetime

# Works:
us_holidays = holidays.UnitedStates()
us_holidays_ = pickle.loads(pickle.dumps(us_holidays))
b = datetime.fromisoformat("2020-01-01") in us_holidays_

# Fails:
us_holidays = holidays.UnitedStates()
b = datetime.fromisoformat("2020-01-01") in us_holidays
dump = pickle.dumps(us_holidays)
pickle.loads(dump)  # <- exception
```

Raises the following exception from the last line:
```
~/.local/share/virtualenvs/sibylla-v2-LxBhzJgn/lib/python3.8/site-packages/holidays/holiday_base.py in __setitem__(self, key, value)
    116
    117     def __setitem__(self, key, value):
--> 118         if key in self:
    119             if self.get(key).find(value) < 0 \
    120                     and value.find(self.get(key)) < 0:

~/.local/share/virtualenvs/sibylla-v2-LxBhzJgn/lib/python3.8/site-packages/holidays/holiday_base.py in __contains__(self, key)
     73
     74     def __contains__(self, key):
---> 75         return dict.__contains__(self, self.__keytransform__(key))
     76
     77     def __getitem__(self, key):

~/.local/share/virtualenvs/sibylla-v2-LxBhzJgn/lib/python3.8/site-packages/holidays/holiday_base.py in __keytransform__(self, key)
     67             raise TypeError("Cannot convert type '%s' to date." % type(key))
     68
---> 69         if self.expand and key.year not in self.years:
     70             self.years.add(key.year)
     71             self._populate(key.year)
```

The `expand` attribute is set by `__init__`, but it's not there during deserialization via unpickling.
I think it's because the `HolidayBase` inherits from dict and there's some weirdness there - it seems to first populate the dict in the deserialized object and only then sets the attributes from the state. But since `HolidayBase` overrides `__setitem__` and in this override it's using state attributes that weren't yet set on the object, the `expand` attribute is missing.

Tested with `holidays=='0.10.4'`.



Discussion:
Hi, 
great package but having the same issue here with `holidays=='0.10.5.2'`
Hi,
thank you, this is not fixed yet, and will be considered in a next release: thx for your patience. 
@dr-prodigy have you started working on this? if not, I can work on it

Resolving patch:
diff --git a/holidays/holiday_base.py b/holidays/holiday_base.py
--- a/holidays/holiday_base.py
+++ b/holidays/holiday_base.py
@@ -209,6 +209,9 @@ def __radd__(self, other):
     def _populate(self, year):
         pass
 
+    def __reduce__(self):
+        return super(HolidayBase, self).__reduce__()
+
 
 def createHolidaySum(h1, h2):
     class HolidaySum(HolidayBase):


Test patch:
diff --git a/test/test_holiday_base.py b/test/test_holiday_base.py
--- a/test/test_holiday_base.py
+++ b/test/test_holiday_base.py
@@ -11,6 +11,7 @@
 #  Website: https://github.com/dr-prodigy/python-holidays
 #  License: MIT (see LICENSE file)
 
+import pickle
 import unittest
 
 from datetime import date, datetime, timedelta
@@ -447,6 +448,16 @@ def test_observed(self):
         self.holidays.observed = True
         self.assertIn(date(2018, 7, 2), self.holidays)
 
+    def test_serialization(self):
+        loaded_holidays = pickle.loads(pickle.dumps(self.holidays))
+        assert loaded_holidays == self.holidays
+
+        dt = datetime(2020, 1, 1)
+        res = dt in self.holidays
+        loaded_holidays = pickle.loads(pickle.dumps(self.holidays))
+        assert loaded_holidays == self.holidays
+        assert (dt in loaded_holidays) == res
+
 
 class TestKeyTransforms(unittest.TestCase):
     def setUp(self):