Sphinx add and refer to rendered markdown file

237 Views Asked by At

Lets say I have the following example project

root
|   readme_sphinx_link.md
|
+---docs
|       example.png
|       readme.md
|       
+---docs_sphinx
|   |   make.bat
|   |   Makefile
|   |   
|   \---source
|       |   conf.py
|       |   index.rst
|       |   
|       +---_autosummary
|       |       example_pkg.main.Dummy.rst
|       |       example_pkg.main.rst
|       |       example_pkg.rst
|       |       readme_include.md
|       |       
|       \---_templates
|               custom-attribute-template.rst
|               custom-class-template.rst
|               custom-module-template.rst
|               
\---example_pkg
    |   main.py
    |   __init__.py

where i want to create a sphinx documentation of the example_pkg with the module main.py. While using sphinx, i want to embed the markdown, located in docs to be available in the created html. To achieve this, I tried using myst-parser and a sphinx setup, using autosummary, based on https://stackoverflow.com/a/62613202

Further i tried to follow the FAQ here

https://myst-parser.readthedocs.io/en/latest/faq/index.html#include-a-file-from-outside-the-docs-folder-like-readme-md

to make the markdown available in main.py

main.py

'''
Example main with example class
'''
class Dummy:
    '''
    .. include:: readme_include.md
        :parser: myst_parser.sphinx_
    '''
    def __init__(self):
        '''
        Class constructor
        '''
        pass

As seen in the docstring, I include and parse readme_include.md (in _autosummary) which looks as followed

readme_include v1

```{include} ../../../readme_sphinx_link.md
:relative-docs: docs/
:relative-images:
```

But when running sphinx, i get the following warning and no link was created

D:\..\..\root\readme_sphinx_link.md:5: WARNING: Unknown source document 'D:/../../root/docs/readme' [myst.xref_missing]

But when using this

readme_include v2

```{include} ../../../docs/readme.md
:relative-docs: docs/
:relative-images:
```

There is no error and the readme is parsed and display in the html. But I would like a link to the rendered readme (as shown in the FAQ I refered to), which i am not able to achieve. Maybe I understood something wrong and it is even not possible to do what I want. I also appreciate any other tips to refer to a rendered markdown file in sphinx

Last but not least the content of the readme.md

# Header for readme

This is just a dummy readme for demonstration purposes

Here we see a dummy picture

![example](./example.png)

and readme_sphinx_link.md

See the following link

[Click me for markdown](docs/readme.md)

and both versions of the created sphinx html, where the blue marked was created with readme_include v1, the red marked with readme_include v2

enter image description here

1

There are 1 best solutions below

0
On

Ok, i kind a solved it by myself. The goal is to provide the following structure in _autosummary.

+---_autosummary
|       docs
|         |---example.png
|         |---readme.md
|       example_pkg.main.Dummy.rst
|       example_pkg.main.rst
|       example_pkg.rst
|       readme_sphinx_link.md

As seen above, the readme_include.md was deleted and the docs folder was copied into the _autosummary folder. Also the readme_sphinx_link.md was copied to that location. Latter was modified to use a relative link

See the following link

[Click me for markdown](./docs/readme.md)

In main.py change the docstring to

class Dummy:
    '''

    .. include:: readme_sphinx_link.md
        :parser: myst_parser.sphinx_
    
    '''
    def __init__(self):
        '''
        Class constructor
       
        '''
        pass

After those modifications were done and make html was called, we get the following result.

enter image description here

To automate this, i wrote a little helper module, located relative to conf.py. It may be more a nasty hack than a elegant solution, but it solves the problem for me (and maybe for some others in near future :) )

Content helpers.py

import os
import shutil

ROOT = os.path.dirname(__file__)
AUTOSUMMARY = os.path.join(ROOT, "_autosummary")

def setup_markdown_links(main_markdown: str, markdown_files: list, link_markdown: str, autosummary_folder: str, 
                         link_txt: str="click here", optional_txt: str=""):
    # copy files
    markdown_name = os.path.basename(main_markdown)
    dst_folder = os.path.join(AUTOSUMMARY, autosummary_folder)
    if os.path.exists(dst_folder) is False:
        os.makedirs(dst_folder)
    entry_markdown = os.path.join(dst_folder, markdown_name)
    shutil.copy(main_markdown, entry_markdown)
    for markdown_file in markdown_files:
        file_name = os.path.basename(markdown_file)
        dst = os.path.join(dst_folder, file_name)
        shutil.copy(markdown_file, dst)
        
    # create link markdown -> this markdown should be set in docstring to create the link
    link_markdown_file = os.path.join(AUTOSUMMARY, link_markdown)
    rel_link = os.path.join(autosummary_folder, markdown_name)
    rel_link = rel_link.replace("\\", "/")
    with open(link_markdown_file, "w") as f:
        f.write(f"{optional_txt}")
        f.write(f"[{link_txt}](./{rel_link})")

Modification in conf.py

import os
import sys

file_path = os.path.abspath(os.path.dirname(__file__))

# append to sys path or module will not be found
sys.path.insert(0, file_path)
from helpers import setup_markdown_links

docs_folder = os.path.abspath(os.path.join(file_path, "..", "..", "docs"))
main_markdown = os.path.join(docs_folder, "readme.md")
markdown_files = [os.path.join(docs_folder, "example.png")]
link_markdown = "readme_sphinx_link.md"
link_txt = "Click me for markdown"
optional_txt = "See the following link\n\n"
autosummary_folder = "docs"

setup_markdown_links(main_markdown, markdown_files, link_markdown, autosummary_folder, 
                     link_txt, optional_txt)

Maybe there will be some others that improve that hack. Better solutions will be welcome as well.