Skip to content

Commit 992b137

Browse files
authored
Make validation notes picklable. (#408)
Certain test runners pickle the exceptions raised to accumulate them from multiple processes. For exceptions with `AttributeValidationNote` and/or `IterableValidationNote` attached, because we have overridden `__new__` on these classes, pickle cannot correctly unpickle them with the required arguments. For this use case, exceptions being picklable is necessary to accumulate errors with the `multiprocessing` setup. This implements `__getnewargs__` to fix this problem. Certain test runners that pickle the exceptions raised to accumulate them from multiple processes. For exceptions with `AttributeValidationNote` and/or `IterableValidationNote`, because we have overriden __new__ on these classes, pickle is not able to correctly unpickle them with the required arguments. This implements __getnewargs__ to fix this problem. Signed-off-by: Zixuan James Li <p359101898@gmail.com>
1 parent d1b106a commit 992b137

3 files changed

Lines changed: 32 additions & 1 deletion

File tree

HISTORY.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626
([#398](https://github.com/python-attrs/cattrs/issues/398) [#399](https://github.com/python-attrs/cattrs/pull/399))
2727
- Broaden loads' type definition for the preconf orjson converter.
2828
([#400](https://github.com/python-attrs/cattrs/pull/400))
29+
- `AttributeValidationNote` and `IterableValidationNote` are now picklable.
30+
([#408](https://github.com/python-attrs/cattrs/pull/408))
2931

3032
## 23.1.2 (2023-06-02)
3133

src/cattrs/errors.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ def __new__(
4040
instance.type = type
4141
return instance
4242

43+
def __getnewargs__(self) -> Tuple[str, Union[int, str], Any]:
44+
return (str(self), self.index, self.type)
45+
4346

4447
class IterableValidationError(BaseValidationError):
4548
"""Raised when structuring an iterable."""
@@ -76,6 +79,9 @@ def __new__(cls, string: str, name: str, type: Any) -> "AttributeValidationNote"
7679
instance.type = type
7780
return instance
7881

82+
def __getnewargs__(self) -> Tuple[str, str, Any]:
83+
return (str(self), self.name, self.type)
84+
7985

8086
class ClassValidationError(BaseValidationError):
8187
"""Raised when validating a class if any attributes are invalid."""

tests/test_validation.py

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,19 @@
22
from typing import Dict, FrozenSet, List, Set, Tuple
33

44
import pytest
5+
import pickle
56
from attrs import define, field
67
from attrs.validators import in_
78
from hypothesis import given
89

910
from cattrs import Converter
1011
from cattrs._compat import Counter
11-
from cattrs.errors import ClassValidationError, IterableValidationError
12+
from cattrs.errors import (
13+
AttributeValidationNote,
14+
ClassValidationError,
15+
IterableValidationError,
16+
IterableValidationNote,
17+
)
1218

1319

1420
def test_class_validation():
@@ -178,3 +184,20 @@ def test_hetero_tuple_validation():
178184
assert exc.value.exceptions[0].__notes__ == [
179185
"Structuring typing.Tuple[int, int, int] @ index 2"
180186
]
187+
188+
189+
def test_notes_pickling():
190+
"""Validation notes should be picklable"""
191+
note = pickle.loads( # noqa: S301
192+
pickle.dumps(IterableValidationNote("foo", "key", str))
193+
)
194+
assert note == "foo"
195+
assert note.index == "key"
196+
assert note.type is str
197+
198+
note = pickle.loads( # noqa: S301
199+
pickle.dumps(AttributeValidationNote("foo", "name", int))
200+
)
201+
assert note == "foo"
202+
assert note.name == "name"
203+
assert note.type is int

0 commit comments

Comments
 (0)