monomeg Lab. 第2開発室

プログラミングで学んだことの覚書やら、考察ネタやら、なんでもあり

WSL1+外部ストレージ+Pipenvの組み合わせでハマった

Introduction

 Pythonの環境構築を行うツールとして,最近はAnacondaやPipenvなど収まるところに収まってきている感があります。比較的シンプルにPython仮想環境の構築を行うことができ,依存パッケージの再現やデプロイもしやすいです。
 ただ,公式ドキュメント含め多くのサイトではMacUbuntu上でPipenvのインストールを行っていることが多く,WSL上でインストールを行った際のトラブルシューティングを紹介しているサイトがあまりありません。Anacondaはロックインがこわいので,個人的にもPipenvをメインにしたのですが,当初は原因不明のエラーに悩まされました。
 最終的には何とか解決したので,ことの経過を書いてみたいと思います。

環境

  • Windows 10 Home バージョン 1909 (OSビルド 18363.836)
  • Windows Subsystem for Linux
  • Ubuntu 18.04.4 LTS
    • kernel release: 4.4.0-18362-Microsoft
    • kernel version: #836-Microsoft Mon May 05 16:04:00 PST 2020
  • Python 3.7.5
  • Pipenv, version 2018.11.26

PIPENV_VENV_IN_PROJECT をtrueに設定すると(何故か)仮想環境の作成に失敗する

 デフォルトでは仮想環境の実体である.venvディレクトリは,~/.local/share/virtualenvsに作成されます1。しかし,このPIPENV_VENV_IN_PROJECT 環境変数をセットすると,プロジェクトを作成するディレクトリに.venvを作成することができます2。そのため,仮想環境をそのまま移動させたい時や,既に存在する仮想環境に新たにpipenv installしたい時,仮想環境を消し去りたい時などに便利です。
 しかし,ある時WSL上で外部ストレージに仮想環境を作成しようとすると,以下のようなエラーメッセージが表示されました。

✘ Failed creating virtual environment
[pipenv.exceptions.VirtualenvCreationException]:   File "/home/ubuntu-user/.local/lib/python2.7/site-packages/pipenv/cli/command.py", line 254, in install
[pipenv.exceptions.VirtualenvCreationException]:       editable_packages=state.installstate.editables,
[pipenv.exceptions.VirtualenvCreationException]:   File "/home/ubuntu-user/.local/lib/python2.7/site-packages/pipenv/core.py", line 1741, in do_install
[pipenv.exceptions.VirtualenvCreationException]:       pypi_mirror=pypi_mirror,
[pipenv.exceptions.VirtualenvCreationException]:   File "/home/ubuntu-user/.local/lib/python2.7/site-packages/pipenv/core.py", line 574, in ensure_project
[pipenv.exceptions.VirtualenvCreationException]:       pypi_mirror=pypi_mirror,
[pipenv.exceptions.VirtualenvCreationException]:   File "/home/ubuntu-user/.local/lib/python2.7/site-packages/pipenv/core.py", line 506, in ensure_virtualenv
[pipenv.exceptions.VirtualenvCreationException]:       python=python, site_packages=site_packages, pypi_mirror=pypi_mirror
[pipenv.exceptions.VirtualenvCreationException]:   File "/home/ubuntu-user/.local/lib/python2.7/site-packages/pipenv/core.py", line 935, in do_create_virtualenv
[pipenv.exceptions.VirtualenvCreationException]:       extra=[crayons.blue("{0}".format(c.err)),]
[pipenv.exceptions.VirtualenvCreationException]: Traceback (most recent call last):
  File "/home/ubuntu-user/.local/lib/python2.7/site-packages/virtualenv.py", line 417, in copyfile
    os.symlink(os.path.realpath(src), dest)
PermissionError: [Errno 1] Operation not permitted: '/usr/lib/python3.7/config-3.7m-x86_64-linux-gnu' -> '/mnt/d/python_projects/test/.venv/lib/python3.7/config-3.7m-x86_64-linux-gnu'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/ubuntu-user/.local/lib/python2.7/site-packages/virtualenv.py", line 2580, in <module>
    main()
  File "/home/ubuntu-user/.local/lib/python2.7/site-packages/virtualenv.py", line 831, in main
    symlink=options.symlink,
  File "/home/ubuntu-user/.local/lib/python2.7/site-packages/virtualenv.py", line 1106, in create_environment
    install_python(home_dir, lib_dir, inc_dir, bin_dir, site_packages=site_packages, clear=clear, symlink=symlink)
  File "/home/ubuntu-user/.local/lib/python2.7/site-packages/virtualenv.py", line 1390, in install_python
    copy_required_files(stdlib_dir, lib_dir, symlink)
  File "/home/ubuntu-user/.local/lib/python2.7/site-packages/virtualenv.py", line 1300, in copy_required_files
    copyfile(join(src_dir, fn), join(lib_dir, fn), symlink)
  File "/home/ubuntu-user/.local/lib/python2.7/site-packages/virtualenv.py", line 420, in copyfile
    copy_file_or_folder(src, dest, symlink)
  File "/home/ubuntu-user/.local/lib/python2.7/site-packages/virtualenv.py", line 398, in copy_file_or_folder
    shutil.copytree(src, dest, symlink)
  File "/usr/lib/python3.7/shutil.py", line 368, in copytree
    raise Error(errors)
shutil.Error: [('/usr/lib/python3.7/config-3.7m-x86_64-linux-gnu/libpython3.7.so', '/mnt/d/python_projects/test/.venv/lib/python3.7/config-3.7m-x86_64-linux-gnu/libpython3.7.so', "[Errno 1] Operation not permitted: '../../x86_64-linux-gnu/libpython3.7m.so.1' -> '/mnt/d/python_projects/test/.venv/lib/python3.7/config-3.7m-x86_64-linux-gnu/libpython3.7.so'"), ('/usr/lib/python3.7/config-3.7m-x86_64-linux-gnu/libpython3.7m.so', '/mnt/d/python_projects/test/.venv/lib/python3.7/config-3.7m-x86_64-linux-gnu/libpython3.7m.so', "[Errno 1] Operation not permitted: '../../x86_64-linux-gnu/libpython3.7m.so.1' -> '/mnt/d/python_projects/test/.venv/lib/python3.7/config-3.7m-x86_64-linux-gnu/libpython3.7m.so'")]
Error in sys.excepthook:
Traceback (most recent call last):
  File "/usr/lib/python3/dist-packages/apport_python_hook.py", line 63, in apport_excepthook
    from apport.fileutils import likely_packaged, get_recent_crashes
  File "/usr/lib/python3/dist-packages/apport/__init__.py", line 5, in <module>
    from apport.report import Report
  File "/usr/lib/python3/dist-packages/apport/report.py", line 30, in <module>
    import apport.fileutils
  File "/usr/lib/python3/dist-packages/apport/fileutils.py", line 23, in <module>
    from apport.packaging_impl import impl as packaging
  File "/usr/lib/python3/dist-packages/apport/packaging_impl.py", line 24, in <module>
    import apt
  File "/usr/lib/python3/dist-packages/apt/__init__.py", line 23, in <module>
    import apt_pkg
ModuleNotFoundError: No module named 'apt_pkg'

Original exception was:
Traceback (most recent call last):
  File "/home/ubuntu-user/.local/lib/python2.7/site-packages/virtualenv.py", line 417, in copyfile
    os.symlink(os.path.realpath(src), dest)
