Migrarea la Python 3: The HealthifyMe Experience.

( 18 noiembrie 2020)

Bună ziua !, acest blog este despre cum să faci migrarea Python 3 a unui proiect moștenit de moștenire fără a lua nicio perioadă de inactivitate. Avem 2 părți pentru acest lucru.

Primul blog: acest blog conține informații despre motivul pentru care ar trebui să migrați către Python 3 și diferența de cazuri rare în Python 2 și Python 3 și Soluții compatibile pentru cei .

(Al doilea blog): Cum HealthifyMe s-a mutat în Python 3 fără a avea timp de oprire cu dezvoltarea continuă.

Introducere

Python este limbajul de codare principal la HealthifyMe. Folosim Python 3 pentru toate noile proiecte, dar proiectul nostru vechi era încă în desfășurare cu Python 2.7 cu Django (1.11). Proiectul nostru monolitic are 7 ani, cu peste 2 milioane de linii de cod Python. Câteva dintre motivele pentru care am trecut la python 3:

  1. Asistența a renunțat la python 2: După ianuarie 2020, nu va fi oferit suport pentru Python 2.
  2. Adopție Python 3: majoritatea companiilor, proiectul open-source adoptă deja Python 3. Noi biblioteci, instrumente, module, cadre vor fi scrise în Python 3.
    Proiectul open-source existent a migrat și o nouă caracteristică, remedieri, îmbunătățirea securității vin în Python 3.
  3. Securitate software: Asigurarea securității software este o cerință legală, mai ales atunci când aveți de-a face cu informații personale în domeniul GDPR. Menținerea software-ului la zi se înțelege foarte bine printre cele mai bune practici de securitate, și un interpret Python învechit ar fi garantat apare ca un steag roșu în timpul unui audit de securitate. Instrumentele de testare a securității, cum ar fi Black Duck, au raportat multe vulnerabilități, exploatări și probleme de securitate în Python 2. Și cele mai multe dintre ele sunt remediate în cele mai recente versiuni Python 3 (3.7.5, 3.8.0).
  4. Performanță și funcții noi : Python 3 are performanțe mai bune decât python 2. Noul produs software care folosește python 3 a raportat o creștere a performanței procesorului cu 12\% și o îmbunătățire cu 30\% a utilizării resurselor de memorie .
    de asemenea, python 3 ne oferă:
    * programare nativă asincronă.
    * adnotări de tip
    pe care le puteți utiliza pentru a îmbunătăți analiza statică a codului și utilizabilitatea generală.
    * excepții înlănțuite , care sunt utile în special la depanare.
    * alte caracteristici utile care fac codificarea în Python mult mai eficientă.

Această listă continuă și este sigur că va crește cu fiecare nouă versiune Python 3.

Acestea sunt câteva motive pentru a migra către Python 3. Avem aproximativ 12-15 echipe de backend pentru dezvoltatori. Acesta este proiectul nostru de bază cu versiunea de versiuni 7-10 zilnic, cu remedieri de erori, îmbunătățiri, remedieri de securitate, dezvoltare de caracteristici noi etc. Principala noastră provocare a fost să nu oprim procesul de dezvoltare actual. A trebuit să ne asigurăm că proiectul nostru este compatibil cu Python 3.X fără a rupe compatibilitatea cu Python 2.X. Migrarea a fost condusă de un dezvoltator (bineînțeles, cu ajutorul altor dezvoltatori).

În acest articol, vom încerca să ilustrăm toți pașii parcurși, problemele întâmpinate și câteva detalii suplimentare. .

Diferența dintre python 2 și python 3.

Putem afla diferența comună aici:

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

Acum vom descrie pe scurt câteva dintre cazurile rare și marginale pe care le când ne-am confruntat cu migrarea.

Comparație tip de date:

1. În Python 2, compararea unui integer cu none va funcționa, astfel încât none este considerat mai puțin decât un număr întreg, chiar negativ.De asemenea, puteți compara none cu string, string cu int.
Compararea diferitelor tipuri de date nu este permisă în Python 3.
Acest lucru este cunoscut de majoritatea dezvoltatorilor, dar ne-am confruntat cu un caz de margine în care NotImplementedType a fost comparat cu int și acest lucru nu va funcționa în python 3.

Fragment de cod:

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")

Dacă salvăm acest cu phone\_number\_validation.py și rulați codul:

# 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"

Compatibil Soluție:
Trebuie să verificăm dacă base.PHONE\_NO\_SIZE\_LIMIT este implementat sau nu, dacă nu, atunci trebuie să îl gestionăm. De exemplu:

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 Funcții matematice:
Comparația lucrării cantului în int la none, int la str, none la str în python 3, deci funcția matematică min și Max s-au schimbat și în python 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"

Soluție compatibilă:
1. Lista trebuie să conțină date de un singur tip, fie string sau int
2. Cu un singur tip, dacă există none, putem avea propria noastră metodă pentru a gestiona acest lucru.

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

Codificare / decodare hexagonală

În timp ce codificăm un șir în python 2 dacă urmăm acest .encode(‘hex’) Acest lucru nu va funcționa în Python 3

# 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

Soluție compatibilă:
Ar trebui să folosim codecs pentru Python 3 și 2. În intrarea și ieșirea Python 3 ambele sunt byte tip de date.

# 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"

Șir majuscul:

string.uppercase nu funcționează în Python 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"

Soluție compatibilă:
Utilizați ascii\_uppercase

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

hasattr ():

hasattr() prezintă un caz de margine imens atunci când scrie Python 2 și 3 cod compatibil. hasattr() verifică existența unui atribut încercând să îl recupereze.

Python 2

hasattr ( obiect , nume )
Argumentele sunt un obiect și un șir. Rezultatul este adevărat dacă șirul este numele unuia dintre atributele obiectului, fals dacă nu. (Acest lucru este implementat apelând getattr (obiect, nume) și verificând dacă creează sau nu o excepție.)

Python 3

hasattr ( object , nume ) Argumentele sunt un obiect și un șir. Rezultatul este adevărat dacă șirul este numele unuia dintre atributele obiectului, fals dacă nu. (Acest lucru este implementat apelând getattr (obiect, nume) și verificând dacă creează un AttributeError sau nu.)

Găsiți mai multe detalii aici: (https://medium.com/@k.wahome/python-2-vs-3-hasattr-behaviour-f1bed48b068)

Exemplu de cod

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")

Salvați fragmentul de mai sus cu 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"

Soluție compatibilă:
Pentru a face compatibilitatea codului în Python 2 și Python 3, trebuie să schimbăm funcția \_\_getattr\_\_ ca mai jos.

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

Dictul este ordonat în python 3:

De la Python 3.6+, dicționarul este acum implicit inserare comandată .

# 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}

Cod compatibil:
În mod ideal, acest lucru nu ar trebui să rupă codul aplicației, deoarece acesta s-a schimbat în dictare neordonată. Dacă totuși, avem nevoie de același rezultat în ambele versiuni python (ordinea este importantă, testul nu reușește) trebuie să folosim OrderedDict pentru a păstra ambele ieșiri de limbă la fel.

Hashing:

În Python 2 intrarea poate fi tastată unicode și str, dar în Python 3 are nevoie de 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

Soluție compatibilă:
Python 3 are nevoie de bytes ca intrare, unde python 2 funcționează cu tipul unicode și str ambele.

# 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\_\_ supraîncărcare operator:

În Python 3, Operatorul \_\_div\_\_ pare să nu existe deoarece a fost înlocuit cu \_\_truediv\_\_ în întregime.

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"

Soluție compatibilă:
În Python 3.x, trebuie să supraîncărcăm operatorii \_\_truediv\_\_, nu \_\_div\_\_ operator. Pentru a face codul compatibil, trebuie să păstrăm ambele metode, cum ar fi:

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

Codificare Base64:

Facem codificarea base64 folosind base64.b64encode(). În Python 2 putem trece unicode sau str ca intrare. Dar în Python 3 are nevoie de bytes ca intrare.

# 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"

Soluție compatibilă:
Putem avea propria noastră metodă de codificare base64 și poate avea input string și bytes ambele.

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

Metodă rotundă încorporată:

Python2 : rotunjirea se face departe de (deci. de exemplu, rotund (0,5) este 1,0 și rotund (-0,5) este -1,0)
Python 3 : rotunjirea se face spre alegerea uniformă (deci, de exemplu, atât rotund (0,5) cât și rotund (-0,5) sunt 0, iar rotund (1,5) este 2).

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

Soluție compatibilă:
Am creat o Este propria metodă rotundă care funcționează la fel ca și rotația Python 2 și în python 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

struct. tip de intrare pachet:

tipul de intrare este str, Python 3 ar trebui să fie 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

Soluție compatibilă:
encode intrarea .

variabilă de înțelegere a listei scope modificări:

În Python 3, variabila de înțelegere a listei utilizează domeniul de aplicare înconjurător înseamnă că nu veți putea accesa variabila de plan în afara funcției nu este cazul în Python 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

Soluție compatibilă:
Ar trebui să evităm astfel de cazuri. Pentru a doua metodă (two\_or\_three\_with\_method) trebuie să trecem valoarea x ca argument.

math.floor și math.ceil returnează tipul de date modificat:

În Python 2 podeaua și plafonul returnează tipul de date plutitoare, dar în Python 3 returnează tipul de date int.

# 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

Soluție compatibilă:
Putem faceți ieșirea ca float în python 3. Nu va avea impact asupra python 2, float(floor(4.345))

Unpickling a python 2 obiect murat în python 3:

Ca menționare, facem codul compatibil pentru a rula ambele versiuni python. ne-am confruntat cu o problemă în timp ce obiectul este murat în Python 2, dar nu suntem în măsură să dezlipim în Python 3. Acest lucru se poate întâmpla și pentru obiectele cache Redis Pickled.

pickle.load(), implicit este de a încerca și decoda toate datele șirului ca ASCII, iar decodarea eșuează. Consultați documentația

pickle.load() :

Argumentele opționale ale cuvintelor cheie sunt fix\_imports , codificare și erori , care sunt utilizate pentru a controla compatibilitatea pentru fluxul de murături generat de Python 2. Dacă fix\_imports este adevărat, pickle va încerca să mapeze vechile nume Python 2 la noile nume utilizate în Python 3. codificarea și erorile spun pickle cum să decodeze instanțele de șir de 8 biți decapate de Python 2; acestea implicit sunt „ASCII” și respectiv „stricte”. codificarea poate fi „octeți” pentru a citi aceste instanțe de șir de 8 biți ca obiecte de octeți.

https : // stackoverflow.com / questions / 28218466 / unpickling-a-python-2-object-with-python-3

Soluție compatibilă:
Putem folosi metoda de mai jos pentru a desface obiectele.

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

Remedii bibliotecă terță parte:

În proiectul nostru folosim o mulțime de pachete terță parte, în timp ce le actualizăm, ne-am confruntat cu câteva cazuri . Puteți sări peste acest lucru dacă nu utilizați niciunul dintre ele.

  1. Django :
    a. Fișiere de migrare Django
    Când rulăm Django makemigrations în Python 3 vedeam noi fișiere de migrare. dar nu s-a întâmplat același lucru pentru python 2. Pot exista mai multe motive pentru acest lucru.

b prefix: La makemigration majoritatea fișierelor noi sunt generate fără a avea b prefix pentru valorile șirului. Acest lucru se datorează faptului că toate literele șirului utilizate în modelele și câmpurile dvs. „verbose\_name“, „related\_name“ etc.), trebuie să fie în mod consecvent fie șiruri de octeți, fie șiruri de text (unicode) atât în ​​Python 2, cât și în 3.

Soluție compatibilă: Cel mai simplu mod de a realiza o migrare pentru o nouă migrare adăugați from \_\_future\_\_ import unicode\_literal la toate fișierele de modele. Pentru fișierele de migrare existente fie executăm makemigration și asta ar trebui să se întâmple o singură dată, fie putem elimina b prefix din fișierele de migrare existente.

Câmp de alegere: În modele folosim dict.items (). După cum știm acum, dict devine ordonat în python 3 astfel încât valorile revin din dict.items () vor fi diferite în Python 2 și Python 3.

Soluție compatibilă: Pentru a face compatibile ambele am sortat (dict. items ()) și fișierul de migrare generat, care este acum compatibil pentru ambele versiuni python.

b. Afișarea „ Object” în consola de administrare
Pentru python 3 în consola de administrare, putem vedea obiectul ca valoare câmpuri în loc de șir. dacă se întâmplă deoarece clasa noastră model are metodă.

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

Putem avea metoda str \_\_str\_\_ și aceasta funcționează atât pentru Python 2, cât și pentru Python 3. Dar va eșua dacă versiunea str are caractere non-ASCII.

Compatibil Soluție: Am primit soluția de la aici , a adăugat decoratorul @python\_2\_unicode\_compatible pentru modele și \_\_unicode\_\_ modificat la \_\_str\_\_.

c . Felierea obiectului de interogare Django
obiectul de interogare Django are capacitatea de a tranșa înregistrări. pentru versiunea Django (1.11), python 2 acceptă felierea pentru int și str. În Python 3 acceptă doar tranșarea prin 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]: ]>

Soluție compatibilă: Evitați felierea șirurilor care oricum nu este o abordare bună.

2. Redis : Redis este pachetul Python folosit în mod obișnuit. redis-py 3.0 introduce multe funcții noi, dar a necesitat mai multe modificări incompatibile înapoi în proces.

Soluție compatibilă:
https://pypi.org/project/redis/ de aici putem afla modificările și cum să facem compatibile cod. Am creat propriile noastre metode Redis compatibile cu ambele versiuni Redis. Îmi place

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 este o aplicație simplă care acceptă stocarea în cache a querysetului automat sau manual și invalidarea automată granulară determinată de evenimente. Există un gotcha în timp ce cache-urile Django stochează valori la Redis, acestea le fac un obiect de murătură.

În Python 2 există 3 protocoale diferite (0, 1, 2) și implicit este 0. În Python 3 există 5 protocoale diferite (0, 1, 2, 3, 4) și implicit este 3.

Pickle folosește protocolul implicit de pickle pentru a arunca date. python 3 dacă facem un obiect de murătură și dorim să dezamorsăm în Python 2 nu va funcționa deoarece protocolul de murare 3 nu este disponibil în Python 2.

Soluție compatibilă:
Putem specifica parametrul protocolului atunci când invocăm pickle.dump.
django-cacheops nu au o opțiune pentru a furniza protocolul de murături. Am folosit patch-uri de maimuță pentru a rezolva acest lucru.

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

De asemenea, ca mențiune mai sus cum să Desfaceți un python 2 obiect murat în python 3 . Vrem să obținem date în python 3, ne putem confrunta cu UnicodeDecodeError datorită selectării efectuate în diferite versiuni python.
aceasta este, de asemenea, sortată folosind patch-uri

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 : avem o metodă pentru ștergerea tastelor pe baza modelului. În Python 2 folosim versiunea

1.6.5 cheia de căutare / ștergere se desfășura fără scanare, dar pentru python 3 am actualizat versiunea la

2.1 în cazul în care căutarea de tipare se întâmplă utilizând scanarea Redis, aceasta o face atât de lentă. Aceasta a cauzat problema. Problemă Git hub pentru aceasta .

Soluție compatibilă:
Am sortat problema utilizând vechiul mod de ștergere a modelului. în loc să apelăm cache.delete\_pattern(pattern) facem

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

Ce urmează

În (partea a doua) ) din acest blog, unde vom explora cum să trecem la Python 3 fără a avea timp de inactivitate cu dezvoltarea continuă.

Lasă un răspuns

Adresa ta de email nu va fi publicată. Câmpurile obligatorii sunt marcate cu *