Skip to content

Commit e9c66d9

Browse files
committed
FIX: account for changes to cpython with copy/deepcopy to super()
See python/cpython#126817 for upstream discussion. This works around the change by using (private) methods from the copy module to re-implement the path though copy/deepcopy that we would like to use but avoid the special-casing for `super()` objects that is breaking us. We could vendor the current versions of `_keep_alive` (weakref work to manage lifecycles) and `_reconstruct` (where the recursion happens) to superficially avoid using private functions from CPython. However, if these functions do change significantly I worry that our copies would not inter-operate anyway. Closes #29157
1 parent 529b3e5 commit e9c66d9

File tree

2 files changed

+19
-3
lines changed

2 files changed

+19
-3
lines changed

lib/matplotlib/path.py

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
import copy
1313
from functools import lru_cache
14+
import sys
1415
from weakref import WeakValueDictionary
1516

1617
import numpy as np
@@ -281,13 +282,22 @@ def __deepcopy__(self, memo=None):
281282
readonly, even if the source `Path` is.
282283
"""
283284
# Deepcopying arrays (vertices, codes) strips the writeable=False flag.
284-
p = copy.deepcopy(super(), memo)
285+
if sys.version_info >= (3, 14):
286+
from copy import _reconstruct, _keep_alive
287+
rv = super().__reduce_ex__(4)
288+
p = _reconstruct(self, memo, *rv)
289+
if memo is not None:
290+
memo[id(self)] = p
291+
_keep_alive(self, memo)
292+
293+
else:
294+
p = copy.deepcopy(super(), memo)
285295
p._readonly = False
286296
return p
287297

288298
def deepcopy(self, memo=None):
289299
"""
290-
Return a shallow copy of the `Path`, which will share the
300+
Return a deep copy of the `Path`, with copies of the
291301
vertices and codes with the source `Path`.
292302
"""
293303
return copy.deepcopy(self, memo=memo)

lib/matplotlib/transforms.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
import copy
3939
import functools
4040
import itertools
41+
import sys
4142
import textwrap
4243
import weakref
4344
import math
@@ -139,7 +140,12 @@ def __setstate__(self, data_dict):
139140
for k, v in self._parents.items() if v is not None}
140141

141142
def __copy__(self):
142-
other = copy.copy(super())
143+
if sys.version_info >= (3, 14):
144+
from copy import _reconstruct
145+
rv = super().__reduce_ex__(4)
146+
other = _reconstruct(self, None, *rv)
147+
else:
148+
other = copy.copy(super())
143149
# If `c = a + b; a1 = copy(a)`, then modifications to `a1` do not
144150
# propagate back to `c`, i.e. we need to clear the parents of `a1`.
145151
other._parents = {}

0 commit comments

Comments
 (0)