( 2020年11月18日)

こんにちは、このブログダウンタイムを発生させずにレガシーモノリスプロジェクトのPython3移行を行う方法についてです。これには2つの部分があります。
最初のブログ:このブログには、Python 3に移行する必要がある理由と、Python2とPython3およびそれらの互換性のあるソリューション。
(2番目のブログ):進行中の開発でダウンタイムを発生させることなく、HealthifyMeがPython3に移行した方法。
はじめに
PythonはHealthifyMeの主要なコーディング言語です。すべての新しいプロジェクトにPython3を使用していますが、レガシープロジェクトはDjango(1.11)を使用したPython2.7で引き続き実行されていました。私たちのモノリスプロジェクトは7年前のもので、200万行を超えるPythonコードがあります。 Python 3に移行した理由のいくつか:
- Python 2のサポートが終了しました: 2020年1月以降、Python2のサポートは提供されなくなります。
- Python 3の採用: Asほとんどの企業では、オープンソースプロジェクトはすでにPython 3を採用しています。新しいライブラリ、ツール、モジュール、フレームワークはPython 3で記述されます。
既存のオープンソースプロジェクトも移行され、新しい機能、修正、セキュリティの改善が行われます。 Python3で提供されます。 - ソフトウェアセキュリティ:ソフトウェアセキュリティの確保は、特に次の場合に法的要件です。あなたはGDPRの領域で個人情報を扱っています。 ソフトウェアを最新の状態に保つことは、セキュリティのベストプラクティスの中で当然のことながら非常に高いランクにあり、と古いPythonインタープリターはほとんど保証されていますセキュリティ監査中に危険信号として表示されます。 Black Duckのようなセキュリティテストツールは、Python 2の多くの脆弱性、エクスプロイト、セキュリティの問題を報告しています。それらのほとんどは、最新のPython 3バージョン(3.7.5、3.8.0)で修正されています。
- パフォーマンスと新機能:Python3のパフォーマンスはpython2よりも優れています。 python 3を使用する新しいソフトウェア製品は、CPUパフォーマンスが12%向上し、メモリリソースの使用が30%向上したことを報告しています 。
また、python3は次のようになります。
* ネイティブ非同期プログラミング。
*タイプアノテーション静的コード分析と全体的な使いやすさを向上させるために使用できます。
*連鎖例外。これは、デバッグ時に特に役立ちます。
* Pythonでのコーディングをはるかに効率的にするその他の便利な機能。
このリストは続き、確実に増えていきます。新しいPython3リリースごとに。
これらが、Python 3に移行する理由です。約12〜15の開発者バックエンドチームがあります。これは、バグ修正、改善、セキュリティ修正、新機能開発などを含む、毎日7〜10のビルドリリースを伴う基本プロジェクトです。私たちの主な課題は、現在の開発プロセスを停止しないことでした。 Python 2.Xとの互換性を損なうことなく、プロジェクトがPython3.Xと互換性があることを確認する必要がありました。移行は1人の開発者が主導しました(もちろん、他の開発者の助けを借りて)。
この記事では、実行されたさまざまな手順、直面した問題、およびいくつかの詳細を説明しようとします。 。
Python2とPython3の違い。
ここで一般的な違いを確認できます:

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では、integer
をnone
と比較すると機能するため、none
は少なくなります。整数よりも、負の数でも。また、none
とstring
、string
とint
。
Python 3では異なるデータ型の比較は許可されていません。
これはほとんどの開発者に知られていますが、NotImplementedType
がint
これはPython3では機能しません。
コードスニペット:
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
、int
からstr
、none
python3ではstr
に変更されたため、python3では数学関数minとMaxも変更されました。
# 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。 1つのタイプで、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進エンコード/デコード
このこれはPython3では機能しません
# 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
互換性のあるソリューション:
Python3と2にはcodecs
を使用する必要があります。Python3の入力と出力はどちらも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
はPython3では機能しません。
# 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()
は、Python2および3を作成する際の大きなエッジケースを示します互換性のあるコード。hasattr()
は、属性を取得しようとして、属性の存在を確認します。
Python 2
hasattr (オブジェクト、名前)
引数はオブジェクトと文字列です。文字列がオブジェクトの属性の1つの名前である場合、結果はTrueになり、そうでない場合はFalseになります。 (これは、getattr(object、name)を呼び出して、例外が発生するかどうかを確認することで実装されます。)
Python 3
hasattr (オブジェクト、 name )引数はオブジェクトと文字列です。文字列がオブジェクトの属性の1つの名前である場合、結果は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"
互換性のあるソリューション:
Python2とPython3でコードの互換性を持たせるには、\_\_getattr\_\_
関数を次のように変更する必要があります。
def \_\_getattr\_\_(self, key):
try:
return self[key]
except KeyError:
raise AttributeError
辞書はPython3で順序付けられます:
Python3.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
を使用する必要があります。
ハッシュ:
Python2では入力をunicode
とstr
ですが、Python3ではbytes
互換性のあるソリューション:
Python3にはを入力として使用します。ここで、python2はタイプunicode
と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\_\_演算子のオーバーロード:
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エンコーディング用の独自のメソッドを使用でき、入力string
とbytes
両方。
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
互換性のあるソリューション:
作成したo Python3のPython2ラウンドと同じように機能する独自のラウンドメソッド。
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
構造体。パック入力タイプ:
入力タイプはstr
、Python3は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
互換性のあるソリューション:
encode
入力。
リスト内包変数 scope
の変更:
Python 3では、リスト内包変数の使用を囲むスコープは、関数の外部でプラン変数にアクセスできないことを意味します。これはPython2には当てはまりません。
# 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
互換性のあるソリューション:
このような場合は避けてください。 2番目のメソッド(two\_or\_three\_with\_method
)では、x値を引数として渡す必要があります。
math.floor math.ceilの戻りデータ型が変更されました:
Python 2ではfloorとceilがfloatデータ型を返しますが、Python3では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
互換性のあるソリューション:
Python3でfloatとして出力を作成します。Python2、float(floor(4.345))
の選択解除には影響しません。 python2がオブジェクトをpython3にピクルスしました:
前述のように、両方のpythonバージョンを実行するための互換性のあるコードを作成しています。 Python 2でオブジェクトをpickle化するときに問題が発生しましたが、python3ではpickle化を解除できません。これはRedisPickledのキャッシュオブジェクトでも発生する可能性があります。
pickle.load(),
defaultは、すべての文字列データをASCIIとしてデコードしようとしますが、そのデコードは失敗します。
pickle.load() のドキュメントを参照してください:
オプションのキーワード引数は fix\_importsです、 encoding 、 errors は、Python2によって生成されたpickleストリームの互換性サポートを制御するために使用されます。 fix\_imports がtrueの場合、 pickleは、古いPython2の名前をPython3で使用される新しい名前にマッピングしようとします。 encoding と errors は、Pythonによってpickle化された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
サードパーティライブラリの修正:
プロジェクトでは、多くのサードパーティパッケージを使用していますが、それらを更新しているときに、いくつかのエッジケースに直面しました。いずれも使用していない場合は、これをスキップできます。
- Django :
a。 Django移行ファイル
Python3でDjangomakemigrations
を実行すると、新しい移行ファイルが表示されていました。しかし、Python2では同じことが起こりませんでした。これには複数の理由が考えられます。
bプレフィックス: makemigrationでは、ほとんどの新しいファイルは、文字列値に
b prefix
がなくても生成されます。これは、モデルとフィールドですべての文字列リテラルが使用されているためです(例: “ verbose\_name“、 “ related\_name“など)は、Python 2と3の両方で一貫してバイト文字列またはtext(unicode)文字列である必要があります。互換性のあるソリューション:新しい移行のために1つの移行を実現する最も簡単な方法は、すべてのモデルファイルに
from \_\_future\_\_ import unicode\_literal
を追加します。既存の移行ファイルの場合、makemigration
を実行して1回だけ実行するか、既存の移行ファイルからb prefix
を削除できます。選択フィールド:モデルでは、dict.items()を使用します。 dictがpython3 で順序付けられるようになるため、dict.items()から返される値は次のように異なります。 Python2とPython3。
互換性のあるソリューション:両方に互換性を持たせるために、sorted(dict。 items())と生成された移行ファイルは、両方のPythonバージョンと互換性があります。
b。管理コンソールでの「
def \_\_unicode\_\_(self):
return "MyModel: {}".format(self.name)
strメソッド\_\_str\_\_を使用できます。これは、Python2とPython3の両方で機能します。ただし、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プロトコルを使用してデータをダンプします。ピクルスプロトコル
3
がpython2で使用できないため、ピクルスオブジェクトを作成し、Python2でピクルスを解除したい場合はpython3は機能しません。
互換性のあるソリューション:
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
また、上記のように Python2のピクルスを外す方法オブジェクトをpython3 にピクルスしました。 Python 3でデータを取得したいのですが、さまざまなPythonバージョンでピッキングが行われるため、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)
次のステップ
(パート2 )このブログの ここでは、進行中の開発でダウンタイムを発生させずにPython3に移行する方法を探ります。