クラウド上のPython2系のスクリプトを3系に移行しアップデートする作業メモ

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/110571/a73b4899-0882-63f5-df12-de3b073326e1.png)


# これは何
- ~~いまだに動いている~~ Python2系のスクリプトを３系にアップデートするときの作業（事前準備含む）の備忘メモ

# 前提
- pyenvを使用してPythonのバージョン管理を行っている
- Dockerやvirtualenv, Pipenvは未使用

# 概要
1. 移行するPythonを決める
2. ローカルで検証する
3. 本番（stg）でバージョンを上げる

# 手順
- 以下で簡単に移行手順を示します

## 事前調査
### 現在の動作環境を確認
- 使用しているOSによってアップデートする先のPythonのバージョンが限られているので、事前に調査する
- サーバ上で `pyenv install --list`して、移行できるPythonのバージョンを確認する
- 以下はLinuxのOSが `Linux: Amazon Linux AMI release 2016.03` だった場合の例

```shell
[ec2-user@app]$ pyenv install --list
Available versions:
# 中略
  3.6.0
  3.6-dev
  3.6.1
  3.6.2
  3.6.3
  3.6.4
  3.7.0b2
  3.7-dev
  3.8-dev
# 後略
```

### 移行先のPythonバージョンを決定
- 先ほどの`pyenv install --list`でアップデートできるバージョンの中から、Pythonのバージョンを選定する
    - パッチバージョンの数字の後に`dev`や`b*` が入っているものは、stableでないバージョンなので避ける
- [Python Developer’s Guide](https://devguide.python.org/#status-of-python-branches)に各バージョンのメンテナンス期限などが書かれているので、EOLができる限り長いものを選ぶ
- 先ほどの`Linux: Amazon Linux AMI release 2016.03`の例では、 上記の理由より`3.6.4`にするのが妥当

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/110571/84a65b78-5133-1569-eeea-47f02d073484.png)

- Python Developer’s Guide: https://devguide.python.org/#status-of-python-branches

## ローカルでの動作検証
### Python3系で使用できないロジックの洗い出し
- [futurize](http://python-future.org/automatic_conversion.html#stage-1-safe-fixes)でPythonのコードのアップデートを試みます
    - applyされなかったファイルやメソッドをについて検証していきます
- Pythonの公式でも推奨されているやり方っぽいです

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/110571/46f90548-c41a-2fc2-6899-b2e73b85b756.png)

- Python 2 から Python 3 への移植: https://docs.python.org/ja/3/howto/pyporting.html#porting-python-2-code-to-python-3
- futurize　Py2 to Py2/3: http://python-future.org/automatic_conversion.html#stage-1-safe-fixes

### Python3系の記法に自動変換を行ってみる
- [2to3](2to3 - Python 2 から 3 への自動コード変換)というライブラリで、Python２でのみ通用する記法を（ある程度）自動的に３系の記法に変換してくれます
- ３系への影響範囲が大きい場合は、こちらで一気に変換してみても良いかもしれません（過信は禁物、動作検証はすべき）
- これも、[Python３の公式ドキュメントで記載](https://docs.python.org/ja/3/library/2to3.html#module-lib2to3)されている方法論になります

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/110571/f966bc51-06ad-8a12-d21c-3d051414a527.png)

- 2to3 Python 2 から 3 への自動コード変換: https://docs.python.org/ja/3/library/2to3.html#module-lib2to3

### Pythonバージョンの切り替え&スクリプト実行
- 上記のライブラリでのコードやロジックの移行にある程度目処がついたら、実際にローカルで動かしていきます
- 主な順序は「`ｐｙenv`でバージョン切り替え」「ライブラリのインストール」「スクリプトの実行」となります
- DBとの接続が必要な部分など、ローカルだけでは完結しないロジックがある場合はスキップするなど、実行可能な部分だけでも実行する

### コケた場合のバックアップ方法をメモする
- ローカルで問題なく動作した場合、stgや本番環境のアップデートを行うことになります
- その前に、「仮にPython３のコードでの動作に問題があった場合」の切り戻しオペレーションをドキュメントか何かに記載しておきましょう
- 具体的に想定される切り戻し作業
    - `pyenv`で旧バージョンのPythonに切り替え
    - アップデートしたコードのcommitをrevertする
    - 旧バージョンでライブラリをインストールし直す

## 移行実施
- 以降でいよいよ本番のスクリプトのPythonバージョンを変更していきます
- stg環境があればstg環境で先に実施する

### Pythonバージョンの切り替え
- `pyenv`でPythonのバージョンを切り替えていきます

```shell
# 3.6.4をインストール
$ pyenv install 3.6.4
$ pyenv versions  
  system  
* 2.7 (set by /home/ec2-user/.pyenv/version)
  3.6.4 # 追加されているはず
$ pyenv local 3.6.4
$ pyenv rehash
$ pyenv versions  
  system  
　 2.7 (set by /home/ec2-user/.pyenv/version)
＊ 3.6.4 # 変更されているはず
$ python --version
Python 3.6.4 # 変更されているはず
```

### ライブラリの再インストール
- Python3環境にライブラリをインストールします
- 以下は`requirements.txt`を用いてインストールする例です

```shell
$ pip install -r requirements.txt
Collecting urllib3!=1.25.0,!=1.25.1,<1.26,>=1.21.1 (from requests->-r requirements.txt (line 5))
  Cache entry deserialization failed, entry ignored
  Downloading https://files.pythonhosted.org/packages/e1/e5/df302e8017440f111c11cc41a6b432838672f5a70aa29227bf58149dc72f/urllib3-1.25.9-py2.py3-none-any.whl (126kB)
    100% |████████████████████████████████| 133kB 9.1MB/s
Collecting certifi>=2017.4.17 (from requests->-r requirements.txt (line 5))
  Cache entry deserialization failed, entry ignored
  Cache entry deserialization failed, entry ignored
  Downloading https://files.pythonhosted.org/packages/57/2b/26e37a4b034800c960a00c4e1b3d9ca5d7014e983e6e729e33ea2f36426c/certifi-2020.4.5.1-py2.py3-none-any.whl (157kB)
    100% |████████████████████████████████| 163kB 7.3MB/s
# 以下略
```

### スクリプトの動作確認
- 手動で動かせるスクリプトの場合は、実際にスクリプトを動かしてみます
- エラーを吐かずに想定通り動作する場合は、移行成功です
- 一応動作ログを取得してファイルに吐き出すなり、ドキュメントに貼り付けておくなりしておくとベストです

```shell

[ec2-user@app]$ python main.py
```

# 参考ドキュメント
[![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/110571/1612c01a-df14-756b-a412-d3441adbd7c2.png)](https://devguide.python.org/#status-of-python-branches)
[![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/110571/2195fceb-4adb-3b3c-d9d0-91dd0c39904b.png)](https://docs.python.org/ja/3/howto/pyporting.html#porting-python-2-code-to-python-3)