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

Skip to content

Improved implementation of Path.copy and deepcopy #20731

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

Merged
merged 2 commits into from
Jul 25, 2021

Conversation

deep-jkl
Copy link
Contributor

@deep-jkl deep-jkl commented Jul 24, 2021

PR Summary

The original Path.copy raised RecursionError, the new implementation mimicks deepcopy, but Path members are shared.

Path tests were updated to utilize member functions copy and deepcopy instead of builtin copy functional protocol.

PR Checklist

  • Has pytest style unit tests (and pytest passes).
  • Is Flake 8 compliant (run flake8 on changed files to check).
  • [n/a] New features are documented, with examples if plot related.
  • [n/a] Documentation is sphinx and numpydoc compliant (the docs should build without error).
  • [n/a] Conforms to Matplotlib style conventions (install flake8-docstrings and run flake8 --docstring-convention=all).
  • [n/a] New features have an entry in doc/users/next_whats_new/ (follow instructions in README.rst there).
  • [n/a] API changes documented in doc/api/next_api_changes/ (follow instructions in README.rst there).

The original Path.copy raised RecursionError, new implementation mimicks
deepcopy, creating shared members.

Path tests were updated to utilize member functions copy and deepcopy
instead of builtin copy functional protocol.
try:
codes = self.codes
except AttributeError:
codes = None
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to check for attributeerror, codes is always there (it can be None though; the AttributeError in deepcopy is to handle the codes is None case).

Actually I think the default implementation of __copy__ will just work, i.e. removing the implementation of __copy__ and just having def copy(x): return copy.copy(x) would work? And likewise for __deepcopy__?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that copy is redundant and copy.copy will work, it just didn't in previous case because it was in copy call what caused infinite recurrence. On the other hand __deepcopy__ implementation is kind of optimized, isn't it? I mean that it is not recurrently calling deepcopy on each of its members as default implementation should. However, custom __deepcopy__ implementation can backfire, when we add important members and it is more code to maintain.

To conclude, there are two implementations of __deepcopy__ in the sources, and three implementations of __copy__. The other __deepcopy__ implementation is for TransformNode class and sorts out descendants (without reading through the details I assume that it implements design requirements of the whole Transform system). The other __copy__ implementations are for TransformNode class again and Colormap class, where it seems legitimate (again meeting some external requirements).

I don't see any heavy lifting done in the Path copy functions, therefore I suggest removing __copy__, __deepcopy__, and optionally the members: Path.copy(), Path.deepcopy(), and their tests.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure the optimization of deepcopy really matters here. So I agree with removing __copy__ and __deepcopy__; copy and deepcopy on the other hand can stay as convenience methods (plus it avoids breaking backcompat gratuitiously, and there's precedent for copy methods existing for convenience (e.g. on list or np.ndarray), and leaving the tests for copy and deepcopy also verify (somewhat) that using the default impls of __copy__ and __deepcopy__ is indeed semantically correct.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have found out that __deepcopy__ implementation cannot be done by means of copy.deepcopy, the reason is even stated in the docstring:

The `Path` will not be readonly, even if the source `Path` is.

Some tests relies on this behavior, one example from test_artist.py :

@image_comparison(["clip_path_clipping"], remove_text=True)
def test_clipping():
    exterior = mpath.Path.unit_rectangle().deepcopy()
    exterior.vertices *= 4
    exterior.vertices -= 2
...

The Path from mpath.Path.unit_rectangle() is readonly, but its deepcopy isn't.
I think that at this point I should just fix the shallow copy code, which throws exceptions and keep __deepcopy__ as it is, since it has some consequences.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, sure enough.

copy.deepcopy(path2)
path1_copy = path1.deepcopy()
path2_copy = path2.deepcopy()
assert(path1 is not path1_copy)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no parentheses around the assert

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in f765d04.

@anntzer anntzer added the PR: bugfix Pull requests that fix identified bugs label Jul 24, 2021
Copy link
Contributor

@anntzer anntzer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Modulo ci.

@timhoffm timhoffm added this to the v3.5.0 milestone Jul 25, 2021
@timhoffm timhoffm merged commit 45528c3 into matplotlib:master Jul 25, 2021
@deep-jkl deep-jkl deleted the path-copy branch April 13, 2022 18:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
PR: bugfix Pull requests that fix identified bugs
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants