Surface GoにAnaconda3をインストール

先日保留にしていた Python だけれど、結局 Jupyter Notebook を使ってみたくて Anaconda をインストールした。Anaconda は Python のディストリビューションのひとつで、科学計算関係のライブラリが多くバンドルされていて、そっち方面でよく使われているようだ。
まぁ、科学計算はともかく、Jupyter Notebook も使えるようになるというので Anaconda を選んだってわけ。
インストールは chocolatey を使った。

PS C:\Windows\system32> choco install anaconda3
Chocolatey v0.10.11
Installing the following packages:
anaconda3
By installing you accept licenses for the packages.
Progress: Downloading anaconda3 5.1.0... 100%

anaconda3 v5.1.0 [Approved]
anaconda3 package files install completed. Performing other installation steps.
The package anaconda3 wants to run 'chocolateyinstall.ps1'.
Note: If you don't run this script, the installation will fail.
Note: To confirm automatically next time, use '-y' or consider:
choco feature enable -n allowGlobalConfirmation
Do you want to run the script?([Y]es/[N]o/[P]rint): Y

WARNING: installing anaconda3, this can take a long time, because the installer will write tons of files on your disk
WARNING: Please sit back and relax
WARNING: This usually will take 10-15 mins on an SSD, and about 30 mins on HDD
WARNING:
WARNING: If you want to make sure the program is running, you can open Task Manager
WARNING: you will find the installer running in Background Process
WARNING: Url has SSL/TLS available, switching to HTTPS for download
Downloading anaconda3 64 bit
  from 'https://repo.continuum.io/archive/Anaconda3-5.1.0-Windows-x86_64.exe'
Progress: 100% - Completed download of C:\Users\takatoh\AppData\Local\Temp\chocolatey\anaconda3\5.1.0\Anaconda3-5.1.0-Windows-x86_64.exe (537.08 MB).
Download of Anaconda3-5.1.0-Windows-x86_64.exe (537.08 MB) completed.
Hashes match.
Installing anaconda3...
anaconda3 has been installed.
  anaconda3 can be automatically uninstalled.
 The install of anaconda3 was successful.
  Software installed to 'C:\tools\anaconda3'

Chocolatey installed 1/1 packages.
 See the log for details (C:\ProgramData\chocolatey\logs\chocolatey.log).

コマンドひとつでインストールできるのはいいけど、500MB 以上あるじゃないか。えらい時間がかかった。

で、これで Python が使えるようになったかというと、だめだった。python は C:\tools\Anaconda3 にインストールされているけど、パスが通っていない。chocolatey がやってくれなかったみたいだ。仕方がないので、自分でパスを通した。

PS C:\Users\takatoh> python -V
Python 3.6.4 :: Anaconda, Inc.

これでOK。

[追記]

上記で Python は使えるようになったけど、pip や Anaconda のパッケージマネージャである conda コマンドは相変わらず使えない。使えるようにするには C:\tools\Anaconda\Scripts にパスを通す。通したうえで conda list コマンドを実行してみると、これだけのパッケージがインストールされているのがわかる。

PS C:\Users\takatoh> conda list
# packages in environment at C:\tools\Anaconda3:
#
# Name                    Version                   Build  Channel
_ipyw_jlab_nb_ext_conf    0.1.0            py36he6757f0_0
alabaster                 0.7.10           py36hcd07829_0
anaconda                  5.1.0                    py36_2
anaconda-client           1.6.9                    py36_0
anaconda-navigator        1.7.0                    py36_0
anaconda-project          0.8.2            py36hfad2e28_0
asn1crypto                0.24.0                   py36_0
astroid                   1.6.1                    py36_0
astropy                   2.0.3            py36hfa6e2cd_0
attrs                     17.4.0                   py36_0
babel                     2.5.3                    py36_0
backports                 1.0              py36h81696a8_1
backports.shutil_get_terminal_size 1.0.0            py36h79ab834_2
beautifulsoup4            4.6.0            py36hd4cc5e8_1
bitarray                  0.8.1            py36hfa6e2cd_1
bkcharts                  0.2              py36h7e685f7_0
blaze                     0.11.3           py36h8a29ca5_0
bleach                    2.1.2                    py36_0
bokeh                     0.12.13          py36h047fa9f_0
boto                      2.48.0           py36h1a776d2_1
bottleneck                1.2.1            py36hd119dfa_0
bzip2                     1.0.6                hbe05fcf_4
ca-certificates           2017.08.26           h94faf87_0
certifi                   2018.1.18                py36_0
cffi                      1.11.4           py36hfa6e2cd_0
chardet                   3.0.4            py36h420ce6e_1
click                     6.7              py36hec8c647_0
cloudpickle               0.5.2                    py36_1
clyent                    1.2.2            py36hb10d595_1
colorama                  0.3.9            py36h029ae33_0
comtypes                  1.1.4                    py36_0
conda                     4.4.10                   py36_0
conda-build               3.4.1                    py36_0
conda-env                 2.6.0                h36134e3_1
conda-verify              2.0.0            py36h065de53_0
console_shortcut          0.1.1                h6bb2dd7_3
contextlib2               0.5.5            py36he5d52c0_0
cryptography              2.1.4            py36he1d7878_0
curl                      7.58.0               h7602738_0
cycler                    0.10.0           py36h009560c_0
cython                    0.27.3           py36h22f4c84_0
cytoolz                   0.9.0            py36hfa6e2cd_0
dask                      0.16.1                   py36_0
dask-core                 0.16.1                   py36_0
datashape                 0.5.4            py36h5770b85_0
decorator                 4.2.1                    py36_0
distributed               1.20.2                   py36_0
docutils                  0.14             py36h6012d8f_0
entrypoints               0.2.3            py36hfd66bb0_2
et_xmlfile                1.0.1            py36h3d2d736_0
fastcache                 1.0.2            py36hfa6e2cd_2
filelock                  2.0.13           py36h20000bf_0
flask                     0.12.2           py36h98b5e8f_0
flask-cors                3.0.3            py36h8a3855d_0
freetype                  2.8                  h51f8f2c_1
get_terminal_size         1.0.0                h38e98db_0
gevent                    1.2.2            py36h342a76c_0
glob2                     0.6              py36hdf76b57_0
greenlet                  0.4.12           py36ha00ad21_0
h5py                      2.7.1            py36he54a1c3_0
hdf5                      1.10.1               h98b8871_1
heapdict                  1.0.0                    py36_2
html5lib                  1.0.1            py36h047fa9f_0
icc_rt                    2017.0.4             h97af966_0
icu                       58.2                 ha66f8fd_1
idna                      2.6              py36h148d497_1
imageio                   2.2.0            py36had6c2d2_0
imagesize                 0.7.1            py36he29f638_0
intel-openmp              2018.0.0             hd92c6cd_8
ipykernel                 4.8.0                    py36_0
ipython                   6.2.1            py36h9cf0123_1
ipython_genutils          0.2.0            py36h3c5d0ee_0
ipywidgets                7.1.1                    py36_0
isort                     4.2.15           py36h6198cc5_0
itsdangerous              0.24             py36hb6c5a24_1
jdcal                     1.3              py36h64a5255_0
jedi                      0.11.1                   py36_0
jinja2                    2.10             py36h292fed1_0
jpeg                      9b                   hb83a4c4_2
jsonschema                2.6.0            py36h7636477_0
jupyter                   1.0.0                    py36_4
jupyter_client            5.2.2                    py36_0
jupyter_console           5.2.0            py36h6d89b47_1
jupyter_core              4.4.0            py36h56e9d50_0
jupyterlab                0.31.4                   py36_0
jupyterlab_launcher       0.10.2                   py36_0
lazy-object-proxy         1.3.1            py36hd1c21d2_0
libcurl                   7.58.0               h7602738_0
libiconv                  1.15                 h1df5818_7
libpng                    1.6.34               h79bbb47_0
libssh2                   1.8.0                hd619d38_4
libtiff                   4.0.9                h0f13578_0
libxml2                   2.9.7                h79bbb47_0
libxslt                   1.1.32               hf6f1972_0
llvmlite                  0.21.0           py36he0b0552_0
locket                    0.2.0            py36hfed976d_1
lxml                      4.1.1            py36hef2cd61_1
lzo                       2.10                 h6df0209_2
markupsafe                1.0              py36h0e26971_1
matplotlib                2.1.2            py36h016c42a_0
mccabe                    0.6.1            py36hb41005a_1
menuinst                  1.4.11           py36hfa6e2cd_0
mistune                   0.8.3                    py36_0
mkl                       2018.0.1             h2108138_4
mkl-service               1.1.2            py36h57e144c_4
mpmath                    1.0.0            py36hacc8adf_2
msgpack-python            0.5.1            py36he980bc4_0
multipledispatch          0.4.9            py36he44c36e_0
navigator-updater         0.1.0            py36h8a7b86b_0
nbconvert                 5.3.1            py36h8dc0fde_0
nbformat                  4.4.0            py36h3a5bc1b_0
networkx                  2.1                      py36_0
nltk                      3.2.5            py36h76d52bb_0
nose                      1.3.7            py36h1c3779e_2
notebook                  5.4.0                    py36_0
numba                     0.36.2          np114py36h12cb543_0
numexpr                   2.6.4            py36h30784b8_0
numpy                     1.14.0           py36h4a99626_1
numpydoc                  0.7.0            py36ha25429e_0
odo                       0.5.1            py36h7560279_0
olefile                   0.45.1                   py36_0
openpyxl                  2.4.10                   py36_0
openssl                   1.0.2n               h74b6da3_0
packaging                 16.8             py36ha0986f6_1
pandas                    0.22.0           py36h6538335_0
pandoc                    1.19.2.1             hb2460c7_1
pandocfilters             1.4.2            py36h3ef6317_1
parso                     0.1.1            py36hae3edee_0
partd                     0.3.8            py36hc8e763b_0
path.py                   10.5             py36h2b94a8f_0
pathlib2                  2.3.0            py36h7bfb78b_0
patsy                     0.5.0                    py36_0
pep8                      1.7.1                    py36_0
pickleshare               0.7.4            py36h9de030f_0
pillow                    5.0.0            py36h0738816_0
pip                       9.0.1            py36h226ae91_4
pkginfo                   1.4.1            py36hb0f9cfa_1
pluggy                    0.6.0            py36hc7daf1e_0
ply                       3.10             py36h1211beb_0
prompt_toolkit            1.0.15           py36h60b8f86_0
psutil                    5.4.3            py36hfa6e2cd_0
py                        1.5.2            py36hbcfbabc_0
pycodestyle               2.3.1            py36h7cc55cd_0
pycosat                   0.6.3            py36h413d8a4_0
pycparser                 2.18             py36hd053e01_1
pycrypto                  2.6.1            py36hfa6e2cd_7
pycurl                    7.43.0.1         py36h74b6da3_0
pyflakes                  1.6.0            py36h0b975d6_0
pygments                  2.2.0            py36hb010967_0
pylint                    1.8.2                    py36_0
pyodbc                    4.0.22           py36h6538335_0
pyopenssl                 17.5.0           py36h5b7d817_0
pyparsing                 2.2.0            py36h785a196_1
pyqt                      5.6.0            py36hb5ed885_5
pysocks                   1.6.7            py36h698d350_1
pytables                  3.4.2            py36h71138e3_2
pytest                    3.3.2                    py36_0
python                    3.6.4                h6538335_1
python-dateutil           2.6.1            py36h509ddcb_1
pytz                      2017.3           py36h1d3fa6b_0
pywavelets                0.5.2            py36hc649158_0
pywin32                   222              py36hfa6e2cd_0
pywinpty                  0.5              py36h6538335_1
pyyaml                    3.12             py36h1d1928f_1
pyzmq                     16.0.3           py36he714bf5_0
qt                        5.6.2           vc14h6f8c307_12  [vc14]
qtawesome                 0.4.4            py36h5aa48f6_0
qtconsole                 4.3.1            py36h99a29a9_0
qtpy                      1.3.1            py36hb8717c5_0
requests                  2.18.4           py36h4371aae_1
rope                      0.10.7           py36had63a69_0
ruamel_yaml               0.15.35          py36hfa6e2cd_1
scikit-image              0.13.1           py36hfa6e2cd_1
scikit-learn              0.19.1           py36h53aea1b_0
scipy                     1.0.0            py36h1260518_0
seaborn                   0.8.1            py36h9b69545_0
send2trash                1.4.2                    py36_0
setuptools                38.4.0                   py36_0
simplegeneric             0.8.1                    py36_2
singledispatch            3.4.0.3          py36h17d0c80_0
sip                       4.18.1           py36h9c25514_2
six                       1.11.0           py36h4db2310_1
snowballstemmer           1.2.1            py36h763602f_0
sortedcollections         0.5.3            py36hbefa0ab_0
sortedcontainers          1.5.9                    py36_0
sphinx                    1.6.6                    py36_0
sphinxcontrib             1.0              py36hbbac3d2_1
sphinxcontrib-websupport  1.0.1            py36hb5e5916_1
spyder                    3.2.6                    py36_0
sqlalchemy                1.2.1            py36hfa6e2cd_0
sqlite                    3.22.0               h9d3ae62_0
statsmodels               0.8.0            py36h6189b4c_0
sympy                     1.1.1            py36h96708e0_0
tblib                     1.3.2            py36h30f5020_0
terminado                 0.8.1                    py36_1
testpath                  0.3.1            py36h2698cfe_0
tk                        8.6.7                hcb92d03_3
toolz                     0.9.0                    py36_0
tornado                   4.5.3                    py36_0
traitlets                 4.3.2            py36h096827d_0
typing                    3.6.2            py36hb035bda_0
unicodecsv                0.14.1           py36h6450c06_0
urllib3                   1.22             py36h276f60a_0
vc                        14                   h0510ff6_3
vs2015_runtime            14.0.25123                    3
wcwidth                   0.1.7            py36h3d5aa90_0
webencodings              0.5.1            py36h67c50ae_1
werkzeug                  0.14.1                   py36_0
wheel                     0.30.0           py36h6c3ec14_1
widgetsnbextension        3.1.0                    py36_0
win_inet_pton             1.0.1            py36he67d7fd_1
win_unicode_console       0.5              py36hcdbd4b5_0
wincertstore              0.2              py36h7fe50ca_0
winpty                    0.4.3                         4
wrapt                     1.10.11          py36he5f5981_0
xlrd                      1.1.0            py36h1cb58dc_1
xlsxwriter                1.0.2            py36hf723b7d_0
xlwings                   0.11.5                   py36_0
xlwt                      1.3.0            py36h1a4751e_0
yaml                      0.1.7                hc54c509_2
zict                      0.1.3            py36h2d8e73e_0
zlib                      1.2.11               h8395fce_2

CentOS 7にpyenvでPythonをインストール

CentOS 7 には標準で Python がインストールされている。ただちょっと古い。それに pip もない。

[takatoh@aybesea ~]$ python -V
Python 2.7.5
[takatoh@aybesea ~]$ pip list
bash: pip: コマンドが見つかりませんでした...

なので、pyenv を使って Python をインストールすることにした。これはひと月程前にも Ubuntu でやってるから大丈夫だろう。

環境

  • CentOS 7.4.1708 (Core)
  • システムの Python: 2.7.5

参考ページ

依存ライブラリのインストール

これ、なにが必要なんだかいつもよく分からないんだけど、とりあえず参考ページの通りにしてみる。

[takatoh@aybesea ~]$ sudo yum -y install zlib-devel bzip2 bzip2-devel readline readline-devel sqlite sqlite-devel openssl openssl-devel

pyenvとpyenv-virtualenvのインストール

[takatoh@aybesea ~]$ git clone https://github.com/yyuu/pyenv.git .pyenv
[takatoh@aybesea ~]$ git clone https://github.com/yyuu/pyenv-virtualenv.git .pyenv/plugins/pyenv-virtualenv

.bash_profile に追記。

export PYENV_ROOT=$HOME/.pyenv
export PATH=$PYENV_ROOT/bin:$PATH
eval "$(pyenv init -)"
eval "$(pyenv virtualenv-init -)"

.bash_profile を読み直し。

[takatoh@aybesea ~]$ source .bash_profile

Pythonのインストール

2.7.14 と 3.6.4 をインストール。

[takatoh@aybesea ~]$ pyenv install 2.7.14
Downloading Python-2.7.14.tar.xz...
-> https://www.python.org/ftp/python/2.7.14/Python-2.7.14.tar.xz
Installing Python-2.7.14...
Installed Python-2.7.14 to /home/takatoh/.pyenv/versions/2.7.14

[takatoh@aybesea ~]$ pyenv install 3.6.4
Downloading Python-3.6.4.tar.xz...
-> https://www.python.org/ftp/python/3.6.4/Python-3.6.4.tar.xz
Installing Python-3.6.4...
Installed Python-3.6.4 to /home/takatoh/.pyenv/versions/3.6.4

バージョンの切り替え

[takatoh@aybesea ~]$ pyenv versions
* system (set by /home/takatoh/.pyenv/version)
  2.7.14
  3.6.4

とりあえず、常用には 2.7.14。

[takatoh@aybesea ~]$ pyenv global 2.7.14
[takatoh@aybesea ~]$ pyenv versions
  system
* 2.7.14 (set by /home/takatoh/.pyenv/version)
  3.6.4

