Migrating to Python 3: The HealthifyMe Experience.

( 18. nov 2020)

Hei !, denne bloggen handler om hvordan du gjør Python 3-migrering av et eldre monolitprosjekt uten å ta nedetid. Vi har to deler til dette.

Første blogg: Denne bloggen har informasjon om hvorfor du bør migrere til Python 3 og sjeldne tilfelle forskjeller i Python 2 og Python 3 og Kompatible løsninger for de .

(Andre blogg): Hvordan HealthifyMe flyttet til Python 3 uten å ha nedetid med pågående utvikling.

Introduksjon

Python er det primære kodningsspråket på HealthifyMe. Vi bruker Python 3 for alle de nye prosjektene, men vårt eldre prosjekt kjørte fortsatt med Python 2.7 med Django (1.11). Vårt monolitprosjekt er 7 år gammelt med mer enn 2 millioner linjer med python-kode. Noen av grunnene til at vi flyttet til python 3:

  1. Støtten falt for python 2: Etter januar 2020 vil ikke støtte for Python 2 bli gitt.
  2. Python 3 Adopsjon: Som de fleste selskapene, vedtar open source-prosjektet allerede Python 3. Nye biblioteker, verktøy, moduler, rammeverk vil bli skrevet i Python 3.
    Det eksisterende open source-prosjektet migrerte også og ny funksjon, reparasjoner, sikkerhetsforbedring kommer i Python 3.
  3. Programvaresikkerhet: Å sikre programvaresikkerhet er et lovlig krav, spesielt når du har med personlig informasjon å gjøre i GDPR. Hvis du holder programvaren din oppdatert, er det forståelig nok veldig høyt blant sikkerhetsrutiner, og en utdatert Python-tolk vil være alt annet enn garantert å dukker opp som et rødt flagg under en sikkerhetsrevisjon. Sikkerhetstestingsverktøy som Black Duck har rapportert om mange sårbarheter, utnyttelser og sikkerhetsproblemer i Python 2. Og de fleste av dem er løst i de siste Python 3-versjonene (3.7.5, 3.8.0).
  4. Ytelse og nye funksjoner : Python 3 har bedre ytelse enn python 2. Det nye programvareproduktet som bruker python 3, har rapportert en 12\% CPU-ytelsesforbedring og en 30\% forbedring i bruk av minneressurser .
    Python 3 gir oss også:
    * opprinnelig asynkron programmering.
    * skriv merknader
    du kan bruke til å forbedre analysen av statisk kode og den generelle brukervennligheten.
    * kjedede unntak , som er spesielt nyttige når du feilsøker.
    * andre nyttige funksjoner som gjør koding i Python mye mer effektiv.

Denne listen fortsetter, og den vil helt sikkert vokse med hver nye Python 3-utgivelse.

Dette er noen grunner til at vi migrerer til python 3. Vi har omtrent 12–15 utviklerbackend-team. Dette er vårt baseprosjekt med 7–10 build-utgivelser daglig, med feilrettinger, forbedringer, sikkerhetsrettinger, utvikling av nye funksjoner osv. Vår viktigste utfordring var ikke å stoppe den nåværende utviklingsprosessen. Vi måtte sørge for at prosjektet vårt er kompatibelt med Python 3.X uten å bryte kompatibiliteten med Python 2.X. Migreringen ble ledet av en utvikler (selvfølgelig, med hjelp fra andre utviklere).

I denne artikkelen vil vi prøve å illustrere alle de forskjellige trinnene som er tatt, problemer som står overfor, og noen få detaljer. .

Forskjell mellom python 2 og python 3.

Vi kan finne ut den vanlige forskjellen her:

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

Nå vil vi kort beskrive noen av de sjeldne og kanttilfellene vi møtte da vi startet migrasjonen.

Datatype Sammenligning:

1. I Python 2 vil sammenligning av en integer til none fungere, slik at none blir ansett som mindre enn et helt tall, til og med negative.Du kan også sammenligne none med string, string med int.
Ulike datatypesammenligning er ikke tillatt i python 3.
Dette er kjent av de fleste av utvikleren, men vi møtte edge case der NotImplementedType ble sammenlignet med int og dette fungerer ikke i python 3.

Kodebit:

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

Hvis vi lagrer dette med phone\_number\_validation.py og kjør koden:

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

Kompatibel Løsning:
Vi må sjekke om base.PHONE\_NO\_SIZE\_LIMIT er implementert eller ikke, hvis ikke så må vi håndtere det. Som:

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 Math-funksjoner:
Sammenligning kan ikke arbeide i int til none, int til str, none til str i python 3, så matematikkfunksjonen min og Max har også endret seg i 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"

Kompatibel løsning:
1. Listen skal ha data av en type enten string eller int
2. Med en type hvis none er der, kan vi ha vår egen metode for å håndtere dette som.

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

Hex-koding / dekoding

Mens vi koder en streng i python 2 hvis vi følger denne .encode(‘hex’) Dette fungerer ikke i 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

Kompatibel løsning:
Vi bør bruke codecs for Python 3 og 2. I python 3 er både inngang og utgang byte datatype.

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

Store streng:

string.uppercase fungerer ikke i 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"

Kompatibel løsning:
Bruk ascii\_uppercase

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

hasattr ():

hasattr() presenterer en enorm kantstilfelle når du skriver Python 2 og 3 kompatibel kode. hasattr() sjekker for eksistensen av et attributt ved å prøve å hente det.

Python 2

hasattr ( objekt , navn )
Argumentene er et objekt og en streng. Resultatet er sant hvis strengen er navnet på et av objektets attributter, Falsk hvis ikke. (Dette implementeres ved å ringe getattr (objekt, navn) og se om det gir et unntak eller ikke.)

Python 3

hasattr ( objekt , navn ) Argumentene er et objekt og en streng. Resultatet er sant hvis strengen er navnet på et av objektets attributter, Falsk hvis ikke. (Dette implementeres ved å ringe getattr (objekt, navn) og se om det reiser AttributeError eller ikke.)

Vennligst finn flere detaljer her: (https://medium.com/@k.wahome/python-2-vs-3-hasattr-behaviour-f1bed48b068)

Eksempelkode

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

Lagre kodebiten ovenfor med 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"

Kompatibel løsning:
For å gjøre kodekompatibilitet i Python 2 og Python 3, må vi endre \_\_getattr\_\_ -funksjonen som nedenfor.

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

Dikt blir ordnet i python 3:

Fra Python 3.6+ er ordboken nå standard innsetting bestilt .

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

Kompatibel kode:
Ideelt sett burde dette ikke bryte applikasjonskoden, fordi den endret til uordnet til bestilt dict. Hvis vi fortsatt trenger det samme resultatet i begge pythonversjonene (rekkefølge er viktig, testtilfelle mislykkes), må vi bruke OrderedDict for å holde begge språkutdataene like.

Hashing:

I python 2 kan inngang skrives unicode og str, men i Python 3 trenger den 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

Kompatibel løsning:
Python 3 trenger bytes som inngang, der python 2 fungerer med både type unicode og str.

# 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\_\_ operatøroverbelastning:

I python 3 er \_\_div\_\_-operator synes ikke å eksistere, siden den ble erstattet av \_\_truediv\_\_ helt.

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"

Kompatibel løsning:
I Python 3.x må vi overbelaste operatørene \_\_truediv\_\_, ikke \_\_div\_\_ operatør. For å gjøre kode kompatibel, må vi beholde begge metodene som:

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

Base64-koding:

Vi gjør base64-kodingen ved hjelp av base64.b64encode(). I Python 2 kan vi sende unicode eller str som inngang. Men i python 3 trenger den bytes som 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"

Kompatibel løsning:
Vi kan ha vår egen metode for base64-koding, og den kan ha input string og bytes begge deler.

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

Innebygd rund metode:

Python2 : Avrunding er gjort borte fra (slik at for eksempel runde (0.5) er 1.0 og runde (-0.5) er -1.0)
Python 3 : Avrunding er gjort mot det jevne valget (så for eksempel er både runde (0.5) og runde (-0.5) 0, og runde (1.5) er 2).

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

Kompatibel løsning:
Vi opprettet o din egen runde metode som fungerer på samme måte som Python 2 runde i python 3 også.

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. pakkeinngangstype:

inngangstypen er str, Python 3 skal det være 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

Kompatibel løsning:
encode inngangen .

listeforståelsesvariabel scope endringer:

I Python 3 bruker listeforståelsesvariabelen vedlagte omfang at du ikke får tilgang til planvariabelen utenfor i funksjonen, dette var ikke tilfelle i 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

Kompatibel løsning:
Vi bør unngå slike tilfeller. For den andre metoden (two\_or\_three\_with\_method) må vi passere x-verdien som argument.

math.floor og math.ceil datatype endret:

I python 2 returnerer gulvet og taket flytedatatypen, men i python 3 returnerer den int datatypen.

# 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

Kompatibel løsning:
Vi kan lage utdata som en flyt i python 3. Det vil ikke påvirke python 2, float(floor(4.345))

Fjern markering av en python 2 syltet objekt i python 3:

Som nevnt gjør vi kode kompatibel for å kjøre begge pythonversjonene. vi sto overfor et problem mens objektet er syltet i Python 2, men vi kan ikke fjerne dem i python 3. Dette kan også skje for Redis Pickled-bufrede objekter.

pickle.load(), standard er å prøve å dekode alle strengdata som ASCII, og at dekoding mislykkes. Se

pickle.load() dokumentasjon :

Valgfrie søkeordargumenter er fix\_imports , koding og feil , som brukes til å kontrollere kompatibilitetsstøtte for pickle stream generert av Python 2. Hvis fix\_imports stemmer, pickle vil prøve å kartlegge de gamle Python 2-navnene til de nye navnene som brukes i Python 3. kodingen og feilene forteller pickle hvordan man dekoder 8-biters strenginstanser syltet av Python 2; disse er henholdsvis ‘ASCII’ og ‘streng’. kodingen kan være byte for å lese disse 8-biters strengforekomster som byteobjekter.

https : // stackoverflow.no / spørsmål / 28218466 / unpickling-a-python-2-object-with-python-3

Kompatibel løsning:
Vi kan bruke metoden nedenfor til å fjerne markering av objekter.

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

Rettelser fra tredjepartsbiblioteker:

I prosjektet vårt bruker vi mange tredjepartspakker, mens vi oppdaterer dem sto vi overfor noen kanttilfeller . Du kan hoppe over dette hvis du ikke bruker noen av dem.

  1. Django :
    a. Django migreringsfiler
    Når vi kjører Django makemigrations i python 3 så vi nye migreringsfiler. men det samme skjedde ikke for python 2. Det kan være flere grunner til dette.

b prefiks: Ved makemigrering genereres de fleste av de nye filene uten å ha b prefix for strengverdier. Dette skyldes at alle strenglitteraler som brukes i modellene og feltene dine (f.eks « verbose\_name«, « related\_name«, etc.), må være konsekvent enten byte-strenger eller tekst (unicode) -strenger i både Python 2 og 3.

Kompatibel løsning: Den enkleste måten å oppnå en migrering for ny migrering, legg til from \_\_future\_\_ import unicode\_literal til alle modellfiler. For eksisterende migreringsfiler kjører vi enten makemigration, og det skal bare skje en gang, eller så kan vi fjerne b prefix fra eksisterende migreringsfiler.

Valgfelt: I modeller bruker vi dict.items (). Som vi vet nå at dict blir ordnet i python 3 slik at verdiene returneres fra dict.items () vil være forskjellige i Python 2 og Python 3.

Kompatibel løsning: For å gjøre kompatible for begge sorterte vi (dict. items ()) og generert migreringsfil som nå er kompatibel for begge pythonversjonene.

b. Visning av objekt i administrasjonskonsollen
For python 3 i administrasjonskonsollen kan vi se Objekt som feltverdi i stedet for en streng. hvis skjedde fordi modellklassen vår har metode.

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

Vi kan ha str-metoden \_\_str\_\_, og det fungerer for både Python 2 og Python 3. Men den vil mislykkes hvis str-versjonen har ikke-ASCII-tegn.

Kompatibel Løsning: Fikk løsningen fra her , la til @python\_2\_unicode\_compatible dekoratør for modeller og modifiserte \_\_unicode\_\_ til \_\_str\_\_.

c . Django søkeobjekt skiver
Django søkeobjekt har skiveregenskap for å hente poster. for Django versjon (1.11), python 2 støtter den kutting for int og str. I Python 3 støtter den bare kutting gjennom 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]: ]>

Kompatibel løsning: Unngå strengskjæring som uansett ikke er en god tilnærming.

2. Redis : Redis er vanlig brukt python-pakke. redis-py 3.0 introduserer mange nye funksjoner, men krevde at flere tilbakekompatible endringer måtte gjøres i prosessen.

Kompatibel løsning:
https://pypi.org/project/redis/ herfra kan vi finne ut endringene og hvordan vi kan gjøre kompatible kode. Vi laget våre egne Redis-metoder som er kompatible med begge Redis-versjonene. Som

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 er en glatt app som støtter automatisk eller manuell hurtigbufring av queryset og automatisk granulær hendelsesdrevet ugyldighet. Det er en gotcha mens Django cacheops lagrer verdier til Redis, det gjør dem til et pickle-objekt.

I python 2 er det 3 forskjellige protokoller (0, 1, 2) og standard er 0.I python 3 er det 5 forskjellige protokoller (0, 1, 2, 3, 4) og standard er 3.

Pickle bruker standard pickle-protokollen for å dumpe data. Så i python 3 hvis vi lager et pickle-objekt og ønsker å fjerne markeringen i Python 2, vil ikke fungere fordi pickle-protokoll 3 ikke er tilgjengelig i python 2.

Kompatibel løsning:
Vi kan spesifisere protokollparameteren når vi påkaller pickle.dump.
django-cacheops har ikke et alternativ for å gi pickle-protokollen. Vi brukte monkey patch for å ordne opp i dette.

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

Også som nevnt ovenfor hvordan Fjern markeringen av en python 2 syltet objekt i python 3 . Vi ønsker å få data i python 3, vi kan møte UnicodeDecodeError på grunn av plukking gjort i forskjellige pythonversjoner.
dette er også sortert ved hjelp av lapping

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 : vi har en metode for å slette nøkler basert på mønsteret. I Python 2 bruker vi versjonen

1.6.5 nøkkelen som ble søkt / slettet skjedde uten skanning, men for python 3 oppdaterte vi versjonen til

2.1 der mønstersøket som skjer ved hjelp av Redis-skanning, gjør det så sakte. Dette forårsaket problemet. Git hub-problem for dette .

Kompatibel løsning:
Vi sorterte problemet ved hjelp av den gamle måten å slette mønster på. i stedet for å ringe cache.delete\_pattern(pattern) gjør vi

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

Hva er neste

I (del to ) av denne bloggen, der vi vil utforske hvordan vi kan gå til python 3 uten å ha nedetid med pågående utvikling.

Legg igjen en kommentar

Din e-postadresse vil ikke bli publisert. Obligatoriske felt er merket med *