Migrace na Python 3: The HealthifyMe Experience.

( 18. listopadu 2020)

Dobrý den, tento blog je o tom, jak provést migraci staršího monolitického projektu Pythonu 3 bez nutnosti prostojů. Máme pro to 2 části.

První blog: Tento blog obsahuje informace o tom, proč byste měli migrovat na Python 3, a ojedinělý rozdíl v Pythonu 2 a Pythonu 3 a Kompatibilní řešení pro ty .

(Druhý blog): Jak se HealthifyMe přesunul do Pythonu 3 bez nutnosti prostojů s probíhajícím vývojem.

Úvod

Python je primární kódovací jazyk na HealthifyMe. Používáme Python 3 pro všechny nové projekty, ale náš starší projekt stále běžel s Pythonem 2.7 s Django (1.11). Náš monolitický projekt je starý 7 let s více než 2 miliony řádků kódu pythonu. Některé z důvodů, proč jsme se přesunuli do pythonu 3:

  1. Podpora pro python 2 klesla: Po lednu 2020 nebude podpora pro Python 2 poskytována.
  2. Přijetí Pythonu 3: As většina společností, open-source projekt již používá Python 3. Nové knihovny, nástroje, moduly, rámce budou napsány v Pythonu 3.
    Stávající open-source projekt také migroval a nová funkce, opravy, vylepšení zabezpečení přicházejí v Pythonu 3.
  3. Zabezpečení softwaru: Zajištění zabezpečení softwaru je zákonným požadavkem, zvláště když máte co do činění s osobními údaji v oblasti GDPR. Udržování aktuálního softwaru je pochopitelně na velmi vysoké úrovni mezi osvědčenými postupy zabezpečení, a zastaralý tlumočník Pythonu by kromě zaručených během auditu zabezpečení se zobrazí jako červená vlajka. Nástroje pro testování zabezpečení, jako je Black Duck, v Pythonu 2 ohlásily mnoho chyb zabezpečení, zneužití a zabezpečení. A většina z nich je opravena v nejnovějších verzích Pythonu 3 (3.7.5, 3.8.0).
  4. Výkon a nové funkce : Python 3 má lepší výkon než python 2. Nový softwarový produkt, který používá python 3, zaznamenal zvýšení výkonu CPU o 12\% a zlepšení využití paměti o 30\% .
    také nám python 3 dává:
    * nativní asynchronní programování.
    * zadejte poznámky
    , které můžete použít ke zlepšení statické analýzy kódu a celkové použitelnosti.
    * zřetězené výjimky , které jsou obzvláště užitečné při ladění.
    * další užitečné funkce , díky nimž je kódování v Pythonu mnohem efektivnější.

Tento seznam bude pokračovat a určitě se rozroste s každým novým vydáním Pythonu 3.

To je několik důvodů, proč migrovat na python 3. Máme přibližně 12–15 vývojářského back-endového týmu. Toto je náš základní projekt s vydáním sestavení 7–10 denně, který má opravy chyb, vylepšení, opravy zabezpečení, vývoj nových funkcí atd. Naší hlavní výzvou nebylo zastavit současný vývojový proces. Museli jsme se ujistit, že náš projekt je kompatibilní s Pythonem 3.X, aniž by došlo k porušení kompatibility s Pythonem 2.X. Migraci vedl 1 vývojář (samozřejmě s pomocí dalších vývojářů).

V tomto článku se pokusíme ilustrovat všechny podniknuté kroky, problémy, kterým čelíme, a několik dalších podrobností .

Rozdíl mezi pythonem 2 a pythonem 3.

Společný rozdíl zjistíme zde:

https://docs.python.org/ 3 / whatsnew / 3.0.html
https://sebastianraschka.com/Articles/2014\_python\_2\_3\_key\_diff.html
https://jaxenter.com/differences-python-2-3-148432.html
https: // python-future.org/compatible\_idioms.html

Nyní stručně popíšeme některé vzácné a okrajové případy když jsme zahájili naši migraci.

Porovnání datového typu:

1. V Pythonu 2 bude fungovat porovnání integer s none, takže none je považováno za méně než celé číslo, dokonce i záporné.Můžete také porovnat none s string, string s int.
Porovnání různých datových typů není v pythonu 3 povoleno.
To je známo většině vývojářů, ale čelili jsme případu hrany, kde NotImplementedType byl srovnáván s int a toto nebude fungovat v pythonu 3.

Úryvek kódu:

class Base(object):
PHONE\_NO\_SIZE\_LIMIT = NotImplementedbase = Base()
if base.PHONE\_NO\_SIZE\_LIMIT > 10:
print("Pass correct phone number")
else:
print("Valid phone number")

Pokud to uložíme s phone\_number\_validation.py a spusťte kód:

# Python 2
[email protected] ~ \% python phone\_number\_validation.py
Pass correct phone number# Python 3
[email protected] ~ \% python3.7 phone\_number\_validation.py
Traceback (most recent call last):
File "phone\_number\_validation.py", line 4, in
if base.PHONE\_NO\_SIZE\_LIMIT > 10:
TypeError: ">" not supported between instances of "NotImplementedType" and "int"

kompatibilní Řešení:
Musíme zkontrolovat, zda je base.PHONE\_NO\_SIZE\_LIMIT implementován nebo ne, pokud ne, musíme to zvládnout. Líbí se:

if isinstance(base.PHONE\_NO\_SIZE\_LIMIT, type(NotImplemented)):
# Have logic here, also exit/return here.
print("Phone size is not implemented")if base.PHONE\_NO\_SIZE\_LIMIT > 10:
print("Pass correct phone number")
else:
print("Valid phone number")

2. Min, Max Matematické funkce:
Srovnání práce převýšení v int to none, intstr, none to str v pythonu 3, takže matematická funkce min a Max se také změnila v pythonu 3.

# Python 2 
>>> max([1, None, 2])
2
>>> max([1, None, 2, "abc"])
"abc"
>>> min([1, None, 2, "abc"])
None # Python 3
>>> max([1, None, 2])
Traceback (most recent call last):
File "", line 1, in
TypeError: ">" not supported between instances of "NoneType" and "int" >>> max([1, 2, "abc", None])
Traceback (most recent call last):
File "", line 1, in TypeError: ">" not supported between instances of "str" and "int"

Kompatibilní řešení:
1. Seznam by měl obsahovat data jednoho typu buď string nebo int
2. S jedním typem, pokud none existuje, můžeme mít vlastní metodu, jak to zvládnout.

def py2max(input\_list):
"""Get the maximum item from list."""
if not input\_list:
raise ValueError("List should not be empty")
formated\_input\_list = [rec for rec in input\_list if rec is not None]
return max(formated\_input\_list) if formated\_input\_list else None

Hexové kódování / dekódování

Zatímco kódujeme řetězec v pythonu 2, pokud postupujeme podle tohoto .encode(‘hex’) V Pythonu 3 to nebude fungovat

# ENCODING# Python 2
>>> "msg\_to\_be\_encoded".encode("hex")
"6d73675f746f5f62655f656e636f646564"# Python 3
>>> "msg\_to\_be\_encoded".encode("hex")
Traceback (most recent call last):
File "", line 1, in
LookupError: "hex" is not a text encoding; use codecs.encode() to handle arbitrary codecs# DECODING# Python 2
>>> "6d73675f746f5f62655f656e636f646564".decode("hex") "msg\_to\_be\_encoded"# Python 3
>>> b"6d73675f746f5f62655f656e636f646564".decode("hex")
Traceback (most recent call last):
File "", line 1, in
LookupError: "hex" is not a text encoding; use codecs.decode() to handle arbitrary codecs# Also look at exception here in python 2 and python 3.# Python 3>>> b"6d73675f746f5f62655f656e636f646564".decode("hex")
Traceback (most recent call last):
File "", line 1, in
LookupError: "hex" is not a text encoding; use codecs.decode() to handle arbitrary codecs# Python 2>>> "6d73675f746f5f62655f656e636f64656".decode("hex")
Traceback (most recent call last):
File "", line 1, in
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/encodings/hex\_codec.py", line 42, in hex\_decode
output = binascii.a2b\_hex(input)
TypeError: Odd-length string

Kompatibilní řešení:
Měli bychom použít codecs pro Python 3 a 2. V Pythonu 3 je vstup i výstup byte datový typ.

# Python 2
>>> import codecs
>>> message = "msg\_to\_be\_encoded"
>>> codecs.encode(message.encode(), "hex")
"6d73675f746f5f62655f656e636f646564"# Python 3
>>> message = "msg\_to\_be\_encoded"
>>> codecs.encode(message.encode(), "hex")
b"6d73675f746f5f62655f656e636f646564"

Velká písmena řetězce:

string.uppercase nefunguje v pythonu 3.

# Python 2 
>>> import string
>>> string.uppercase
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"# Python 3
>>> import string
>>> string.uppercase
Traceback (most recent call last):
File "", line 1, in
AttributeError: module "string" has no attribute "uppercase"

Kompatibilní řešení:
Použijte ascii\_uppercase

# Python 2
>>> import string
>>> string.ascii\_uppercase
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"# Python 3
>>> import string
>>> string.ascii\_uppercase
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"

hasattr ():

hasattr() představuje při psaní Pythonu 2 a 3 obrovský okrajový případ kompatibilní kód. hasattr() kontroluje existenci atributu pokusem o jeho načtení.

Python 2

hasattr ( object , name )
Argumenty jsou objekt a řetězec. Výsledek je True, pokud je řetězec názvem jednoho z atributů objektu, False, pokud ne. (To je implementováno voláním getattr (objekt, název) a kontrolou, zda vyvolává výjimku nebo ne.)

Python 3

hasattr ( objekt , name ) Argumenty jsou objekt a řetězec. Výsledek je True, pokud je řetězec názvem jednoho z atributů objektu, False, pokud ne. (To je implementováno voláním getattr (objekt, název) a kontrolou, zda vyvolá AttributeError nebo ne.)