OK。当面これで運用してみる。

pyenvとvirtualenvを使ってみる

Python は 2.7.12 を常用しているけど、Python3 系に対応する必要があったので、これを機会にやってみることにした。まあ、今時 Python2 系だけってものなんだと思うので、ちょうどいいといえばそのとおりではある。
で、複数のバージョンを共存させるには pyenv を使うのがいいらしい。virtualenv については、以前エントリを書いたこともあるけど、pyenv と合わせて使うには pyenv のプラグインである pyenv-virtualenv を使うといいらしいので、そうすることにする。

環境

  • Ubuntu 16.04 LTS
  • システムの Python : 2.7.12

インストール

pyenv のインストール。インストールというか github からクローンする。システムにインストールする方法と、ユーザーディレクトリにインストールする方法があるようだけど、今回は後者にした。

takatoh@envelopes $ git clone https://github.com/yyuu/pyenv.git ~/.pyenv

pyenv-virtualenv のインストール

takatoh@envelopes $ git clone https://github.com/yyuu/pyenv-virtualenv.git ~/.pyenv/plugins/pyenv-virtualenv

.bachrcに設定を書き加える

以下を追記

export PYENV_ROOT=$HOME/.pyenv
export PATH=$PYENV_ROOT/bin:$PATH
eval "$(pyenv init -)"
eval "$(pyenv virtualenv-init -)"

書いたらいったん端末を起動し直す。

Pythonのインストール

特定のバージョンをインストールするには pyenv install コマンド。-l オプションをつけると、インストール可能なバージョンの一覧が見られる。すげーいっぱい出てくるけど、今回は 3.6.3 をインストールしてみた。

takatoh@envelopes $ pyenv install 3.6.3
Downloading Python-3.6.3.tar.xz...
-> https://www.python.org/ftp/python/3.6.3/Python-3.6.3.tar.xz
Installing Python-3.6.3...
WARNING: The Python bz2 extension was not compiled. Missing the bzip2 lib?
WARNING: The Python readline extension was not compiled. Missing the GNU readline lib?
Installed Python-3.6.3 to /home/takatoh/.pyenv/versions/3.6.3

おっと、なんか WARNING がでたな。コンパイルするときにライブラリが足りないようだ。

takatoh@envelopes $ sudo apt install libbz2-dev libreadline6-dev

インストールした 3.6.3 をいったんアンインストールしてやり直し。

takatoh@envelopes $ pyenv uninstall 3.6.3
pyenv: remove /home/takatoh/.pyenv/versions/3.6.3? Y
takatoh@envelopes $ pyenv install 3.6.3
Downloading Python-3.6.3.tar.xz...
-> https://www.python.org/ftp/python/3.6.3/Python-3.6.3.tar.xz
Installing Python-3.6.3...
Installed Python-3.6.3 to /home/takatoh/.pyenv/versions/3.6.3

今度はうまくいった。
インストールしたバージョンは pyenv versions コマンドで見られる。

takatoh@envelopes $ pyenv versions
* system (set by /home/takatoh/.pyenv/version)
  3.6.3

* がついてるのがアクティブなバージョンだ。

バージョンの切り替え

上に書いたように、現状ではシステムにインストールされている Python が使われている。これを切り替えるには、pyenv global または pyenv local コマンドを使う。global は全体に、local はカレントディレクトリに適用される。

takatoh@envelopes $ pyenv local 3.6.3
takatoh@envelopes $ pyenv versions
  system
* 3.6.3 (set by /home/takatoh/.python-version)
takatoh@envelopes $ python -V
Python 3.6.3

つぎの作業に入る前にいったんシステムのバージョンに戻しておく。

virtualenvで新しい環境を作る

pyenv virtualenv [version] <virtualenv-name> とすることで新しい環境を作れる。

takatoh@envelopes $ pyenv virtualenv 3.6.3 brs-3.6.3
Requirement already satisfied: setuptools in /home/takatoh/.pyenv/versions/3.6.3/envs/brs-3.6.3/lib/python3.6/site-packages
Requirement already satisfied: pip in /home/takatoh/.pyenv/versions/3.6.3/envs/brs-3.6.3/lib/python3.6/site-packages
takatoh@envelopes $ pyenv local brs-3.6.3
(brs-3.6.3) takatoh@envelopes $

プロンプトのカッコ内に環境名が表示されている。この環境は保存されていて、以後、このディレクトリに戻ってくるたびに自動的に適用される。

(brs-3.6.3) takatoh@envelopes $ cd ..
takatoh@envelopes $ pyenv versions
* system (set by /home/takatoh/.python-version)
  3.6.3
  3.6.3/envs/brs-3.6.3
  brs-3.6.3
takatoh@envelopes $ cd brs
(brs-3.6.3) takatoh@envelopes $ pyenv versions
  system
  3.6.3
  3.6.3/envs/brs-3.6.3
* brs-3.6.3 (set by /home/takatoh/w/brs/.python-version)
(brs-3.6.3) takatoh@envelopes $

パッケージのインストール

通常通り pip コマンドを使う。

(brs-3.6.3) takatoh@envelopes $ pip list --format=columns
Package    Version
---------- -------
pip        9.0.1  
setuptools 28.8.0 
(brs-3.6.3) takatoh@envelopes $ pip install click
Collecting click
  Using cached click-6.7-py2.py3-none-any.whl
Installing collected packages: click
Successfully installed click-6.7
(brs-3.6.3) takatoh@envelopes $ pip list --format=columns
Package    Version
---------- -------
click      6.7    
pip        9.0.1  
setuptools 28.8.0

とりあえずこんなもんかな。

Ubuntuで作ったwheelパッケージをWindowsにインストールする

このあいだ Ubuntu で作った wheel パッケージ、Windows にインストールしてみた。
pure Python なのであたりまえだけど、何の問題もなくインストールできた。

^o^ > pip install myapp-0.0.1-py2-none-any.whl
Processing c:\users\hiro\documents\tmp\myapp-0.0.1-py2-none-any.whl
Installing collected packages: myapp
Successfully installed myapp-0.0.1

動作確認。

^o^ > myapp foo bar baz
['C:\\Python27\\Scripts\\myapp', 'foo', 'bar', 'baz']

^o^ > which myapp
C:/Python27/Scripts/myapp.EXE

へぇ、Windows だと .EXE ファイルができるんだ。

Pythonのwith構文を覚えた!

基本的な使い方

ファイルを開いて読み込む処理が典型的かな。今まではこうしていた:

f = open('data.txt', 'r')
print f.read()
f.close()

with 構文を使うとこう書ける:

with open('data.txt', 'r') as f:
    print f.read()

開いたファイルは、ブロックを抜けるときに自動で閉じてくれる。

withに対応したクラス

with 構文に書けるクラスはファイルだけじゃない。__enter____exit__ の2つのメソッドを持ったクラス(のインスタンス)なら何でもいい。__enter__ はブロックに入るときに、__exit__ はブロックを抜けるときに呼ばれる。
ちょっと試してみよう。

class Test():
    def __init__(self, hoge):
        print 'init'
        self.hoge = hoge

    def print_hoge(self):
        print self.hoge

    def __enter__(self):
        print 'enter'
        return self

    def __exit__(self, type, value, traceback):
         print 'exit'

with Test('hoge') as t:
    t.print_hoge()

実行結果:

takatoh@apostrophe $ python test_with.py
init
enter
hoge
exit

ブロックの中を実行する前に __enter__ が、あとに __exit__ が呼ばれているのがわかる。
as t: の部分の t には、__enter__ の返り値が代入されるので、self を返している。

ともあれ、1行短くなって見やすくなる。これからはこうやって書くようにしよう。

Python: コマンドを含むwheelパッケージを作る

Python のパッケージというと、以前は Egg だったけど最近では wheel パッケージが増えているようだ。Egg パッケージは一度作ってみたことがあるので、今回は wheel パッケージを作ってみる。ついでに、ライブラリではなく、実行できるコマンドを含んだパッケージを作る。

環境

  • Ubuntu 16.04 LTS
  • Python 2.7.12
  • wheel 0.29.0

ソースファイル

ソースの構成はこう:

takatoh@apostrophe $ tree .
.
├── myapp
│   ├── __init__.py
│   └── main.py
└── setup.py

1 directory, 3 files

myapp/main.py が実行されるコマンドのソースで、setup.py が wheel パッケージを作るためのファイル。

import sys

def main():
    print(sys.argv)
import setuptools

setuptools.setup(
    name='myapp',
    version='0.0.1',
    packages=setuptools.find_packages(),
    entry_points={
        'console_scripts':[
            'myapp=myapp.main:main'
        ]
    }
)

myapp/main.py は main 関数があるだけ。setup.py も最低限だけど、色を付けた行が実行コマンドを指定していて、'myapp=myapp.main:main'myapp コマンドを実行すると myapp/main.py の main 関数を呼び出すことを意味している。myapp/__init__.py は空でいい。

wheelパッケージの作成

setup.py にパラメータとして bdist_wheel を渡してやる。

takatoh@apostrophe $ python setup.py bdist_wheel
running bdist_wheel
running build
running build_py
creating build
creating build/lib.linux-x86_64-2.7
creating build/lib.linux-x86_64-2.7/myapp
copying myapp/main.py -> build/lib.linux-x86_64-2.7/myapp
copying myapp/__init__.py -> build/lib.linux-x86_64-2.7/myapp
installing to build/bdist.linux-x86_64/wheel
running install
running install_lib
creating build/bdist.linux-x86_64
creating build/bdist.linux-x86_64/wheel
creating build/bdist.linux-x86_64/wheel/myapp
copying build/lib.linux-x86_64-2.7/myapp/main.py -> build/bdist.linux-x86_64/wheel/myapp
copying build/lib.linux-x86_64-2.7/myapp/__init__.py -> build/bdist.linux-x86_64/wheel/myapp
running install_egg_info
running egg_info
creating myapp.egg-info
writing myapp.egg-info/PKG-INFO
writing top-level names to myapp.egg-info/top_level.txt
writing dependency_links to myapp.egg-info/dependency_links.txt
writing entry points to myapp.egg-info/entry_points.txt
writing manifest file 'myapp.egg-info/SOURCES.txt'
reading manifest file 'myapp.egg-info/SOURCES.txt'
writing manifest file 'myapp.egg-info/SOURCES.txt'
Copying myapp.egg-info to build/bdist.linux-x86_64/wheel/myapp-0.0.1.egg-info
running install_scripts
creating build/bdist.linux-x86_64/wheel/myapp-0.0.1.dist-info/WHEEL

すると dist ディレクトリに wheel パッケージができる。

takatoh@apostrophe $ ls dist
myapp-0.0.1-py2-none-any.whl

インストールと確認

インストールは普通に sudo pip install で。

takatoh@apostrophe $ sudo pip install dist/myapp-0.0.1-py2-none-any.whl
Processing ./dist/myapp-0.0.1-py2-none-any.whl
Installing collected packages: myapp
Successfully installed myapp-0.0.1

テスト。

takatoh@apostrophe $ myapp foo bar baz
['/usr/local/bin/myapp', 'foo', 'bar', 'baz']

うまく行った。myapp コマンドが /use/local/bin/myapp にインストールされているのがわかる。

参考ページ

 cf. Pythonでグローバルコマンドを含んだパッケージを作る – Qiita
 cf. Python: Wheel でパッケージを配布する – CUBE SUGAR STORAGE

Flaskアプリを引っ越す(5)解決編

データの更新ができない問題が解決した。
アプリのせいじゃなくて、パーミッションの問題だった。次のようにして解決。

bruschetta@wplj:~/bruschetta$ chmod 666 bruschetta/bruschetta.db
bruschetta@wplj:~/bruschetta$ chmod 777 bruschetta

最初、データベースファイルのパーミッションだけ変えてみたけどダメで、ディレクトリのパーミッションも変えたら OK になった。

Flaskアプリを引っ越す(3)

昨日は、Ubuntu 起動時にアプリを自動起動する systemctl enable コマンドでエラーが出る、というところで終わった。

動いている

エラーが出ているのでてっきり自動起動に失敗しているものとばかり思っていたけど、ちゃんと enabled になっていた。

bruschetta@wplj:~$ systemctl list-unit-files | grep bruschetta
bruschetta.service                         enabled
bruschetta@wplj:~$ systemctl status bruschetta
● bruschetta.service - Bruschetta service
   Loaded: loaded (/lib/systemd/system/bruschetta.service; enabled; vendor prese
   Active: active (running) since 火 2017-02-28 22:42:15 JST; 6h ago
  Process: 1105 ExecStart=/etc/init.d/bruschetta start (code=exited, status=0/SU
 Main PID: 1138 (uwsgi)
   CGroup: /system.slice/bruschetta.service
           ├─1138 uwsgi bruschetta.ini
           ├─1569 uwsgi bruschetta.ini
           └─1570 uwsgi bruschetta.ini

試しにリブートしてみても、ちゃんと自動起動していることを確認した。
うーん、ますます訳がわかんなくなってきた。が、まあいい、この件は後でゆっくり調べることにして、先に進めよう。

データベースファイルをコピー

apostrophe からデータベースファイルをコピーする。Sqlite3 なので、単にファイルを上書きコピーすればいいだけだ。

bruschetta@wplj:~/bruschetta/bruschetta$ scp bruschetta@apostrophe:bruschetta/bruschetta/bruschetta.db .
bruschetta@apostrophe's password: 
bruschetta.db                                 100%  876KB 876.0KB/s   00:00

そんで、アプリを再起動。

bruschetta@wplj:~/bruschetta/bruschetta$ sudo systemctl stop bruschetta
[sudo] bruschetta のパスワード: 
bruschetta@wplj:~/bruschetta/bruschetta$ sudo systemctl start bruschetta

service コマンドじゃなくて systemctl コマンドを使ってみた。ブラウザで確認してみると、無事データが移行されていた。

Nginxのヴァーチャルホスト

ヴァーチャルホスト bruschetta でアプリにアクセスできるようにする。これも設定ファイルを apostrophe からコピーしてくる。

bruschetta@wplj:~/bruschetta/bruschetta$ cd /etc/nginx/sites-available
bruschetta@wplj:/etc/nginx/sites-available$ sudo scp bruschetta@apostrophe:/etc/nginx/sites-available/bruschetta .
bruschetta@apostrophe's password: 
bruschetta                                    100%  905     0.9KB/s   00:00

コピーした設定ファイルがこれ。

upstream uwsgi-bruschetta {
    server 127.0.0.1:9090;
}

server {
    # port
    listen 80;

    # server name
    server_name bruschetta;

    # document root
    root /home/bruschetta/bruschetta;

    # index
    #index index.php index.html index.htm;

    # log files
    access_log /var/log/nginx/bruschetta/access.log combined;
    error_log /var/log/nginx/bruschetta/error.log warn;

    keepalive_timeout 60;
    proxy_connect_timeout 60;
    proxy_read_timeout 60;
    proxy_send_timeout 60;

    #client_max_body_size 20M;

    location / {
        #root /home/bruschetta/bruschetta;

        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $http_host;
        proxy_pass http://uwsgi-bruschetta;
    }

}

sites-enabled/bruschetta にリンクを張る。

bruschetta@wplj:/etc/nginx/sites-available$ sudo ln -s /etc/nginx/sites-available/bruschetta /etc/nginx/sites-enabled/bruschetta
bruschetta@wplj:/etc/nginx/sites-available$ cd ../sites-enabled
bruschetta@wplj:/etc/nginx/sites-enabled$ ls
bruschetta  default

ログファイル用のディレクトリを作る。

bruschetta@wplj:/etc/nginx/sites-enabled$ sudo mkdir /var/log/nginx/bruschetta

/etc/hosts ファイルを編集して1行追加。

192.168.1.6 bruschetta

そして Nginx を再起動。

bruschetta@wplj:/etc/nginx/sites-enabled$ sudo systemctl restart nginx

OK。あとは他のマシンの hosts ファイルも編集して bruschetta にアクセスできるようにするだけだ。はー、長かった。

Flaskアプリを引っ越す(2)

前のエントリは、/lib/systemd/system/bruschetta.service ファイルの書き方がわからない、というところで終わった。

Unit設定ファイル

その後、なんとなくではあるけど書けたので、とりあえずは動くようになった。参考になったページはここ。

 cf. 第4回 Unit設定ファイルの記述方法 – ITPro

Unit設定ファイルというらしい。ともかく、このページを参考にして書いたのがこれ。

[Unit]
Description=Bruschetta service
After=network.target

[Service]
ExecStart=/etc/init.d/bruschetta start
ExecStop=/etc/init.d/bruschetta stop
Restart=always
Type=forking
PIDFile=/var/run/bruschetta.pid

このファイルから、/etc/systemd/system/bruschetta.service にリンクを張る。

bruschetta@wplj:~$ sudo ln -s /lib/systemd/system/bruschetta.service /etc/systemd/system/bruschetta.service

起動確認

で、起動。

bruschetta@wplj:~$ sudo service bruschetta start

やった!動いた!
startstop を繰り返してみると、ちゃんと起動、停止する。今度は大丈夫そうだ。

ところが

Ubuntu 自体を再起動すると、サービスが動いていない。またもググッて調べた結果、systemctl enable コマンドを使えばいいらしい。

 cf. Systemdを使ってさくっと自作コマンドをサービス化してみる – Qiita

bruschetta@wplj:~$ sudo systemctl enable bruschetta
[sudo] bruschetta のパスワード: 
Synchronizing state of bruschetta.service with SysV init with /lib/systemd/systemd-sysv-install...
Executing /lib/systemd/systemd-sysv-install enable bruschetta
insserv: warning: script 'bruschetta' missing LSB tags and overrides
update-rc.d: error: bruschetta Default-Start contains no runlevels, aborting.

あれぇ?

更にググるとこのページを見つけた。

 cf. Systemd入門(4) – serviceタイプUnitの設定ファイル – めもめも

/lib/systemd/system/bruschetta.service ファイルに [Install] セクションを付け足してみる。

[Unit]
Description=Bruschetta service
After=network.target

[Service]
ExecStart=/etc/init.d/bruschetta start
ExecStop=/etc/init.d/bruschetta stop
Restart=always
Type=forking
PIDFile=/var/run/bruschetta.pid

[Install]
WantedBy=multi-user.target

今度はどうだ。

bruschetta@wplj:~$ sudo systemctl enable bruschetta
Synchronizing state of bruschetta.service with SysV init with /lib/systemd/systemd-sysv-install...
Executing /lib/systemd/systemd-sysv-install enable bruschetta
insserv: warning: script 'K01bruschetta' missing LSB tags and overrides
insserv: warning: script 'bruschetta' missing LSB tags and overrides
update-rc.d: error: bruschetta Default-Start contains no runlevels, aborting.

ああダメだ。……というところで今日は時間切れ。

Flaskアプリを引っ越す

apostrophe で動いている Flask アプリを wplj に引っ越す。

virtualenvとuwsgiのインストール

takatoh@wplj $ pip install virtualenv
プログラム 'pip' はまだインストールされていません。 次のように入力することでインストールできます:
sudo apt install python-pip

あれ、pip がインストールされてなかった。

takatoh@wplj $ sudo apt install python-pip

では、あらためて。

takatoh@wplj $ sudo pip install virtualenv
takatoh@wplj $ sudo pip install uwsgi

以前のエントリを見たら sudo をつけていたので、今回もつけて実行した。

takatoh@wplj $ pip list
adium-theme-ubuntu (0.3.4)
dnspython (1.12.0)
pip (8.1.1)
pycrypto (2.6.1)
setuptools (20.7.0)
unity-lens-photos (1.0)
uWSGI (2.0.14)
virtualenv (15.1.0)
wheel (0.29.0)
You are using pip version 8.1.1, however version 9.0.1 is available.
You should consider upgrading via the 'pip install --upgrade pip' command.

virtualenv と uWSGI がちゃんとインストールされてる。pip をアップグレードしろみたいなメッセージが出てるけど、これはあとでいいや。

ユーザを作る

Flask アプリを動かす専用のユーザを作る。

takatoh@wplj $ sudo adduser bruschetta
ユーザー `bruschetta' を追加しています...
新しいグループ `bruschetta' (1001) を追加しています...
新しいユーザー `bruschetta' (1001) をグループ `bruschetta' に追加しています...
ホームディレクトリ `/home/bruschetta' を作成しています...
`/etc/skel' からファイルをコピーしています...
新しい UNIX パスワードを入力してください: 
新しい UNIX パスワードを再入力してください: 
passwd: パスワードは正しく更新されました
bruschetta のユーザ情報を変更中
新しい値を入力してください。標準設定値を使うならリターンを押してください
	フルネーム []: 
	部屋番号 []: 
	職場電話番号 []: 
	自宅電話番号 []: 
	その他 []: 
以上で正しいですか? [Y/n] y

sudo する権利をつける。

takatoh@wplj $ sudo gpasswd -a bruschetta sudo
ユーザ bruschetta をグループ sudo に追加

ここで今作ったユーザでログインしなおす。

Flaskアプリの配置

GitHub からクローン。

bruschetta@wplj:~$ git clone https://github.com/takatoh/Bruschetta.git bruschetta
Cloning into 'bruschetta'...
remote: Counting objects: 358, done.
remote: Total 358 (delta 0), reused 0 (delta 0), pack-reused 358
Receiving objects: 100% (358/358), 31.96 KiB | 0 bytes/s, done.
Resolving deltas: 100% (241/241), done.
Checking connectivity... done.

virtualenv を作って必要なライブラリをインストール。

bruschetta@wplj:~/bruschetta$ virtualenv env
New python executable in /home/bruschetta/bruschetta/env/bin/python
Installing setuptools, pip, wheel...done.
bruschetta@wplj:~/bruschetta$ source env/bin/activate
(env) bruschetta@wplj:~/bruschetta$ pip install -r requirements.txt
Collecting Flask (from -r requirements.txt (line 1))
  Downloading Flask-0.12-py2.py3-none-any.whl (82kB)
    100% |████████████████████████████████| 92kB 3.0MB/s 
Collecting Flask-SQLAlchemy (from -r requirements.txt (line 2))
  Downloading Flask_SQLAlchemy-2.2-py2.py3-none-any.whl
Collecting Flask-Script (from -r requirements.txt (line 3))
  Downloading Flask-Script-2.0.5.tar.gz (42kB)
    100% |████████████████████████████████| 51kB 4.0MB/s 
Collecting itsdangerous>=0.21 (from Flask->-r requirements.txt (line 1))
  Downloading itsdangerous-0.24.tar.gz (46kB)
    100% |████████████████████████████████| 51kB 4.6MB/s 
Collecting click>=2.0 (from Flask->-r requirements.txt (line 1))
  Downloading click-6.7-py2.py3-none-any.whl (71kB)
    100% |████████████████████████████████| 71kB 4.4MB/s 
Collecting Werkzeug>=0.7 (from Flask->-r requirements.txt (line 1))
  Downloading Werkzeug-0.11.15-py2.py3-none-any.whl (307kB)
    100% |████████████████████████████████| 317kB 3.0MB/s 
Collecting Jinja2>=2.4 (from Flask->-r requirements.txt (line 1))
  Downloading Jinja2-2.9.5-py2.py3-none-any.whl (340kB)
    100% |████████████████████████████████| 348kB 4.0MB/s 
Collecting SQLAlchemy>=0.8.0 (from Flask-SQLAlchemy->-r requirements.txt (line 2))
  Downloading SQLAlchemy-1.1.5.tar.gz (5.1MB)
    100% |████████████████████████████████| 5.1MB 237kB/s 
Collecting MarkupSafe>=0.23 (from Jinja2>=2.4->Flask->-r requirements.txt (line 1))
  Downloading MarkupSafe-0.23.tar.gz
Building wheels for collected packages: Flask-Script, itsdangerous, SQLAlchemy, MarkupSafe
  Running setup.py bdist_wheel for Flask-Script ... done
  Stored in directory: /home/bruschetta/.cache/pip/wheels/e2/ea/d8/8d114e46cef819f7d9879504a7f9cb2a88a479af2858223d9f
  Running setup.py bdist_wheel for itsdangerous ... done
  Stored in directory: /home/bruschetta/.cache/pip/wheels/fc/a8/66/24d655233c757e178d45dea2de22a04c6d92766abfb741129a
  Running setup.py bdist_wheel for SQLAlchemy ... done
  Stored in directory: /home/bruschetta/.cache/pip/wheels/8d/0a/3b/2109101a4052e58ad64f83661383a6b483a1c383eb09aad6d6
  Running setup.py bdist_wheel for MarkupSafe ... done
  Stored in directory: /home/bruschetta/.cache/pip/wheels/a3/fa/dc/0198eed9ad95489b8a4f45d14dd5d2aee3f8984e46862c5748
Successfully built Flask-Script itsdangerous SQLAlchemy MarkupSafe
Installing collected packages: itsdangerous, click, Werkzeug, MarkupSafe, Jinja2, Flask, SQLAlchemy, Flask-SQLAlchemy, Flask-Script
Successfully installed Flask-0.12 Flask-SQLAlchemy-2.2 Flask-Script-2.0.5 Jinja2-2.9.5 MarkupSafe-0.23 SQLAlchemy-1.1.5 Werkzeug-0.11.15 click-6.7 itsdangerous-0.24

データベースの初期化。

(env) bruschetta@wplj:~/bruschetta$ python manage.py init_db
manage.py:2: ExtDeprecationWarning: Importing flask.ext.script is deprecated, use flask_script instead.
  from flask.ext.script import Manager
/home/bruschetta/bruschetta/bruschetta/__init__.py:2: ExtDeprecationWarning: Importing flask.ext.sqlalchemy is deprecated, use flask_sqlalchemy instead.
  from flask.ext.sqlalchemy import SQLAlchemy

なんか SQLAlchemy がらみでメッセージが出てるな。まあいい、先に進めよう。
起動確認。

(env) bruschetta@wplj:~/bruschetta$ python manage.py runserver
manage.py:2: ExtDeprecationWarning: Importing flask.ext.script is deprecated, use flask_script instead.
  from flask.ext.script import Manager
/home/bruschetta/bruschetta/bruschetta/__init__.py:2: ExtDeprecationWarning: Importing flask.ext.sqlalchemy is deprecated, use flask_sqlalchemy instead.
  from flask.ext.sqlalchemy import SQLAlchemy
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
127.0.0.1 - - [28/Feb/2017 20:14:23] "GET / HTTP/1.1" 200 -
127.0.0.1 - - [28/Feb/2017 20:14:23] "GET /static/style.css HTTP/1.1" 200 -
127.0.0.1 - - [28/Feb/2017 20:14:23] "GET /favicon.ico HTTP/1.1" 404 -

OK。ブラウザでも確認できた。
ここで virtualenv から抜ける。

(env) bruschetta@wplj:~/bruschetta$ deactivate

uWSGIで起動

uWSGI の設定ファイルを apostrophe からコピーしてくる。

bruschetta@wplj:~/bruschetta$ scp bruschetta@apostrophe:bruschetta/bruschetta.ini .
bruschetta@apostrophe's password: 
bruschetta.ini                                100%  350     0.3KB/s   00:00

内容はこんな感じ。

[uwsgi]
uid = www-data
gid = www-data
http = :9090
venv = /home/bruschetta/bruschetta/env
python-path = /home/bruschetta/bruschetta
wsgi-file = /home/bruschetta/bruschetta/manage.py
callable = app
master=true
pidfile=/var/run/bruschetta.pid
daemonize=/home/bruschetta/bruschetta/bruschetta.log

ディレクトリ構成は一緒だから、このまま行けるはずだ。

bruschetta@wplj:~/bruschetta$ sudo uwsgi bruschetta.ini
sudo bruschetta のパスワード: 
uWSGI getting INI configuration from bruschetta.ini

OK。今度もブラウザで確認できた。

起動スクリプト

これも以前のスクリプトと同じ。

#!/bin/sh

PATH=/usr/local/bin:/usr/bin:/bin:/sbin
APP_ROOT=/home/bruschetta/bruschetta

case "$1" in
start)
cd ${APP_ROOT}
uwsgi bruschetta.ini
;;
stop)
PID_FILE=/var/run/bruschetta.pid
PID=`cat ${PID_FILE}`
kill -INT ${PID}
rm ${PID_FILE}
;;
*)
echo "Usage: bruschetta {start|stop}" >&2
exit 1
;;
esac

exit 0

起動確認。

bruschetta@wplj:~/bruschetta$ sudo service bruschetta start
Failed to start bruschetta.service: Unit bruschetta.service not found.

あれ、ダメだ。bruschetta.service ってなんだ?
ググってみると、/lib/systemd/system/bruschetta.service ファイルを作ってやればいいようだけど、書き方がよくわからない。どうしたもんか……