はじめに
Windowsでpdm add torchvision
しようとしたら
assert record is not None, "In {}, {} is not mentioned in RECORD".format(
AssertionError: In C:\Users\hoge\AppData\Local\Temp\pip-unpack-XXXXXXXX\torchvision-0.11.1-cp39-cp39-win_amd64.whl, torchvision-0.11.1.dist-info/LICENSE is not mentioned in RECORD
とassertが出てインストールできなかった。本稿では何とかしてインストールするための方法を紹介する。
実行環境
OS: Windows 10 Pro (19044.1348)
pdm: version 1.10.3
pyenv-win: version 2.64.11
Python: version 3.9.6
エラーの再現方法
-
$mkdir tmp
で空のディレクトリを作成する。 - powershellを開いて、上で作成したディレクトリをカレントディレクトリにする。
- powershellのコンソールに
$pdm init
を入力しenter。この時聞かれるパッケージの初期設定については、Please enter the Python interpreter to use に対してはpyenv-winのpython 3.9.6を選択し、他の設定はデフォルトを選択する(何も入力せずにenter)。 -
$pdm add torchvision==0.11.1
を入力する。
出力されるエラーメッセージ
ERRORS:
add torchvision failed:
Traceback (most recent call last):
File "C:\Users\hoge\scoop\apps\pyenv\current\pyenv-win\versions\3.9.6\lib\concurrent\futures\thread.py", line 52, in run
result = self.fn(*self.args, **self.kwargs)
File "C:\Users\hoge\AppData\Roaming\pdm\venv\lib\site-packages\pdm\installers\synchronizers.py", line 190, in install_candidate
self.manager.install(can)
File "C:\Users\hoge\AppData\Roaming\pdm\venv\lib\site-packages\pdm\installers\manager.py", line 38, in install
installer(candidate.build(), self.environment, candidate.direct_url())
File "C:\Users\hoge\AppData\Roaming\pdm\venv\lib\site-packages\pdm\installers\installers.py", line 124, in install_wheel_with_cache
dist_info_dir = _install_wheel(
File "C:\Users\hoge\AppData\Roaming\pdm\venv\lib\site-packages\pdm\installers\installers.py", line 176, in _install_wheel
for record_elements, stream in source.get_contents():
File "C:\Users\hoge\AppData\Roaming\pdm\venv\lib\site-packages\installer\sources.py", line 170, in get_contents
assert record is not None, "In {}, {} is not mentioned in RECORD".format(
AssertionError: In C:\Users\hoge\AppData\Local\Temp\pip-unpack-XXXXXXXX\torchvision-0.11.1-cp39-cp39-win_amd64.whl, torchvision-0.11.1.dist-info/LICENSE is not mentioned in RECORD
See C:\Users\hoge\AppData\Local\Temp\pdm-install-XXXXXXXXX.log for detailed debug log.
[InstallationError]: Some package operations are not complete yet
Add '-v' to see the detailed traceback
エラーの原因
エラーメッセージの下の方を見てみると、
File "C:\Users\hoge\AppData\Roaming\pdm\venv\lib\site-packages\installer\sources.py", line 170, in get_contents
assert record is not None, "In {}, {} is not mentioned in RECORD".format(
AssertionError: In C:\Users\hoge\AppData\Local\Temp\pip-unpack-XXXXXXXX\torchvision-0.11.1-cp39-cp39-win_amd64.whl, torchvision-0.11.1.dist-info/LICENSE is not mentioned in RECORD
とある。C:\Users\hoge\AppData\Roaming\pdm\venv\lib\site-packages\installer\sources.py
を見てみると、エラーの発生個所は以下の関数らしい。
def get_contents(self):
# type: () -> Iterator[WheelContentElement]
"""Sequential access to all contents of the wheel (including dist-info files).
This implementation requires that every file that is a part of the wheel
archive has a corresponding entry in RECORD. If they are not, an
:any:`AssertionError` will be raised.
"""
# Convert the record file into a useful mapping
record_lines = self.read_dist_info("RECORD").splitlines()
records = installer.records.parse_record_file(record_lines)
record_mapping = {record[0]: record for record in records}
for item in self._zipfile.infolist():
if item.filename[-1:] == "/": # looks like a directory
continue
record = record_mapping.pop(item.filename, None)
assert record is not None, "In {}, {} is not mentioned in RECORD".format(
self._zipfile.filename,
item.filename,
) # should not happen for valid wheels
with self._zipfile.open(item) as stream:
stream_casted = cast("BinaryIO", stream)
yield record, stream_casted
この関数はPDMが内部で使っているinstallerというパッケージのものらしい。この関数の説明文によると、この関数はインストールしようとしたwheelに含まれている各ファイルが、whlのdist-infoのRECORDに書いてあることを確認し、書いてなかったら先のエラーを出すらしい。
先のエラーを見直すと、AssertionError: In C:\Users\hoge\AppData\Local\Temp\pip-unpack-XXXXXXXX\torchvision-0.11.1-cp39-cp39-win_amd64.whl, torchvision-0.11.1.dist-info/LICENSE is not mentioned in RECORD
とあり、torchvision-0.11.1.dist-info/LICENSEがRECORDに書いてないらしい。1
本当に書いていないことを確かめるため、torchvisionのpypiからtorchvision-0.11.1-cp39-cp39-win_amd64.whlをダウンロードして、unzipしてみる。unzipしたファイルのtorchvision-0.11.1.dist-info/RECORDを見てみると、121行目にtorchvision-0.11.1.dist-info\LICENSE
とある。一方で、上のエラーメッセージではtorchvision-0.11.1.dist-info/LICENSE is not mentioned in RECORD
と書いてあった。確かにtorchvision-0.11.1.dist-info/LICENSE
はRECORDに記載されていないものの、ディレクトリ区切り文字が異なるtorchvision-0.11.1.dist-info\LICENSE
が記載されている。ということで、RECORDに記載されているパス(torchvision-0.11.1.dist-info\LICENSE
)と、wheelに含まれているファイルのパス(torchvision-0.11.1.dist-info/LICENSE
)の区切り文字が異なってしまうことがエラーの原因らしい。
エラーの回避方法
回避方法の方針としては、RECORDとinstallerのディレクトリ区切り文字を一致させてやれば良さそうである。そのための雑な方法として、
record_lines = self.read_dist_info("RECORD").splitlines()
を
record_lines = [ line.replace("\\", "/") for line in self.read_dist_info("RECORD").splitlines()]
に置換する。self.read_dist_info("RECORD").splitlines()
の返り値はRECORDファイルの各行が入っているリストで、[ line.replace("\\", "/") for line in self.read_dist_info("RECORD").splitlines()]
はRECORDの各行の\
を/
に置換したものである。
この方法だと、Pathだけでなく、RECORDに含まれる各ファイルのハッシュも置換してしまう。セキュリティ的にまずいはずなので、パスのみを置換の範囲にするべきです。
-
RECORDが何なのかというと、"RECORD is a list of (almost) all the files in the wheel and their secure hashes. Unlike PEP 376, every file except RECORD, which cannot contain a hash of itself, must include its hash. The hash algorithm must be sha256 or better; specifically, md5 and sha1 are not permitted, as signed wheel files rely on the strong hashes in RECORD to validate the integrity of the archive." らしい。PEP 427より。 ↩