Portál AbcLinuxu, 13. července 2025 19:04


Dotaz: Python - Pickle a shelve a kódování

9.12.2013 20:12 alfonz mucha
Python - Pickle a shelve a kódování
Přečteno: 663×
Odpovědět | Admin

Dobrý den,

mám tady takový dost specifický dotaz na Python a pickle/shelve. Mám kód který bych chtěl udělat pro python2 a python3. Na první pohled se zdá, že vše správně funguje, jenže po otevření dat na python 3 se objevuje chyba v kódování, která ani nejde moc dobře vyřešit.

python2 > vytvoření souboru

import shelve
try:
    import dumbdbm as dumb
except:
    from dbm import dumb

db = dumb.open('/tmp/test',flag='c')
database = shelve.Shelf(db,writeback=True)
database['aa'] = {"ščř":2343, 'aaa3':'šřdd'}
database.sync()
database.close()

python3 > otevření souboru

import shelve
try:
    import dumbdbm as dumb
except:
    from dbm import dumb

db = dumb.open('/tmp/test',flag='c')
database = shelve.Shelf(db,writeback=True)
database['aa']

Traceback (most recent call last):
  File "/usr/lib/python3.2/shelve.py", line 111, in __getitem__
    value = self.cache[key]
KeyError: 'aa'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python3.2/shelve.py", line 114, in __getitem__
    value = Unpickler(f).load()
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc5 in position 0: ordinal not in range(128)

Chyba je dle mého špatně vyřešitelná na úrovni kódu vlastního a je možné jí řešit upravením __getitem__()

/usr/lib/python3.2/shelve.py
/usr/lib/python3.3/shelve.py > Shelf v částí Unpickler(), kde chybí kódování. Do Shelf předat kódování nejde.

def __getitem__(self, key):
    try:
        value = self.cache[key]
    except KeyError:
        f = BytesIO(self.dict[key.encode(self.keyencoding)])
        value = Unpickler(f,encoding="???").load()
        if self.writeback:
            self.cache[key] = value
    return value

Upravením funkce __getitem__() na na "UTF-8" funguje

from pickle import Pickler, Unpickler
from io import BytesIO
def getitem(database, key):
    try:
        value = database.cache[key]
    except KeyError:
        f = BytesIO(database.dict[key.encode("UTF-8")])
        value = Unpickler(f,encoding="UTF-8").load()
        if database.writeback:
            database.cache[key] = value
    return value

from pickle import Pickler, Unpickler
from io import BytesIO
getitem(database, 'aa')
{'ščř': 2343, 'aaa3': 'šřdd'}

Takže teď nevím, jakým způsobem bych to měl řešit.. mám dělat nějaký wrapper nebo to je normální chování?? Někdo znalý Pythonu poraďte.

Nástroje: Začni sledovat (1) ?Zašle upozornění na váš email při vložení nového komentáře.

Odpovědi

9.12.2013 20:17 alfonz mucha
Rozbalit Rozbalit vše Re: Python - Pickle a shelve a kódování
Odpovědět | | Sbalit | Link | Blokovat | Admin
Blbé je, že většina tříd/dat, které se ukládají je potřeba právě otevírat jak v python2/python3 tak obsahují hodně unicode. Napadlo, mě ještě nějak upravit pickle protocol? Ale to asi také nepomůže, že ano?
Fuky avatar 10.12.2013 13:04 Fuky | skóre: 52 | blog: 4u
Rozbalit Rozbalit vše Re: Python - Pickle a shelve a kódování
Odpovědět | | Sbalit | Link | Blokovat | Admin
Příloha:
Na Debianu s Pythonem 3.2.3-7 kód funguje bez problémů. Konstruktor Shelf má defaultně nastavený parametr keyencoding="utf-8", který se používá pro nastavení self.keyencoding.
-- RÁMO: psí tábor , ETriatlon: Výuka plavání
10.12.2013 15:31 alfonz mucha
Rozbalit Rozbalit vše Re: Python - Pickle a shelve a kódování
Ano... ale to je pouze pro otevření textu > ale ne pro pickle. Ta chyba se objeví až pro pickle jak je ukázáno.

