Python 3 : HealthifyMe 환경으로 마이그레이션

( 2020 년 11 월 18 일)

안녕하세요!,이 블로그 다운 타임없이 레거시 모노리스 프로젝트의 Python 3 마이그레이션을 수행하는 방법에 관한 것입니다. 이에 대해 두 부분으로 구성되어 있습니다.

첫 번째 블로그 :이 블로그에는 Python 3으로 마이그레이션해야하는 이유와 Python 2와 Python 3 및 이를위한 호환 솔루션 .

(두 번째 블로그) : HealthifyMe가 지속적인 개발로 인해 다운 타임없이 Python 3으로 이전 한 방법

소개

Python은 HealthifyMe의 기본 코딩 언어입니다. 모든 새 프로젝트에 Python 3을 사용하고 있지만 레거시 프로젝트는 Django (1.11)와 함께 Python 2.7로 계속 실행되었습니다. 우리의 모놀리스 프로젝트는 2 백만 줄 이상의 파이썬 코드로 7 년이되었습니다. Python 3으로 이전 한 몇 가지 이유 :

  1. python 2에 대한 지원이 중단되었습니다. 2020 년 1 월 이후에는 Python 2에 대한 지원이 제공되지 않습니다.
  2. Python 3 채택 : As 대부분의 회사에서 오픈 소스 프로젝트는 이미 Python 3을 채택하고 있습니다. 새로운 라이브러리, 도구, 모듈, 프레임 워크는 Python 3으로 작성됩니다.
    기존 오픈 소스 프로젝트도 마이그레이션되고 새로운 기능, 수정, 보안 개선 Python 3에서 제공됩니다.
  3. 소프트웨어 보안 : 소프트웨어 보안 보장은 특히 다음과 같은 경우 법적 요구 사항입니다. GDPR 영역에서 개인 정보를 다루고 있습니다. 소프트웨어를 최신 상태로 유지하는 것은 당연히 보안 모범 사례 중에서 매우 높은 순위를 차지합니다. 그리고 오래된 Python 인터프리터는 보안 감사 중에 빨간색 플래그로 표시됩니다. Black Duck과 같은 보안 테스트 도구는 Python 2에서 많은 취약성, 악용 및 보안 문제를보고했으며 대부분 최신 Python 3 버전 (3.7.5, 3.8.0)에서 수정되었습니다.
  4. 성능 및 새로운 기능 : Python 3은 Python 2보다 성능이 뛰어납니다. python 3을 사용하는 새로운 소프트웨어 제품은 CPU 성능이 12 \% 향상되고 메모리 리소스 사용이 30 \% 향상되었다고보고했습니다. .
    또한 Python 3는 다음과 같은 이점을 제공합니다.
    * 네이티브 비동기 프로그래밍.
    * 유형 주석
    정적 코드 분석 및 전반적인 유용성을 개선하는 데 사용할 수 있습니다.
    * 체인 예외 는 디버깅 할 때 특히 유용합니다.
    * Python 코딩을 훨씬 더 효율적으로 만드는 기타 유용한 기능

이 목록은 계속되고 있으며 앞으로도 계속 늘어날 것입니다. 새로운 Python 3 릴리스가 나올 때마다.

이는 Python 3으로 마이그레이션해야하는 몇 가지 이유입니다. 약 12 ​​~ 15 명의 개발자 백엔드 팀이 있습니다. 이것은 버그 수정, 개선, 보안 수정, 새로운 기능 개발 등을 포함하는 매일 7–10 빌드 릴리스가있는 기본 프로젝트입니다. 우리의 주요 과제는 현재 개발 프로세스를 중단하지 않는 것이 었습니다. 우리는 프로젝트가 Python 2.X와의 호환성을 깨지 않고 Python 3.X와 호환되는지 확인해야했습니다. 마이그레이션은 1 명의 개발자가 주도했습니다 (물론 다른 개발자의 도움을 받아).

이 기사에서는 모든 다양한 단계, 직면 한 문제 및 몇 가지 세부 사항을 설명하려고합니다. .

python 2와 python 3의 차이점

공통적 인 차이점은 다음에서 확인할 수 있습니다.

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

이제 몇 가지 드문 경우와 예외적 인 경우를 간단히 설명하겠습니다. 마이그레이션을 시작할 때 직면했습니다.

