Migrazione a Python 3: The HealthifyMe Experience.

Pubblicato il

( 18 novembre 2020)

Salve !, questo blog riguarda come eseguire la migrazione in Python 3 di un progetto monolite legacy senza tempi di inattività. Abbiamo 2 parti per questo.

Primo blog: questo blog contiene informazioni sul motivo per cui dovresti migrare a Python 3 e rare differenze di casi in Python 2 e Python 3 e Soluzioni compatibili per questi .

(Secondo blog): Come HealthifyMe è passato a Python 3 senza tempi di inattività con lo sviluppo in corso.

Introduzione

Python è il linguaggio di programmazione principale in HealthifyMe. Stiamo usando Python 3 per tutti i nuovi progetti, ma il nostro progetto precedente era ancora in esecuzione con Python 2.7 con Django (1.11). Il nostro progetto monolite ha 7 anni con oltre 2 milioni di righe di codice Python. Alcuni dei motivi per cui siamo passati a python 3:

  1. Supporto abbandonato per python 2: Dopo gennaio 2020, il supporto per Python 2 non verrà fornito.
  2. Adozione di Python 3: As la maggior parte delle aziende, il progetto open-source sta già adottando Python 3. Nuove librerie, strumenti, moduli, framework saranno scritti in Python 3.
    Anche il progetto open-source esistente è migrato e nuove funzionalità, correzioni, miglioramento della sicurezza stanno arrivando in Python 3.
  3. Sicurezza del software: Garantire la sicurezza del software è un requisito legale, soprattutto quando hai a che fare con informazioni personali nellambito del GDPR. Mantenere aggiornato il software è comprensibilmente ai primi posti tra le migliori pratiche di sicurezza, e un interprete Python obsoleto sarebbe quasi garantito apparire come una bandiera rossa durante un controllo di sicurezza. Strumenti di test di sicurezza come Black Duck hanno segnalato molte vulnerabilità, exploit e problemi di sicurezza in Python 2. E la maggior parte di essi è stata risolta nelle ultime versioni di Python 3 (3.7.5, 3.8.0).
  4. Prestazioni e nuove funzionalità : Python 3 ha prestazioni migliori di Python 2. Il nuovo prodotto software che utilizza python 3 ha riportato un aumento del 12\% delle prestazioni della CPU e un miglioramento del 30\% nellutilizzo delle risorse di memoria .
    inoltre, python 3 ci offre:
    * programmazione asincrona nativa.
    * digita annotazioni
    che puoi utilizzare per migliorare lanalisi statica del codice e lusabilità complessiva.
    * eccezioni concatenate , particolarmente utili durante il debug.
    * altre utili funzionalità che rendono la codifica in Python molto più efficiente.

Questo elenco continua ed è destinato a crescere con ogni nuova versione di Python 3.

Questi sono alcuni dei motivi per cui migriamo a python 3. Abbiamo circa 12-15 team di backend di sviluppatori. Questo è il nostro progetto base con versioni 7-10 build giornaliere, con correzioni di bug, miglioramenti, correzioni di sicurezza, sviluppo di nuove funzionalità ecc. La nostra sfida principale era non interrompere il processo di sviluppo corrente. Abbiamo dovuto assicurarci che il nostro progetto fosse compatibile con Python 3.X senza interrompere la compatibilità con Python 2.X. La migrazione è stata guidata da 1 sviluppatore (ovviamente con laiuto di altri sviluppatori).

In questo articolo proveremo a illustrare tutti i vari passaggi compiuti, i problemi affrontati e qualche dettaglio in più .

Differenza tra python 2 e python 3.

Possiamo scoprire la differenza comune qui:

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

Ora descriveremo in breve alcuni dei casi rari ed estremi che abbiamo affrontato quando abbiamo iniziato la nostra migrazione.

Confronto del tipo di dati:

1. In Python 2 il confronto di integer con none funzionerà, in modo tale che none sia considerato rispetto a un numero intero, anche negativo.Inoltre, puoi confrontare none con string, string con int.
Il confronto di diversi tipi di dati non è consentito in python 3.
Questo è noto alla maggior parte degli sviluppatori, ma abbiamo affrontato un caso limite in cui NotImplementedType è stato confrontato con int e questo non funzionerà in Python 3.

Snippet di codice:

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

Se salviamo questo con phone\_number\_validation.py ed esegui il codice:

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

Compatibile Soluzione:
Dobbiamo controllare se base.PHONE\_NO\_SIZE\_LIMIT è implementato o meno, altrimenti dobbiamo gestirlo. Mi piace:

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 Funzioni matematiche:
Confronto del lavoro di cant in int a Da none, int a str, none a str in python 3, quindi anche le funzioni matematiche min e Max sono state modificate in 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"

Soluzione compatibile:
1. Lelenco deve contenere dati di un tipo string o int
2. Con un tipo se none possiamo avere il nostro metodo per gestirlo in questo modo.

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

Codifica / decodifica esadecimale

Mentre codifichiamo una stringa in python 2 se seguiamo questo .encode(‘hex’) Questo non funzionerà in 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

Soluzione compatibile:
Dobbiamo usare codecs per Python 3 e 2. In Python 3 sia linput che loutput sono byte tipo di dati.

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

Stringa maiuscola:

string.uppercase non funziona in 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"

Soluzione compatibile:
Utilizza ascii\_uppercase

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

hasattr ():

hasattr() presenta un enorme caso limite durante la scrittura di Python 2 e 3 codice compatibile. hasattr() verifica lesistenza di un attributo cercando di recuperarlo.

Python 2

hasattr ( oggetto , nome )
Gli argomenti sono un oggetto e una stringa. Il risultato è True se la stringa è il nome di uno degli attributi delloggetto, False in caso contrario. (Questo è implementato chiamando getattr (oggetto, nome) e vedendo se solleva uneccezione o meno.)

Python 3

hasattr ( oggetto , nome ) Gli argomenti sono un oggetto e una stringa. Il risultato è True se la stringa è il nome di uno degli attributi delloggetto, False in caso contrario. (Questo è implementato chiamando getattr (oggetto, nome) e vedendo se solleva un AttributeError o meno.)

Puoi trovare maggiori dettagli qui: (https://medium.com/@k.wahome/python-2-vs-3-hasattr-behaviour-f1bed48b068)

Codice di esempio

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 lo snippet precedente con 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"

Soluzione compatibile:
Per rendere il codice compatibile in Python 2 e Python 3, dobbiamo cambiare la funzione \_\_getattr\_\_ come sotto.

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

Dict diventa ordinato in Python 3:

Da Python 3.6+ in poi il dizionario è ora predefinito inserimento ordinato .

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

Codice compatibile:
Idealmente, questo non dovrebbe rompere il codice dellapplicazione, perché è cambiato in unordered in dict ordinato. Se ancora, abbiamo bisogno dello stesso risultato in entrambe le versioni di python (lordine è importante, caso di test non riuscito) dobbiamo usare OrderedDict per mantenere lo stesso output di entrambi i linguaggi.

Hashing:

In Python 2 è possibile digitare linput unicode e str, ma in Python 3 necessita di 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

Soluzione compatibile:
Python 3 necessita di bytes come input, dove python 2 funziona con il tipo unicode e str entrambi.

# 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\_\_ sovraccarico delloperatore:

In python 3, il Loperatore \_\_div\_\_ sembra non esistere poiché è stato sostituito completamente da \_\_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"

Soluzione compatibile:
In Python 3.x, dobbiamo sovraccaricare gli operatori \_\_truediv\_\_, non gli \_\_div\_\_ operatore. Per rendere il codice compatibile, dobbiamo mantenere entrambi i metodi come:

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

Codifica Base64:

Eseguiamo la codifica base64 utilizzando base64.b64encode(). In Python 2 possiamo passare unicode o str come input. Ma in Python 3 ha bisogno di bytes come input.

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

Soluzione compatibile:
Possiamo avere il nostro metodo per la codifica base64 e può avere input string e bytes entrambi.

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

Metodo round integrato:

Python2 : larrotondamento è terminato lontano da (quindi, ad esempio, round (0,5) è 1,0 e round (-0,5) è -1,0)
Python 3 : larrotondamento viene eseguito verso la scelta pari (quindi, ad esempio, sia round (0,5) che round (-0,5) sono 0 e round (1.5) è 2).

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

Soluzione compatibile:
Abbiamo creato o Il tuo metodo round che funziona allo stesso modo di Python 2 round anche in 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. tipo di input del pacchetto:

il tipo di input è str, Python 3 dovrebbe essere 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

Soluzione compatibile:
encode linput .

variabile di comprensione dellelenco scope cambia:

In Python 3 la variabile di comprensione dellelenco usa lambito di inclusione significa che non sarai in grado di accedere alla variabile del piano fuori dalla funzione, questo era non è il caso di 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

Soluzione compatibile:
Dobbiamo evitare questi casi. Per il secondo metodo (two\_or\_three\_with\_method) dobbiamo passare il valore x come argomento.

math.floor e math.ceil restituiscono il tipo di dati modificato:

In python 2 il tipo di dati floor e ceil restituiscono float ma in python 3 restituisce il tipo di dati 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

Soluzione compatibile:
Possiamo crea loutput come float in python 3. Non avrà alcun impatto su python 2, float(floor(4.345))

Unpickling a python 2 ha decapitato loggetto in python 3:

Come accennato, stiamo rendendo il codice compatibile per eseguire entrambe le versioni di python. abbiamo riscontrato un problema mentre loggetto è stato decapato in Python 2 ma non siamo in grado di deselezionarlo in Python 3. Questo può accadere anche per gli oggetti memorizzati nella cache Redis Pickled.

pickle.load(), default è provare a decodificare tutti i dati di stringa come ASCII e la decodifica fallisce. Consulta la

pickle.load() documentazione :

Gli argomenti delle parole chiave facoltative sono fix\_imports , codifica e errori , che vengono utilizzati per controllare il supporto di compatibilità per il flusso pickle generato da Python 2. Se fix\_imports è vero, pickle proverà a mappare i vecchi nomi di Python 2 ai nuovi nomi usati in Python 3. La codifica e gli errori dicono a pickle come decodificare istanze di stringhe a 8 bit selezionate da Python 2; questi valori predefiniti sono rispettivamente “ASCII” e “rigoroso”. La codifica può essere “byte” per leggere queste istanze di stringa a 8 bit come oggetti byte.

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

Soluzione compatibile:
Possiamo utilizzare il metodo seguente per separare gli oggetti.

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

Correzioni di librerie di terze parti:

Nel nostro progetto stiamo utilizzando molti pacchetti di terze parti, mentre li aggiorniamo abbiamo affrontato alcuni casi limite . Puoi saltare questo passaggio se non ne stai utilizzando nessuno.

  1. Django :
    a. File di migrazione Django
    Quando abbiamo eseguito Django makemigrations in python 3, abbiamo visto nuovi file di migrazione. ma lo stesso non stava accadendo per Python 2. Ci possono essere più ragioni per questo.

b prefisso: Su makemigration la maggior parte dei nuovi file vengono generati senza b prefix per i valori di stringa. Questo perché tutti i valori letterali di stringa utilizzati nei modelli e nei campi “ verbose\_name“, “ related\_name“, ecc.), devono essere coerentemente stringhe di byte o stringhe di testo (unicode) sia in Python 2 che 3.

Soluzione compatibile: Il modo più semplice per ottenere una migrazione per una nuova migrazione aggiungi from \_\_future\_\_ import unicode\_literal a tutti i file dei modelli. Per i file di migrazione esistenti, eseguiamo makemigration e ciò dovrebbe avvenire solo una volta, oppure possiamo rimuovere b prefix dai file di migrazione esistenti.

