Skip to content

Commit 996b647

Browse files
committed
Implement Path.__deepcopy__ avoiding infinite recursion
Give it a metaclass that lets us remove the __deepcopy__ method from sight when executing that method. Closes #29157 without relying on private CPython methods. Does not fix the other issue with TransformNode.__copy__.
1 parent bebb263 commit 996b647

File tree

1 file changed

+34
-4
lines changed

1 file changed

+34
-4
lines changed

lib/matplotlib/path.py

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,38 @@
1616
import numpy as np
1717

1818
import matplotlib as mpl
19+
1920
from . import _api, _path
2021
from .cbook import _to_unmasked_float_array, simple_linear_interpolation
2122
from .bezier import BezierSegment
2223

2324

24-
class Path:
25+
class _HideDeepcopyMeta(type):
26+
"""Metaclass that allows conditionally hiding the __deepcopy__ method.
27+
28+
Set __hide_deepcopy__ to True to hide the __deepcopy__ method,
29+
which will then be looked up in the usual method resolution order.
30+
"""
31+
32+
def __new__(cls, name, bases, namespace):
33+
orig_ga = namespace.get("__getattribute__") or object.__getattribute__
34+
35+
def __getattribute__(self, attr_name):
36+
if attr_name == "__deepcopy__" and orig_ga(self, "__hide_deepcopy__"):
37+
for base in type(self).__mro__[1:]:
38+
if attr_name in base.__dict__:
39+
method = base.__dict__[attr_name]
40+
return method.__get__(self, type(self))
41+
raise AttributeError(
42+
f"'{type(self).__name__}' object has no attribute '{attr_name}'"
43+
)
44+
return orig_ga(self, attr_name)
45+
46+
namespace["__getattribute__"] = __getattribute__
47+
return super().__new__(cls, name, bases, namespace)
48+
49+
50+
class Path(metaclass=_HideDeepcopyMeta):
2551
"""
2652
A series of possibly disconnected, possibly closed, line and curve
2753
segments.
@@ -281,9 +307,13 @@ def __deepcopy__(self, memo=None):
281307
readonly, even if the source `Path` is.
282308
"""
283309
# Deepcopying arrays (vertices, codes) strips the writeable=False flag.
284-
p = copy.deepcopy(super(), memo)
285-
p._readonly = False
286-
return p
310+
self.__hide_deepcopy__ = True
311+
try:
312+
p = copy.deepcopy(self, memo)
313+
p._readonly = False
314+
return p
315+
finally:
316+
self.__hide_deepcopy__ = False
287317

288318
deepcopy = __deepcopy__
289319

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy