AWS Cloud9にてpython3を使用して開発する際の注意事項

# 1.はじめに
lambdaのコードを書くにあたってCloud9を使えるという噂を以前聞いていたので、実際に試してみました。その中でいくつかの **罠** がありましたので紹介したいと思います。

# 2.前提
Cloud9で環境構築する際に、既存の環境にSSHで接続する方法と専用のEC2を新規に立てる方法がありますが、後者の環境とします。また、OSはAmazonLinux2とします。なお、pythonのバージョンですが、[PEP-373](https://www.python.org/dev/peps/pep-0373/)と[PEP-404](https://www.python.org/dev/peps/pep-0404/)で定義されている通り2系は2019年末にはEOL( End Of Life = サポート終了 )となりますので3系を用います。
ある程度pythonとLinuxコマンドを理解していることも前提になります。

# 3.注意事項

## 3-1. rootユーザと開発ユーザのpythonコマンドのバージョンが異なっている!?
linux系のOSですので特に珍しいことではないかもしれませんが、rootユーザと開発ユーザのpythonコマンド[^1]のバージョンの設定が違うのです。。。
試しに以下コマンドを実行します。

~~~bash
$ python --version
Python 3.6.8		#　想定通り
$ sudo python --version
Python 2.7.16		#　え” !?
~~~

さらに、pythonコマンドとpipコマンドのバージョンが違います。。。

~~~bash
$ python --version
Python 3.6.8		#　想定通り
$ pip --version
pip 9.0.3 from /usr/lib/python2.7/dist-packages (python 2.7)		#　え” !?
~~~

標準モジュールを用いるにあたっては特に問題にならないのですが、pipで外部モジュールをinstallする際[^2]に何も手当をしないと、想定通りにinstallがなされません。。。

何が起きているのでしょう。各コマンドの場所を確認してみます。
まず、pythonコマンドから。

~~~bash
$ which python
alias python='python36'		# aliasが設定されている
        /usr/bin/python36

$ sudo which python
/usr/bin/python
$ ls -la /usr/bin/python
lrwxrwxrwx 1 root root 24 Sep  3 14:51 /usr/bin/python -> /etc/alternatives/python	# alternativesが利用されている
~~~

開発ユーザはaliasにてpython36を向くようにされており、rootユーザはalternativesが使われていることが分かります。

続いて、pipコマンド。

~~~bash
$ which pip
/usr/bin/pip

$ sudo which pip
/usr/bin/pip

$ ls -la /usr/bin/pip
lrwxrwxrwx 1 root root 21 Sep  3 14:51 /usr/bin/pip -> /etc/alternatives/pip	# alternativesが利用されている
~~~

環境によっては、rootのpythonのバージョンを変えられないということはありがちなのですが、今更python2は無いでしょうということと、開発用の新しい環境ですし、python3にスイッチすることで悪影響はないと思いますので、alternativesの設定を変えるのが一番シンプルで良いと考えます。
以下手順にて設定を変更します。

~~~bash
$ sudo update-alternatives --config python

There are 2 programs which provide 'python'.

  Selection    Command
-----------------------------------------------
*+ 1           /usr/bin/python2.7
   2           /usr/bin/python3.6

Enter to keep the current selection[+], or type selection number: 2

$ python --version
Python 3.6.8		#　想定通り
$ sudo python --version
Python 3.6.8		#　想定通り

$ pip --version
pip 9.0.3 from /usr/lib/python3.6/dist-packages (python 3.6)		#　想定通り
$ sudo pip --version
pip 9.0.3 from /usr/lib/python3.6/dist-packages (python 3.6)		#　想定通り
~~~

これで、心置きなく外部モジュールを導入できます。
試しにjinaja2をインストールしてみます。

~~~bash
$ sudo python -m pip install jinja2
Collecting jinja2
  Using cached https://files.pythonhosted.org/packages/...../Jinja2-2.10.1-py2.py3-none-any.whl
Requirement already satisfied: MarkupSafe>=0.23 in /usr/local/lib64/python3.6/site-packages (from jinja2) (1.1.1)
Installing collected packages: jinja2
Successfully installed jinja2-2.10.1

$ sudo python -m pip freeze
astroid==1.6.6
Django==2.0.2
ikp3db==1.1.4
isort==4.3.21
jedi==0.11.1
Jinja2==2.10.1			#　← インストールされた！
lazy-object-proxy==1.4.2
MarkupSafe==1.1.1
mccabe==0.6.1
parso==0.1.1
pbr==5.4.2
pylint==1.8.1
pylint-django==0.8.0
pylint-flask==0.5
pylint-plugin-utils==0.5
pytz==2019.2
six==1.12.0
stevedore==1.30.1
virtualenv==16.2.0
virtualenv-clone==0.5.3
virtualenvwrapper==4.8.4
wrapt==1.11.2
~~~

なお、jinja2以外のモジュールは最初からインストールされているものです。


## 3-2.pylintが外部モジュールを認識しない!?
さて、これでやっと開発ができると思って、実際にコードを書いてみますと、なんと先ほどインストールした外部モジュールが正しく読み込めていないのか、「no value for argument 'backend' in constructor call」というエラーが発生します。。。
![20190928_01.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/490256/3157b69b-5cc8-2c41-6efa-fd0aa37c2692.png)

原因ですが、Cloud9の設定の問題でした。。。

Preferencesを開きます。
![20190928_02.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/490256/56659a16-832b-b16c-ab36-e45353653869.png)

左ペインのPython Supportをクリックすると、pythonの設定画面が開きます。
![20190928_03.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/490256/eee0f2de-033c-db0e-480b-449dac5a1873.png)

設定内容の中に、"PYTHONPATH"という設定があるのですが、以下の通り記述されています

~~~
/usr/local/lib/python2.7/dist-packages:/usr/local/lib/python3.4/dist-packages:/usr/local/lib/python3.5/dist-packages
~~~

3系と2系のモジュールを全部読み込んでいるという、雑な設定にも驚きなのですが、そもそも3.4も3.5入ってすらないです。。。この記事を作成している段階で標準導入されているものは3.6ですので以下のように変更します。[^3]

~~~
/usr/local/lib64/python3.6/site-packages:/usr/local/lib/python3.6/site-packages
~~~


これで、上述のエラーは解決します。
![20190928_04.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/490256/e3bb12e9-f21f-c766-1a4a-cc76999b8747.png)


## 3-3.lambdaにデプロイしたファイルが消せない!?
Cloud9ではlambdaを開発できることがウリなのですがここでも罠が。。。

まず、ウィザードでlambdaの関数を新規に作成します。
![20190929_07.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/490256/0c19f280-b0b0-5573-0007-acbb3fe1170e.jpeg)

続いて、適当にファイルを作ります。例としてsample.txtというファイルを作成しました。
![20190929_10.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/490256/c56fe31f-5d9a-69af-3006-4dac0ab68a10.png)

lambdaをデプロイします。ボタン一つでデプロイできます!
![20190929_11.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/490256/c34c5635-d559-43c8-8a11-fd03b3005f01.png)

lambda側の管理画面でsample.txtがアップされていることを確認します。無事上がってます。
![20190929_12.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/490256/6d633ec8-dcbe-7f7f-6c88-75ef64c04aa4.png)

では、Cloud9側でsample.txtを削除します。
![20190929_13.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/490256/e928f22b-7853-6ca3-b897-5e0631f95954.png)

削除した上で、再度デプロイします。
![20190929_14.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/490256/8bbaa38b-5e6e-ac55-619b-1980c0cdad39.png)

消えてません。。。
![20190929_12.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/490256/6d633ec8-dcbe-7f7f-6c88-75ef64c04aa4.png)


ここで、私は混乱しました。Cloud9上では確かに削除しており、実体は残っていないのですが、何度lambdaにデプロイしなおしても削除したファイルは残り続けるのです。いろいろ調べたのですが、 .debug という隠しフォルダがあり、その中に削除したファイルのコピーが置かれていることに気づきました。

隠しフォルダはデフォルトでは非表示になっているので、表示するように設定します。歯車ボタンで表示されるメニューの「Show Hidden Files」にチェックを入れます。
![20190929_15.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/490256/16639c2d-18f2-4fd8-acc0-4a302ecc52e9.png)

すると、 .debug フォルダが表示され、削除したはずのsample.txtが。。。
![20190929_16.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/490256/d3ec8946-a04d-013b-dbf5-0f978e3e1701.png)

.debug フォルダのsample.txtを削除し、改めてデプロイしなおします。
![20190929_17.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/490256/e4857092-700b-2365-c7b8-d814f9220b0b.png)

削除されました!
![20190929_18.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/490256/36e601a3-d268-a345-b5b1-6a37218b55be.png)


## 3-4.lambda実行で外部モジュールが見つからない!?
サンプル作成した関数にて外部モジュールをimportします。先にpipでinstallしておけば、pylintでエラーになることは当然ありません。
![20190929_20.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/490256/24a11b29-6fed-fad3-9aee-6487b20b71b1.png)

この状態でlambdaを実行すると、モジュールが見つからないと言われ動きません。
![20190929_21.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/490256/f61edf5e-1dc0-90b2-725a-a1126f2fa6ca.png)

実は、lambdaとして動かすためには、ローカルにもモジュールを入れておく必要があります。
関数の名前がついているフォルダ（の親の方のフォルダ）にて右クリックをして「Open Terminal Here」をクリックしてターミナルを開きます。
![20190929_22.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/490256/521eb6c4-da9b-ee93-b031-5078e9e13ed3.png)

ターミナルにてpip installコマンドに「-t .」オプションを付けて実行します。

~~~Bash
$ python -m pip install jinja2 -t .
Collecting jinja2
  Downloading https://files.pythonhosted.org/packages/1d/e7/fd8b501e7a6dfe492a433deb7b9d833d39ca74916fa8bc63dd1a4947a671/Jinja2-2.10.1-py2.py3-none-any.whl (124kB)
    100% |████████████████████████████████| 133kB 3.1MB/s 
Collecting MarkupSafe>=0.23 (from jinja2)
  Downloading https://files.pythonhosted.org/packages/b2/5f/23e0023be6bb885d00ffbefad2942bc51a620328ee910f64abe5a8d18dd1/MarkupSafe-1.1.1-cp36-cp36m-manylinux1_x86_64.whl
Installing collected packages: MarkupSafe, jinja2
Successfully installed MarkupSafe-1.1.1 jinja2-2.10.1
~~~

すると、ローカルに外部モジュールがインストールされます。
![20190929_23.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/490256/0d21896f-95b7-6497-5797-1d40cb396f73.png)

この状態で、再度実行すると、今度はちゃんと動きます!
![20190929_24.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/490256/11c5253c-e16c-e4ae-caae-41fd473e63e8.png)

当然、デプロイしてlambdaの管理画面側でも動きます!



#4. おわりに
IDEもブラウザで完結させることができる世の中になったことは感慨深いです。が、もう少しユーザフレンドリーだとありがたいなと思いました。当記事が、Cloud9利用にあたって躓いている方の一助になれば幸甚です。


[^1]: python3というコマンドも用意されているので、そっちを使えという話かもしれませんが、AWSのウィザードで環境を作っているのに、不親切だなと思う限りです・・・。

[^2]: 開発ユーザはpipで外部モジュールを導入する権限を持っていないためsudoコマンドでrootとして実行する必要があります。

[^3]: モジュールによって/usr/local/lib64に入ったり、/usr/local/libに入ったりするようです。どこにモジュールが入っているかは、python -m pip show [モジュール名]　で確認できます。