Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit d05b43d

Browse files
Merge pull request matplotlib#30198 from jkseppan/path-deepcopy-via-metaclass
Implement Path.__deepcopy__ avoiding infinite recursion
2 parents 58b6a06 + 5c7c915 commit d05b43d

File tree

4 files changed

+64
-9
lines changed

4 files changed

+64
-9
lines changed

‎lib/matplotlib/path.py‎

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -275,17 +275,37 @@ def copy(self):
275275
"""
276276
return copy.copy(self)
277277

278-
def __deepcopy__(self, memo=None):
278+
def __deepcopy__(self, memo):
279279
"""
280280
Return a deepcopy of the `Path`. The `Path` will not be
281281
readonly, even if the source `Path` is.
282282
"""
283283
# Deepcopying arrays (vertices, codes) strips the writeable=False flag.
284-
p = copy.deepcopy(super(), memo)
284+
cls = type(self)
285+
memo[id(self)] = p = cls.__new__(cls)
286+
287+
for k, v in self.__dict__.items():
288+
setattr(p, k, copy.deepcopy(v, memo))
289+
285290
p._readonly = False
286291
return p
287292

288-
deepcopy = __deepcopy__
293+
def deepcopy(self, memo=None):
294+
"""
295+
Return a deep copy of the `Path`. The `Path` will not be readonly,
296+
even if the source `Path` is.
297+
298+
Parameters
299+
----------
300+
memo : dict, optional
301+
A dictionary to use for memoizing, passed to `copy.deepcopy`.
302+
303+
Returns
304+
-------
305+
Path
306+
A deep copy of the `Path`, but not readonly.
307+
"""
308+
return copy.deepcopy(self, memo)
289309

290310
@classmethod
291311
def make_compound_path_from_polys(cls, XY):

‎lib/matplotlib/path.pyi‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,8 @@ class Path:
4444
@property
4545
def readonly(self) -> bool: ...
4646
def copy(self) -> Path: ...
47-
def __deepcopy__(self, memo: dict[int, Any]|None= ...) -> Path: ...
48-
deepcopy=__deepcopy__
47+
def __deepcopy__(self, memo: dict[int, Any]) -> Path: ...
48+
defdeepcopy(self, memo: dict[int, Any] |None=None) ->Path: ...
4949

5050
@classmethod
5151
def make_compound_path_from_polys(cls, XY: ArrayLike) -> Path: ...

‎lib/matplotlib/tests/test_path.py‎

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -355,15 +355,49 @@ def test_path_deepcopy():
355355
# Should not raise any error
356356
verts = [[0, 0], [1, 1]]
357357
codes = [Path.MOVETO, Path.LINETO]
358-
path1 = Path(verts)
359-
path2 = Path(verts, codes)
358+
path1 = Path(verts, readonly=True)
359+
path2 = Path(verts, codes, readonly=True)
360360
path1_copy = path1.deepcopy()
361361
path2_copy = path2.deepcopy()
362362
assert path1 is not path1_copy
363363
assert path1.vertices is not path1_copy.vertices
364+
assert_array_equal(path1.vertices, path1_copy.vertices)
365+
assert path1.readonly
366+
assert not path1_copy.readonly
364367
assert path2 is not path2_copy
365368
assert path2.vertices is not path2_copy.vertices
369+
assert_array_equal(path2.vertices, path2_copy.vertices)
366370
assert path2.codes is not path2_copy.codes
371+
assert_array_equal(path2.codes, path2_copy.codes)
372+
assert path2.readonly
373+
assert not path2_copy.readonly
374+
375+
376+
def test_path_deepcopy_cycle():
377+
class PathWithCycle(Path):
378+
def __init__(self, *args, **kwargs):
379+
super().__init__(*args, **kwargs)
380+
self.x = self
381+
382+
p = PathWithCycle([[0, 0], [1, 1]], readonly=True)
383+
p_copy = p.deepcopy()
384+
assert p_copy is not p
385+
assert p.readonly
386+
assert not p_copy.readonly
387+
assert p_copy.x is p_copy
388+
389+
class PathWithCycle2(Path):
390+
def __init__(self, *args, **kwargs):
391+
super().__init__(*args, **kwargs)
392+
self.x = [self] * 2
393+
394+
p2 = PathWithCycle2([[0, 0], [1, 1]], readonly=True)
395+
p2_copy = p2.deepcopy()
396+
assert p2_copy is not p2
397+
assert p2.readonly
398+
assert not p2_copy.readonly
399+
assert p2_copy.x[0] is p2_copy
400+
assert p2_copy.x[1] is p2_copy
367401

368402

369403
def test_path_shallowcopy():

‎lib/matplotlib/transforms.py‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@
3535
# `np.minimum` instead of the builtin `min`, and likewise for `max`. This is
3636
# done so that `nan`s are propagated, instead of being silently dropped.
3737

38-
import copy
3938
import functools
4039
import itertools
4140
import textwrap
@@ -139,7 +138,9 @@ def __setstate__(self, data_dict):
139138
for k, v in self._parents.items() if v is not None}
140139

141140
def __copy__(self):
142-
other = copy.copy(super())
141+
cls = type(self)
142+
other = cls.__new__(cls)
143+
other.__dict__.update(self.__dict__)
143144
# If `c = a + b; a1 = copy(a)`, then modifications to `a1` do not
144145
# propagate back to `c`, i.e. we need to clear the parents of `a1`.
145146
other._parents = {}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /