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

Merged
merged 3 commits into from
Jun 26, 2025

Conversation

jkseppan
Copy link
Member

@jkseppan jkseppan commented Jun 21, 2025

Implement Path.__deepcopy__ avoiding infinite recursion

To deep copy an object without calling deepcopy on the object itself, create a new object of the correct class and iterate calling deepcopy on its __dict__.

Closes #29157 without relying on private CPython methods.

In a separate commit, fix the other issue with TransformNode.__copy__.

PR summary

PR checklist

@jkseppan jkseppan mentioned this pull request Jun 21, 2025
2 tasks
@jkseppan jkseppan force-pushed the path-deepcopy-via-metaclass branch 2 times, most recently from fa7ed8b to 68d25b3 Compare June 21, 2025 08:00
@jkseppan jkseppan requested a review from tacaswell June 21, 2025 08:56
@jkseppan
Copy link
Member Author

jkseppan commented Jun 21, 2025

I tried the same strategy with TransformNode.__copy__ but copy.copy looks at getattr(cls, "__copy__", None) so we'd need to override attribute lookup on the class and not its instances, and then we can't have an instance-specific flag.

EDIT: this is actually easier to fix by just doing the shallow copy without copy.copy

@jkseppan
Copy link
Member Author

jkseppan commented Jun 23, 2025

I changed this to remove the metaclass and implement the __deepcopy__ method without calling it recursively. Also made sure that the tests check that the readonly property is reset in the copy. (The branch path-deepcopy-via-metaclass is now misnamed.)

if memo is None:
memo = {}
Copy link
Contributor

Choose a reason for hiding this comment

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

This is not needed, memo is always a dict.

The default value for memo is also not needed. The memo argument is always passed to __deepcopy__().

Copy link
Member Author

@jkseppan jkseppan Jun 24, 2025

Choose a reason for hiding this comment

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

Yes, when called via copy.deepcopy, but this __deepcopy__ method is also aliased to deepcopy (just below the GitHub patch context) that is part of the public API of this class

Copy link
Member

Choose a reason for hiding this comment

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

we could change our deepcopy to call copy.deepcopy internally (which might bet better?)

@jkseppan jkseppan force-pushed the path-deepcopy-via-metaclass branch 4 times, most recently from 2d93b8e to a76ab5a Compare June 25, 2025 02:59
jkseppan and others added 3 commits June 25, 2025 07:06
To deep copy an object without calling deepcopy on the object itself,
create a new object of the correct class and iterate calling deepcopy on
its __dict__.

Closes matplotlib#29157 without relying on private CPython methods.
Does not fix the other issue with TransformNode.__copy__.

Co-authored-by: Serhiy Storchaka <[email protected]>
Co-authored-by: Elliott Sales de Andrade <[email protected]>
without calling copy.copy
@jkseppan jkseppan force-pushed the path-deepcopy-via-metaclass branch from db1a305 to 5c7c915 Compare June 25, 2025 04:07
Copy link
Member

@QuLogic QuLogic left a comment

Choose a reason for hiding this comment

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

I tried this out on Fedora Rawhide with Python 3.14, and it worked.

@tacaswell tacaswell added this to the v3.10.4 milestone Jun 26, 2025
@tacaswell tacaswell merged commit d05b43d into matplotlib:main Jun 26, 2025
40 checks passed
meeseeksmachine pushed a commit to meeseeksmachine/matplotlib that referenced this pull request Jun 26, 2025
@tacaswell
Copy link
Member

Thank you @jkseppan ! This is a better solution than my "reach up and touch private methods" version.

@jkseppan jkseppan deleted the path-deepcopy-via-metaclass branch June 26, 2025 03:11
QuLogic added a commit that referenced this pull request Jun 26, 2025
…198-on-v3.10.x

Backport PR #30198 on branch v3.10.x (Implement Path.__deepcopy__ avoiding infinite recursion)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

FUTURE BUG: reconsider how we deep-copy path objects
4 participants