This issue tracker has been migrated to GitHub, and is currently read-only.
For more information, see the GitHub FAQs in the Python's Developer Guide.

classification
Title: Patch to make pickle aware of __qualname__
Type: enhancement Stage: resolved
Components: Extension Modules Versions: Python 3.4
process
Status: closed Resolution: duplicate
Dependencies: Superseder: Implement PEP 3154 (pickle protocol 4)
View: 17810
Assigned To: alexandre.vassalotti Nosy List: alexandre.vassalotti, lukasz.langa, pitrou, sbt, vstinner
Priority: normal Keywords: patch

Created on 2011-12-02 16:29 by sbt, last changed 2022-04-11 14:57 by admin. This issue is now closed.

Files
File name Uploaded Description Edit
pickle_qualname.patch sbt, 2011-12-02 16:29
Messages (5)
msg148759 - (view) Author: Richard Oudkerk (sbt) * (Python committer) Date: 2011-12-02 16:29
The attached patch makes pickle use an object's __qualname__ attribute if __name__ does not work.

This makes nested classes, unbound instance methods and static methods picklable (assuming that __module__ and __qualname__ give the correct "address").


BTW, It would not be hard to make instance methods picklable (and, as a by-product, class methods) by giving instance methods a __reduce__ method equivalent to

  def __reduce__(self):
    return (getattr, (self.__self__, self.__name__))

Is there any reason not to make such a change?
msg148770 - (view) Author: Antoine Pitrou (pitrou) * (Python committer) Date: 2011-12-02 19:10
> The attached patch makes pickle use an object's __qualname__ attribute
> if __name__ does not work.
> 
> This makes nested classes, unbound instance methods and static methods
> picklable (assuming that __module__ and __qualname__ give the correct
> "address").

Thanks. I'm not sure that's a good approach for protocol 3: some pickles
created by Python 3.3 would not be readable by Python 3.2. That's why I
initially added that idea to PEP 3154, for a hypothetical protocol 4.

However, perhaps it is possible to use the same reduce/getattr trick you
are proposing for instance methods?

> BTW, It would not be hard to make instance methods picklable (and, as
> a by-product, class methods) by giving instance methods a __reduce__
> method equivalent to
> 
>   def __reduce__(self):
>     return (getattr, (self.__self__, self.__name__))
> 
> Is there any reason not to make such a change?

I don't see any personally. Actually, multiprocessing has a Pickler
subclass called ForkingPickler which does something similar (in
Lib/multiprocessing/forking.py).
msg148903 - (view) Author: Alexandre Vassalotti (alexandre.vassalotti) * (Python committer) Date: 2011-12-06 02:36
This might not be the case anymore, but __module__ can sometime be None. There is some discussion about this in Issue 3657. We should define the expected behavior when this happens.

Also, I don't think backward-compatibility of the protocol is a big issue if we use the getattr approach. I feel bumping the protocol version is only necessary if we introduce new opcodes or change their interpretation.
msg148920 - (view) Author: Richard Oudkerk (sbt) * (Python committer) Date: 2011-12-06 14:52
It looks like Issue 3657 is really about builtin methods (i.e. builtin_function_or_method objects where __self__ is not a module).  It causes no problem for normal python instance methods.

If we tried the getattr approach for builtin methods too then it should only be used as a fallback when saving as a global will not work.
msg168820 - (view) Author: Richard Oudkerk (sbt) * (Python committer) Date: 2012-08-21 23:16
There is a cute way to use operator.attrgetter to produce backwards compatible pickles using the qualname:

import pickle, copyreg, operator, sys, pickletools, types

class AttrGetter(object):
    def __init__(self, name):
        self.name = name
    def __call__(self):                 # pretend to be callable
        raise RuntimeError
    def __reduce__(self):
        return operator.attrgetter, (self.name,)

def reduce_by_qualname(obj):
    mod = sys.modules[obj.__module__]
    first, rest = obj.__qualname__.split('.', 1)
    firstobj = getattr(mod, first)
    assert operator.attrgetter(rest)(firstobj) is obj
    return AttrGetter(rest), (firstobj,)

# FunctionType defaults to save_global but uses fallback if it fails
copyreg.pickle(types.FunctionType, reduce_by_qualname)

class A(object):
    class B(object):
        class C(object):
            @staticmethod
            def foo():
                print("foo foo foo")

def bar():
    print("bar bar bar")

for obj in [A.B.C.foo, bar]:
    data = pickle.dumps(obj, 2)
    pickletools.dis(data)
    func = pickle.loads(data)
    assert func is obj
    func()

This produces

    0: \x80 PROTO      2
    2: c    GLOBAL     'operator attrgetter'
   23: q    BINPUT     0
   25: X    BINUNICODE 'B.C.foo'
   37: q    BINPUT     1
   39: \x85 TUPLE1
   40: q    BINPUT     2
   42: R    REDUCE
   43: q    BINPUT     3
   45: c    GLOBAL     '__main__ A'
   57: q    BINPUT     4
   59: \x85 TUPLE1
   60: q    BINPUT     5
   62: R    REDUCE
   63: q    BINPUT     6
   65: .    STOP
highest protocol among opcodes = 2
foo foo foo
    0: \x80 PROTO      2
    2: c    GLOBAL     '__main__ bar'
   16: q    BINPUT     0
   18: .    STOP
highest protocol among opcodes = 2
bar bar bar
History
Date User Action Args
2022-04-11 14:57:24adminsetgithub: 57729
2013-11-30 04:52:45alexandre.vassalottisetstatus: open -> closed
versions: + Python 3.4, - Python 3.3
resolution: duplicate
assignee: alexandre.vassalotti
superseder: Implement PEP 3154 (pickle protocol 4)
stage: patch review -> resolved
2012-08-21 23:16:03sbtsetmessages: + msg168820
2012-06-24 07:31:31hyneksetnosy: - hynek
2012-02-26 18:43:44lukasz.langasetnosy: + lukasz.langa
2012-01-22 19:48:15hyneksetnosy: + hynek
2011-12-06 14:52:15sbtsetmessages: + msg148920
2011-12-06 02:36:52alexandre.vassalottisetmessages: + msg148903
2011-12-02 19:13:05pitrousetnosy: + alexandre.vassalotti
2011-12-02 19:10:40pitrousetmessages: + msg148770
2011-12-02 16:36:13vstinnersetnosy: + vstinner
2011-12-02 16:33:31ezio.melottisettype: enhancement
components: + Extension Modules
stage: patch review
2011-12-02 16:29:12sbtcreate