Skip to content

Commit 2909053

Browse files
authored
Add copy.replace for 3.13 (#12262)
1 parent 128d8eb commit 2909053

File tree

2 files changed

+34
-1
lines changed

2 files changed

+34
-1
lines changed
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
from __future__ import annotations
2+
3+
import copy
4+
import sys
5+
from typing_extensions import Self, assert_type
6+
7+
8+
class ReplaceableClass:
9+
def __init__(self, val: int) -> None:
10+
self.val = val
11+
12+
def __replace__(self, val: int) -> Self:
13+
cpy = copy.copy(self)
14+
cpy.val = val
15+
return cpy
16+
17+
18+
if sys.version_info >= (3, 13):
19+
obj = ReplaceableClass(42)
20+
cpy = copy.replace(obj, val=23)
21+
assert_type(cpy, ReplaceableClass)

stdlib/copy.pyi

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,16 @@
1-
from typing import Any, TypeVar
1+
import sys
2+
from typing import Any, Protocol, TypeVar
3+
from typing_extensions import ParamSpec, Self
24

35
__all__ = ["Error", "copy", "deepcopy"]
46

57
_T = TypeVar("_T")
8+
_SR = TypeVar("_SR", bound=_SupportsReplace[Any])
9+
_P = ParamSpec("_P")
10+
11+
class _SupportsReplace(Protocol[_P]):
12+
# In reality doesn't support args, but there's no other great way to express this.
13+
def __replace__(self, *args: _P.args, **kwargs: _P.kwargs) -> Self: ...
614

715
# None in CPython but non-None in Jython
816
PyStringMap: Any
@@ -11,6 +19,10 @@ PyStringMap: Any
1119
def deepcopy(x: _T, memo: dict[int, Any] | None = None, _nil: Any = []) -> _T: ...
1220
def copy(x: _T) -> _T: ...
1321

22+
if sys.version_info >= (3, 13):
23+
__all__ += ["replace"]
24+
def replace(obj: _SR, /, **changes: Any) -> _SR: ...
25+
1426
class Error(Exception): ...
1527

1628
error = Error

0 commit comments

Comments
 (0)