kód v Pythonu 2 zapsat soubor do /tmp/ a pak zkusit otevřít v pyhtonu 3. Takto jste to zkoušel?
Fuky avatar 11.12.2013 11:26 Fuky | skóre: 52 | blog: 4u
Rozbalit Rozbalit vše Re: Python - Pickle a shelve a kódování

Omlouvám se, máte pravdu, řešil jsem to v rychlosti.

Lze to vyřešit i bez zásahu do standartního modulu:

def u(string):
    if (sys.version_info[0] < 3):
        return unicode(string, "utf-8")

    return string

...

database = shelve.Shelf(db, protocol=2, writeback=True)

...

database['aa'] = {u('ščř'): 2343, 'aaa3': u('šřdd')}

Zkoušel jsem to s Pythonem 2.5 a 3.2.

11.12.2013 12:12 alfonz mucha
Rozbalit Rozbalit vše Re: Python - Pickle a shelve a kódování
To je sice také dobré řešení, ale znamená to úpravu velkého množství kódu. Jak jsem psal potřebuji to pro další sérii tříd a to by znamenalo velké množství duplicity kódu.

Přemýšlím, jestli to není možné reportovat jako chybu/podivné chování > v pythonu 2 totiž Pickle encoding parametr neměl. Avšak nově od asi od 3.2 má parameter encoding="acsii". Pokud by se do shelve přidal parametr/ či se připojil do části Unpickler, tak by to bylo řešitelné více systémově a bylo by to výhodnější i pro další projekty, které nepracují pouze s "ascii".

Na druhou stranu nevím, jestli to je špatné chování nebo není.
Fuky avatar 11.12.2013 14:03 Fuky | skóre: 52 | blog: 4u
Rozbalit Rozbalit vše Re: Python - Pickle a shelve a kódování

Pak lze ještě použít "obezličku", kterou jsi nastínil:

from pickle import Pickler, Unpickler

try:
    from io import BytesIO
except:
    pass

class MyShelf(shelve.Shelf):

    def __init__(self, db):
        shelve.Shelf.__init__(self, db, protocol=2, writeback=True)

    def __getitem__(self, key):
        if (sys.version_info[0] < 3):
            return shelve.Shelf.__getitem__(self, key)

        try:
            value = self.cache[key]
        except KeyError:
            f = BytesIO(self.dict[key.encode(self.keyencoding)])
            value = Unpickler(f, encoding="utf-8").load()
            if self.writeback:
                self.cache[key] = value
        return value

...

database = MyShelf(db)
database['aa'] = {'ščř':2343, 'aaa3':'šřdd'}

Případně lze překrýt metodu __setitem__(), v Pythonu 2, tak aby všechny řetězce převedla na unicode řetězce.

Také mi přijde, že by bylo šikovné mít možnost nastavit kódování přímo v objektu Shelf, vzhledem k tomu, že metody, které volá tento parametr přijímají.

11.12.2013 18:41 alfonz mucha
Rozbalit Rozbalit vše Re: Python - Pickle a shelve a kódování
Tusite jak spravne poslat zadost na python buglist? Ma s tim nekdo zkusenosti? Pripadne tusite jestli to lze podat jako zadost na vylepseni? Nebo je to zbytecne
Fuky avatar 12.12.2013 11:25 Fuky | skóre: 52 | blog: 4u
Rozbalit Rozbalit vše Re: Python - Pickle a shelve a kódování
10.12.2013 15:33 alfonz mucha
Rozbalit Rozbalit vše Re: Python - Pickle a shelve a kódování
Třída Unpickler() má totiž také parameter a tam je encoding="ascii" Což pro obvykle data bez parameteru encoding "utf-8" neotevře

Založit nové vláknoNahoru

Tiskni Sdílej: Linkuj Jaggni to Vybrali.sme.sk Google Del.icio.us Facebook

ISSN 1214-1267, (c) 1999-2007 Stickfish s.r.o.