Repository: jorenham/optype (language: python)

Issue:
stdlib `pickle` integration
https://docs.python.org/3/library/pickle.html#pickling-class-instances

- `CanGetnewargsEx`
- `CanGetnewargs`
- `CanGetstate`
- `CanSetstate`
- `CanReduce`
- `CanReduceEx`


Discussion / hints:

Fix patch:
diff --git a/README.md b/README.md
index 458affa1..726a739c 100644
--- a/README.md
+++ b/README.md
@@ -1256,7 +1256,7 @@ interfaces:
     </tr>
     <tr>
         <td>expression</td>
-        <td>method(s)</td>
+        <td>method</td>
         <th>type</th>
     </tr>
     <tr>
@@ -1285,14 +1285,70 @@ type CanDeepcopySelf = CanDeepcopy[CanDeepcopySelf]
 type CanReplaceSelf[V] = CanReplace[CanReplaceSelf[V], V]
 ```
 
-[CP]: https://docs.python.org/3.13/library/copy.html
+[CP]: https://docs.python.org/3/library/copy.html
+
+### `pickle`
+
+For the [`pickle`][PK] standard library, `optype` provides the following
+interfaces:
+
+[PK]: https://docs.python.org/3/library/pickle.html
+
+<table>
+    <tr>
+        <th>method(s)</th>
+        <th>signature (bound)</th>
+        <th>type</th>
+    </tr>
+    <tr>
+        <td><code>__reduce__</code></td>
+        <td><code>() -> R</code></td>
+        <td><code>CanReduce[R: str | tuple]</code></td>
+    </tr>
+    <tr>
+        <td><code>__reduce_ex__</code></td>
+        <td><code>(CanIndex) -> R</code></td>
+        <td><code>CanReduceEx[R: str | tuple]</code></td>
+    </tr>
+    <tr>
+        <td><code>__getstate__</code></td>
+        <td><code>() -> State</code></td>
+        <td><code>CanGetstate[State: object]</code></td>
+    </tr>
+    <tr>
+        <td><code>__setstate__</code></td>
+        <td><code>(State) -> None</code></td>
+        <td><code>CanSetstate[State: object]</code></td>
+    </tr>
+    <tr>
+        <td>
+            <code>__getnewargs__</code><br>
+            <code>__new__</code>
+        </td>
+        <td>
+            <code>() -> tuple[*Args]</code><br>
+            <code>(*Args) -> Self</code><br>
+        </td>
+        <td><code>CanGetnewargs[*Args]</code></td>
+    </tr>
+    <tr>
+        <td>
+            <code>__getnewargs_ex__</code><br>
+            <code>__new__</code>
+        </td>
+        <td>
+            <code>() -> tuple[tuple[*Args], dict[str, Kw]]</code><br>
+            <code>(*Args, **dict[str, Kw]) -> Self</code><br>
+        </td>
+        <td><code>CanGetnewargsEx[*Args, Kw]</code></td>
+    </tr>
+</table>
 
 ## Future plans
 
 - Support for Python versions before 3.12 (#19).
 - More standard library protocols, e.g.
     - `dataclasses` (#21)
-    - `pickle` (#22).
     - `typing.NamedTuple` (#23)
 - [numpy][NP] interfaces for arrays-like types (no deps) (#24)
 - [array-api][API-ND] interfaces (no deps) (#25)
diff --git a/optype/__init__.py b/optype/__init__.py
index c145f26f..2f271208 100644
--- a/optype/__init__.py
+++ b/optype/__init__.py
@@ -38,6 +38,9 @@
     'CanGetattr',
     'CanGetattribute',
     'CanGetitem',
+    'CanGetnewargs',
+    'CanGetnewargsEx',
+    'CanGetstate',
     'CanGt',
     'CanHash',
     'CanIAdd',
@@ -89,6 +92,8 @@
     'CanRSub',
     'CanRTruediv',
     'CanRXor',
+    'CanReduce',
+    'CanReduceEx',
     'CanReleaseBuffer',
     'CanReplace',
     'CanReplaceSelf',
@@ -103,6 +108,7 @@
     'CanSetName',
     'CanSetattr',
     'CanSetitem',
+    'CanSetstate',
     'CanStr',
     'CanSub',
     'CanTruediv',
@@ -333,6 +339,9 @@
     CanGetattr,
     CanGetattribute,
     CanGetitem,
+    CanGetnewargs,
+    CanGetnewargsEx,
+    CanGetstate,
     CanGt,
     CanHash,
     CanIAdd,
@@ -384,6 +393,8 @@
     CanRSub,
     CanRTruediv,
     CanRXor,
+    CanReduce,
+    CanReduceEx,
     CanReleaseBuffer,
     CanReplace,
     CanReplaceSelf,
@@ -398,6 +409,7 @@
     CanSetName,
     CanSetattr,
     CanSetitem,
+    CanSetstate,
     CanStr,
     CanSub,
     CanTruediv,
diff --git a/optype/_can.py b/optype/_can.py
index 98d6b4a8..97fb542a 100644
--- a/optype/_can.py
+++ b/optype/_can.py
@@ -742,3 +742,42 @@ class CanReplaceSelf[V](CanReplace['CanReplaceSelf[Any]', V], Protocol):
     """Variant of `CanReplace` that returns `Self`."""
     @override
     def __replace__(self, /, **changes: V) -> Self: ...
+
+
+# `pickle` stdlib
+# https://docs.python.org/3.13/library/pickle.html
+
+
+@runtime_checkable
+class CanReduce[R: str | tuple[Any, ...]](Protocol):
+    @override
+    def __reduce__(self) -> R: ...
+
+
+@runtime_checkable
+class CanReduceEx[R: str | tuple[Any, ...]](Protocol):
+    @override
+    def __reduce_ex__(self, protocol: CanIndex, /) -> R: ...
+
+
+@runtime_checkable
+class CanGetstate[S: object](Protocol):
+    @override
+    def __getstate__(self) -> S: ...
+
+
+@runtime_checkable
+class CanSetstate[S: object](Protocol):
+    def __setstate__(self, state: S, /) -> None: ...
+
+
+@runtime_checkable
+class CanGetnewargs[*Args](Protocol):
+    def __new__(cls, *__args: *Args) -> Self: ...
+    def __getnewargs__(self) -> tuple[*Args]: ...
+
+
+@runtime_checkable
+class CanGetnewargsEx[*Args, Kw](Protocol):
+    def __new__(cls, *__args: *Args, **__kwargs: Kw) -> Self: ...
+    def __getnewargs_ex__(self) -> tuple[tuple[*Args], dict[str, Kw]]: ...


Test patch:
diff --git a/tests/helpers.py b/tests/helpers.py
index c6776d48..c8fd56b9 100644
--- a/tests/helpers.py
+++ b/tests/helpers.py
@@ -35,8 +35,11 @@ def get_protocol_members(cls: type) -> frozenset[str]:
     members = annotations.keys() | {
         name for name, v in vars(cls).items()
         if (
-            callable(v) and (
-                v.__module__ == module or (
+            name != '__new__'
+            and callable(v)
+            and (
+                v.__module__ == module
+                or (
                     # Fun fact: Each `@overload` returns the same dummy
                     # function; so there's no reference your wrapped method :).
                     # Oh and BTW; `typing.get_overloads` only works on the