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

"pickle.Unpickler.persistent_load" and "pickle.Pickler.persistent_id" became read-only in python 3.13 #125631

Open
jcea opened this issue Oct 17, 2024 · 6 comments
Assignees
Labels
3.13 bugs and security fixes 3.14 new features, bugs and security fixes extension-modules C modules in the Modules dir type-bug An unexpected behavior, bug, or error

Comments

@jcea
Copy link
Member

jcea commented Oct 17, 2024

Bug report

Bug description:

In Python releases previous to 3.13, the following code used to work (this idiom is used in Durus and ZODB persistence systems):

Python 3.12.7 (main, Oct  3 2024, 03:21:52) [GCC 10.5.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from pickle import Unpickler
>>> a=Unpickler(open('/dev/zero'))  
>>> a.persistent_load=lambda x: x
>>> 

Fine so far.

In Python 3.13, this doesn't work anymore:

Python 3.13.0 (main, Oct  9 2024, 14:54:06) [GCC 10.5.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from pickle import Unpickler
>>> a=Unpickler(open('/dev/zero'))
>>> a.persistent_load=lambda x: x
Traceback (most recent call last):
  File "<python-input-2>", line 1, in <module>
    a.persistent_load=lambda x: x
    ^^^^^^^^^^^^^^^^^
AttributeError: '_pickle.Unpickler' object attribute 'persistent_load' is read-only

I don't know if this is an intended change or a regression.

PS: The subclass approach works:

Python 3.13.0 (main, Oct  9 2024, 14:54:06) [GCC 10.5.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from pickle import Unpickler
>>> def x(Unpickler):
...     def persistent_load(self, x):
...         return x
...         
>>> b=x(open("/dev/zero"))
>>> 

CPython versions tested on:

3.13

Operating systems tested on:

Linux

Linked PRs

@jcea jcea added type-bug An unexpected behavior, bug, or error extension-modules C modules in the Modules dir 3.13 bugs and security fixes labels Oct 17, 2024
@jcea jcea changed the title "pickle.Unpickler.persistent_load" became read-only in python 3.13 "pickle.Unpickler.persistent_load" and "pickle.Pickler.persistent_id" became read-only in python 3.13 Oct 17, 2024
@jcea
Copy link
Member Author

jcea commented Oct 17, 2024

The same issue happens with pickle.Pickler.persistent_id:

Python 3.12.7 (main, Oct  3 2024, 03:21:52) [GCC 10.5.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from pickle import Pickler
>>> a=Pickler(open('/dev/zero'))
>>> a.persistent_id=lambda x: x
>>> 

Good so far.

But in Python 3.13.0:

Python 3.13.0 (main, Oct  9 2024, 14:54:06) [GCC 10.5.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from pickle import Pickler
>>> a=Pickler(open('/dev/zero'))
>>> a.persistent_id=lambda x: x
Traceback (most recent call last):
  File "<python-input-6>", line 1, in <module>
    a.persistent_id=lambda x: x
    ^^^^^^^^^^^^^^^
AttributeError: '_pickle.Pickler' object attribute 'persistent_id' is read-only
>>> 

@jcea
Copy link
Member Author

jcea commented Oct 17, 2024

It seems related to bug #89850 and PR #113579.

@jcea
Copy link
Member Author

jcea commented Oct 17, 2024

@serhiy-storchaka, Could you possibly verify this? Thanks.

@serhiy-storchaka serhiy-storchaka self-assigned this Oct 17, 2024
@serhiy-storchaka serhiy-storchaka added the 3.14 new features, bugs and security fixes label Oct 17, 2024
serhiy-storchaka added a commit to serhiy-storchaka/cpython that referenced this issue Oct 19, 2024
…pickler and unpickler

pickle.Pickler and pickle.Unpickler instances have now managed dicts.
Arbitrary instance attributes, including persistent_id and persistent_load,
can now be set.
@serhiy-storchaka
Copy link
Member

Yes, this is a consequence of the #89850 change. I missed such use case because there were no tests for it. You still can override attributes of instances of subclasses.

The proposed fix adds managed dicts to these classes (I would add simple instance dicts, but it is easier to add managed dicts). This may open a can of worms, because you can now set arbitrary attributes of instances of these classes. There will be no way to close it. But I think this is the only way to make this working while keeping super() working too.

@jcea
Copy link
Member Author

jcea commented Oct 19, 2024

What about restoring the property support (get/set) for those two attributes?

It could even support "delete" to reactivate current configuration, although I don't think that anybody overwriting "persistent_id" or "persistent_load" would be interested in that.

serhiy-storchaka added a commit to serhiy-storchaka/cpython that referenced this issue Oct 20, 2024
…pickler and unpickler

pickle.Pickler.persistent_id and pickle.Unpickler.persistent_load can
again be overridden as instance attributes.
@serhiy-storchaka
Copy link
Member

This would be very complex solution and it would not work with super().

But we can make attributes (and only these attributes) writable by implementing tp_getattro and tp_setattro. This is slightly more complex solution, but not extremely complex. See #125752.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
3.13 bugs and security fixes 3.14 new features, bugs and security fixes extension-modules C modules in the Modules dir type-bug An unexpected behavior, bug, or error
Projects
None yet
Development

No branches or pull requests

2 participants