Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Implement Path.__deepcopy__ avoiding infinite recursion #30198

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 29 additions & 4 deletions lib/matplotlib/path.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,33 @@
import numpy as np

import matplotlib as mpl

from . import _api, _path
from .cbook import _to_unmasked_float_array, simple_linear_interpolation
from .bezier import BezierSegment


class Path:
class _HideDeepcopyMeta(type):
"""Metaclass that allows conditionally hiding the __deepcopy__ method.

Set __hide_deepcopy__ to True to hide the __deepcopy__ method.
"""

def __new__(cls, name, bases, namespace):
orig_ga = namespace.get("__getattribute__") or object.__getattribute__

def __getattribute__(self, attr_name):
if attr_name == "__deepcopy__" and orig_ga(self, "__hide_deepcopy__"):
raise AttributeError(
f"'{type(self).__name__}' object has no attribute '{attr_name}'"
)
return orig_ga(self, attr_name)

namespace["__getattribute__"] = __getattribute__
return super().__new__(cls, name, bases, namespace)


class Path(metaclass=_HideDeepcopyMeta):
"""
A series of possibly disconnected, possibly closed, line and curve
segments.
Expand Down Expand Up @@ -281,9 +302,13 @@ def __deepcopy__(self, memo=None):
readonly, even if the source `Path` is.
"""
# Deepcopying arrays (vertices, codes) strips the writeable=False flag.
p = copy.deepcopy(super(), memo)
p._readonly = False
return p
self.__hide_deepcopy__ = True
try:
p = copy.deepcopy(self, memo)
p._readonly = False
return p
finally:
self.__hide_deepcopy__ = False

deepcopy = __deepcopy__

Expand Down
6 changes: 5 additions & 1 deletion lib/matplotlib/path.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@ from numpy.typing import ArrayLike

from typing import Any, overload

class Path:
class _HideDeepcopyMeta(type):
def __new__(cls, name, bases, namespace):
...

class Path(metaclass=_HideDeepcopyMeta):
code_type: type[np.uint8]
STOP: np.uint8
MOVETO: np.uint8
Expand Down
5 changes: 3 additions & 2 deletions lib/matplotlib/transforms.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@
# `np.minimum` instead of the builtin `min`, and likewise for `max`. This is
# done so that `nan`s are propagated, instead of being silently dropped.

import copy
import functools
import itertools
import textwrap
Expand Down Expand Up @@ -139,7 +138,9 @@ def __setstate__(self, data_dict):
for k, v in self._parents.items() if v is not None}

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