Repository: hgrecco/pint

Issue:
__slots__ without __getstate__
Hi, I'm trying to pickle a df with hdf5. 

That fails with the below error because `__getstate__` has vanished from class `UnitsContainer`

```python
  File "/Users/csxds/workspaces/venvs/dimpact_test/lib/python3.7/site-packages/tables/attributeset.py", line 494, in __setattr__
    self._g__setattr(name, value)
  File "/Users/csxds/workspaces/venvs/dimpact_test/lib/python3.7/site-packages/tables/attributeset.py", line 436, in _g__setattr
    self._g_setattr(self._v_node, name, stvalue)
  File "tables/hdf5extension.pyx", line 708, in tables.hdf5extension.AttributeSet._g_setattr
  File "/usr/local/Cellar/python/3.7.6_1/Frameworks/Python.framework/Versions/3.7/lib/python3.7/copyreg.py", line 73, in _reduce_ex
    "defining __getstate__ cannot be pickled") from None
TypeError: a class that defines __slots__ without defining __getstate__ cannot be pickled
```
Any chance this can be brought back?

Many thanks

Discussion:

Patch:
diff --git a/.travis.yml b/.travis.yml
index ec164c3..efea2b1 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -31,7 +31,7 @@ env:
   - PKGS="python=3.6 numpy uncertainties"
   - PKGS="python=3.7 numpy uncertainties"
   - PKGS="python=3.8 numpy uncertainties"
-  - PKGS="python xarray netCDF4"
+  - PKGS="python=3.8 numpy uncertainties sparse xarray netCDF4"
 
   # TODO: pandas tests
   # - PKGS="python=3.7 numpy pandas uncertainties pandas"
diff --git a/CHANGES b/CHANGES
index b897f29..67c173f 100644
--- a/CHANGES
+++ b/CHANGES
@@ -10,6 +10,8 @@ Pint Changelog
   the registry.
 - Fixed bug where numpy.pad didn't work without specifying constant_values or
   end_values (Issue #1026)
+- Reinstated support for pickle protocol 0 and 1, which is required by pytables
+  (Issue #1036, Thanks Guido Imperiale)
 
 
 0.11 (2020-02-19)
diff --git a/pint/util.py b/pint/util.py
index aeebff1..6148493 100644
--- a/pint/util.py
+++ b/pint/util.py
@@ -412,6 +412,13 @@ class UnitsContainer(Mapping):
             self._hash = hash(frozenset(self._d.items()))
         return self._hash
 
+    # Only needed by pickle protocol 0 and 1 (used by pytables)
+    def __getstate__(self):
+        return self._d, self._hash, self._one, self._non_int_type
+
+    def __setstate__(self, state):
+        self._d, self._hash, self._one, self._non_int_type = state
+
     def __eq__(self, other):
         if isinstance(other, UnitsContainer):
             # UnitsContainer.__hash__(self) is not the same as hash(self); see
@@ -638,6 +645,14 @@ class ParserHelper(UnitsContainer):
             raise ValueError(mess)
         return super().__hash__()
 
+    # Only needed by pickle protocol 0 and 1 (used by pytables)
+    def __getstate__(self):
+        return super().__getstate__() + (self.scale,)
+
+    def __setstate__(self, state):
+        super().__setstate__(state[:-1])
+        self.scale = state[-1]
+
     def __eq__(self, other):
         if isinstance(other, ParserHelper):
             return self.scale == other.scale and super().__eq__(other)


Test patch:
diff --git a/pint/testsuite/helpers.py b/pint/testsuite/helpers.py
index 2bf743c..eae7650 100644
--- a/pint/testsuite/helpers.py
+++ b/pint/testsuite/helpers.py
@@ -29,14 +29,6 @@ def requires_not_array_function_protocol():
     )
 
 
-def requires_numpy18():
-    if not HAS_NUMPY:
-        return unittest.skip("Requires NumPy")
-    return unittest.skipUnless(
-        StrictVersion(NUMPY_VER) >= StrictVersion("1.8"), "Requires NumPy >= 1.8"
-    )
-
-
 def requires_numpy_previous_than(version):
     if not HAS_NUMPY:
         return unittest.skip("Requires NumPy")
@@ -60,7 +52,7 @@ def requires_numpy():
 
 
 def requires_not_numpy():
-    return unittest.skipIf(HAS_NUMPY, "Requires NumPy is not installed.")
+    return unittest.skipIf(HAS_NUMPY, "Requires NumPy not to be installed.")
 
 
 def requires_babel():
@@ -68,7 +60,7 @@ def requires_babel():
 
 
 def requires_not_babel():
-    return unittest.skipIf(HAS_BABEL, "Requires Babel is not installed")
+    return unittest.skipIf(HAS_BABEL, "Requires Babel not to be installed")
 
 
 def requires_uncertainties():
@@ -77,7 +69,7 @@ def requires_uncertainties():
 
 def requires_not_uncertainties():
     return unittest.skipIf(
-        HAS_UNCERTAINTIES, "Requires Uncertainties is not installed."
+        HAS_UNCERTAINTIES, "Requires Uncertainties not to be installed."
     )
 
 
diff --git a/pint/testsuite/test_application_registry.py b/pint/testsuite/test_application_registry.py
index 6e2055c..a068445 100644
--- a/pint/testsuite/test_application_registry.py
+++ b/pint/testsuite/test_application_registry.py
@@ -14,48 +14,59 @@ from pint import (
 from pint.testsuite import BaseTestCase
 from pint.testsuite.helpers import requires_uncertainties
 
+from .parameterized import ParameterizedTestMixin
 
-class TestDefaultApplicationRegistry(BaseTestCase):
-    def test_unit(self):
+allprotos = ParameterizedTestMixin.parameterize(
+    ("protocol",), [(p,) for p in range(pickle.HIGHEST_PROTOCOL + 1)]
+)
+
+
+class TestDefaultApplicationRegistry(BaseTestCase, ParameterizedTestMixin):
+    @allprotos
+    def test_unit(self, protocol):
         u = Unit("kg")
         self.assertEqual(str(u), "kilogram")
-        u = pickle.loads(pickle.dumps(u))
+        u = pickle.loads(pickle.dumps(u, protocol))
         self.assertEqual(str(u), "kilogram")
 
-    def test_quantity_1arg(self):
+    @allprotos
+    def test_quantity_1arg(self, protocol):
         q = Quantity("123 kg")
         self.assertEqual(str(q.units), "kilogram")
         self.assertEqual(q.to("t").magnitude, 0.123)
-        q = pickle.loads(pickle.dumps(q))
+        q = pickle.loads(pickle.dumps(q, protocol))
         self.assertEqual(str(q.units), "kilogram")
         self.assertEqual(q.to("t").magnitude, 0.123)
 
-    def test_quantity_2args(self):
+    @allprotos
+    def test_quantity_2args(self, protocol):
         q = Quantity(123, "kg")
         self.assertEqual(str(q.units), "kilogram")
         self.assertEqual(q.to("t").magnitude, 0.123)
-        q = pickle.loads(pickle.dumps(q))
+        q = pickle.loads(pickle.dumps(q, protocol))
         self.assertEqual(str(q.units), "kilogram")
         self.assertEqual(q.to("t").magnitude, 0.123)
 
     @requires_uncertainties()
-    def test_measurement_2args(self):
+    @allprotos
+    def test_measurement_2args(self, protocol):
         m = Measurement(Quantity(123, "kg"), Quantity(15, "kg"))
         self.assertEqual(m.value.magnitude, 123)
         self.assertEqual(m.error.magnitude, 15)
         self.assertEqual(str(m.units), "kilogram")
-        m = pickle.loads(pickle.dumps(m))
+        m = pickle.loads(pickle.dumps(m, protocol))
         self.assertEqual(m.value.magnitude, 123)
         self.assertEqual(m.error.magnitude, 15)
         self.assertEqual(str(m.units), "kilogram")
 
     @requires_uncertainties()
-    def test_measurement_3args(self):
+    @allprotos
+    def test_measurement_3args(self, protocol):
         m = Measurement(123, 15, "kg")
         self.assertEqual(m.value.magnitude, 123)
         self.assertEqual(m.error.magnitude, 15)
         self.assertEqual(str(m.units), "kilogram")
-        m = pickle.loads(pickle.dumps(m))
+        m = pickle.loads(pickle.dumps(m, protocol))
         self.assertEqual(m.value.magnitude, 123)
         self.assertEqual(m.error.magnitude, 15)
         self.assertEqual(str(m.units), "kilogram")
@@ -65,25 +76,27 @@ class TestDefaultApplicationRegistry(BaseTestCase):
         u = ureg.Unit("kg")
         self.assertEqual(str(u), "kilogram")
 
-    def test_pickle_crash(self):
+    @allprotos
+    def test_pickle_crash(self, protocol):
         ureg = UnitRegistry(None)
         ureg.define("foo = []")
         q = ureg.Quantity(123, "foo")
-        b = pickle.dumps(q)
+        b = pickle.dumps(q, protocol)
         self.assertRaises(UndefinedUnitError, pickle.loads, b)
-        b = pickle.dumps(q.units)
+        b = pickle.dumps(q.units, protocol)
         self.assertRaises(UndefinedUnitError, pickle.loads, b)
 
     @requires_uncertainties()
-    def test_pickle_crash_measurement(self):
+    @allprotos
+    def test_pickle_crash_measurement(self, protocol):
         ureg = UnitRegistry(None)
         ureg.define("foo = []")
         m = ureg.Quantity(123, "foo").plus_minus(10)
-        b = pickle.dumps(m)
+        b = pickle.dumps(m, protocol)
         self.assertRaises(UndefinedUnitError, pickle.loads, b)
 
 
-class TestCustomApplicationRegistry(BaseTestCase):
+class TestCustomApplicationRegistry(BaseTestCase, ParameterizedTestMixin):
     def setUp(self):
         super().setUp()
         self.ureg_bak = get_application_registry()
@@ -97,52 +110,57 @@ class TestCustomApplicationRegistry(BaseTestCase):
         super().tearDown()
         set_application_registry(self.ureg_bak)
 
-    def test_unit(self):
+    @allprotos
+    def test_unit(self, protocol):
         u = Unit("foo")
         self.assertEqual(str(u), "foo")
-        u = pickle.loads(pickle.dumps(u))
+        u = pickle.loads(pickle.dumps(u, protocol))
         self.assertEqual(str(u), "foo")
 
-    def test_quantity_1arg(self):
+    @allprotos
+    def test_quantity_1arg(self, protocol):
         q = Quantity("123 foo")
         self.assertEqual(str(q.units), "foo")
         self.assertEqual(q.to("bar").magnitude, 246)
-        q = pickle.loads(pickle.dumps(q))
+        q = pickle.loads(pickle.dumps(q, protocol))
         self.assertEqual(str(q.units), "foo")
         self.assertEqual(q.to("bar").magnitude, 246)
 
-    def test_quantity_2args(self):
+    @allprotos
+    def test_quantity_2args(self, protocol):
         q = Quantity(123, "foo")
         self.assertEqual(str(q.units), "foo")
         self.assertEqual(q.to("bar").magnitude, 246)
-        q = pickle.loads(pickle.dumps(q))
+        q = pickle.loads(pickle.dumps(q, protocol))
         self.assertEqual(str(q.units), "foo")
         self.assertEqual(q.to("bar").magnitude, 246)
 
     @requires_uncertainties()
-    def test_measurement_2args(self):
+    @allprotos
+    def test_measurement_2args(self, protocol):
         m = Measurement(Quantity(123, "foo"), Quantity(10, "bar"))
         self.assertEqual(m.value.magnitude, 123)
         self.assertEqual(m.error.magnitude, 5)
         self.assertEqual(str(m.units), "foo")
-        m = pickle.loads(pickle.dumps(m))
+        m = pickle.loads(pickle.dumps(m, protocol))
         self.assertEqual(m.value.magnitude, 123)
         self.assertEqual(m.error.magnitude, 5)
         self.assertEqual(str(m.units), "foo")
 
     @requires_uncertainties()
-    def test_measurement_3args(self):
+    @allprotos
+    def test_measurement_3args(self, protocol):
         m = Measurement(123, 5, "foo")
         self.assertEqual(m.value.magnitude, 123)
         self.assertEqual(m.error.magnitude, 5)
         self.assertEqual(str(m.units), "foo")
-        m = pickle.loads(pickle.dumps(m))
+        m = pickle.loads(pickle.dumps(m, protocol))
         self.assertEqual(m.value.magnitude, 123)
         self.assertEqual(m.error.magnitude, 5)
         self.assertEqual(str(m.units), "foo")
 
 
-class TestSwapApplicationRegistry(BaseTestCase):
+class TestSwapApplicationRegistry(BaseTestCase, ParameterizedTestMixin):
     """Test that the constructors of Quantity, Unit, and Measurement capture
     the registry that is set as the application registry at creation time
 
@@ -167,12 +185,13 @@ class TestSwapApplicationRegistry(BaseTestCase):
     def tearDown(self):
         set_application_registry(self.ureg_bak)
 
-    def test_quantity_1arg(self):
+    @allprotos
+    def test_quantity_1arg(self, protocol):
         set_application_registry(self.ureg1)
         q1 = Quantity("1 foo")
         set_application_registry(self.ureg2)
         q2 = Quantity("1 foo")
-        q3 = pickle.loads(pickle.dumps(q1))
+        q3 = pickle.loads(pickle.dumps(q1, protocol))
         assert q1.dimensionality == {"[dim1]": 1}
         assert q2.dimensionality == {"[dim2]": 1}
         assert q3.dimensionality == {"[dim2]": 1}
@@ -180,12 +199,13 @@ class TestSwapApplicationRegistry(BaseTestCase):
         assert q2.to("bar").magnitude == 3
         assert q3.to("bar").magnitude == 3
 
-    def test_quantity_2args(self):
+    @allprotos
+    def test_quantity_2args(self, protocol):
         set_application_registry(self.ureg1)
         q1 = Quantity(1, "foo")
         set_application_registry(self.ureg2)
         q2 = Quantity(1, "foo")
-        q3 = pickle.loads(pickle.dumps(q1))
+        q3 = pickle.loads(pickle.dumps(q1, protocol))
         assert q1.dimensionality == {"[dim1]": 1}
         assert q2.dimensionality == {"[dim2]": 1}
         assert q3.dimensionality == {"[dim2]": 1}
@@ -193,23 +213,25 @@ class TestSwapApplicationRegistry(BaseTestCase):
         assert q2.to("bar").magnitude == 3
         assert q3.to("bar").magnitude == 3
 
-    def test_unit(self):
+    @allprotos
+    def test_unit(self, protocol):
         set_application_registry(self.ureg1)
         u1 = Unit("bar")
         set_application_registry(self.ureg2)
         u2 = Unit("bar")
-        u3 = pickle.loads(pickle.dumps(u1))
+        u3 = pickle.loads(pickle.dumps(u1, protocol))
         assert u1.dimensionality == {"[dim1]": 1}
         assert u2.dimensionality == {"[dim2]": 1}
         assert u3.dimensionality == {"[dim2]": 1}
 
     @requires_uncertainties()
-    def test_measurement_2args(self):
+    @allprotos
+    def test_measurement_2args(self, protocol):
         set_application_registry(self.ureg1)
         m1 = Measurement(Quantity(10, "foo"), Quantity(1, "foo"))
         set_application_registry(self.ureg2)
         m2 = Measurement(Quantity(10, "foo"), Quantity(1, "foo"))
-        m3 = pickle.loads(pickle.dumps(m1))
+        m3 = pickle.loads(pickle.dumps(m1, protocol))
 
         assert m1.dimensionality == {"[dim1]": 1}
         assert m2.dimensionality == {"[dim2]": 1}
@@ -222,12 +244,13 @@ class TestSwapApplicationRegistry(BaseTestCase):
         self.assertEqual(m3.to("bar").error.magnitude, 3)
 
     @requires_uncertainties()
-    def test_measurement_3args(self):
+    @allprotos
+    def test_measurement_3args(self, protocol):
         set_application_registry(self.ureg1)
         m1 = Measurement(10, 1, "foo")
         set_application_registry(self.ureg2)
         m2 = Measurement(10, 1, "foo")
-        m3 = pickle.loads(pickle.dumps(m1))
+        m3 = pickle.loads(pickle.dumps(m1, protocol))
 
         assert m1.dimensionality == {"[dim1]": 1}
         assert m2.dimensionality == {"[dim2]": 1}
diff --git a/pint/testsuite/test_errors.py b/pint/testsuite/test_errors.py
index cb75dd7..758ba97 100644
--- a/pint/testsuite/test_errors.py
+++ b/pint/testsuite/test_errors.py
@@ -94,24 +94,28 @@ class TestErrors(BaseTestCase):
         ureg = UnitRegistry(filename=None)
         ureg.define("foo = [bar]")
         ureg.define("bar = 2 foo")
-        pik = pickle.dumps(ureg.Quantity("1 foo"))
-        with self.assertRaises(UndefinedUnitError):
-            pickle.loads(pik)
         q1 = ureg.Quantity("1 foo")
         q2 = ureg.Quantity("1 bar")
 
-        for ex in [
-            DefinitionSyntaxError("foo", filename="a.txt", lineno=123),
-            RedefinitionError("foo", "bar"),
-            UndefinedUnitError("meter"),
-            DimensionalityError("a", "b", "c", "d", extra_msg=": msg"),
-            OffsetUnitCalculusError(Quantity("1 kg")._units, Quantity("1 s")._units),
-            OffsetUnitCalculusError(q1._units, q2._units),
-        ]:
-            with self.subTest(etype=type(ex)):
-                # assert False, ex.__reduce__()
-                ex2 = pickle.loads(pickle.dumps(ex))
-                assert type(ex) is type(ex2)
-                self.assertEqual(ex.args, ex2.args)
-                self.assertEqual(ex.__dict__, ex2.__dict__)
-                self.assertEqual(str(ex), str(ex2))
+        for protocol in range(pickle.HIGHEST_PROTOCOL + 1):
+            for ex in [
+                DefinitionSyntaxError("foo", filename="a.txt", lineno=123),
+                RedefinitionError("foo", "bar"),
+                UndefinedUnitError("meter"),
+                DimensionalityError("a", "b", "c", "d", extra_msg=": msg"),
+                OffsetUnitCalculusError(
+                    Quantity("1 kg")._units, Quantity("1 s")._units
+                ),
+                OffsetUnitCalculusError(q1._units, q2._units),
+            ]:
+                with self.subTest(protocol=protocol, etype=type(ex)):
+                    pik = pickle.dumps(ureg.Quantity("1 foo"), protocol)
+                    with self.assertRaises(UndefinedUnitError):
+                        pickle.loads(pik)
+
+                    # assert False, ex.__reduce__()
+                    ex2 = pickle.loads(pickle.dumps(ex, protocol))
+                    assert type(ex) is type(ex2)
+                    self.assertEqual(ex.args, ex2.args)
+                    self.assertEqual(ex.__dict__, ex2.__dict__)
+                    self.assertEqual(str(ex), str(ex2))
diff --git a/pint/testsuite/test_issues.py b/pint/testsuite/test_issues.py
index 5f42717..7401018 100644
--- a/pint/testsuite/test_issues.py
+++ b/pint/testsuite/test_issues.py
@@ -325,16 +325,6 @@ class TestIssues(QuantityTestCase):
         self.assertQuantityAlmostEqual(x + y, 5.1 * ureg.meter)
         self.assertQuantityAlmostEqual(z, 5.1 * ureg.meter)
 
-    @helpers.requires_numpy_previous_than("1.10")
-    def test_issue94(self):
-        v1 = np.array([5, 5]) * ureg.meter
-        v2 = 0.1 * ureg.meter
-        v3 = np.array([5, 5]) * ureg.meter
-        v3 += v2
-
-        np.testing.assert_array_equal((v1 + v2).magnitude, np.array([5.1, 5.1]))
-        np.testing.assert_array_equal(v3.magnitude, np.array([5, 5]))
-
     def test_issue104(self):
 
         x = [ureg("1 meter"), ureg("1 meter"), ureg("1 meter")]
@@ -376,7 +366,7 @@ class TestIssues(QuantityTestCase):
 
         self.assertEqual(sum([v * ureg.meter, v * ureg.meter]), 2 * v * ureg.meter)
 
-    @helpers.requires_numpy18()
+    @helpers.requires_numpy()
     def test_issue121b(self):
         sh = (2, 1)
 
diff --git a/pint/testsuite/test_non_int.py b/pint/testsuite/test_non_int.py
index 47409f2..bb56792 100644
--- a/pint/testsuite/test_non_int.py
+++ b/pint/testsuite/test_non_int.py
@@ -1,6 +1,7 @@
 import copy
 import math
 import operator as op
+import pickle
 from decimal import Decimal
 from fractions import Fraction
 
@@ -351,15 +352,17 @@ class _TestBasic:
         )
 
     def test_pickle(self):
-        import pickle
-
-        def pickle_test(q):
-            self.assertEqual(q, pickle.loads(pickle.dumps(q)))
-
-        pickle_test(self.QP_("32", ""))
-        pickle_test(self.QP_("2.4", ""))
-        pickle_test(self.QP_("32", "m/s"))
-        pickle_test(self.QP_("2.4", "m/s"))
+        for protocol in range(pickle.HIGHEST_PROTOCOL + 1):
+            for magnitude, unit in (
+                ("32", ""),
+                ("2.4", ""),
+                ("32", "m/s"),
+                ("2.4", "m/s"),
+            ):
+                with self.subTest(protocol=protocol, magnitude=magnitude, unit=unit):
+                    q1 = self.QP_(magnitude, unit)
+                    q2 = pickle.loads(pickle.dumps(q1, protocol))
+                    self.assertEqual(q1, q2)
 
     def test_notiter(self):
         # Verify that iter() crashes immediately, without needing to draw any
diff --git a/pint/testsuite/test_numpy.py b/pint/testsuite/test_numpy.py
index 8e3d116..e057ed9 100644
--- a/pint/testsuite/test_numpy.py
+++ b/pint/testsuite/test_numpy.py
@@ -1,5 +1,6 @@
 import copy
 import operator as op
+import pickle
 import unittest
 
 from pint import DimensionalityError, OffsetUnitCalculusError, UnitStrippedWarning
@@ -765,16 +766,6 @@ class TestNumpyUnclassified(TestNumpyMethods):
             np.nanstd(self.q_nan), 0.81650 * self.ureg.m, rtol=1e-5
         )
 
-    @helpers.requires_numpy_previous_than("1.10")
-    def test_integer_div(self):
-        a = [1] * self.ureg.m
-        b = [2] * self.ureg.m
-        c = a / b  # Should be float division
-        self.assertEqual(c.magnitude[0], 0.5)
-
-        a /= b  # Should be integer division
-        self.assertEqual(a.magnitude[0], 0)
-
     def test_conj(self):
         self.assertQuantityEqual((self.q * (1 + 1j)).conj(), self.q * (1 - 1j))
         self.assertQuantityEqual((self.q * (1 + 1j)).conjugate(), self.q * (1 - 1j))
@@ -837,14 +828,12 @@ class TestNumpyUnclassified(TestNumpyMethods):
         self.assertQuantityEqual(x - u, -(u - x))
 
     def test_pickle(self):
-        import pickle
-
-        def pickle_test(q):
-            pq = pickle.loads(pickle.dumps(q))
-            self.assertNDArrayEqual(q.magnitude, pq.magnitude)
-            self.assertEqual(q.units, pq.units)
-
-        pickle_test([10, 20] * self.ureg.m)
+        for protocol in range(pickle.HIGHEST_PROTOCOL + 1):
+            with self.subTest(protocol):
+                q1 = [10, 20] * self.ureg.m
+                q2 = pickle.loads(pickle.dumps(q1, protocol))
+                self.assertNDArrayEqual(q1.magnitude, q2.magnitude)
+                self.assertEqual(q1.units, q2.units)
 
     def test_equal(self):
         x = self.q.magnitude
diff --git a/pint/testsuite/test_quantity.py b/pint/testsuite/test_quantity.py
index 51faf1a..70cf157 100644
--- a/pint/testsuite/test_quantity.py
+++ b/pint/testsuite/test_quantity.py
@@ -2,6 +2,7 @@ import copy
 import datetime
 import math
 import operator as op
+import pickle
 import warnings
 from unittest.mock import patch
 
@@ -483,15 +484,12 @@ class TestQuantity(QuantityTestCase):
         )
 
     def test_pickle(self):
-        import pickle
-
-        def pickle_test(q):
-            self.assertEqual(q, pickle.loads(pickle.dumps(q)))
-
-        pickle_test(self.Q_(32, ""))
-        pickle_test(self.Q_(2.4, ""))
-        pickle_test(self.Q_(32, "m/s"))
-        pickle_test(self.Q_(2.4, "m/s"))
+        for protocol in range(pickle.HIGHEST_PROTOCOL + 1):
+            for magnitude, unit in ((32, ""), (2.4, ""), (32, "m/s"), (2.4, "m/s")):
+                with self.subTest(protocol=protocol, magnitude=magnitude, unit=unit):
+                    q1 = self.Q_(magnitude, unit)
+                    q2 = pickle.loads(pickle.dumps(q1, protocol))
+                    self.assertEqual(q1, q2)
 
     @helpers.requires_numpy()
     def test_from_sequence(self):