데이터 유형 비교 :

1. Python 2에서는 integernone와 비교하여 작동하므로 none가 더 적은 것으로 간주됩니다. 정수보다, 심지어 음수보다.또한 nonestring, string
python 3에서는 다른 데이터 유형 비교가 허용되지 않습니다.
이것은 대부분의 개발자에게 알려져 있지만 NotImplementedTypeint python 3에서는 작동하지 않습니다.

코드 스 니펫 :

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

저장하면 phone\_number\_validation.py와 함께 코드를 실행합니다.

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

호환 솔루션 :
base.PHONE\_NO\_SIZE\_LIMIT가 구현되었는지 확인해야합니다. 그렇지 않은 경우 처리해야합니다. 좋아요 :

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. 최소, 최대 수학 함수 :
int에서의 비교 작업은 none, intstr, none를 python 3에서 str로 변경하므로 수학 함수 min과 Max도 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"

호환 솔루션 :
1. 목록에는 string 또는 int
2. 하나의 유형으로 none가 있으면이를 처리하는 자체 방법을 가질 수 있습니다.

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

16 진수 인코딩 / 디코딩

이 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

호환 솔루션 :
Python 3 및 2에는 codecs를 사용해야합니다. Python 3 입력 및 출력은 모두 byte 데이터 유형.

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

문자열 대문자 :

string.uppercase는 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"

호환 솔루션 :
사용 ascii\_uppercase

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

hasattr () :

hasattr()는 Python 2 및 3을 작성할 때 매우 중요한 사례를 제공합니다. 호환 코드. hasattr()는 검색을 시도하여 속성의 존재를 확인합니다.

Python 2

hasattr ( 개체 , 이름 )
인수는 개체와 문자열입니다. 문자열이 객체 속성 중 하나의 이름이면 결과는 True이고 그렇지 않으면 False입니다. (getattr (object, name)을 호출하고 예외 발생 여부를 확인하여 구현됩니다.)

Python 3

hasattr ( 개체 , 이름 ) 인수는 객체와 문자열입니다. 결과는 문자열이 객체 속성 중 하나의 이름이면 True이고 그렇지 않으면 False입니다. (이는 getattr (object, name)을 호출하고 AttributeError 가 발생하는지 여부를 확인하여 구현됩니다.)

여기에서 자세한 내용을 확인하십시오 : (https://medium.com/@k.wahome/python-2-vs-3-hasattr-behaviour-f1bed48b068)

샘플 코드

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

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"

호환 솔루션 :
Python 2와 Python 3에서 코드 호환성을 유지하려면 \_\_getattr\_\_ 함수를 아래와 같이 변경해야합니다.

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

Dict는 Python 3으로 정렬됩니다.

Python 3.6 이상부터는 이제 사전이 기본값입니다. 삽입 주문 됨 .

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

호환 코드 :
이상적으로 이것은 응용 프로그램 코드를 깨지 않아야합니다. 순서가없는 dict로 변경 되었기 때문입니다. 그래도 두 Python 버전에서 동일한 결과가 필요하면 (순서가 중요하고 테스트 케이스가 실패 함) 두 언어 출력을 동일하게 유지하기 위해 OrderedDict를 사용해야합니다.

해싱 :

python 2 입력은 unicodestr,하지만 Python 3에서는 bytes

호환 솔루션 :
Python 3에는 를 입력으로 사용합니다. 여기서 python 2는 unicodestr 유형 모두에서 작동합니다.

# 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\_\_ 연산자 오버로딩 :

python 3에서 \_\_div\_\_ 연산자가 \_\_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"

호환 솔루션 :
Python 3.x에서는 \_\_div\_\_ 연산자가 아닌 \_\_truediv\_\_ 연산자를 오버로드해야합니다. div> 연산자. 코드가 호환되도록하려면 다음과 같은 두 가지 방법을 모두 유지해야합니다.

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 인코딩 :

base64.b64encode()를 사용하여 base64 인코딩을 수행합니다. Python 2에서는 unicode 또는 str를 입력으로 전달할 수 있습니다. 그러나 Python 3에서는 bytes가 입력으로 필요합니다.

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

호환 솔루션 :
우리는 base64 인코딩을위한 자체 방법을 가질 수 있으며 입력 stringbytes 둘 다.

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

내장 라운드 방법 :

Python2 : 반올림 완료 (예 : round (0.5)는 1.0이고 round (-0.5)는 -1.0)
Python 3 : 반올림이 짝수 선택 (예를 들어 round (0.5)와 round (-0.5)는 모두 0이고 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

호환 솔루션 :
Python 3에서도 Python 2 라운드와 동일하게 작동하는 자체 라운드 메서드입니다.

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. 팩 입력 유형 :

입력 유형은 str, Python 3은

# 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

호환 솔루션 :
encode 입력 .

목록 이해 변수 scope 변경 :

Python 3에서 목록 이해 변수 사용 둘러싸 기 범위는 함수 외부의 계획 변수에 액세스 할 수 없음을 의미합니다. 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

호환 솔루션 :
우리는 이러한 경우를 피해야합니다. 두 번째 방법 (two\_or\_three\_with\_method)의 경우 x 값을 인수로 전달해야합니다.

math.floor and math.ceil return data type changed :

python 2에서는 floor 및 ceil이 float 데이터 유형을 반환하지만 Python 3에서는 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

호환 솔루션 :
출력을 python 3에서 float로 만듭니다. python 2, float(floor(4.345))

python 2 pickled object into python 3 :

언급했듯이, 우리는 두 파이썬 버전을 모두 실행할 수있는 코드를 만들고 있습니다. 객체가 Python 2에서 피클 링되는 동안 문제가 발생했지만 Python 3에서는 피클을 해제 할 수 없습니다. 이는 Redis Pickled 캐시 된 객체에서도 발생할 수 있습니다.

pickle.load(), 기본값은 모든 문자열 데이터를 ASCII로 디코딩하고 디코딩하는 것입니다.

pickle.load() 문서 를 참조하세요.

선택적 키워드 인수는 fix\_imports입니다. , 인코딩 오류 는 Python 2에서 생성 된 피클 스트림에 대한 호환성 지원을 제어하는 ​​데 사용됩니다. fix\_imports 가 true 인 경우 pickle은 이전 Python 2 이름을 Python 3에서 사용되는 새 이름에 매핑하려고합니다. 인코딩 오류 는 Python에서 피클 된 8 비트 문자열 인스턴스를 디코딩하는 방법을 pickle에 알려줍니다. 2; 기본값은 각각 ASCII및 strict입니다. 인코딩 은 이러한 8 비트 문자열 인스턴스를 바이트 객체로 읽는 바이트일 수 있습니다.

https ://스택 오버플로.com / questions / 28218466 / unpickling-a-python-2-object-with-python-3

호환 솔루션 :
아래 방법을 사용하여 개체를 언 피클 할 수 있습니다.

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

타사 라이브러리 수정 :

프로젝트에서 많은 타사 패키지를 사용하고 있지만 업데이트하는 동안 몇 가지 예외적 인 경우에 직면했습니다. . 사용하지 않는 경우 건너 뛸 수 있습니다.

  1. Django :
    a. Django 마이그레이션 파일
    Python 3에서 Django makemigrations를 실행할 때 새 마이그레이션 파일이 표시되었습니다. 하지만 파이썬 2에서도 같은 일이 일어나지 않았습니다. 여기에는 여러 가지 이유가있을 수 있습니다.

b 접두사 : 메이크 마이그레이션시 대부분의 새 파일은 문자열 값에 b prefix없이 생성됩니다. 이는 모델 및 필드에서 사용되는 모든 문자열 리터럴 (예 : “verbose\_name ,“related\_name 등)은 Python 2와 3 모두에서 일관되게 바이트 문자열 또는 텍스트 (유니 코드) 문자열이어야합니다.

호환 솔루션 : 새 마이그레이션을 위해 하나의 마이그레이션을 수행하는 가장 쉬운 방법은 모든 모델 파일에 from \_\_future\_\_ import unicode\_literal를 추가합니다. 기존 마이그레이션 파일의 경우 makemigration를 실행하면 한 번만 실행되거나 기존 마이그레이션 파일에서 b prefix를 제거 할 수 있습니다.

선택 필드 : 모델에서는 dict.items ()를 사용합니다. dict가 python 3 에서 정렬되어 dict.items ()에서 반환되는 값이 Python 2 및 Python 3

호환 솔루션 : 둘 다 호환되도록 정렬했습니다 (dict. items ()) 및 이제 두 Python 버전과 호환되는 생성 된 마이그레이션 파일.

b. 관리 콘솔에 개체표시
관리 콘솔의 Python 3의 경우 개체를 문자열 대신 필드 값으로 볼 수 있습니다. 모델 클래스에 메서드가 있기 때문에 발생한 경우

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

str 메서드 \_\_str\_\_을 사용할 수 있으며 Python 2와 Python 3 모두에서 작동합니다.하지만 str 버전에 ASCII가 아닌 문자가 있으면 실패합니다.

호환 가능 솔루션 : 여기 에서 솔루션을 얻었으며 @python\_2\_unicode\_compatible 데코레이터를 추가했습니다. 모델의 경우 \_\_unicode\_\_\_\_str\_\_로 수정했습니다.

c . Django 쿼리 개체 슬라이싱
Django 쿼리 개체에는 레코드를 가져 오는 슬라이싱 기능이 있습니다. Django 버전 (1.11), python 2의 경우 int 및 str에 대한 슬라이싱을 지원합니다. Python 3에서는 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]: ]>