PermissionError: [Errno 1] Operation not permitted: '/usr/lib/python3.7/config-3.7m-x86_64-linux-gnu' -> '/mnt/d/python_projects/test/.venv/lib/python3.7/config-3.7m-x86_64-linux-gnu'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/ubuntu-user/.local/lib/python2.7/site-packages/virtualenv.py", line 2580, in <module>
    main()
  File "/home/ubuntu-user/.local/lib/python2.7/site-packages/virtualenv.py", line 831, in main
    symlink=options.symlink,
  File "/home/ubuntu-user/.local/lib/python2.7/site-packages/virtualenv.py", line 1106, in create_environment
    install_python(home_dir, lib_dir, inc_dir, bin_dir, site_packages=site_packages, clear=clear, symlink=symlink)
  File "/home/ubuntu-user/.local/lib/python2.7/site-packages/virtualenv.py", line 1390, in install_python
    copy_required_files(stdlib_dir, lib_dir, symlink)
  File "/home/ubuntu-user/.local/lib/python2.7/site-packages/virtualenv.py", line 1300, in copy_required_files
    copyfile(join(src_dir, fn), join(lib_dir, fn), symlink)
  File "/home/ubuntu-user/.local/lib/python2.7/site-packages/virtualenv.py", line 420, in copyfile
    copy_file_or_folder(src, dest, symlink)
  File "/home/ubuntu-user/.local/lib/python2.7/site-packages/virtualenv.py", line 398, in copy_file_or_folder
    shutil.copytree(src, dest, symlink)
  File "/usr/lib/python3.7/shutil.py", line 368, in copytree
    raise Error(errors)
shutil.Error: [('/usr/lib/python3.7/config-3.7m-x86_64-linux-gnu/libpython3.7.so', '/mnt/d/python_projects/test/.venv/lib/python3.7/config-3.7m-x86_64-linux-gnu/libpython3.7.so', "[Errno 1] Operation not permitted: '../../x86_64-linux-gnu/libpython3.7m.so.1' -> '/mnt/d/python_projects/test/.venv/lib/python3.7/config-3.7m-x86_64-linux-gnu/libpython3.7.so'"), ('/usr/lib/python3.7/config-3.7m-x86_64-linux-gnu/libpython3.7m.so', '/mnt/d/python_projects/test/.venv/lib/python3.7/config-3.7m-x86_64-linux-gnu/libpython3.7m.so', "[Errno 1] Operation not permitted: '../../x86_64-linux-gnu/libpython3.7m.so.1' -> '/mnt/d/python_projects/test/.venv/lib/python3.7/config-3.7m-x86_64-linux-gnu/libpython3.7m.so'")]

Failed to create virtual environment.

 クソ長いエラーメッセージ(と例外)を吐いて,仮想環境の作成に失敗します。どうも,以下のエラーメッセージが怪しそうです。

PermissionError: [Errno 1] Operation not permitted: '/usr/lib/python3.7/config-3.7m-x86_64-linux-gnu' -> '/mnt/d/python_projects/test/.venv/lib/python3.7/config-3.7m-x86_64-linux-gnu'

ファイルをコピーする際に,許可されていない操作(Operation not permitted)というエラーが発生します。今回は外部ストレージをDドライブとしてマウントしています。そこで,mountコマンドでDドライブの詳細を見てみます。

C:\ on /mnt/c type drvfs (rw,noatime,uid=1000,gid=1000,umask=22,metadata,case=off)
D:\ on /mnt/d type drvfs (rw,noatime,uid=1000,gid=1000,umask=22,case=off)

なんとDドライブにmetadataがありません!Cドライブでは正常に仮想環境が作成できるので,このオプションが無いことが原因のようです。「なら付ければいいじゃん」と思ってmount -t drvfs D: /mnt/d -o metadataしても,何とmetadataオプションが反映されません…
 WSLではVolFsとDrvFsという2種類のファイルシステムが使用されています3。Cドライブを含め,Windowsのすべてのボリュームをマウントする際はDrvFsが使用されます。
 ここで,DrvFsの仕様4を読むと

Previously, WSL would automatically mount all fixed NTFS drives when you launch Bash, but there was no support for mounting additional storage like removable drives or network locations. Now, not only can you manually mount any drives on your system, we've also added support for other file systems such as FAT, as well as mounting network locations.

とあります。NTFSとReFS,FATが対応しているようです。
 ここでようやく原因が分かりました。以前Macを使っていたので,外部ストレージをexFATでフォーマットしていたのです…だからmetadataオプション反映されなかったのか~😭


ということで,大人しくデフォルトのままでPipenvを使うことにしました…