JupyterにPython以外のカーネルを導入する 《番外編》

## Jupyterカーネルについて、補足しておきたいこと

### 前置き

やはり環境は以前の記事で構築したものを前提にしています。

https://qiita.com/Q-Lazy/items/ef61ce36b3f47ade91fd

実際の利用者はかなり少数と思いますが、個人的に高頻度で利用している[Jython](https://www.jython.org/)のカーネルについてです。このカーネルは導入に際してはさまざま注意点があり、本格的な利用は難しく、あくまで限定的な利用にとどまっている状況です。
このカーネルに関しては導入、活用ともに一筋縄ではいかないところが有り、もしほかにもユーザーが存在するようなら、情報を共有しておきたくて、この記事を投稿しました。

### セットアップ

はじめにセットアップについてですが、いくつか越えるべきハードルがあります。まずはPython2で実装されているため、当然Python2が利用できる必要が有ります。
私はWindowsでもJupyter Labを利用していますが、Jupyter LabはPython3のアプリケーションなので、あえて追加でPython2を導入するかどうかが考えものです。
ほかの目的でもPython2が必要ならいいのですが、特に予定が無くて却下となりました。

幸いCloudShellには初めからPython2が利用可能なので、実際にはこちらにのみセットアップして利用しています(必然的にSwingなどのGUIは不可)。では早速カーネルの前に、まずJythonのインストールをホームディレクトリで行います。

```shell
wget https://repo1.maven.org/maven2/org/python/jython-installer/2.7.2/jython-installer-2.7.2.jar
```

JythonにはシングルJARファイル版も有りますが、カーネルではインストール版のみ対応していますので、こちらをダウンロードし、インストーラーを実行します。
今回は`~/jython`にインストールします。

```
$ java -jar jython-installer-2.7.2.jar -s -t minimum -d ~/jython
```

カーネルはパッケージが用意されており、pipコマンドで容易にインストール出来るようにはなっています。

```shell
$ pip install --user jython_kernel
```

しかし、実際にはエラーで起動できません。4行目でインポートしようとする`projects`というモジュールが存在しないためです。このモジュールをどうやって入手するのかは不明で、そもそもpipで依存関係は解決されるはずなのに、出来ていません。
実際には該当の`import`をコメントアウトするか削除すれば動くのですが、この際[GitHubのリポジトリ](https://github.com/suvarchal/IJython)からダウンロードしたものを使ってインストールしましょう。

```shell
$ wget https://github.com/suvarchal/IJython/archive/refs/heads/master.zip -O - | jar xv

$ cd ~/IJython-master
$ python2 setup.py install --user
```

後はディレクトリ`~/jython/bin`を`PATH`に追加しておけばそのままカーネルは利用できます。例によって`kernel.json`で設定する例も示しておきます。

```json:kernel.json
{
    "argv": [
        "/usr/bin/python2", 
        "-m", 
        "jython_kernel", 
        "-f", 
        "{connection_file}"
    ],
    "env": {
        "PATH": "${PATH}:${HOME}/jython/bin",
        "JYTHONSTARTUP": "${HOME}/.local/share/jupyter/kernels/jython_kernel/jython_init.py"
    },
    "codemirror_mode": "shell",
    "display_name": "Jython",
    "language": "python",
    "name": "jython_kernel",
    "interrupt_mode": "message"
}
```

またjythonのバージョンが2.7.2であれば環境変数`JYTHONSTARTUP`にスタートアップスクリプトを設定出来るので、よく使用するするモジュールをいちいち毎回インポートしなくてもよいように自動化したりできます。
`kernel.json`と同じパスに`jython_init.py`というファイルで配置する例も併せて記載しました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2513692/cff8b7b3-b690-6e3a-f5f3-4f993ba1e91b.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2513692/f1f986de-d147-5e04-d0a5-10a43aeff6bc.png)

### カスタマイズ

多くを望まなければ、このままでも使用可能ではありますが、他の言語のカーネルと比較すると、機能的にいくつか不便を感じる部分が見えてきます。

まず日本語には全く対応していないことですが、これは諦めるしかないと思います。また、1つのセルに入力するコードの量がが多めになると、実行結果の出力が文字化けするという不具合ですが、出来るだけセルを分割して、1つのセル当たりのコード量を少なくして対処するしかないでしょう。

それ以外に不満を感じるのは、大きく下記の2点となります。

1. セルの実行結果として表示されるのは標準出力とエラー出力のみとなっていて、いわゆるダックタイピングの機能が無い。例えば変数の内容を表示するという場合も`print`関数に引数として渡して表示しなければならない。

2. タブキーによる入力補完機能を一切装備していない。

これらに関しては、カーネル本体に手を加えて対処します。場所はホームディレクトリ直下の`.local/lib/python2.7/site-packages`ディレクトリにある`jython_kernel.py`というファイルです。
このファイルの142行目から154行目がコメントアウトされているため、コメントを外して無効化されている機能を有効化します。Pythonはインデントによりブロックが区切られるため、9桁目を起点としてインデントの位置を揃える必要が有りますが、なぜか147行目だけインデントがずれています。146行目と行の始点が揃うように調節しなければなりません。

ここまでで、他の言語のようにワード単位での入力補完までは出来ませんが、タブキーによりメソッドやプロパティの補完は出来るようになるので、それなりに便利になるのですが、デフォルトで無効化されていることにはやはり意味があり、困った副作用も出てしまいます。

どうもセルのコードを実行するたびに、入力されているコードを2回重複して実行してしまうようなのです。これにより思わぬ実行結果がもたらされて、混乱することになってしまいます。
ごく単純な例を示すと、このような結果が起こります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2513692/6e8698c0-6fea-dc3d-418d-710a0d0f3f69.png)
そのため例えば単に、下記のようなコードをセルに入力して実行したりすると、

```python
conn = DriverManager.getConnection(url, username, password)
```
データベースに二重に接続されてしまい、`close`メソッドで切断できなくなります。
そのため、重複して実行されると困るような処理の場合は、副作用を抑制するために工夫が必要です。一例として次のような方法を使っています。

まず冒頭で例えば`repeat`というグローバル変数を定義し、初期値として`False`を代入しておきます。

```python
repeat = False
```
これを利用し次の形式でコードを組むようにします。

```python
if not repeat:
    2回実行されては困る処理内容
repeat = not repeat
```
煩雑ではありますが、これで2回重複して実行されることを回避できるので、何とかこのカーネルを活用できるようになっています。
前述のデータベースの接続の例では、次のようになります。

```python
if not repeat:
    conn = DriverManager.getConnection(url, username, password)
repeat = not repeat
```
このコードであれば`conn`の接続をちゃんと`close`メソッドで切断できます。

### Jythonカーネルのまとめ

ここまで述べたように、とにかくいろいろ注意点が多く、実際に本格的に活用している方がおられるとは思えませんし、私自身も、あくまで限定的な用途でだましだまし何とか利用しているという状況です。
またJython自体もすでにサポート終了しているPython2準拠なので将来性が心許無いところが有ります。Javaのバージョン9以降はリフレクションに制約がかけられるようになった影響で、Jython起動時に警告が表示されるようになり、バージョン13以降では、異常終了して起動できなくなってしましました。
かといってJythonでのコーディングを経験すると、Javaでコードを書くというのはなかなか厳しいものがあります。
そのような矢先、昨年Scala3がリリースされました。Pythonユーザーに対してフレンドリーな拡張がなされていて、かなり大胆なチャレンジだと感じます。
まだJupyterのカーネルには対応していないようですが、Jythonからの乗り換え先として非常に魅力的に見えます。
そういった状況もあり、このJythonカーネルにはあまり深入りする必要性も感じていません。

## SQL関係をまとめて
CloudShellでもデータベースが利用できるので、いつかそのうちにWebサーバーのセットアアップと合わせて記事を投稿しようと考えています。
まずはここでSQLのカーネルをかいつまんで解説しておきます。いずれもMinicondaでpythonパッケージをインストールします。

**MySQL**
pipでpythonパッケージをインストールして、Jupyterへのインストーラーも実行。

```shell
$ ~/miniconda3/bin/pip install mysql_kernel

$ ~/miniconda3/bin/python -m mysql_kernel.install
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2513692/41cde5bb-25c0-e641-f379-3b628c46a442.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2513692/06e809b6-b2c6-66aa-43ac-82e0ce585ee0.png)

**PostgreSQL**
Miniconda環境にインストールする場合でも、Pythonパッケージのインストール時に、`tabulate`というコマンドもインストールするため、`--user`オプションは必須となるようです。

```shell
$ ~/miniconda3/bin/pip install --user postgres_kernel
```

エラーメッセージが表示されますが、最終的にはインストール成功となります。同時にJupyterへのインストールも完了します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2513692/5f1493a7-7598-ba35-8eed-6ee740d5f1c6.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2513692/ec881128-dca8-1073-83ab-a1f3eb5f4319.png)

またMiniconda環境の場合、`tabulate`が配置される`~/.local/bin`にパスを通さなければならないようなので、`kernel.json`の設定例を掲載します。

```json:kernel.json
{
    "argv": [
        "~/miniconda3/bin/python",
        "-m",
        "postgres_kernel",
        "-f",
        "{connection_file}"],
    "env": {
        "PATH": "${PATH}:${HOME}/.local/bin"
    },
    "codemirror_mode": "sql",
    "display_name": "PostgreSQL",
    "language": "sql"
}
```

どちらのSQLカーネルもシンタックスハイライト、コード補完、ヘルプといった機能は備わっていませんので、テキストベースのクライアントと比べてあまり利点を感じません。
テーブルの出力がアスキーキャラクタではなく、グラフィカルな見栄えのよい表示になるぐらいでしょうか？
ユースケースとしては、Markdownで資料を作成するする際にデータベースのリソースを埋め込むことにより、効率化を図れることが期待できます。

以上で、需要は少ないと思われるカーネルに関してのまとめとしておきます。