JupyterLab App について真面目に考える
以前、JupyterLab は複数の(分析)プロジェクトをまたがって使う共通環境なのだから、pipx に入れても良いんじゃないか、という話をした。
その後に JupyterLab App リリースされたという記事が出て、ようやく世間が俺に追いついたな、と。(何様)
ある程度 JupyterLab App を触って分かってきたので、JupyterLab App 関連についてまとめたいと思う。
JupyterLab App とは
JupyterLab App の GitHub repository の Description には "A desktop application for JupyterLab, based on Electron" ってあるけど、これは JupyterLab App の半分しか言っていない。
上記の記事では "It is a self-contained desktop application which bundles a Python environment with several popular Python libraries" って言ってて、これは正しい。
すなわち JupyterLab App には何が入っているのかというと、
- Electron で作られた(シンプルな)Web Browser
- JupyterLab が入っている conda ベースの Python 仮想環境(venv)
の2つが入っている。
で、これをインストールした人は、全く Python の環境が無くても、いきなり JupyterLab が専用アプリとして使えてしまう。
これは便利っちゃあ便利。
便利な点
- インストールがめちゃ簡単
- GitHub repository のトップページ にあるリンクをクリックしてインストーラーをダウンロードし、それをダブルクリックしてインストールすれば、いきなりすぐに Jupyter が使える。
- 専用アプリで快適
- JupyterLab は、起動するとブラウザが立ち上がり、そのプラウザ上で入力したり結果を見たりするものなので、Jupyter しながら Web で検索するみたいな時には、ちょっと面倒くさかったりする
- その点、 JupyterLab App は専用アプリ(専用ブラウザ)なので、余計なものが入っていなく、シンプルで快適
- ブラウザを閉じたら、うっかり JupyterLab も一緒に閉じちゃった、なんてことも無い
不便な点
リリースされたばかりなので、これから改善するのかもしれないが、現時点(2021/10/06)では以下の不便な点がある。
- 既存のパッケージや拡張機能で(すぐには)使えないものがある
- 突然「専用の特別な conda ベースの venv」というものが導入されてしまったので、特に別コマンド・別サーバを叩きに行く拡張機能は、そのままでは使えない
- パッケージを追加することが難しい
- "Python environment with several popular Python libraries" って言ってるけど、NumPy や Matplotlib すら入っておらず、JupyterLab を動かすための最小限のパッケージしか入っていない
- だから JupyterLab で普通に分析をしようと思ったら、上記 NumPy, Matplotlib を始め、ほぼすべてを scratch からインストールしなければならない
- しかし、パッケージを追加する方法は公式には提供されておらず、実際にちょっとメンドイ
特に後者は結構致命的で、Issues を見ても、これ関係が多いように思う。
JupyterLab App でのパッケージ管理
解決策を示しておく。
JupyterLab App でインストールされる venv は以下の場所にある。
- MacOS X
/Applications/JupyterLab.app/Contents/Resources/jlab_server
- Linux (Ubuntu)
/opt/JupyterLab/resources/jlab_server
なので、この .../jlab_server
の下の bin/pip
か bin/conda
を叩いてパッケージのインストールやアンインストール、インストール済パッケージリストの表示をすれば良い。
例)MaxOSX に入れた JupterLab App で、pip を使ってインストール済みパッケージのリストを表示する
> /Applications/JupyterLab.app/Contents/Resources/jlab_server/bin/pip list
注意点
この時、絶対に注意しなければならないのは、上記 JupyterLab App 内の pip / conda を使ってパッケージをインストール/アンインストールする時は、必ず sudo を付けて、管理者権限でしなければならない、ということである。
そもそも JupyterLab App は、上記の venv の場所を見ても分かる通り、システム領域にインストールされるので、そこにパッケージをインストールする時は管理者権限で行わなければならない。
もしも管理者権限ではなくユーザー権限で pip を叩いた場合は、そのパッケージは ${HOME}/.local
の下にインストールされる。当然、JupyterLab App には認識されず、使えない。
でも管理者権限で pip を叩くと、pip から「管理者権限で pip するのはオススメできないでゴザル」とか言われてしまうので、常になんだかなぁという気持ちになる。
conda も同様。
正解は何なのかは未だに分からない。
JupterLab on pipx
JupyterLab はインストールが簡単で専用アプリで快適、だけどパッケージ管理がしんどいということが分かった。
では pipx に JupyterLab を入れるのはどうか。
同じくらい簡単。
-
pipx のインストール
- MacOS の場合
> brew install pipx
> pipx ensurepath
- Linux (Ubuntu) の場合
> /bin/python3 -m pip install --user pipx
> /bin/python3 -m pipx eusurepath
- MacOS の場合
- pipx の設定
-
PATH
環境変数に${HOME}/.local/bin
を追加する
-
- pipx に JupyterLab をインストールする
> pipx install jupyterlab
これだけ。こっちも簡単じゃない?
こいつを JupyterLab on pipx
と呼ぶことにする。私にネーミングセンスが無いのは勘弁。
で、JupyterLab App と、JupyterLab on pipx は何がどう違うかというと、まぁ状況はほとんど同じ。
JupyterLab App も、JupyterLab on pipx も、通常の venv とは管理方法が違うので、そのままじゃ動かない拡張機能がある。
ただし、pipx はユーザー領域(${HOME}/.local/pipx
)に入るので、パッケージを入れたり消したりする罪悪感は無い。JupyterLab 自体を作ったり消したりするのも JupyterLab App よりずっと簡単。
ということで、私は JupyterLab App より JupyterLab on pipx の方を推したい。推しメン。
でも、以下の問題を解決しなきゃならない。
- JupyterLab 専用アプリが無いので、快適じゃない
- JupyterLab on pipx が JupyterLab App より劣っている点
- JupyerLab の拡張機能が一部動かない
- JupyterLab on pipx と JupyterLab App で共通する問題点
JupyterLab on pipx でのパッケージ管理
JupyterLab on pipx の問題点はちょっと脇に置き、まずは JupyterLab on pipx でのパッケージ管理。
以下の2種類の方法がある。
-
pipx inject
を使う -
pipx runpip
を使う
まずは pipx inject で、pipx が作った JupyterLab 用の venv に pip を入れる。
> pipx inject jupyterlab pip
この inject は普通に使えて、ただ入れるだけならドンドコ inject すれば良い。
inject で pip を入れたら、今度は runpip が使える。これはもうまるっきり pip と同じ。
だから SHELL の設定に alias pipj="pipx runpip jupyterlab"
を追加すると便利じゃないかな。個人的意見。
このようにパッケージ管理が JupyterLab App より簡単でスッキリしてるので、JupyterLab on pipx の方が良いんじゃないかな。個人的意見。
JupyterLab 専用アプリ
JupyterLab on pipx は、専用アプリが無くて快適じゃないというただ1点で JupyterLab App より劣っているので、何とかこれを解決したい。
一番簡単な解決方法は、Chrome のショートカットを作ること。
普通に jupyter-lab
(JupyterLab server の起動コマンド)を叩いたらブラウザが立ち上がるので、その Chrome のメニューから「その他ツール」>「ショートカットを作成」と選び、JupyterLab
という名前で「ウィンドウとして開く」にチェックを入れてショートカットを作れば、あたかも JupyterLab 専用アプリケーションのように使うことが出来る。
(Chrome 以外のブラウザの場合は、知らない(Google 信者))
これで万事解決な気がする。でも騙されてはいけない。こいつは偽装しているだけで立派な Chrome だ。
左上に「戻る」や「リロード」ボタンが有るし、右上には「拡張機能」ボタンが有る。なので、このアプリ自体が Chrome 拡張機能の影響を受ける(Keep Last Two Tabs とか)。まぁそのお蔭で Stylus を使って JupyterLab の見た目を変えることも出来るんだけど。
もうひとつの方法は、JupyterLab App と同じように、Electron で専用アプリを作ってしまうこと。
ブラウザから JupyterLab を使う時は、ブラウザから http://localhost:8888/lab
にアクセスすることで JupyterLab を使えるようになる。よって、単純に http://localhost:8888/lab
にアクセスするだけのブラウザがあれば良い。
同じことを考える人はいるもので、adammenges/JupyterLabApp は私の欲しい物ずばりそのものだった。
Electron は、Electron の簡単なサンプルとして electron/electron-quick-start というものを提供していて、これは単純に中に含まれている index.html
を表示するだけのブラウザである。
この中に含まれている HTML ファイルを表示する Electron の関数、electron.BrowserWindow().loadFIle('index.html')
を、URL を表示する関数である electron.BrowserWindow().loadURL('http://localhost:8888/lab')
に置き換えれば、私の欲しい物になる。
これをやったのがまさに上記のレポジトリ。
まぁ、Electron は Node.js + Chromium なので、Chrome ショートカットと実質ほぼ同じもの。でも通常のブラウジングに使うアプリと JupyterLab 専用に使うアプリが別になるのは喜ばしいことだ。
・・・これ、俺でも作れるんじゃねぇかな。
オリジナルの JupyterLab クライアント
ということで、作った。
最初は jupyter って名前を付けるのは怒られるかもしれないなぁと思って、"YapyterLab" とか付けてたけど、もうどうでも良くなったんで開き直って "JupyterLab" ってそのまんまの名前にした。
ちなみにアイコンは、JuputerLab のアイコンはダサくて嫌なので、Jupyter のロゴを使っている。
さらにちなみに、Node.js や yarn の設定をして install.sh を叩くと、build, install から以下に述べている設定まで全部一気にやるようになっている。
しっかし、Electron 超すごい。Node.js を40行も書いてないのに Web アプリが出来ちまった。ほぼ electron-quick-start のパクリだけど。
アイコンを作ったり electron-builder の設定をする方が時間かかったわ。
でも、全部あわせても半日で出来た。
ビルドの仕方、インストールの仕方、使い方は全部 repository の README に書いたから、見てね。
JupyterLab 設定
まずは Jupyter はどこの設定ファイルを読み込むのか知らなければならない。
Jupyter on pipx の venv は ${HOME}/.local/pipx/venvs/jupyterlab
にあるので、次のようにすると各種パスが分かる。
> ~/.local/pipx/venvs/jupyterlab/bin/jupyter --paths
config:
/Users/[username]/.jupyter
/Users/[username]/.local/pipx/venvs/jupyterlab/etc/jupyter
/usr/local/etc/jupyter
/etc/jupyter
data:
/Users/[username]/Library/Jupyter
/Users/[username]/.local/pipx/venvs/jupyterlab/share/jupyter
/usr/local/share/jupyter
/usr/share/jupyter
runtime:
/Users/[username]/Library/Jupyter/runtime
他の方法で Jupyter を動かしたくなったら困るので、Jupyter on pipx だけに影響する設定をしたい。ということで、${HOME}/.local/pipx/venvs/jupyterlab/etc/jupyter/
の下に設定ファイルを置くの一択である。
そして設定をするのだが、JupyterLab のドキュメントはほとんど存在せず、設定の項目を探るのにすら苦労した。
結局どうしたかというと、以下をやってパラメータを色々試し、実現できるまで頑張るしかない。
-
> ~/.local/pipx/venvs/jupyterlab/bin/jupyter lab --generate-config
で生成される${HOME}/.jupyter_lab_config.py
を読んでウンウン考える -
> ~/.local/pipx/venvs/jupyterlab/bin/jupyter lab --help
の出力を参考にする - ググってJupyter Notebook 時代の設定を探し、それに似た名前の設定項目をいじってみる
最終的に、以下の内容のファイルを ${HOME}/.local/pipx/venvs/jupyterlab/etc/jupyter/jupyter_lab_config.py
として置くことで、JupyterLab App と同じように、JupyterLab on pipx + オリジナルJupyterLab を動かすことが出来るようになった。
c.ServerApp.use_redirect_file = False
# For Mac OSX
c.ServerApp.browser = '/Applications/YapyterLab.app/Contents/MacOS/JupyterLab %s'
# For Linux
c.ServerApp.browser = '/snap/bin/jupyterlab %s'
c.ServerApp.browser
は分かるだろう。
ブラウザが JupyterLab にアクセスするためには2種類の方法があって、ひとつが http://localhost:8888/lab
にトークンを付けてアクセスすること、もうひとつが JupyterLab が生成する runtime 以下の html ファイルにアクセスすること。
デフォルトの挙動は後者(c.ServerApp.user_redirect_file = True
)。
Mac OSX の場合はどちらでも大丈夫なんだけど、Linux の場合には runtime 以下のファイルへのアクセスが Permission denied されてしまう。良く分からん。
なのでこいつを False
にして、URL + token でアクセスするようにすれば良い。
JupyterLab Extension
最近の JupyterLab は、最初からそこそこ機能が付いている(デフォルトで公認の Extension が入っている)ので、そのままでも十分に使える。
でも、どうしてもひとつだけ入れたい Extension がある。
krassowski/jupyterlab-lsp である。
これが出来ることは以下。
- node navigation: 関数を使っている所から関数の定義にジャンブ出来る
- hover suggestion: 関数などをマウスで hover すると、定義がポップアップする
- linter: 文法がおかしい所(PEP8 違反など)の自動チェック
- autocompletion: 補完
- rename: 変数名の一括変換
特に linter と autocompletion は必須。もうこれが無いとコーディングなんて出来ない。
jupyterlab-lsp の他の選択肢としては、kiteco/jupyterlab-kite というものが有るらしいが、外部の Kite Engine が必要。
でも俺はあまり AI って信用してないんだ(笑)。
何よりも現時点(2021/10/07)では、Kite Engine をダウンロードしようとすると "Kite is temporarily unavailable" って出て、ダウンロードできない。
ということで、話を進める。
まず JupyterLab Extension を使うには、色んな所で Node.js が必要になるので、まずはこれを入れる。アプリのビルドにも必要だし。
最近は、言語のバージョン管理は env 系を使うことがデファクトスタンダードになりつつあるので、nodenv を使う。(ちょっと前までは nodebrew とか色々あった)
-
nodenv のインストール
> git clone git://github.com/nodenv/nodenv.git ~/.nodenv
-
node-build のインストール
> git clone https://github.com/nodenv/node-build.git ~/.nodenv/plugins/node-build
-
node-build-update-defs のインストール
> git clone https://github.com/nodenv/node-build-update-defs.git ~/.nodenv/plugins/node-build-update-defs
- nodenv の設定(
~.zshrc
とかで)-
PATH
に${HOME}/.nodenv/bin
を加える -
eval "$(nodenv init -)"
をする
-
- Node.js のインストールと設定
-
> nodenv install --list
- インストールできるバージョンの一覧表示
-
> nodenv install x.x.x
- 特定のバージョンをインストール
-
> nodenv global x.x.x
- どこでもそのバージョンの Node.js を使えるようにする
-
JupyterLab App の人も、conda で Node.js を入れた方が良いと思う。
> sudo (path-to-jlab_server)/bin/conda -c conda-forge nodejs
jupyterlab-lsp
そしたらいよいよ jupyterlab-lsp のインストール。
jupyterlab-lsp は JupyterLab に上記の機能を提供するガワであり、Python だけじゃなくて色々な言語(の Language Server)に対応している。
だから Python の Language Server である python-lsp-server も入れなければならない。
> pipx runpip jupyterlab install jupyterlab-lsp
> pipx runpip jupyterlab install "python-lsp-server[all]"
python-lsp-server に [all]
を付けることによって、関連するパッケージ(flake8 とか)を全部いっぺんに入れちゃえる。
JupyterLab App の人は、pipx runpip jupyterlab install
の代わりに sudo (path-to-jlab_server)/bin/pip install
ってやるんじゃないかと思う。多分。
余談だが、Python の Language Server としては python-language-server というものもある。しかしこれは開発がストップしていて、これを fork してコミュニティで開発しているのが python-lsp-server らしい。昔は python-language-server しか無かった。老人のつぶやき。
python-language-server から python-lsp-server への乗り換えは、python-language-server の設定の pyls
となっている所をすべて pylsp
に置き換えることで可能。
余談が過ぎたが、python-lsp-server, jupyterlab-lsp が無事にインストール出来たからといって、これが動くかというと、ほぼ動くが、ほんのちょっとだけ動かない所がある。
jupyterlab-lsp の設定
python-lsp-server の設定項目は、python-lsp-server/CONFIGURATION.md に一覧がある。
この中で明らかに設定が必要なのは、pylsp.plugins.flake8.executable
。
jupyterlab-lsp から python-lsp-server を叩くのは、jupyterlab-lsp が入っている仮想環境の Python モジュールとして叩くので、両者が同じ仮想環境に入っていれば良い。
しかし、flake8 は pylsp のプラグインだけじゃなく普通のコマンドとしても使うので、python-lsp-server はPATH を通じて flake8 を探してしまう。しかし JupyterLab on pipx(もしくは JupyterLab App)の仮想環境という特殊な所に入っているので、flake8 が見つからない、もしくは変な所の flake8 を見てしまう。
よって、flake8 の絶対パスを jupyterlab-lsp の設定として書き、それを python-lsp-server に渡せば良い。
JupyterLab を起動し、メニューから Settings > Advanced Setting Editor を選び、左ペインから Language Server を選んで設定を記入する。
私の設定をサンプルとして示す。
{
"language_servers": {
"pylsp": {
"serverSettings": {
// disabed services
"pylsp.plugins.pycodestyle.enabled": false,
"pylsp.plugins.pyflakes.enabled": false,
"pylsp.plugins.rope_completion.enabled": false,
"pylsp.plugins.yapf.enabled": false,
// flake8
"pylsp.plugins.flake8.enabled": true,
"pylsp.plugins.flake8.executable":
"${HOME}/.local/pipx/venvs/jupyterlab/bin/flake8",
// ignoring errors
// E402: allows imports which are not on the very top
// E703: allows terminating ";" (for matplotlib plots)
"pylsp.plugins.flake8.ignore": ["E402", "E703"],
// maxLineLength = 79 (default)
"pylsp.plugins.flake8.maxLineLength": null
}
}
}
}
すると、こんな感じになる。
スクリーンショットじゃ、あんまり違いは分からないね・・・
まとめ
JupyterLab App を触ってみたけど、あんまり気に入らなかったんで、自分でアプリ作ったり環境作ったりした。
そこそこ良いものになったんじゃないかと思う。
みなさんも宜しければ是非。