pythonの仮想化環境についてのまとめ

仮想化とは何か?

特定のプロジェクトや特定のディレクトリに限定したpythonの環境をつくることを仮想化という。 またpythonの環境には大きく分けて2つある。 一つはpython本体で、python2.7とかpython3.6とか、どのversionのpythonを使うかということ。 もう一つは、packageで、どこのディレクトリのpackageを使うかということ。

要するに仮想化をするとは、 特定のプロジェクト(ディレクトリ)で どのpython本体とどのpackageディレクトリを使うかを決めるだけの話。

どのプログラムを使うかというのは、 要するに実行するときのPATHを決めるというだけの話である。 だから仮想化を理解するにはPATHのことを理解する必要がある。

仮想化ではPATHが一番大事(たぶん)

そもそもPATHとは環境変数の一つである。 環境変数とは、OSの機能で、タスクが動作するのに必要な情報を書き込む仕組みである。 macではターミナルでprintenvとコマンドを打てば設定されている環境変数が表示される。

TERM_PROGRAM=Apple_Terminal
SHELL=/bin/bash
USER=kapi
SSH_AUTH_SOCK=/private/tmp/com.apple.launchd.t748VX0p9g/Listeners
PATH=/usr/local/opt/llvm/bin:/Users/kapi/.pyenv/shims:/Users/kapi/.pyenv/shims:/Users/kapi/.pyenv/bin:/Applications/MacVim.app/Contents/MacOS:/usr/local/bin:/Users/kapi/.nvm/versions/node/v9.3.0/bin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin:/opt/X11/bin:/Applications/Wireshark.app/Contents/MacOS:Users/kapi/.local/bin
HOME=/Users/kapi
...

USERやSHELLなどが設定されているのがわかる。 で、PATHとは環境変数の一つであり、コマンドがある場所を列挙した変数のことである。

ターミナルでlsコマンドを打つとファイルが表示されるのはlsという実行ファイルを見つけて実行しているからだが、 もしPATH情報がないと、実行ファイルがどこにあるかを全ファイルから探す必要があり、めちゃくちゃ時間がかかってしまう。 もしくは、lsと打つのではなく、/bin/ls のように実行ファイルがある場所を毎回指定する必要がある(フルパスという)

PATHがあれば、PATHの場所だけコマンドを探せばいい。 また実行ファイルはPATHのあるところに置くか、実行ファイルの場所をPATHに追加したらよい。

なお、PATHだけを表示させるには echo $PATH でよく、 PATHは .bashrc または .bash_profile に記載されている。

複数の実行ファイルがあるときの挙動

そして、ここが仮想化で重要なところだが、 例えばpythonインタプリタの実行ファイルが複数あるとき、どれが実行されるのか? それはPATHが書かれている順番に探していって、先に見つかった方である。 PATHにpython2.7のpathが書かれていればpython2.7インタプリタが実行される。 と、いうことはPATHにかかれている順番をコントロールしたら、任意のpythonを実行できるのか? YES. pyenvではまさにそれをしている。

参考 [Mac]環境変数PATHの設定・変更・追加・確認・順番入れ替えの方法 | Gabekore Garage

pyenvのお仕事

pyenvの大きな仕事は2点

pyenvがやらないこと

基本的にコマンドを先回りするので特にactivateなどは必要ない。 ディレクトリに設定ファイルを置いてそれを先回りして読むことをしている。 詳しくはgitをみるべし。 pyenv/pyenv: Simple Python version management

pythonインタプリタのインストール

pyenv install -list でインストール可能なリストを表示できる。 pyenvを最新にしておかないとだめだった気がする。 (pyenv update や pyenvのルートディレクトリで git pullする) anacondaなんかもインストール可能

ディレクトリごとにpythonの環境を分ける

pyenv local 環境名 とすると、そのディレクトリに .python-version が作られ、そこに環境名が書かれている。 で、このディレクトリでpython関係のコマンドを打つと、その環境のものが使われる。 例えばpipをしたら、.python-versionのところにだけインストールされる。 pyenv versions としたら、どのversionがインストールされていて、そのディレクトリではどのversionを使うかが示される また、どこの.python-versionファイルによって設定されたかも表示される

MacBook-Pro:tmp kapi$ pyenv versions
  system
  2.7.16
  3.6.9
  3.7.4
* anaconda3-5.3.1 (set by /Users/kapi/Documents/GitHub/anaconda3-5.3.1/.python-version)

localでの設定がなければ、ディレクトリをさかのぼり、pyenvのrootにいく。 rootではgloalの設定がある。 それもなければ、systemのpythonを使うことになる。

なお実際にpyenvでやっていることは pyenv、pyenv-virtualenv、venv、Anaconda、Pipenv。私はPipenvを使う。 - Qiita から

  • $PATHの先頭にPYENV/shimsを挿入し、あらゆるPython系コマンドへのアクセスに割り込む。
  • いま動かすべきPythonインタプリタを探す。
  • 受け取ったコマンド(例えばpython main.py、pip3 install -r)を、その探したインタプリタに投げる。 すなわち、諸々のコマンドを適切な窓口に振り分ける、受付係を引き受けてくれるのである。 適切なインタプリタを探すために、pyenvは以下の操作を行う:
  • もし環境変数PYTHON_VERSIONが設定されていれば、そのバージョンに従う。
  • そうでなければ、今のディレクトリから順に親ディレクトリを遡っていく。その途中で.python_versionファイルが見つかれば、そこに書かれてい>るバージョンに従う。
  • ルートまで遡っても見つからなければ、グローバルに設定されているバージョンに従う。 そういうわけで、which pythonをしても、pyenvの管理化では、 /Users/kapi/.pyenv/shims/python がどこも表示される

pyenvはpythonインタプリタとpackageは分けることは出来ない

インタプリタとpackageがセットになっていると考えていい。 pipをしたら.python-versionで設定されているところにインストールされるだけである。 だから、python3.7.4で2つのパッケージ環境を作ることはできない。 同じpythonインタプリタでパッケージ環境を分けるにはvenvをつかう必要がある。

venvのお仕事

venvがやるのはパッケージ環境の仮想化である。 環境を作ると、そのディレクトリ下に仮想環境の実行ファイルがおかれ、 どのパッケージのディレクトリを使うか、のpathを決めている。

ただし、pyenvと違ってコマンドを乗っ取るわけではないので、 仮想環境を使うには activateが必要になる。 source [環境作ったディレクトリ]/bin/activate でactivateする。(deactivate としたらdeactivateされる) activateした状態でpipすると、指定されたほうのsite-packagesにinstallされる

ちなみにvenvは公式あまりわかりやすくなかった venv --- 仮想環境の作成 — Python 3.7.4 ドキュメント こっちのqiitaの方がわかりやすかった venv: Python 仮想環境管理 - Qiita

venvのやっていることを確認するにはpythonインタプリタを起動してsite-packagesのパスを確認したらいい。

これは、venvでない、環境でのpythonのimportのpath

MacBook-Pro:~ kapi$ python
Python 3.7.4 (default, Aug 14 2019, 08:40:34) 
[Clang 10.0.1 (clang-1001.0.46.4)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import sys
>>> import pprint
>>> pprint.pprint(sys.path)
['',
 '/Users/kapi/.pyenv/versions/3.7.4/lib/python37.zip',
 '/Users/kapi/.pyenv/versions/3.7.4/lib/python3.7',
 '/Users/kapi/.pyenv/versions/3.7.4/lib/python3.7/lib-dynload',
 '/Users/kapi/.pyenv/versions/3.7.4/lib/python3.7/site-packages']
>>> 

こっちは、python -m venv try_venv で作った環境をactivateして作った環境でのpythonのimportのpath

(try_venv) MacBook-Pro:~ kapi$ python
Python 3.7.4 (default, Aug 14 2019, 08:40:34) 
[Clang 10.0.1 (clang-1001.0.46.4)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import sys
>>> import pprint
>>> pprint.pprint(sys.path)
['',
 '/Users/kapi/.pyenv/versions/3.7.4/lib/python37.zip',
 '/Users/kapi/.pyenv/versions/3.7.4/lib/python3.7',
 '/Users/kapi/.pyenv/versions/3.7.4/lib/python3.7/lib-dynload',
 '/Users/kapi/Documents/GitHub/try/try_venv/lib/python3.7/site-packages']
>>> 

見て分かる通り、python本体は共通で、site-packagesだけが異なる。 これがvenvがやっていること。 (try_venv)という環境でpipをすると、こっちにだけ、installされることになる。なるほど〜〜。

pipenvについて

これはちょっとわかりにくい より抽象化されている。 ディレクトリごとの環境が基本みたいだ。 またpythonはおそらく共通で使っているということだな。 で、activateするんじゃなくて、 pipenvコマンドをつかってinstallやら実行やらをすると。 多分、そのディレクトリにパッケージのパスがかいてあって、それを探してそう。 また使うときに調べよう。

で、どう使うか?

よく考えたら別にvenvも使わなかった。。。 アプリ開発するわけでもない とはいえ、環境を明示的にしておいた方がいいのも確かなので、 cvの勉強用にはpyenvでanacondaをいれておく。 具体的にはpyenvでanacondaをインストールして、
特定のフォルダ以下で
pyenv local anacondaxxx としておいた。

bashを使いやすくする

とても参考になった記事

また設定変更としては以下を行った

  • bashのcontrol-Sを有効にする(履歴を検索するときに便利, .bashrcに設定)
  • メタキーとしてoptionキーを使用(macでescの代わりになる, terminalの設定変更)

Docker, VSCode, pytest, GitHubについて最近みた資料

あとで個別には軽くまとめるつもり。つもり。

筋トレと学びを支える要素

これ、めちゃくちゃおもしろい。 筋トレ初めて1年後の生存率は3.7% !!!

意外なのは指数関数的に減っているところ。 半年継続できた人の生存率はもっと高いかと思ってた。 多少はマシなくらいで、半年継続してもじゃんじゃか脱落する。

結局のところ、短期的には困らないが長期的には利益になるような退屈な作業を、”自分の意志”だけで続けることはまず無理、ということがよくよくわかる。

私も社会人になってから何度も英語学習で挫折したのだけど、短期的に退屈な作業を自分の意志だけに頼っていたので、もう最初から負けは確定していたのだな。

ではどうしたらいいのか? 数学ガールで有名な結城先生が相変わらずいいこと書いてくれている。

学びを支える要素(学ぶときの心がけ) https://mm.hyuki.net/n/n57686f374b39

  • 時間を掛ける
  • 成功を体験する
  • 孤独を避ける

学校や会社では基本この3つは満たされやすい。 時間は強制的に掛けられるし、結果に対する評価も定期的にしてくれるし、集団の圧力があり孤独でない。

社会人などになって集団に属していない人にとってはこの3つの要素を確保できる環境を作ることこそが大事で、意志なんかに頼っても無駄である。

あと、phaさんの知の整理術という本では、勉強をする際に次の3つが大事だと書いてる。

  • 「習慣の力」でやる
  • 「ゲーム感覚」でやる
  • 「楽しいことだけ」やる

こちらはphaさんらしく、いかにストレスをなくして続けるか、ということに重点がおかれている。

たくさん失敗して自分にとって大切だなと思うのは、性急にわかりやすい結果だけを求めず、作業自体を楽しくできるようにすること。

筋トレだったら見た目であったり、ブログだったら閲覧数だったりがわかりやすい結果である。 わかりやすい結果はわかりやすいから注目しやすいのだけど、必ずしも作業量とリアルタイムに相関するわけではない。なので結果がでないとやる気がなくなって、作業量も減っていき、ますます結果がでなくなるみたいなことにもなりがちだ。

次に自分が大切だと思うのは結城先生の孤独を避ける、ということだ。これは喜びを分かち合えたり、わからないことを聞けたりということもあるし、同調圧力みたいなものもある。

ついついここは自分は軽視してしまって、そんなのなくても一人でできるわ!みたいに思ってたけど全然できなかった。やっぱり集団の力は偉大である。

如何に楽しくやるか、如何に他人と絡むか、を重視して筋トレやら勉強はやっていこうと思う。

Dockerを使う

DockerでPython機械学習環境を作る。 以下の記事を参考にする。

dr-asa.hatenablog.com

$ docker run -p 8888:8888 --name ml-container -it asashiho/ml-jupyter-python3

dockerを起動してこのコマンドを打つだけ。 超簡単! これはすごい。 環境構築がめちゃくちゃ楽。

終了はcontrol + C と。

ん、再開はどうしたらいいのか? 一旦閉じると、

http://localhost:8888/?token=<your token>

では当然もうアクセスできない。

$ docker run -p 8888:8888 --name ml-container -it asashiho/ml-jupyter-python3

を再度打つと、error

Error response from daemon: Conflict. The container name "/ml-container" is already in use by container "75a667c2f32b7043e763fdf464d1cc35555f6703e983fd0c8aa99110066468cd". You have to remove (or rename) that container to be able to reuse that name.

コンテナを確認

docker container ls -a
CONTAINER ID        IMAGE                         COMMAND                  CREATED             STATUS                   PORTS               NAMES
75a667c2f32b        asashiho/ml-jupyter-python3   "jupyter notebook --…"   3 hours ago         Exited (0) 3 hours ago                       ml-container
fb20782c9075        hello-world                   "/hello"                 4 hours ago         Exited (0) 4 hours ago                       eager_dhawan

これでみると、たしかにいる。

で、restartしてから、再度localhostにアクセスしたらできた。 またnotebookも残っていた。

docker container restart 75a667c2f32b

コマンドはこれ参考にした

qiita.com

他にも参考になりそうな記事
まずはこれ
docs.docker.jp

karaage.hatenadiary.jp

kaggleのkarnelのDocker
amalog.hateblo.jp

Docker for Mac を uninstall

PC移行してDockerを起動しようとしたら以下のエラー

docker com.docker.osx.hyperkit.linux failed to start Exit code 1

直し方わからず。。残念なことに入れ直す。 通常のDockerのmenuに入れないので、手動で消す。 以下の記事を参考にした。 nektony.com

で、あとは普通にmac用のDockerをダウンロードしていれた。

macOS Mojave でpyenv install 3.4.3 が失敗したときの対応

pyenvでのpythonのinstallが失敗したときの対応を記録しておく。

rMBP01:~ kapi$ pyenv install 3.4.3
python-build: use openssl from homebrew
python-build: use readline from homebrew
Downloading Python-3.4.3.tar.xz...
-> https://www.python.org/ftp/python/3.4.3/Python-3.4.3.tar.xz
Installing Python-3.4.3...
python-build: use readline from homebrew

BUILD FAILED (OS X 10.14.4 using python-build 20180424)

Inspect or clean up the working tree at /var/folders/dj/1y_c81qj4jx9mv7zzv8zmh240000gn/T/python-build.20190509123945.6287
Results logged to /var/folders/dj/1y_c81qj4jx9mv7zzv8zmh240000gn/T/python-build.20190509123945.6287.log

Last 10 log lines:
checking for --without-gcc... no
checking for gcc... clang
checking whether the C compiler works... yes
checking for C compiler default output file name... a.out
checking for suffix of executables... 
checking whether we are cross compiling... configure: error: in `/var/folders/dj/1y_c81qj4jx9mv7zzv8zmh240000gn/T/python-build.20190509123945.6287/Python-3.4.3':
configure: error: cannot run C compiled programs.
If you meant to cross compile, use `--host'.
See `config.log' for more details
make: *** No targets specified and no makefile found.  Stop.

config.logを調べる

$cd /var/folders/dj/1y_c81qj4jx9mv7zzv8zmh240000gn/T/python-build.20190509123945.6287/Python-3.4.3
$cat config.log 
~~~~~~~
configure:3926: clang -V >&5
clang-4.0: error: argument to '-V' is missing (expected 1 value)
clang-4.0: error: no input files
configure:3937: $? = 1
configure:3926: clang -qversion >&5
clang-4.0: error: unknown argument: '-qversion'
clang-4.0: error: no input files
~~~~~~~
configure:4105: clang -o conftest  -I/usr/local/opt/readline/include -I/usr/local/opt/readline/include -I/usr/local/opt/openssl/include -I/Users/prokapi/.pyenv/versions/3.4.3/include  -L/usr/local/opt/readline/lib -L/usr/local/opt/readline/lib -L/usr/local/opt/openssl/lib -L/Users/prokapi/.pyenv/versions/3.4.3/lib  conftest.c  >&5
conftest.c:8:10: fatal error: 'stdio.h' file not found
#include <stdio.h>
         ^~~~~~~~~
1 error generated.

clangのところでerrorが発生している。 macに入っているclangを調べる。

https://teratail.com/questions/176364

which -a clang
/usr/local/opt/llvm/bin/clang
/usr/bin/clang

上のclangが使われていて、それだとビルドが失敗してる? /usr/bin/clang の方はx-codeのclang。本来はこっちが使われるべきでは。 上のclangは使っていないので削除することにする。

$ brew uninstall llvm
Uninstalling /usr/local/Cellar/llvm/4.0.0... (2,245 files, 1GB)

再びpyenvでinstallする。

pyenv install 2.7.1
ERROR: The Python zlib extension was not compiled. Missing the zlib?

Please consult to the Wiki page to fix the problem.
https://github.com/yyuu/pyenv/wiki/Common-build-problems

BUILD FAILED

今度は違うエラー。対処方法はここにあった。

https://qiita.com/TEWi_R/items/aac5bada7c17dba1a7f0

$ CFLAGS="-I$(xcrun --show-sdk-path)/usr/include" pyenv install -v 2.7.1

ようやくpython 2.7.1のビルドができた。 3系も試してみる。

$ pyenv install 3.4.1
zipimport.ZipImportError: can't decompress data; zlib not available
make: *** [install] Error 1

このエラーも対策があった。ありがとう。

https://qiita.com/zreactor/items/c3fd04417e0d61af0afe

xcode-selectの最新バージョン(2354)にMojave用のmacOS SDK headerがデフォルトで入っていないのが原因のようです。

マニュアルで以下をインストールする必要ある:

sudo installer -pkg /Library/Developer/CommandLineTools/Packages/macOS_SDK_headers_for_macOS_10.14.pkg -target /

できました!

$ pyenv install 3.4.1

これで完了。