Премахване на python2 datetime под python3

Избрах да използвам pickle (+base64+TCP сокети) за комуникация на данни между моя код на python3 и наследения код на python2, но имам проблеми с datetime обекти:

Обектът PY3 се справя добре с PY2, но обратното повдига TypeError при извикване на конструктора за дата и час, след това UnicodeEncodeError във функцията load_reduce.

Кратка програма за тестване и дневникът, включително дис изход както на PY2, така и на PY3 туршии, са достъпни в тази основна информация

  • Използвам pickle.dumps(reply, protocol=2) в PY2
    след това pickle._loads(pickled, fix_imports=True, encoding='latin1') в PY3
    (пробвах None и utf-8 без успех)

  • Родното cPickle loads декодиране също е неуспешно, използвам само чист python _loads за отстраняване на грешки.

Това datetime грешка ли е? Може би datetime.__getstate__/__setstate__ реализациите не са съвместими?

Всяка забележка към кода е добре дошла...

Допълнение

PY-3.4.0 туршия:

 0: \x80 PROTO      2
 2: c    GLOBAL     'datetime datetime'
21: q    BINPUT     0
23: c    GLOBAL     '_codecs encode'
39: q    BINPUT     1
41: X    BINUNICODE u'\x07\xde\x07\x11\x0f\x06\x11\x05\n\x90'
58: q    BINPUT     2
60: X    BINUNICODE u'latin1'
71: q    BINPUT     3
73: \x86 TUPLE2
74: q    BINPUT     4
76: R    REDUCE
77: q    BINPUT     5
79: \x85 TUPLE1
80: q    BINPUT     6
82: R    REDUCE
83: q    BINPUT     7
85: .    STOP

PY-2.7.6 туршия:

 0: \\x80 PROTO      2
 2: c    GLOBAL     'datetime datetime'
21: q    BINPUT     0
23: U    SHORT_BINSTRING '\\x07\xc3\x9e\\x07\\x11\\x0f\\x06\\x11\\x05\\n\\x90'
35: q    BINPUT     1
37: \\x85 TUPLE1
38: q    BINPUT     2
40: R    REDUCE
41: q    BINPUT     3
43: ]    EMPTY_LIST
44: q    BINPUT     4
46: N    NONE
47: \\x87 TUPLE3
48: q    BINPUT     5
50: .    STOP

PY-3.4.0 pickle.load_reduce:

def load_reduce(self):
    stack = self.stack
    args = stack.pop()
    func = stack[-1]
    try:
        value = func(*args)
    except:
        print(sys.exc_info())
        print(func, args)
        raise
    stack[-1] = value
dispatch[REDUCE[0]] = load_reduce

Поддръжка на PY-3.4.0 datetime pickle:

# Pickle support.

def _getstate(self):
    yhi, ylo = divmod(self._year, 256)
    us2, us3 = divmod(self._microsecond, 256)
    us1, us2 = divmod(us2, 256)
    basestate = bytes([yhi, ylo, self._month, self._day,
                       self._hour, self._minute, self._second,
                       us1, us2, us3])
    if self._tzinfo is None:
        return (basestate,)
    else:
        return (basestate, self._tzinfo)

def __setstate(self, string, tzinfo):
    (yhi, ylo, self._month, self._day, self._hour,
     self._minute, self._second, us1, us2, us3) = string
    self._year = yhi * 256 + ylo
    self._microsecond = (((us1 << 8) | us2) << 8) | us3
    if tzinfo is None or isinstance(tzinfo, _tzinfo_class):
        self._tzinfo = tzinfo
    else:
        raise TypeError("bad tzinfo state arg %r" % tzinfo)

def __reduce__(self):
    return (self.__class__, self._getstate())

person eddygeek    schedule 17.07.2014    source източник
comment
Не знам как да разреша проблема ви, но pickle не е предназначен като цяло за обмен на данни между различни версии или дългосрочно съхранение. Има ли недвусмислен формат (текст?), който бихте могли да използвате?   -  person mdurant    schedule 17.07.2014
comment
Ако просто искате да запазите информацията за datetime, предлагам да изберете форматирания текст на datetime, да го премахнете някъде другаде и да анализирате низа в datetime отново.   -  person JimmyK    schedule 17.07.2014
comment
@JimmyK Първоначалната цел беше да има гъвкаво решение (т.е. да не се налага преобразуване с твърд код всеки път, когато обектът се промени), без да се прибягва до вечни пакети като пиро...   -  person eddygeek    schedule 17.07.2014
comment
@mdurant Като се има предвид опцията fix_imports на Unpickler (и обратно съвместимите протоколи), бихте си помислили, че са планирали решение за кръстосана версия ;-)   -  person eddygeek    schedule 17.07.2014
comment
@eddygeek - как създадохте PY-3.4.0 pickle dis?   -  person Alexander Belopolsky    schedule 12.10.2015
comment
Няма значение. Такъв pickle се произвежда в 3.x, когато pickle.dump() се извика с protocol=2.   -  person Alexander Belopolsky    schedule 12.10.2015


Отговори (1)


Заобиколното решение е да използвате encoding="bytes" по този начин:

pickled_bytes = bytes(pickled_str, encoding='latin1')  # If your input is a string(not my case)
data = pickle.loads(pickled_bytes, encoding='bytes')

(Благодаря на Тим Питърс за предложението)

Проблемът все още е отворен на адрес http://bugs.python.org/issue22005 относно защо това се изисква.

person eddygeek    schedule 21.07.2014
comment
Приятно... стига да не сте заседнали на Python 3.2 на Cygwin, който изглежда не знае за encoding='bytes'. Аргх - person james.haggerty; 11.11.2014
comment
Трябва да се отбележи, че този проблем е коригиран в най-новите версии на Python 3. Все пак трябва да помните да използвате encoding='latin-1', когато деактивирате на Python 3. Намирам това за неинтуитивно и не чак толкова по-добро от encoding='bytes', защото за мен, не е очевидно, че datetime изобщо ще включва низове. И ако вашият пикел на Python 2 съдържа както дати, така и кодирани низове в кодировки, различни от Latin-1, тогава отново се налага да използвате bytes така или иначе. - person John Y; 24.06.2019