Další podrobnosti naleznete zde: (https://medium.com/@k.wahome/python-2-vs-3-hasattr-behaviour-f1bed48b068)

Ukázkový kód

class Foo(dict): 
def \_\_init\_\_(self):
super(Foo, self).\_\_init\_\_()
self.example\_dict = {}

def \_\_getitem\_\_(self, key):
try:
return super(Foo, self).\_\_getitem\_\_(key)
except KeyError:
return self.example\_dict[key] def \_\_getattr\_\_(self, key):
return self[key]foo = Foo()
if hasattr(foo, "not\_present\_key"):
pass
else:
print("Not Found")

Uložte výše uvedený fragment pomocí hasattr\_test.py

# Python 2[email protected] ~ \% python hasattr\_test.py 
Not Found# Python 3[email protected] ~ \% python3.7 hasattr\_test.py
Traceback (most recent call last):
File "hasattr\_test.py", line 8, in \_\_getitem\_\_
return super(Foo, self).\_\_getitem\_\_(key)
KeyError: "not\_present\_key"During handling of the above exception, another exception occurred:Traceback (most recent call last):
File "hasattr\_test.py", line 17, in
if hasattr(foo, "not\_present\_key"):
File "hasattr\_test.py", line 13, in \_\_getattr\_\_
return self[key]
File "hasattr\_test.py", line 10, in \_\_getitem\_\_
return self.example\_dict[key]
KeyError: "not\_present\_key"

Kompatibilní řešení:
Abychom zajistili kompatibilitu kódu v Pythonu 2 a Pythonu 3, musíme změnit \_\_getattr\_\_ funkci jako níže.

def \_\_getattr\_\_(self, key):
try:
return self[key]
except KeyError:
raise AttributeError

Dict bude uspořádán v pythonu 3:

Od Pythonu 3.6+ je nyní výchozí slovník objednání vložení .

# Python 2
>>> sample\_dict = {}
>>> sample\_dict["a"] = 1
>>> sample\_dict["b"] = 2
>>> sample\_dict["c"] = 3
>>> sample\_dict["d"] = 4
>>> sample\_dict
{"a": 1, "c": 3, "b": 2, "d": 4}# Python 3
>>> sample\_dict = {}
>>> sample\_dict["a"] = 1
>>> sample\_dict["b"] = 2
>>> sample\_dict["c"] = 3
>>> sample\_dict["d"] = 4
>>> sample\_dict
{"a": 1, "b": 2, "c": 3, "d": 4}

Kompatibilní kód:
V ideálním případě by to nemělo porušit kód aplikace, protože se změnil na neuspořádaný na objednaný diktát. Pokud stále potřebujeme stejný výsledek v obou verzích pythonu (pořadí je důležité, testovací případ selhává), musíme použít OrderedDict pro zachování stejného výstupu obou jazyků.

Hashing:

V Pythonu 2 lze zadat vstup unicode a str, ale v Pythonu 3 potřebuje bytes

# Python 2
>>> import hashlib
>>> message = "healthify"
>>> hashlib.sha512(message).hexdigest().lower()
"f910c1fa68087a546512ac3b175c99ee7eba21360fa4e579c2aed649c7e4a43466c56bceedcd60d783bc6e7d069a16f0b9c67140d6c129d2a1898af8cfb62719"# Python 3
>>> message = "healthify"
>>> hashlib.sha512(message).hexdigest().lower()
Traceback (most recent call last):
File "", line 1, in
TypeError: Unicode-objects must be encoded before hashing

Kompatibilní řešení:
Python 3 potřebuje bytes jako vstup, kde python 2 pracuje s typem unicode a str oběma.

# Python 2
>>> import hashlib
>>> message = "healthify"
>>> hashlib.sha512(message.encode("utf-8")).hexdigest().lower()
"f910c1fa68087a546512ac3b175c99ee7eba21360fa4e579c2aed649c7e4a43466c56bceedcd60d783bc6e7d069a16f0b9c67140d6c129d2a1898af8cfb62719"# Python 3
>>> message = "healthify"
>>> hashlib.sha512(message.encode("utf-8")).hexdigest().lower()
"f910c1fa68087a546512ac3b175c99ee7eba21360fa4e579c2aed649c7e4a43466c56bceedcd60d783bc6e7d069a16f0b9c67140d6c129d2a1898af8cfb62719"

\_\_div\_\_ přetížení operátora:

V pythonu 3 se Zdá se, že operátor \_\_div\_\_ neexistuje, protože byl zcela nahrazen \_\_truediv\_\_.

class item:
fats = 0.0

def \_\_div\_\_(self, other):
self.fats = self.fats / otherit = item()
it.fats = 34.0
it / 3
print(it.fats)# python 2 output
11.3333333333# Python 3 output
Traceback (most recent call last):
File "div\_overloading.py", line 16, in
print(AB / 3)
TypeError: unsupported operand type(s) for /: "Vector2" and "int"

Kompatibilní řešení:
V Pythonu 3.x musíme přetížit operátory \_\_truediv\_\_, ne \_\_div\_\_ operátor. Aby byl kód kompatibilní, musíme zachovat obě metody, jako například:

class item:
fats = 0.0 def \_\_div\_\_(self, other):
self.fats = self.fats / other

def \_\_truediv\_\_(self, other):
self.fats = self.fats / otherit = item()
it.fats = 34.0
it / 3
print(it.fats)# python 2 output
11.3333333333# Python 3 output
11.333333333333334

kódování Base64:

Kódování base64 provádíme pomocí base64.b64encode(). V Pythonu 2 můžeme předat unicode nebo str jako vstup. Ale v pythonu 3 potřebuje jako vstup bytes.

# Python 2
>>> from base64 import b64encode
>>> b64encode("man")
"bWFu"
>>> b64encode(u"man")
"bWFu"# Python 3
>>> from base64 import b64encode
>>> b64encode("man")
Traceback (most recent call last):
File "", line 1, in
File "/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.7/lib/python3.7/base64.py", line 58, in b64encode
encoded = binascii.b2a\_base64(s, newline=False)
TypeError: a bytes-like object is required, not "str"

Kompatibilní řešení:
Pro kódování base64 můžeme mít vlastní metodu, která může mít vstupní string a bytes oboje.

import base64
import six
def base64ify(bytes\_or\_str):
if six.PY3 and isinstance(bytes\_or\_str, str):
input\_bytes = bytes\_or\_str.encode("utf8")
else:
input\_bytes = bytes\_or\_str
try:
output\_bytes = base64.b64encode(input\_bytes)
except (UnicodeEncodeError, TypeError):
# This happens when the input message has
# non-ascii encodable characters in an unicode string
# `"`(ascii encodable) vs `’`(non-ascii encodable)
# In this case, we first need to encode it to utf-8
# and then do the base64 encoding
output\_bytes = base64.b64encode(input\_bytes.encode("utf-8"))
if six.PY3:
return output\_bytes.decode("ascii")
else:
return output\_bytes

Integrovaná kulatá metoda:

Python2 : Zaokrouhlení se provádí od (tedy například zaokrouhlení (0,5) je 1,0 a zaokrouhlení (-0,5) je 1,0)
Python 3 : Zaokrouhlení se provádí směrem k sudé volbě (například kulatý (0,5) a kulatý (-0,5) jsou 0 a kulatý (1,5) je 2).

# Python 2
>>> round(15.5)
16.0
>>> round(16.5)
17.0# Python 3
>>> round(15.5)
16
>>> round(16.5)
16

Kompatibilní řešení:
Vytvořili jsme o vlastní metoda, která funguje stejně jako Python 2 v Pythonu 3.

def py2\_round(x, d=0):
"""Round same as PY2 in PY3."""
p = 10 ** d
if x >= 0:
return float(math.floor((x * p) + 0.5)) / p
else:
return float(math.ceil((x * p) - 0.5)) / p

struktura. typ vstupu balíčku:

typ vstupu je str, Python 3 by měl být byte

# Python 2
>>> import struct
>>> import struct
>>> string = "blab"
>>> s = struct.Struct(b"4s")
>>> packed\_data = s.pack(string)# Python 3
>>> import struct
>>> string = "blab"
>>> s = struct.Struct(b"4s")
>>> packed\_data = s.pack(string)
Traceback (most recent call last):
File "", line 1, in
struct.error: argument for "s" must be a bytes object

Kompatibilní řešení:
encode vstup .

proměnná s porozuměním seznamu scope changes:

V Pythonu 3 používá proměnná porozumění seznamu uzavírající obor, což znamená, že nebudete mít přístup k proměnné plánu mimo funkci, to bylo ne v Pythonu 2.

# Python 2
>>> def two\_or\_three():
... x = 3
... [0 for x in range(3)]
... return x
...
>>> two\_or\_three()
2>>> def two\_or\_three\_with\_method():
... def print\_number():
... print(x)
... [0 for x in range(3)]
... print\_number()
...
>>> two\_or\_three\_with\_method()
2# Python 3
>>> def two\_or\_three():
... x = 3
... [0 for x in range(3)]
... return x
...
>>> two\_or\_three()
3>>> def two\_or\_three\_with\_method():
... def print\_number():
... print(x)
... [0 for x in range(3)]
... print\_number()
...
>>> two\_or\_three\_with\_method()
Traceback (most recent call last):
File "", line 1, in
File "", line 5, in two\_or\_three
File "", line 3, in print\_number
NameError: name "x" is not defined

Kompatibilní řešení:
Takovým případům bychom se měli vyhnout. U druhé metody (two\_or\_three\_with\_method) musíme předat hodnotu x jako argument.

math.floor a math.ceil návratový datový typ změněn:

V pythonu 2 vrátí floor a ceil datový typ float, ale v pythonu 3 vrátí int datový typ.

# Python 2
>>> from math import floor,ceil
>>> floor(4.345)
4.0
>>> ceil(4.345)
5.0# Python 3
>>> from math import floor,ceil
>>> floor(4.345)
4
>>> ceil(4.345)
5

Kompatibilní řešení:
Můžeme vytvořte výstup jako plovák v pythonu 3. Nebude to mít vliv na python 2, float(floor(4.345))

Vyjmutí a python 2 nakládaný objekt do pythonu 3:

Jak již bylo zmíněno, vytváříme kód kompatibilní ke spuštění obou verzí pythonu. čelili jsme problému, když je objekt nakládán v Pythonu 2, ale v Pythonu 3 to nemůžeme zrušit. To se může stát i u objektů Redis Pickled v mezipaměti. div> výchozí je pokusit se dekódovat všechna data řetězce jako ASCII a toto dekódování selže. Viz

pickle.load() dokumentace :

Volitelné argumenty klíčových slov jsou fix\_imports , kódování a chyby , které se používají k řízení podpory kompatibility pro nakládaný proud generovaný Pythonem 2. Pokud je fix\_imports true, pickle se pokusí namapovat staré názvy Pythonu 2 na nové názvy používané v Pythonu 3. kódování a chyby říkají pickle, jak dekódovat 8bitové instance řetězce, které Python nabral 2; tyto výchozí hodnoty jsou „ASCII“ a „přísné“. kódování může být bajty pro čtení těchto 8bitových řetězcových instancí jako objekty bajtů.

https ://přetečení zásobníku.com / questions / 28218466 / unfickling-a-python-2-object-with-python-3

Kompatibilní řešení:
K odtržení objektů můžeme použít níže uvedenou metodu.

def unpickling\_py2\_to\_py3(pickled\_value):
"""Unpickling python 2 pickled in to python 3."""
if isPY3():
try:
value = pickle.loads(pickled\_value)
except UnicodeDecodeError:
value = pickle.loads(pickled\_value, encoding="latin1")
else:
value = pickle.loads(pickled\_value)
return value

Opravy knihoven třetích stran:

V našem projektu používáme spoustu balíčků třetích stran, zatímco při jejich aktualizaci jsme čelili některým okrajovým případům . Toto můžete přeskočit, pokud žádný z nich nepoužíváte.

  1. Django :
    a. Migrační soubory Django
    Když spustíme Django makemigrations v pythonu 3, viděli jsme nové migrační soubory. ale totéž se nedělo pro python 2. Může to mít několik důvodů.

prefix b: Při výrobě se většina nových souborů generuje bez b prefix řetězcových hodnot. Je to proto, že všechny řetězcové literály používané ve vašich modelech a polích (např. „ verbose\_name„, „ related\_name„ atd.), musí být konzistentně buď bajtové řetězce, nebo textové (unicode) řetězce v Pythonu 2 i 3.

Kompatibilní řešení: Nejjednodušší způsob, jak dosáhnout jedné migrace pro novou migraci, přidejte from \_\_future\_\_ import unicode\_literal do všech souborů modelů. U stávajících migračních souborů buď spustíme makemigration a mělo by k tomu dojít pouze jednou, nebo můžeme b prefix odebrat ze stávajících migračních souborů.

Pole volby: V modelech používáme dict.items (). Jak nyní víme, dict bude uspořádán v pythonu 3 , takže hodnoty vracené z dict.items () se budou lišit v Python 2 a Python 3.

Kompatibilní řešení: Abychom byli kompatibilní pro oba, roztřídili jsme (dikt. items ()) a vygenerovaný migrační soubor, který je nyní kompatibilní pro obě verze pythonu.

b. Zobrazení „ objektu“ v administrátorské konzole
U pythonu 3 v administrátorské konzoli vidíme objekt jako hodnotu pole namísto řetězce. pokud se to děje, protože naše modelová třída má metodu.

def \_\_unicode\_\_(self):
return "MyModel: {}".format(self.name)

Můžeme mít str metodu \_\_str\_\_, která funguje jak pro Python 2, tak pro Python 3. Ale selže, pokud str verze má znaky jiné než ASCII.

Kompatibilní Řešení: Řešení najdete zde , přidán @python\_2\_unicode\_compatible dekorátor pro modely a upravené \_\_unicode\_\_ na \_\_str\_\_.

c . Řezání objektů dotazu Django
Objekt dotazu Django má schopnost krájení záznamů. pro verzi Django (1.11), python 2 podporuje krájení pro int a str. V Pythonu 3 podporuje pouze krájení přes int.

# Python 2
>>> from food import models
>>> foods = models.foods.objects.all()
>>> foods[1:2] # int slicing
]>
>>> foods["1":"2"] # string slicing
]># Python 3
In [2]: from food import models
In [3]: foods = models.Foods.objects.all()
In [4]: foods["1":"2"]
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
in
----> 1 foods["1":"2"]~/Venvs/py3\_venv/lib/python3.7/site-packages/django/db/models/query.py in \_\_getitem\_\_(self, k)
265 raise TypeError
266 assert ((not isinstance(k, slice) and (k >= 0)) or
--> 267 (isinstance(k, slice) and (k.start is None or k.start >= 0) and
268 (k.stop is None or k.stop >= 0))), \
269 "Negative indexing is not supported."TypeError: ">=" not supported between instances of "str" and "int"In [5]: foods[1:2]
Out[5]: ]>

Kompatibilní řešení: Vyhněte se krájení řetězců, což každopádně není dobrý přístup.

2. Redis : Redis je běžně používaný balíček pythonu. redis-py 3.0 zavádí mnoho nových funkcí, ale vyžadoval několik zpětně nekompatibilních změn, které budou v procesu provedeny.

Kompatibilní řešení:
https://pypi.org/project/redis/ odtud můžeme zjistit změny a zajistit kompatibilitu kód. Vytvořili jsme vlastní metody Redis kompatibilní s oběma verzemi Redis. Jako

import six
def redis\_zadd(redis\_connection, key, **values):
"""Redis method zadd for python 2 and python 3 compatibility."""
if six.PY3:
redis\_connection.zadd(key, values)
else:
redis\_connection.zadd(key, **values)def redis\_zincrby(redis\_connection, key, value, score):
"""Redis method zincrby for python 2 and python 3 compatibility."""
if six.PY3:
redis\_connection.zincrby(key, score, value)
else:
redis\_connection.zincrby(key, value, score)

3. django-cacheops : Cacheops je úhledná aplikace, která podporuje automatické nebo manuální ukládání do mezipaměti dotazů a automatické znehodnocení řízené granulárními událostmi. Tam je gotcha, zatímco Django cacheops ukládá hodnoty do Redis, což z nich dělá objekt okurky.

V pythonu 2 existují 3 různé protokoly (0, 1, 2) a výchozí je 0.V pythonu 3 existuje 5 různých protokolů (0, 1, 2, 3, 4) a výchozí je 3.

Pickle používá výchozí protokol pickle k výpisu dat. python 3, pokud vytvoříme nálevový objekt a chceme v Pythonu 2 odlepit, nebude fungovat, protože v pythonu 2 není k dispozici pickle protokol 3

Kompatibilní řešení:
Můžeme určit parametr protokolu při vyvolání pickle.dump.
django-cacheops nemají možnost poskytnout nakládací protokol. Abychom to vyřešili, použili jsme opičí záplaty.

import cacheops
from cacheops.cross import pickle
@cacheops.redis.handle\_connection\_failure
def
\_custom\_cacheops\_redis\_set(self, cache\_key, data, timeout=None):
pickled\_data = pickle.dumps(data, 2) # Protocol 2 is valid in both Python version.
if timeout is not None:
self.conn.setex(cache\_key, timeout, pickled\_data)
else:
self.conn.set(cache\_key, pickled\_data)cacheops.RedisCache.set = \_custom\_cacheops\_redis\_set

Jak již bylo uvedeno výše, jak Vyzvednout python 2 nakládaný objekt do pythonu 3 . Chceme získat data v pythonu 3, můžeme čelit UnicodeDecodeError kvůli výběru provedenému v různých verzích pythonu.
toto je také tříděno pomocí opravy

import six
from cacheops.simple import CacheMiss
if six.PY3:
import pickle
else:
import cPickle as pickledef unpickling\_py2\_to\_py3(pickled\_value):
"""Unpickling python 2 pickled in to python 3."""
if six.PY3:
try:
value = pickle.loads(pickled\_value)
except UnicodeDecodeError:
value = pickle.loads(pickled\_value, encoding="latin1")
else:
value = pickle.loads(pickled\_value)
return valuedef
\_custom\_cacheops\_redis\_get(self, cache\_key):
data = self.conn.get(cache\_key)
if data is None:
raise CacheMiss
return unpickling\_py2\_to\_py3(data)cacheops.RedisCache.get = \_custom\_cacheops\_redis\_get

4. django-redis-cache : máme metodu pro mazání klíčů na základě vzoru. V Pythonu 2 používáme verzi

1.6.5 klíčové vyhledávání / mazání probíhalo bez skenování, ale pro python 3 jsme verzi aktualizovali na

2.1 kde se vyhledávání vzorů děje pomocí skenování Redis, je to tak pomalé. To způsobilo problém. Problém s centrem Git .

Kompatibilní řešení:
Problém jsme roztřídili pomocí starého způsobu mazání vzorů. místo volání cache.delete\_pattern(pattern) děláme

pattern = cache.make\_key(pattern)
keys = cache.master\_client.keys(pattern)
if len(keys):
cache.master\_client.delete(*keys)

Co bude dál

V (část druhá) ) tohoto blogu, kde prozkoumáme, jak přejít na python 3, aniž bychom museli prostoje s probíhajícím vývojem.

Napsat komentář

Vaše e-mailová adresa nebude zveřejněna. Vyžadované informace jsou označeny *