unpickling derived LogRecord in python 2.7 from python2.6

Peter Otten __peter__ at web.de
Wed Apr 27 18:41:02 CEST 2011


ivdneut at gmail.com wrote:

> I have a service that runs in python 2.6.4. This service sends
> LogRecords to a log monitoring app on my workstation running python
> 2.7. The LogRecord class is derived:
> 
> class LogRecord(logging.LogRecord):
> 
>     def __init__(self, name, level, fn, lno, user, hostname, msg,
> args, exc_info, func=None):
> 
>         if sys.version_info[1] > 4:
>             logging.LogRecord.__init__(self, name, level, fn, lno,
> msg, args, exc_info, func)
>         else:
>             logging.LogRecord.__init__(self, name, level, fn, lno,
> msg, args, exc_info)
> 
> Now when I try to unpickle it:
> 
> record = cPickle.loads(data)
> 
> I get a TypeError exception:
> 
> TypeError: ('__init__() takes at least 8 arguments (1 given)', <class
> '...gRecord'>, ())
> 
> I've searched the web and this group, but most results are old. It
> worked when my workstation still ran python 2.6.

The Problem is that as of Python 2.7 logging.LogRecord has become a newstyle 
class which is pickled/unpickled differently. I don't know if there is an 
official way to do the conversion, but here's what I've hacked up. 
The script can read pickles written with 2.6 in 2.7, but not the other way 
round.

$ cat pickle_logrec.py
import sys
import pickle
import logging

class LogRecord(logging.LogRecord):

    def __init__(self, name, level, fn, lno, user, hostname, msg, args, 
exc_info, func=None):

        if sys.version_info[1] > 4:
            logging.LogRecord.__init__(self, name, level, fn, lno, msg, 
args, exc_info, func)
        else:
            logging.LogRecord.__init__(self, name, level, fn, lno, msg, 
args, exc_info)

def makeLogRecord():
    return LogRecord(*[None]*9)

if issubclass(LogRecord, object):
    print "LogRecord is a newstyle class"
    class MyUnpickler(pickle.Unpickler):
        def find_class(self, *args):
            klass = pickle.Unpickler.find_class(self, *args)
            if klass is LogRecord:
                return makeLogRecord
            return klass
else:
    print "LogRecord is an oldstyle class"
    MyUnpickler = pickle.Unpickler

if __name__ == "__main__":
    if "--load" in sys.argv:
        print "loading"
        with open("tmp.pickle") as f:
            restored = MyUnpickler(f).load()
            print restored
    else:
        print "dumping"
        with open("tmp.pickle", "w") as f:
            f.write(pickle.dumps(LogRecord("yadda", *[None]*8)))


$ python2.6 pickle_logrec.py
LogRecord is an oldstyle class
dumping
$ python2.6 pickle_logrec.py --load
LogRecord is an oldstyle class
loading
<LogRecord: yadda, None, None, None, "None">
$ python2.7 pickle_logrec.py --load
LogRecord is a newstyle class
loading
<LogRecord: yadda, None, None, None, "None">

No warranty, use at your own risk.



More information about the Python-list mailing list