호환 솔루션 : 어쨌든 좋은 접근 방식이 아닌 문자열 슬라이싱을 피하십시오.

2. Redis : Redis는 일반적으로 사용되는 Python 패키지입니다. redis-py 3.0에는 많은 새로운 기능이 도입되었지만이 과정에서 이전 버전과 호환되지 않는 몇 가지 변경 사항이 필요했습니다.

호환 솔루션 :
https://pypi.org/project/redis/ 여기에서 변경 사항과 호환 방법을 확인할 수 있습니다. 암호. 두 Redis 버전과 호환되는 자체 Redis 메서드를 만들었습니다. 좋아요

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는 자동 또는 수동 쿼리 셋 캐싱 및 자동 세부 이벤트 기반 무효화를 지원합니다. Django cacheops가 값을 Redis에 저장하는 동안 문제가 발생하여 피클 객체가됩니다.

python 2에는 3 개의 다른 프로토콜 (0, 1, 2)이 있으며 기본값은 0. Python 3에는 5 개의 다른 프로토콜 (0, 1, 2, 3, 4)이 있으며 기본값은 3입니다.

Pickle은 기본 pickle 프로토콜을 사용하여 데이터를 덤프합니다. python 3 피클 객체를 만들고 Python 2에서 피클을 해제하려는 경우 피클 프로토콜 3는 Python 2에서 사용할 수 없기 때문에 작동하지 않습니다.

호환 솔루션 :
pickle.dump를 호출 할 때 프로토콜 매개 변수를 지정할 수 있습니다.
django-cacheops에는 없습니다 피클 프로토콜을 제공하는 옵션입니다. 이 문제를 해결하기 위해 원숭이 패치를 사용했습니다.

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

또한 위의 Python 2를 언 피클하는 방법을 언급했습니다. 피클 된 객체를 Python 3 로 변환합니다. 우리는 파이썬 3에서 데이터를 얻고 자합니다. 다른 파이썬 버전에서 수행 된 선택으로 인해 UnicodeDecodeError에 직면 할 수 있습니다.
이것은 패치를 사용하여 정렬됩니다

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 : 방법이 있습니다. 패턴에 따라 키를 삭제합니다. Python 2에서는 버전

1.6.5를 사용하여 키 검색 / 삭제가 스캔없이 발생했지만 Python 3의 경우 버전을

2.1로 업데이트했습니다. Redis 스캔을 사용하여 패턴 검색이 발생하는 곳에서 너무 느립니다. 이로 인해 문제가 발생했습니다. Git 허브 문제 .

호환 솔루션 :
이전 패턴 삭제 방법을 사용하여 문제를 분류했습니다. cache.delete\_pattern(pattern)를 호출하는 대신

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

다음 단계

In (2 부 )의 진행중인 개발로 인해 다운 타임없이 Python 3으로 전환하는 방법을 살펴볼 것입니다.

답글 남기기

이메일 주소를 발행하지 않을 것입니다. 필수 항목은 *(으)로 표시합니다