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

Skip to content

Fix problem with (deep)copy of TextPath #20921

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

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

Choose a reason for hiding this comment

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

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.

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

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?

@deep-jkl
Copy link
Contributor Author

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

@deep-jkl deep-jkl force-pushed the textpath-deepcopy-problem branch from f78af38 to feaa882 Compare September 1, 2021 08:32
@deep-jkl deep-jkl requested a review from tacaswell September 1, 2021 10:31
from matplotlib.textpath import TextPath


def test_set_size():
Copy link
Member

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...

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 added a simple docstring. I have revisited the test and I think that it tests the required functionality.

@deep-jkl deep-jkl force-pushed the textpath-deepcopy-problem branch from feaa882 to ebebec0 Compare September 21, 2021 19:50
deep-jkl and others added 6 commits September 21, 2021 23:45
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

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

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

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.

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)?

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

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

@anntzer
Copy link
Contributor

anntzer commented Oct 5, 2021

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

@deep-jkl
Copy link
Contributor Author

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

@deep-jkl
Copy link
Contributor Author

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 :-)

@anntzer
Copy link
Contributor

anntzer commented Oct 5, 2021

Thanks for pointing out the original issue :)

@anntzer anntzer closed this Oct 5, 2021
@QuLogic QuLogic modified the milestones: v3.5.1, v3.5.0, v3.6.0 Oct 28, 2021
@deep-jkl deep-jkl deleted the textpath-deepcopy-problem 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
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants