pipでJupyter入れたら動かなかった件解決の流れ

# はじめに

この記事は最近（2019年3月ごろ）Jupyterを入れた人がぶち当たるであろう問題とその解決方法です。他の方がすでに解決方法は書かれているようですが[^1]、この記事の主な目的はどのように問題を把握し、調べ、解決していったかという**問題解決**の流れを示すことで読む人の参考になればいいなぁと思う次第です。
解決方法だけ知りたいという人は「解決方法」の見出しのところまで飛んでください。

[^1]: 記事が書かれたのは私が問題解決した後なので、記事があるのを知ったのは他の人が書いてないか調べてみたためです。

# 数日前のAnaconda

タイトルにあるようにこの記事はpipでjupyter入れたら動かなかったという話なのですが、数日前に「ちょっとこれ（教材）通りにできるか試してみて」と言われて、その教材ではAnacondaを使っていたので素直にAnaconda（当時最新の2018.12版）を入れてJupyter notebook使ってデータ分析の真似事を動作検証しました。

つまり、PC的にはJupyter notebookは問題なく動いたわけです。それを考えると以降で述べる推測と確認はいくつか省けたわけですがまあそこら辺は私も「まあこれはないだろうけど念のため」と思いながら試してました。

# venv + pip

まずそもそも何故Anacondaじゃないのか？
それはAnacondaの「全部入り」が嫌いだからです。ミニマリストなので「最低限これだけ入れればおｋ」という構成を作るのが好きなもので。

というわけでなどなどの理由によりまずvenvで隔離環境を作ったうえでpipでjupyterを入れました。実際にはもちろんpandasとかmatplotlibとか入れたわけですがそこら辺は最小再現[^2]ということでjupyterだけ入れる。あ、ちなみにWindows使ってるのでコマンド入力はWindowsです。

[^2]: しばらくしたら解消されて再現しなくなりますが。

```
> python -m venv jupyter1
> jupyter1\scripts\activate
(jupyter1)> jupyter notebook
```

ブラウザでjupyterが開くので新規notebook作ってセルに入力、あれ？Tabがきかない、とりあえずShift-Enterで実行・・・
出力されないんですけど(。´･ω･)?

よく見てみると右上が「Connecting to kernel」となっており、切れた鎖にカーソル合わせると「No Connection to Kernel」と出る。あ、これ通信できてないやつということに気づく。

# ファイアウォールか？

「No Connection」と言われるので、もちろん単純にポートにつなげてないことを疑う。jupyterと「No Connection to kernel」を合わせてググってみるが当然「ファイアウォールが悪さしてる」「アンチウィルスソフトが」「プロキシあるとねー」みたいなのばかり出る。
「そんなことはわかっている」と念のためファイアウォール切ってみるが改善せず。

# venvだからいけない？

なさそうだけどvenvだからいけないか？と素のPythonの方にもjupyter入れてみたり、「jupyter virtualenv」で検索して引っかかった「venvして作った環境でipython kernel install」みたいなのも念のためやってみたが相変わらず通信できず。

# まさかと思うがPythonのバージョン？？

ちょっと前に入れたPythonで3.5だったので、最新の3.7.2入れてみるがもちろん以下同文。

# Anacondaと比較してみる

仕方ないからAnaconda使うかなと思うが何がいけないのか解決しきらない限りは探究者魂が治まらないのでAnaconda、pipで入れた方両者のコマンドラインを眺めていると、

```
c:\work\python\jupyter\jupyter1\lib\site-packages\notebook\base\zmqhandlers.py:284: RuntimeWarning: coroutine 'WebSocketHandler.get' was never awaited
  super(AuthenticatedZMQStreamHandler, self).get(*args, **kwargs)
```

なんやお前。ていうか気になってたけどWarningだからまあいいかと思ってたが・・・

# 犯人はお前やー

他に手がかりもないので見てみる。

```py:zmqhandlers.py抜粋
    @gen.coroutine
    def get(self, *args, **kwargs):
        # pre_get can be a coroutine in subclasses
        # assign and yield in two step to avoid tornado 3 issues
        res = self.pre_get()
        yield gen.maybe_future(res)
        super(AuthenticatedZMQStreamHandler, self).get(*args, **kwargs)
```

genはこれ。tornado、Webのフレームワークか。

```py:zmqhandlers.py抜粋
from tornado import gen, ioloop, web
```

そもそも「coroutine was never awaited」てどういうことやねんとググってみて[こちらの記事](https://qiita.com/maueki/items/8f1e190681682ea11c98)を見かける。ふむ、ともかくasyncなものがawaitされてないのがいけないのだな。前からそうならこんなWarningが出るわけないから最近何かあったのだろう。とりあえずtornado側の言い分（ドキュメント）を見てみよう。

https://www.tornadoweb.org/en/stable/gen.html#decorators

> Changed in version 6.0: The callback argument was removed. Use the returned awaitable object instead.

callbackを渡してるわけじゃなさうだけどawaitableなオブジェクトが返されるようになったのが原因っぽいな。

# 解決方法

さてここまでポエムな問題解決を読んでいただいたかスキップしたかはともかく、解決方法です。

tornadoを削除した後、ver5のものを入れる。

```
(jupyter1)> pip uninstall -y tornado
(jupyter1)> pip install tornado==5.1.1
```

新規インストールの場合はjupyter入れる前にtornadoをバージョン指定で入れとく。

```
> python -m venv jupyter2
> jupyter1\scripts\activate
(jupyter2)> jupyter tornado==5.1.1 notebook
```

# あとがき

答えがわかった後でバグ報告しようかと思って見てみたら[もう当然されてました](https://github.com/jupyter/notebook/issues/4399)。まずはIssueを見に行けということですね。tornadoへの依存を「4以上6未満」にする修正はもう入っているので多分しばらくしたらnotebookの新しいパッケージがリリースされてこの記事は過去の遺物になるでしょう。