The Wayback Machine - https://web.archive.org/web/20211211081353/https://github.com/matplotlib/matplotlib/pull/20921
Skip to content
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

Fix problem with (deep)copy of TextPath #20921

Closed

Conversation

@deep-jkl
Copy link
Contributor

@deep-jkl deep-jkl commented Aug 27, 2021

PR Summary

The problem is that deepcopy of TextPath calls deepcopy of Path. In turn Path utilizes super().__init__ for creating a new
instance. However, Path.__init__ and TextPath.__init__ are completely different.

Related to Issue #20943

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.
  • Documentation is sphinx and numpydoc compliant (the docs should build without error).
  • 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).
Copy link
Member

@tacaswell tacaswell left a comment

Thank you for working on this, however I think this is the wrong fix.

__deepcopy__ should return the same type as the object being copied (I pushed a commit updating the test to check this)!. I think a better fix is to override the __deepcopy__ on TextPath.

It may also be worth looking into using Path._fast_from_codes_and_verts in Path.__deepcopy__ + patching on some other state + extending it in TextPath to add any additional state if needed.

Anyone can dismiss this review.

Loading

@tacaswell tacaswell added this to the v3.5.1 milestone Aug 28, 2021
@deep-jkl
Copy link
Contributor Author

@deep-jkl deep-jkl commented Aug 29, 2021

Thank you for working on this, however I think this is the wrong fix.

__deepcopy__ should return the same type as the object being copied (I pushed a commit updating the test to check this)!. I think a better fix is to override the __deepcopy__ on TextPath.

It may also be worth looking into using Path._fast_from_codes_and_verts in Path.__deepcopy__ + patching on some other state + extending it in TextPath to add any additional state if needed.

Anyone can dismiss this review.

Mea culpa! It was mine bad design. With the help of stack overflow I have come to another implementation.

Regarding the Path._fast_from_codes_and_verts, can I have a look at it in another PR?
However, I have noticed that some methods of Path return Path object, so for example TextPath((0,0), ".").cleaned() will convert TextPath into plain Path, shouldn't we address this?

Loading

@deep-jkl
Copy link
Contributor Author

@deep-jkl deep-jkl commented Aug 29, 2021

I have filled an issue tracker for case that I will fail in this PR :-)
Issue #20943

Loading

@deep-jkl deep-jkl force-pushed the textpath-deepcopy-problem branch from f78af38 to feaa882 Sep 1, 2021
@deep-jkl deep-jkl requested a review from tacaswell Sep 1, 2021
from matplotlib.textpath import TextPath


def test_set_size():
Copy link
Member

@jklymak jklymak Sep 21, 2021

Choose a reason for hiding this comment

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

Can this get a docstring, even if very cursory? I can't really tell what it is supposed to be testing...

Loading

Copy link
Contributor Author

@deep-jkl deep-jkl Sep 22, 2021

Choose a reason for hiding this comment

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

I have added a simple docstring. I have revisited the test and I think that it tests the required functionality.

Loading

@deep-jkl deep-jkl force-pushed the textpath-deepcopy-problem branch from feaa882 to ebebec0 Sep 21, 2021
deep-jkl and others added 6 commits Sep 21, 2021
The problem is that deepcopy of TextPath calls deepcopy of Path.
In turn `Path` utilizes `super().__init__` for creating a new
instance. However, `Path.__init__ and TextPath.__init__ are
completely different. Moreover, `TextPath.__init__` converts
some arguments directly to list of vertices, hence it is not
possible to create a new instance of `TextPath` from its members.

Additionally, there is a problem with caching. When copying, the
vertices can be copied uncached, which causes discrepancies
between members of copy and original members (recaching creates new
members). Therefore validation of cache was removed and new vertices
are generated at every size set.
This time, revalidation is enabled. Added test for member deepcopy().
@deep-jkl
Copy link
Contributor Author

@deep-jkl deep-jkl commented Sep 22, 2021

@jklymak @tacaswell can you review this again, please?

Loading

@deep-jkl deep-jkl mentioned this pull request Sep 28, 2021
9 tasks
@jklymak
Copy link
Member

@jklymak jklymak commented Oct 5, 2021

Maybe this is fine, however, I don't understand why you want to deep copy this class in the first place to know if it is correct, and what use it will be put to.

Loading

setattr(new_instance, k, deepcopy(v, memo))
return new_instance

deepcopy = __deepcopy__
Copy link
Contributor

@anntzer anntzer Oct 5, 2021

Choose a reason for hiding this comment

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

Do you need this as a public method? I'd say copy.deepcopy(textpath) is good enough (and in fact, per https://github.com/matplotlib/matplotlib/pull/20921/files#r721988323, it does a bit more)?

Loading

self._revalidate_path()
cls = self.__class__
new_instance = cls.__new__(cls)
memo[id(self)] = new_instance
Copy link
Contributor

@anntzer anntzer Oct 5, 2021

Choose a reason for hiding this comment

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

I don't think you need to fill in the memo yourself, copy.deepcopy already does that for you: https://github.com/python/cpython/blob/7c2a040a10654d67ff543a55858ba2d7a9f7eea8/Lib/copy.py#L175-L176

Loading

@anntzer
Copy link
Contributor

@anntzer anntzer commented Oct 5, 2021

Actually, having reviewed this, I think #21280 is a better solution?

Loading

@deep-jkl
Copy link
Contributor Author

@deep-jkl deep-jkl commented Oct 5, 2021

Maybe this is fine, however, I don't understand why you want to deep copy this class in the first place to know if it is correct, and what use it will be put to.

I might done it the other way around, sorry. This requirement comes from another PR, where I have changed MarkerStyle instantiation from MarkerStyle to utilize deepcopy instead of update, because I felt that update does not assure immutability.

see
https://github.com/matplotlib/matplotlib/pull/20914/files#diff-0463a29ec8272d330266fc7f9314e6d1ee12dd8099a8b7184dd510823326991bL345

I got into the whole copy/deepcopy mess when I unexpectedly discovered some bug see #20731 .
I am sorry that this is so complex, I hope that we can figure it out :-D

Loading

@deep-jkl
Copy link
Contributor Author

@deep-jkl deep-jkl commented Oct 5, 2021

Actually, having reviewed this, I think #21280 is a better solution?

This looks much better, I didn't knew that you can use super() this way. Thanks, feel free to close this PR :-)

Loading

@anntzer
Copy link
Contributor

@anntzer anntzer commented Oct 5, 2021

Thanks for pointing out the original issue :)

Loading

@anntzer anntzer closed this Oct 5, 2021
@QuLogic QuLogic removed this from the v3.5.1 milestone Oct 28, 2021
@QuLogic QuLogic added this to the v3.5.0 milestone Oct 28, 2021
@QuLogic QuLogic removed this from the v3.5.0 milestone Oct 28, 2021
@QuLogic QuLogic added this to the v3.6.0 milestone Oct 28, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Linked issues

Successfully merging this pull request may close these issues.

None yet

5 participants