Campo di scelta: Nei modelli utilizziamo dict.items (). Come sappiamo ora che il dict diventa ordinato in python 3 quindi i valori restituiti da dict.items () saranno diversi in Python 2 e Python 3.

Soluzione compatibile: Per renderli compatibili per entrambi abbiamo ordinato (dict. items ()) e il file di migrazione generato che ora è compatibile con entrambe le versioni di python.

b. Visualizzazione di “ Object” nella console di amministrazione
Per python 3 nella Console di amministrazione, possiamo vedere Object come valore dei campi invece di una stringa. se stava accadendo perché la nostra classe modello ha un metodo.

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

Possiamo avere il metodo str \_\_str\_\_ e funziona sia per Python 2 che per Python 3. Ma fallirà se la versione str ha caratteri non ASCII.

Compatibile Soluzione: ho ottenuto la soluzione da qui , aggiunto @python\_2\_unicode\_compatible decoratore per i modelli e \_\_unicode\_\_ modificato in \_\_str\_\_.

c . Affettamento delloggetto query Django
Loggetto query Django ha capacità di taglio per recuperare i record. per la versione Django (1.11), python 2 supporta lo slicing per int e str. In Python 3 supporta solo lo slicing attraverso lint.

# 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]: ]>

Soluzione compatibile: Evita il taglio delle stringhe che comunque non è un buon approccio.

2. Redis : Redis è un pacchetto Python di uso comune. redis-py 3.0 introduce molte nuove funzionalità ma richiedeva che nel processo fossero apportate diverse modifiche incompatibili con le versioni precedenti.

Soluzione compatibile:
https://pypi.org/project/redis/ da qui possiamo scoprire le modifiche e come renderle compatibili codice. Abbiamo creato i nostri metodi Redis compatibili con entrambe le versioni di Redis. Mi piace

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 è unapp intelligente che supporta la memorizzazione nella cache automatica o manuale dei set di query e linvalidazione granulare automatica guidata dagli eventi. Cè un trucco mentre Django cacheops memorizza i valori su Redis, li rende un oggetto pickle.

In python 2 ci sono 3 diversi protocolli (0, 1, 2) e il valore predefinito è 0.In python 3 ci sono 5 diversi protocolli (0, 1, 2, 3, 4) e il valore predefinito è 3.

Pickle usa il protocollo pickle predefinito per eseguire il dump dei dati. python 3 se creiamo un oggetto pickle e vogliamo unpickle in Python 2 non funzionerà perché il protocollo pickle 3 non è disponibile in python 2.

Soluzione compatibile:
Possiamo specificare il parametro protocol quando invociamo pickle.dump.
django-cacheops non ha unopzione per fornire il protocollo pickle. Abbiamo utilizzato le patch delle scimmie per risolvere questo problema.

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

Anche come menzionato sopra come Unpickle a python 2 oggetto pickled in python 3 . Vogliamo ottenere dati in python 3, possiamo affrontare UnicodeDecodeError a causa della selezione eseguita in diverse versioni di python.
anche questo viene ordinato utilizzando patch

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 : abbiamo un metodo per eliminare le chiavi in ​​base al modello. In Python 2 usiamo la versione

1.6.5 la chiave di ricerca / eliminazione stava avvenendo senza scansione e ma per python 3 abbiamo aggiornato la versione a

2.1 dove la ricerca del modello avviene utilizzando la scansione Redis, che lo rende così lento. Questo stava causando il problema. Problema dellhub Git per questo .

Soluzione compatibile:
Abbiamo risolto il problema utilizzando il vecchio modo di eliminare il pattern. invece di chiamare cache.delete\_pattern(pattern) stiamo facendo

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

Whats Next

In (seconda parte ) di questo blog, dove esploreremo come passare a Python 3 senza avere tempi di inattività con lo sviluppo in corso.

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *