pypiにpackageをuploadするときに、HTTPError: 400 Client Error: The description failed to render in the default format of reStructuredText.が出た話。

pypiにuploadした時に以下のようなエラーが出た。

$ python setup.py sdist bdist_wheel
$ twine upload dist/<package>
...
HTTPError: 400 Client Error: The description failed to render in the default format of reStructuredText. See https://pypi.org/help/#description-content-type for more information. for url: https://upload.pypi.org/legacy/

一応verboseオプション付きでメッセージを表示してみる。

$ twine upload --verbose dist/<package>
...
Content received from server:
<html>
 <head>
  <title>400 The description failed to render in the default format of reStructuredText. See https://pypi.org/help/#description-content-type for more information.</title>
 </head>
 <body>
  <h1>400 The description failed to render in the default format of reStructuredText. See https://pypi.org/help/#description-content-type for more information.</h1>
  The server could not comply with the request since it is either malformed or otherwise incorrect.<br/><br/>
The description failed to render in the default format of reStructuredText. See https://pypi.org/help/#description-content-type for more information.


 </body>
</html>
HTTPError: 400 Client Error: The description failed to render in the default format of reStructuredText. See https://pypi.org/help/#description-content-type for more information. for url: https://upload.pypi.org/legacy/

このときのパッケージのsetup.pyに description-content-type を指定していなかった(結構前からtext/markdownを指定するとreadmeをmarkdownで書ける様になっている。デフォルトはReST)。

ただし今回に限って言うと、エラーメッセージがほとんどかけらも役に立たない。唯一役に立つのはrenderに失敗しているということがわかるだけ。なので、ヘルプメッセージの指すリンク先をみてdescription-content-typeを指定しても意味がない。

(追記: 以下の文が役に立つ。一応。)

PyPI will reject uploads if the description fails to render. To check a description locally for validity, you may use readme_renderer, which is the same description renderer used by PyPI.

(追記おしまい)

ちなみに、text/x-rstに変えた場合のメッセージは以下の様に変わる。

HTTPError: 400 Client Error: The description failed to render for 'text/x-rst'. See https://pypi.org/help/#description-content-type for more information. for url: https://upload.pypi.org/legacy/

readme_renderer

解決にはこのPRが参考になった。

何やら以下の様な感じらしい。

  • readmeのrenderingにはreadme_renderer パッケージを利用している
  • 少しのwarningであってもfailed扱いになる

github.com

実際試してみたら以下のようなwarningが出ていた。これだけのwarningでもだめ。

$ python -m readme_renderer README.rst
<string>:417: (WARNING/2) Title underline too short.

available info (extensions and additional modules)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

このときの環境

$ pip freeze | grep -i readme
readme-renderer==24.0

追記

twine checkを教えてもらった。たしかに存在する。便利そう。

$ twine -h
usage: twine [-h] [--version] {check,register,upload}

positional arguments:
  {check,register,upload}

optional arguments:
  -h, --help            show this help message and exit
  --version             show program's version number and exit

$ twine check dist/<package>
Checking distribution dist/<package>-py2.py3-none-any.whl: Passed