How to make PyPi description Markdown work?
PythonRestructuredtextPypiPython Problem Overview
I uploaded a package to PyPi using:
python setup.py register -r pypi
python setup.py sdist upload -r pypi
I'm trying to modify the description, I wrote (please don't edit the formatting of the following piece of code, I made it on purpose to demonstrate my problem):
**my plugin**
This plugin enables you to ... For example:
```python
@attr(section='MySection', id=1)
def test_function(self):
"""
Bla bla bla
"""
pass
```
However, the text appears as it is, without the markdown formatting. What am I doing wrong?
Python Solutions
Solution 1 - Python
As of March 16, 2018, PyPI.org aka Warehouse (finally) supports Markdown in long descriptions. Warehouse replaced the old legacy PyPI implementation in April 2018.
You need to:
-
Make sure
setuptools
is upgraded to version 38.6.0 or newer -
Make sure
twine
is upgraded to version 1.11.0 or newer -
Make sure
wheel
is upgraded to version 0.31.0 or newer -
Add a new field named
long_description_content_type
to yoursetup()
call, and set it to'text/markdown'
:setup( long_description="""# Markdown supported!\n\n* Cheer\n* Celebrate\n""", long_description_content_type='text/markdown', # .... )
-
Use
twine
to upload your distributions to PyPI:$ python setup.py sdist bdist_wheel # adjust as needed $ twine upload dist/*
The old legacy PyPI infrastructure would not render Markdown, only the new Warehouse infrastructure does. The legacy infrastructure is now gone (as of 2018-04-30).
Currently, PyPI uses cmarkgfm
as the markdown renderer, via the readme_renderer
library (using readme_renderer.markdown.render(long_description)
to produce HTML output). This means that your markdown documents will render exactly the same as on GitHub; it is essentially the same renderer.
You can validate your package long_description
with the twine check
command (twine
1.12.0 or newer).
The old < 2018-03-16 answer follows below.
> Note: this is the old, now outdated answer, as of 2018-03-16 Markdown is supported provided you use the right tools, see above.
PyPI does not support Markdown, so your README will not be rendered into HTML.
If you want a rendered README, stick with reStructuredText; the Sphinx introduction to reStructuredText is a good starting point.
You probably want to install the docutils
package so you can test your document locally; you want to run the included rst2html.py
script on your README to see what errors are produced, if any. Your specific sample has too many errors:
$ bin/rst2html.py test.rst > /tmp/test.html
test.rst:7: (ERROR/3) Unexpected indentation.
test.rst:3: (WARNING/2) Inline literal start-string without end-string.
test.rst:3: (WARNING/2) Inline interpreted text or phrase reference start-string without end-string.
test.rst:11: (WARNING/2) Block quote ends without a blank line; unexpected unindent.
test.rst:11: (WARNING/2) Inline literal start-string without end-string.
test.rst:11: (WARNING/2) Inline interpreted text or phrase reference start-string without end-string.
Your code block is using Github's Markdown extensions, which are entirely wrong for reStructuredText. You could use a reST code block (probably, if the PyPI version of docutils is new enough):
.. code-block:: python
@attr(section='MySection', type='functional+', module='MyModule', id=1)
def test_function(self):
"""
This is the original docstring
"""
pass
To test this locally you'll need to install Pygments as well.
There is a feature request with pull request to add support for Markdown, if you are interested.
Solution 2 - Python
As @Martijn Pieters
stated, PyPi does not support Markdown. I'm not sure where I learned the following trick, but you can use Pandoc and PyPandoc to convert your Markdown files into RestructuredText before uploading into PyPi. To accomplish this, add the following to your setup.py
file:
try:
import pypandoc
long_description = pypandoc.convert_file('README.md', 'rst')
except(IOError, ImportError):
long_description = open('README.md').read()
setup(
name='blah',
version=find_version('blah.py'),
description='Short description',
long_description=long_description,
)
To install Pandoc on OS X, I used Homebrew:
brew install pandoc
To install PyPandoc, I used pip:
pip install pypandoc
Solution 3 - Python
PyPI supports rst and not markdown as mentioned on other answers. But you don't need pypandoc
perse, just pandoc
is fine. You can generate the rst file locally first and then run setup.py to upload the package.
upload.sh
:
#!/bin/bash
pandoc --from=markdown --to=rst --output=README README.md
python setup.py sdist upload
The generated file named README
will be automatically recognized. Be sure to add it to your .gitignore
! The setup.py
doesn't have to do anything special.
setup.py
:
from distutils.core import setup
setup(
name='mypackage',
packages=['mypackage'], # this must be the same as the name above
version='0.2.8',
description='short',
author='Chiel ten Brinke',
author_email='<email>',
url='<github url>', # use the URL to the github repo
keywords=[], # arbitrary keywords
classifiers=[],
)
Then just run bash upload.sh
to upload the stuff to PyPI.
Solution 4 - Python
You can set markdown in setup.cfg
file too:
[metadata]
...
long_description = file: README.md
long_description_content_type = text/markdown
Solution 5 - Python
I've had issues with \r
characters causing parsing issues where only the first line of the README appears in pypi. The code below fixes the issue, it comes from the pypandoc
module repository:
try:
long_description = pypandoc.convert('README.md', 'rst')
long_description = long_description.replace("\r","") # Do not forget this line
except OSError:
print("Pandoc not found. Long_description conversion failure.")
import io
# pandoc is not installed, fallback to using raw contents
with io.open('README.md', encoding="utf-8") as f:
long_description = f.read()
This way long_description
contains a sanitized version of your Readme and you can pass it to the setup() function in your setup.py
script.
Solution 6 - Python
There is a good pip package that worked for me
https://pypi.python.org/pypi/restructuredtext_lint/
I'm using it on my setup now:
https://github.com/pablodav/burp_server_reports/blob/master/setup.py
def check_readme(file='README.rst'):
"""
Checks readme rst file, to ensure it will upload to pypi and be formatted correctly.
:param file:
:return:
"""
errors = rst_lint.lint_file(file)
if errors:
msg = 'There are errors in {}, errors \n {}'.format(file, errors[0].message)
raise SystemExit(msg)
else:
msg = 'No errors in {}'.format(file)
print(msg)
Also I have created a lib to be able to use in py.test later
https://github.com/pablodav/burp_server_reports/blob/master/burp_reports/lib/check_readme.py
Solution 7 - Python
I found the other answers here sort of complicated, and old. I am new to all this, so I'm going to give an answer that uses tools I understand, and worked on my first try.
To get my github page readme.md
file to show on my PyPi page, I followed the following two steps in my setup.py
file.
setup.py
file
1. Include the following at the top of my from pathlib import Path
this_directory = Path(__file__).parent
long_description = (this_directory / "readme.md").read_text()
These steps directly read in the data from your markdown file into the long_description
variable.
setup()
function
2. Input the following parameters to the long_description = long_description
long_description_content_type="text/markdown"
This inserts the data from your markdown file into long_description
, and then tells setup()
what type of data it is (markdown).
As I said, the above two steps worked great on my first try, without any hassles or complexity. Perfect for my little brain. :)
Note this code is adapted from:
https://packaging.python.org/guides/making-a-pypi-friendly-readme/