Jupyteter notebookがPermissionErrorで立ち上がらない

2022年3月12日

先日pythonの実験環境を用意していた時に、jupyter notebookの起動に失敗する事象が発生したので、その備忘録を残す。

事象

venvで新しい環境を作成し、activate、jupyterのインストールを行った。

$ python -m venv newproject
$ source newproject/Scripts/activate


その後 jupyter notebookを起動しようとすると、PermissionErrorが発生した。

$ jupyter notebook
Traceback (most recent call last):
  File "d:\documents\git\pyenv-test\newproject\lib\site-packages\traitlets\traitlets.py", line 537, in get
    value = obj._trait_values[self.name]
KeyError: 'runtime_dir'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "C:\Program Files\Python\Python38\lib\runpy.py", line 193, in _run_module_as_main
    return _run_code(code, main_globals, None,
  File "C:\Program Files\Python\Python38\lib\runpy.py", line 86, in _run_code
    exec(code, run_globals)
  File "D:\Documents\git\pyenv-test\newproject\Scripts\jupyter-notebook.EXE\__main__.py", line 9, in <module>
  File "d:\documents\git\pyenv-test\newproject\lib\site-packages\jupyter_core\application.py", line 264, in launch_instance
    return super(JupyterApp, cls).launch_instance(argv=argv, **kwargs)
  File "d:\documents\git\pyenv-test\newproject\lib\site-packages\traitlets\config\application.py", line 845, in launch_instance
    app.initialize(argv)
  File "d:\documents\git\pyenv-test\newproject\lib\site-packages\traitlets\config\application.py", line 88, in inner
    return method(app, *args, **kwargs)
  File "d:\documents\git\pyenv-test\newproject\lib\site-packages\notebook\notebookapp.py", line 2148, in initialize
    self.init_configurables()
  File "d:\documents\git\pyenv-test\newproject\lib\site-packages\notebook\notebookapp.py", line 1650, in init_configurables
    connection_dir=self.runtime_dir,
  File "d:\documents\git\pyenv-test\newproject\lib\site-packages\traitlets\traitlets.py", line 577, in __get__
    return self.get(obj, cls)
  File "d:\documents\git\pyenv-test\newproject\lib\site-packages\traitlets\traitlets.py", line 540, in get
    default = obj.trait_defaults(self.name)
  File "d:\documents\git\pyenv-test\newproject\lib\site-packages\traitlets\traitlets.py", line 1580, in trait_defaults
    return self._get_trait_default_generator(names[0])(self)
  File "d:\documents\git\pyenv-test\newproject\lib\site-packages\jupyter_core\application.py", line 95, in _runtime_dir_default
    ensure_dir_exists(rd, mode=0o700)
  File "d:\documents\git\pyenv-test\newproject\lib\site-packages\jupyter_core\utils\__init__.py", line 11, in ensure_dir_exists
    os.makedirs(path, mode=mode)
  File "C:\Program Files\Python\Python38\lib\os.py", line 211, in makedirs
    makedirs(head, exist_ok=exist_ok)
  File "C:\Program Files\Python\Python38\lib\os.py", line 211, in makedirs
    makedirs(head, exist_ok=exist_ok)
  File "C:\Program Files\Python\Python38\lib\os.py", line 211, in makedirs
    makedirs(head, exist_ok=exist_ok)
  [Previous line repeated 1 more time]
  File "C:\Program Files\Python\Python38\lib\os.py", line 221, in makedirs
    mkdir(name, mode)
PermissionError: [WinError 5] アクセスが拒否されました。: 'C:\\Users\\雉蟷ク'

原因

はっきりした原因は不明なのだが、下記の2つが怪しいと思っている。

  • ホームディレクトリで出力されるパスに日本語混ざっていること
  • 環境構築しようとしているディレクトリがホームディレクトリと違うドライブであること
$ ls ~
ls: cannot access '/c/Users/雉蟷ク': No such file or directory
$ pwd
/d/Documents/git/pyenv-test

試したこと

まずググってみると XDG_RUNTIME_DIR変数に空文字を設定すれば直るという記事を見つけたので、試してみた。https://stackoverflow.com/questions/35878178/jupyter-notebook-permission-error

$ export XDG_RUNTIME_DIR=""
$ echo $XDG_RUNTIME_DIR

XDG_RUNTIME_DIR変数を設定したが直らなかった。

$ jupyter notebook
Traceback (most recent call last):
  File "d:\documents\git\pyenv-test\newproject\lib\site-packages\traitlets\traitlets.py", line 537, in get
    value = obj._trait_values[self.name]
KeyError: 'runtime_dir'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "C:\Program Files\Python\Python38\lib\runpy.py", line 193, in _run_module_as_main
    return _run_code(code, main_globals, None,
  File "C:\Program Files\Python\Python38\lib\runpy.py", line 86, in _run_code
    exec(code, run_globals)
  File "D:\Documents\git\pyenv-test\newproject\Scripts\jupyter-notebook.EXE\__main__.py", line 9, in <module>
  File "d:\documents\git\pyenv-test\newproject\lib\site-packages\jupyter_core\application.py", line 264, in launch_instance
    return super(JupyterApp, cls).launch_instance(argv=argv, **kwargs)
  File "d:\documents\git\pyenv-test\newproject\lib\site-packages\traitlets\config\application.py", line 845, in launch_instance
    app.initialize(argv)
  File "d:\documents\git\pyenv-test\newproject\lib\site-packages\traitlets\config\application.py", line 88, in inner
    return method(app, *args, **kwargs)
  File "d:\documents\git\pyenv-test\newproject\lib\site-packages\notebook\notebookapp.py", line 2148, in initialize
    self.init_configurables()
  File "d:\documents\git\pyenv-test\newproject\lib\site-packages\notebook\notebookapp.py", line 1650, in init_configurables
    connection_dir=self.runtime_dir,
  File "d:\documents\git\pyenv-test\newproject\lib\site-packages\traitlets\traitlets.py", line 577, in __get__
    return self.get(obj, cls)
  File "d:\documents\git\pyenv-test\newproject\lib\site-packages\traitlets\traitlets.py", line 540, in get
    default = obj.trait_defaults(self.name)
  File "d:\documents\git\pyenv-test\newproject\lib\site-packages\traitlets\traitlets.py", line 1580, in trait_defaults
    return self._get_trait_default_generator(names[0])(self)
  File "d:\documents\git\pyenv-test\newproject\lib\site-packages\jupyter_core\application.py", line 95, in _runtime_dir_default
    ensure_dir_exists(rd, mode=0o700)
  File "d:\documents\git\pyenv-test\newproject\lib\site-packages\jupyter_core\utils\__init__.py", line 11, in ensure_dir_exists
    os.makedirs(path, mode=mode)
  File "C:\Program Files\Python\Python38\lib\os.py", line 211, in makedirs
    makedirs(head, exist_ok=exist_ok)
  File "C:\Program Files\Python\Python38\lib\os.py", line 211, in makedirs
    makedirs(head, exist_ok=exist_ok)
  File "C:\Program Files\Python\Python38\lib\os.py", line 211, in makedirs
    makedirs(head, exist_ok=exist_ok)
  [Previous line repeated 1 more time]
  File "C:\Program Files\Python\Python38\lib\os.py", line 221, in makedirs
    mkdir(name, mode)
PermissionError: [WinError 5] アクセスが拒否されました。: 'C:\\Users\\雉蟷ク'

エラーメッセージを読むに、ホームディレクトリ('C:\Users\雉蟷ク’)へのアクセスができないみたい。
パスに日本語入れるなっていうのはあると思うけど、そこは目をつぶってもらって、ホームを別のところに変更してみることにした。

$ ls ~
ls: cannot access '/c/Users/雉蟷ク': No such file or directory

jupyter notebookでどの変数を参照しているのかわからなかったので、調べてみるとドキュメント見つけた。
ランタイムパスを表示できるらしい。

https://docs.jupyter.org/en/latest/use/jupyter-directories.html?highlight=runtime-files#runtime-files

jupyterのランタイムパスを確認すると、ホームを指している環境変数が3個くらいありそう。

$ jupyter --paths
config:
    C:\Users\雉蟷ク\.jupyter
    d:\documents\git\pyenv-test\newproject\etc\jupyter
    C:\ProgramData\jupyter
data:
    C:\Users\雉蟷ク\AppData\Roaming\jupyter
    d:\documents\git\pyenv-test\newproject\share\jupyter
    C:\ProgramData\jupyter
runtime:
    C:\Users\雉蟷ク\AppData\Roaming\jupyter\runtime

いろいろ試した結果、USERPROFILE と APPDATA が jupyterのランタイムパスに効いているみたい。

$ printenv | grep USERPROFILE
USERPROFILE=C:\Users\雉蟷ク
$ printenv | grep APPDATA
APPDATA=C:\Users\雉蟷ク\AppData\Roaming

これらを上書きすると無事立ち上がるようになった。

$ pwd
/d/Documents/git/pyenv-test
$ USERPROFILE=/d/Documents/git/pyenv-test
$ APPDATA=/d/Documents/git/pyenv-test
$ jupyter --paths
config:
    D:\Documents\git\pyenv-test\.jupyter
    d:\documents\git\pyenv-test\newproject\etc\jupyter
    C:\ProgramData\jupyter
data:
    D:\Documents\git\pyenv-test\jupyter
    d:\documents\git\pyenv-test\newproject\share\jupyter
    C:\ProgramData\jupyter
runtime:
    D:\Documents\git\pyenv-test\jupyter\runtime
$ jupyter notebook

毎回 USERPROFILE と APPDATAを設定するのも面倒くさいので、 venvの環境読み込みスクリプトに追記しておく。

$ vim newproject/Scripts/activate
....
# This should detect bash and zsh, which have a hash command that must
# be called to get it to forget past commands.  Without forgetting
# past commands the $PATH changes we made may not be respected
if [ -n "${BASH:-}" -o -n "${ZSH_VERSION:-}" ] ; then
    hash -r
fi

USERPROFILE=/d/Documents/git/pyenv-test
APPDATA=/d/Documents/git/pyenv-test