Migrera till Python 3: HealthifyMe-upplevelsen.

Publicerad

( 18 nov 2020)

Hej !, den här bloggen handlar om hur man gör Python 3-migrering av ett äldre monolitprojekt utan att ta stillestånd. Vi har två delar för detta.

Första bloggen: Denna blogg har information om varför du ska migrera till Python 3 och sällsynta skillnader i Python 2 och Python 3 och Kompatibla lösningar för de .

(Andra bloggen): Hur HealthifyMe flyttade till Python 3 utan att ha stillestånd med pågående utveckling.

Introduktion

Python är det primära kodningsspråket på HealthifyMe. Vi använder Python 3 för alla nya projekt men vårt äldre projekt körs fortfarande med Python 2.7 med Django (1.11). Vårt monolitprojekt är 7 år gammalt med mer än 2 miljoner rader med pythonkod. Några av anledningarna till att vi flyttade till python 3:

  1. Stödet för python 2: Efter januari 2020 tillhandahålls inte stöd för Python 2.
  2. Python 3 Antagande: Som de flesta företag, öppen källkodsprojekt antar redan Python 3. Nya bibliotek, verktyg, moduler, ramar kommer att skrivas i Python 3.
    Det befintliga open source-projektet migrerade också och ny funktion, korrigeringar, säkerhetsförbättring kommer i Python 3.
  3. Programvarusäkerhet: Säkerställa att mjukvarusäkerhet är ett lagligt krav, särskilt när du har att göra med personlig information inom ramen för GDPR. Att hålla din programvara uppdaterad förstås rankas mycket högt bland bästa säkerhetsmetoder, och en föråldrad Python-tolk skulle bara vara garanterad visas som en röd flagga under en säkerhetsgranskning. Säkerhetstestverktyg som Black Duck har rapporterat om många sårbarheter, exploateringar och säkerhetsproblem i Python 2. Och de flesta av dem är fixade i de senaste Python 3-versionerna (3.7.5, 3.8.0).
  4. Prestanda och nya funktioner : Python 3 har bättre prestanda än python 2. Den nya programvaruprodukten som använder python 3 har rapporterat en 12\% CPU-prestandaförbättring och en 30\% förbättring av användning av minnesresurser .
    Python 3 ger oss också:
    * inbyggd asynkron programmering.
    * typnoteringar
    du kan använda för att förbättra statisk kodanalys och övergripande användbarhet.
    * kedjade undantag , som är särskilt användbara vid felsökning.
    * andra användbara funktioner som gör kodning i Python mycket effektivare.

Den här listan fortsätter och den kommer säkert att växa med varje ny Python 3-utgåva.

Det här är några skäl för oss att migrera till python 3. Vi har cirka 12–15 utvecklarbackend-team. Detta är vårt basprojekt med 7–10 versioner dagligen, med buggfixar, förbättringar, säkerhetsfixar, utveckling av nya funktioner etc. Vår största utmaning var inte att stoppa den nuvarande utvecklingsprocessen. Vi var tvungna att se till att vårt projekt är kompatibelt med Python 3.X utan att bryta kompatibiliteten med Python 2.X. Migreringen leddes av en utvecklare (naturligtvis med hjälp av andra utvecklare).

I den här artikeln ska vi försöka illustrera alla steg som har tagits, problem som står inför och några fler detaljer .

Skillnad mellan python 2 och python 3.

Vi kan ta reda på den vanliga skillnaden här:

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

Nu kommer vi i korthet att beskriva några av de sällsynta och kantfall som vi inför när vi startade vår migrering.

Jämförelse av datatyp:

1. I Python 2 fungerar en integer med none så att none anses vara mindre än ett heltal, även negativa.Du kan också jämföra none med string, string med int.
Olika datatypjämförelser är inte tillåtna i python 3.
Detta är känt av de flesta utvecklare men vi mötte kantfall där NotImplementedType jämfördes med int och detta fungerar inte i python 3.

Kodavsnitt:

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

Om vi ​​sparar detta med phone\_number\_validation.py och kö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åste kontrollera om base.PHONE\_NO\_SIZE\_LIMIT är implementerat eller inte, om inte måste vi hantera det. Gilla:

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-funktioner:
Jämförelse kan inte arbeta i int till none, int till str, none till str i python 3, så matematikfunktionen min och Max har också ändrats 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. Listan ska ha data av en typ antingen string eller int
2. Med en typ om none är där kan vi ha vår egen metod för att hantera detta 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-kodning / avkodning

Medan vi kodar en sträng i python 2 om vi följer detta .encode(‘hex’) Detta fungerar inte 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 borde använda codecs för Python 3 och 2. I python 3 är in- och utdata båda byte datatyp.

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

Stora versaler:

string.uppercase fungerar inte 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:
Använd ascii\_uppercase

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

hasattr ():

hasattr() presenterar ett enormt kantfall när du skriver Python 2 och 3 kompatibel kod. hasattr() kontrollerar om det finns ett attribut genom att försöka hämta det.

Python 2

hasattr ( objekt , namn )
Argumenten är ett objekt och en sträng. Resultatet är sant om strängen är namnet på ett av objektets attribut, Falskt om inte. (Detta implementeras genom att anropa getattr (objekt, namn) och se om det ger ett undantag eller inte.)

Python 3

hasattr ( objekt , namn ) Argumenten är ett objekt och en sträng. Resultatet är sant om strängen är namnet på ett av objektets attribut, Falskt om inte. (Detta implementeras genom att anropa getattr (objekt, namn) och se om det ger upp en AttributeError eller inte.)

Mer information finns här: (https://medium.com/@k.wahome/python-2-vs-3-hasattr-behaviour-f1bed48b068)

Exempelkod

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

Spara ovanstående kod 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:
För att göra kodkompatibilitet i Python 2 och Python 3 måste vi ändra \_\_getattr\_\_ -funktionen som nedan.

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

Dikt ordnas i python 3:

Från Python 3.6+ är ordlistan nu standard infogning beställt .

# 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 kod:
Helst bör detta inte bryta applikationskoden, eftersom den ändrades till oordnad till beställd dikt. Om vi ​​fortfarande behöver samma resultat i båda pythonversionerna (ordning är viktig, testfall misslyckas) måste vi använda OrderedDict för att hålla båda språken samma.

Hashing:

I python 2 kan ingång skrivas unicode och str, men i Python 3 behöver det 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 behöver bytes som inmatning, där python 2 fungerar med både typ unicode och 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örsöverbelastning:

I python 3 är \_\_div\_\_-operatören verkar inte existera eftersom den ersattes helt av \_\_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"

Kompatibel lösning:
I Python 3.x måste vi överbelasta \_\_truediv\_\_ -operatorerna, inte \_\_div\_\_ operatör. För att göra kodkompatibel måste vi behålla båda metoderna 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-kodning:

Vi gör bas64-kodningen med base64.b64encode(). I Python 2 kan vi skicka unicode eller str som ingång. Men i python 3 behöver den bytes som inmatning.

# 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 metod för base64-kodning och den kan ha input string och bytes båda.

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

Inbyggd rund metod:

Python2 : Avrundning är klar bort från (så till exempel är runda (0.5) 1.0 och runda (-0.5) är -1.0)
Python 3 : Avrundning görs mot det jämna valet (så är till exempel både runda (0.5) och runda (-0.5) 0 och runda (1.5) är 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 skapade o din egen runda metod som fungerar på samma sätt som Python 2-runda i python 3 också.

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. packingångstyp:

ingångstypen är str, Python 3 ska vara 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 ingången .

lista förståelse variabel scope ändringar:

I Python 3 använder listförståelsesvariabelen ett omslutande omfång betyder att du inte kommer åt planvariabeln utanför i funktionen, detta var inte fallet 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 undvika sådana fall. För den andra metoden (two\_or\_three\_with\_method) måste vi skicka x-värde som ett argument.

math.floor och math.ceil-datatypen har ändrats:

I python 2 returnerar golvet och taket flytdatatyp men i python 3 returnerar det 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 gör utdata som en flottör i python 3. Det påverkar inte python 2, float(floor(4.345))

Avmarkera en python 2 inlagda objekt i python 3:

Som nämns gör vi kodkompatibel för att köra båda pythonversionerna. vi stod inför ett problem medan objektet inlagdes i Python 2 men vi kan inte avmarkera i python 3. Detta kan också hända för Redis Pickled-cachade objekt.

pickle.load(), standard är att försöka avkoda all strängdata som ASCII, och avkodningen misslyckas. Se

pickle.load() dokumentation :

Valfria argument för sökord är fix\_import , kodning och fel , som används för att kontrollera kompatibilitetsstöd för pickle stream genererad av Python 2. Om fix\_imports är sant, pickle kommer att försöka mappa de gamla Python 2-namnen till de nya namnen som används i Python 3. -kodningen och -felen berättar hur man avkodar 8-bitars stränginstanser inlagda av Python 2; dessa är som standard ASCII respektive strikt. kodningen kan vara byte för att läsa dessa 8-bitars stränginstanser som byteobjekt.

https : // stackoverflow.com / frågor / 28218466 / unpickling-a-python-2-object-with-python-3

Kompatibel lösning:
Vi kan använda metoden nedan för att avmarkera objekt.

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

Tredjepartsbibliotekskorrigeringar:

I vårt projekt använder vi många tredjepartspaket, medan vi uppdaterar dem stod vi inför några kantfall . Du kan hoppa över detta om du inte använder någon av dem.

  1. Django :
    a. Django-migreringsfiler
    När vi kör Django makemigrations i python 3 såg vi nya migreringsfiler. men detsamma hände inte för python 2. Det kan finnas flera orsaker till detta.

b prefix: Vid makemigrering genereras de flesta av de nya filerna utan att ha b prefix för strängvärden. Detta beror på att alla stränglitterärer som används i dina modeller och fält (t.ex. ” verbose\_name , ” related\_name , etc.), måste vara konsekvent antingen byte strängar eller text (unicode) strängar i både Python 2 och 3.

Kompatibel lösning: Det enklaste sättet att uppnå en migrering för ny migrering lägg till from \_\_future\_\_ import unicode\_literal till alla modellfiler. För befintliga migreringsfiler kör vi antingen makemigration och det ska bara ske en gång, eller så kan vi ta bort b prefix från befintliga migreringsfiler.

Valfält: I modeller använder vi dict.items (). Som vi vet nu att dict ordnas i python 3 så att värdena returneras från dict.items () kommer att vara olika i Python 2 och Python 3.

Kompatibel lösning: För att göra kompatibla för båda sorterade vi (dict. objekt ()) och genererad migreringsfil som nu är kompatibel för båda pythonversionerna.

b. “ Objekt” visas i administratörskonsolen
För python 3 i administratörskonsolen kan vi se Objekt som fältvärde istället för en sträng. om händer eftersom vår modellklass har metod.

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

Vi kan ha str-metoden \_\_str\_\_ och det fungerar för både Python 2 och Python 3. Men det misslyckas om str-versionen har icke-ASCII-tecken.

Kompatibel Lösning: Fick lösningen från här , lade till @python\_2\_unicode\_compatible dekoratör för modeller och modifierade \_\_unicode\_\_ till \_\_str\_\_.

c . Django frågeobjekt skivning
Django frågeobjekt har kapningsförmåga att hämta poster. för Django-versionen (1.11), python 2 stöder den skivning för int och str. I Python 3 stöder den bara skärning genom 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: Undvik strängskärning som ändå inte är bra.

2. Redis : Redis är vanligt förekommande pythonpaket. redis-py 3.0 introducerar många nya funktioner men krävde flera bakåt-inkompatibla ändringar i processen.

Kompatibel lösning:
https://pypi.org/project/redis/ härifrån kan vi ta reda på ändringarna och hur vi kan göra kompatibla koda. Vi skapade våra egna Redis-metoder som är kompatibla med båda Redis-versionerna. Gilla

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 är en smidig app som stöder automatisk eller manuell cachning av queryset och automatisk granulerad händelsestyrd ogiltigförklaring. Det finns en gotcha medan Django cacheops lagrar värden i Redis, det gör dem till ett pickle-objekt.

I python 2 finns det 3 olika protokoll (0, 1, 2) och standard är 0.I python 3 finns 5 olika protokoll (0, 1, 2, 3, 4) och standard är 3.

Pickle använder standardpickprotokollet för att dumpa data. python 3 om vi gör ett pickle-objekt och vill avmarkera i Python 2 fungerar inte eftersom pickle-protokoll 3 inte är tillgängligt i python 2.

Kompatibel lösning:
Vi kan ange protokollparametern när vi anropar pickle.dump.
django-cacheops har inte ett alternativ att tillhandahålla pickle-protokollet. Vi använde apa-lapp för att reda ut detta.

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

Också som nämns ovan hur man Avmarkera en python 2 inlagda objekt i python 3 . Vi vill få data i python 3, vi kan möta UnicodeDecodeError på grund av plockning gjort i olika pythonversioner.
detta sorteras också med lapp

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 metod för att radera nycklar baserat på mönstret. I Python 2 använder vi versionen

1.6.5 nyckel-sökning / radering hände utan skanning och för python 3 uppdaterade vi versionen till

2.1 där mönstersökningen sker med hjälp av Redis-skanning, det gör det så långsamt. Detta orsakade problemet. Git hub-problem för detta .

Kompatibel lösning:
Vi sorterade problemet med det gamla sättet att radera mönster. istället för att ringa cache.delete\_pattern(pattern) gör vi

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

Vad är nästa

I (del två ) på den här bloggen, där vi kommer att undersöka hur man går till python 3 utan att ha stillestånd med pågående utveckling.

Lämna ett svar

Din e-postadress kommer inte publiceras. Obligatoriska fält är märkta *