Skip to content

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

Description

@jcea

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

Activity

  1. added
    type-bugAn unexpected behavior, bug, or error
    3.13only security fixes
    on Oct 17, 2024
  2. 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[/+] on Oct 17, 2024
  3. jcea commented on Oct 17, 2024

    @jcea
    MemberAuthor

    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
    >>> 
    
  4. jcea commented on Oct 17, 2024

    @jcea
    MemberAuthor

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

  5. jcea commented on Oct 17, 2024

    @jcea
    MemberAuthor

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

  6. added a commit that references this issue on Oct 19, 2024
  7. serhiy-storchaka commented on Oct 19, 2024

    @serhiy-storchaka
    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.

  8. jcea commented on Oct 19, 2024

    @jcea
    MemberAuthor

    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.

  9. added a commit that references this issue on Oct 20, 2024
  10. serhiy-storchaka commented on Oct 20, 2024

    @serhiy-storchaka
    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.

  11. added a commit that references this issue on Nov 7, 2024
  12. added a commit that references this issue on Nov 7, 2024
  13. added a commit that references this issue on Nov 7, 2024
  14. added a commit that references this issue on Dec 8, 2024
  15. paulankita137 commented on Dec 19, 2024

    @paulankita137

    Does this problem have a fix ? I am facing the same issues

  16. jcea commented on Dec 19, 2024

    @jcea
    MemberAuthor

    It was solved in Python 3.13.1, already a couple of weeks old.

  17. added a commit that references this issue on Jan 12, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

3.13only security fixes3.14bugs and security fixesextension-modulesC modules in the Modules dirtype-bugAn unexpected behavior, bug, or error

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions