Extended Guide#

How it works#

  1. The RST text is converted to a modified version of docutils AST, which preserves lossless information about the source text.

  2. The docutils AST is converted to Markdown-It syntax tokens.

  3. The tokens are converted to Markdown text with mdformat.

The conversion is designed to be fault tolerant, i.e. it will not check if referenced targets, roles, directives, etc exist nor fail if they do not.

The only syntax where some checks are required is matching anonymous references and auto-number/symbol footnotes with their definitions; these definitions must be available.

Conversion notes:

  • enumerated lists with roman numerals or alphabetic prefixes will be converted to numbers

  • only one kind of footnote (i.e. no symbol prefixes)

  • citation are turned into footnotes, with label prepended by cite_prefix

  • inline targets are not convertible (and so ignored)

  • If tables are not compatible with Markdown (single header row, no merged cells, etc), then they will be wrapped in an eval-rst directive

  • Markdown blockquotes do not have an attribution syntax, so it is converted instead to <p class="attribution">—text</p> (the standard HTML render)

Converting text snippets#

Either use the stream CLI command, parsing in stdin:

$ echo ":role:`content`" | rst2myst stream -
{role}`content`

or use the API:

from rst_to_myst import rst_to_myst
output = rst_to_myst(":role:`content`")
print(output.text)

Converting multiple files#

Use the convert CLI command, with standard file globbing. The --dry-run option will run without actually writing any files:

$ rst2myst convert --dry-run docs/**/*.rst
docs/source/api.rst -> docs/source/api.md
CONVERTED (extensions: [])
docs/source/cli.rst -> docs/source/cli.md
CONVERTED (extensions: ['deflist'])

FINISHED ALL! (extensions: ['deflist'])

Extensions specify which MyST optional extensions are required to reparse the Markdown text.

Configuring the conversion#

The CLI and API documentation list all the available configurations.

For the CLI, you can directly use the option flags, or you can provide all the options in a YAML configuration file, with the --config option:

$ rst2myst convert --config config.yaml docs/**/*.rst

YAML config options mirror the CLI options, except using _ instead of -, e.g.

language: en
sphinx: true
extensions:
  - sphinx_panels
default_domain: py
consecutive_numbering: true
colon_fences: true
dollar_math: true
conversions:
  sphinx_panels.dropdown.DropdownDirective: parse_all

Directive conversion#

Directives are converted according to a mapping of the directive module path to a conversion type:

  • “eval_rst” (the default): no conversion, wrap in MyST eval-rst directive

    ```{eval-rst}
    .. name:: argument `link`_
       :option: value
    
       content `link`_
    ```
    
  • “direct”: convert directly to MyST directive, keeping original argument/content

    ```{name} argument `link`_
    :option: value
    
    content `link`_
    ```
    
  • “parse_argument”: convert to MyST directive and convert the argument to Markdown

    ```{name} argument [link](link)
    :option: value
    
    content `link`_
    ```
    
  • “parse_content”: convert to MyST directive and convert the content to Markdown

    ```{name} argument `link`_
    :option: value
    
    content [link](link)
    ```
    
  • “parse_all”: convert to MyST directive and convert the content to Markdown

    ```{name} argument [link](link)
    :option: value
    
    content [link](link)
    ```
    

The default conversions are listed below, or you can use the conversions options to update these conversions. Also use the colon_fence option to control whether directives with Markdown content are delimited by :::.

Directive conversion defaults
# value one of:
# - "eval_rst": no conversion, wrap in MyST eval_rst directive
# - "direct": convert directly to MyST directive, keeping original argument/content
# - "parse_argument":  convert to MyST directive and convert the argument to Markdown
# - "parse_content":  convert to MyST directive and convert the content to Markdown
# - "parse_all":  convert to MyST directive and convert the argument and content to Markdown

# admonitions (docutils)
docutils.parsers.rst.directives.admonitions.Admonition: parse_all
docutils.parsers.rst.directives.admonitions.Attention: parse_content
docutils.parsers.rst.directives.admonitions.Caution: parse_content
docutils.parsers.rst.directives.admonitions.Danger: parse_content
docutils.parsers.rst.directives.admonitions.Error: parse_content
docutils.parsers.rst.directives.admonitions.Hint: parse_content
docutils.parsers.rst.directives.admonitions.Important: parse_content
docutils.parsers.rst.directives.admonitions.Note: parse_content
docutils.parsers.rst.directives.admonitions.Tip: parse_content
docutils.parsers.rst.directives.admonitions.Warning: parse_content

# docutils other (see https://docutils.sourceforge.io/docs/ref/rst/directives.html#figure)
docutils.parsers.rst.directives.body.CodeBlock: direct
docutils.parsers.rst.directives.body.Compound: eval_rst
docutils.parsers.rst.directives.body.Container: parse_content
docutils.parsers.rst.directives.body.Epigraph: eval_rst
docutils.parsers.rst.directives.body.Highlights: eval_rst
docutils.parsers.rst.directives.body.LineBlock: eval_rst
docutils.parsers.rst.directives.body.MathBlock: direct
docutils.parsers.rst.directives.body.ParsedLiteral: eval_rst
docutils.parsers.rst.directives.body.PullQuote: eval_rst
docutils.parsers.rst.directives.body.Rubric: parse_argument
docutils.parsers.rst.directives.body.Sidebar: parse_all
docutils.parsers.rst.directives.body.Topic: parse_all
docutils.parsers.rst.directives.html.Meta: eval_rst
docutils.parsers.rst.directives.images.Figure: parse_content
docutils.parsers.rst.directives.images.Image: direct
docutils.parsers.rst.directives.misc.Class: eval_rst
docutils.parsers.rst.directives.misc.Date: direct
docutils.parsers.rst.directives.misc.DefaultRole: eval_rst
docutils.parsers.rst.directives.misc.Include: direct
docutils.parsers.rst.directives.misc.Raw: direct
docutils.parsers.rst.directives.misc.Replace: parse_content
docutils.parsers.rst.directives.misc.Role: eval_rst
docutils.parsers.rst.directives.misc.TestDirective: eval_rst
docutils.parsers.rst.directives.misc.Title: direct
docutils.parsers.rst.directives.misc.Unicode: eval_rst
docutils.parsers.rst.directives.parts.Contents: parse_argument
docutils.parsers.rst.directives.parts.Footer: parse_content
docutils.parsers.rst.directives.parts.Header: parse_content
docutils.parsers.rst.directives.parts.Sectnum: eval_rst
docutils.parsers.rst.directives.references.TargetNotes: eval_rst
docutils.parsers.rst.directives.tables.CSVTable: direct
docutils.parsers.rst.directives.tables.ListTable: eval_rst
docutils.parsers.rst.directives.tables.RSTTable: eval_rst

## Sphinx

# code
sphinx.directives.patches.Code: direct
# math
sphinx.directives.patches.MathDirective: direct
# table
sphinx.directives.patches.RSTTable: eval_rst
# csv-table
sphinx.directives.patches.CSVTable: eval_rst
# list-table
sphinx.directives.patches.ListTable: eval_rst
# figure
sphinx.directives.patches.Figure: parse_content
# meta
sphinx.directives.patches.Meta: eval_rst

# deprecated, versionadded, versionchanged
sphinx.domains.changeset.VersionChange: parse_content
# seealso
sphinx.directives.other.SeeAlso: parse_content
# index
sphinx.domains.index.IndexDirective: direct
# default-domain
sphinx.directives.DefaultDomain: eval_rst
# describe
# object
sphinx.directives.ObjectDescription: eval_rst
# highlight
sphinx.directives.code.Highlight: direct
# highlightlang
# code-block
# sourcecode
sphinx.directives.code.CodeBlock: direct
# literalinclude
sphinx.directives.code.LiteralInclude: direct
# toctree
sphinx.directives.other.TocTree: direct
# sectionauthor
# moduleauthor
# codeauthor
sphinx.directives.other.Author: eval_rst
# tabularcolumns
sphinx.directives.other.TabularColumns: eval_rst
# centered
sphinx.directives.other.Centered: eval_rst
# acks
sphinx.directives.other.Acks: eval_rst
# hlist
sphinx.directives.other.HList: eval_rst
# only
sphinx.directives.other.Only: parse_content_titles

# c:member
# c:var
sphinx.domains.c.CMemberObject: eval_rst
# c:function
sphinx.domains.c.CFunctionObject: eval_rst
# c:macro
sphinx.domains.c.CMacroObject: eval_rst
# c:struct
sphinx.domains.c.CStructObject: eval_rst
# c:union
sphinx.domains.c.CUnionObject: eval_rst
# c:enum
sphinx.domains.c.CEnumObject: eval_rst
# c:enumerator
sphinx.domains.c.CEnumeratorObject: eval_rst
# c:type
sphinx.domains.c.CTypeObject: eval_rst
# c:namespace
sphinx.domains.c.CNamespaceObject: eval_rst
# c:namespace-push
sphinx.domains.c.CNamespacePushObject: eval_rst
# c:namespace-pop
sphinx.domains.c.CNamespacePopObject: eval_rst
# c:alias
sphinx.domains.c.CAliasObject: eval_rst
# cpp:class
# cpp:struct
sphinx.domains.cpp.CPPClassObject: eval_rst
# cpp:union
sphinx.domains.cpp.CPPUnionObject: eval_rst
# cpp:function
sphinx.domains.cpp.CPPFunctionObject: eval_rst
# cpp:member
# cpp:var
sphinx.domains.cpp.CPPMemberObject: eval_rst
# cpp:type
sphinx.domains.cpp.CPPTypeObject: eval_rst
# cpp:concept
sphinx.domains.cpp.CPPConceptObject: eval_rst
# cpp:enum
# cpp:enum-struct
# cpp:enum-class
sphinx.domains.cpp.CPPEnumObject: eval_rst
# cpp:enumerator
sphinx.domains.cpp.CPPEnumeratorObject: eval_rst
# cpp:namespace
sphinx.domains.cpp.CPPNamespaceObject: eval_rst
# cpp:namespace-push
sphinx.domains.cpp.CPPNamespacePushObject: eval_rst
# cpp:namespace-pop
sphinx.domains.cpp.CPPNamespacePopObject: eval_rst
# cpp:alias
sphinx.domains.cpp.CPPAliasObject: eval_rst
# js:function
# js:method
sphinx.domains.javascript.JSCallable: eval_rst
# js:class
sphinx.domains.javascript.JSConstructor: eval_rst
# js:data
# js:attribute
sphinx.domains.javascript.JSObject: eval_rst
# js:module
sphinx.domains.javascript.JSModule: eval_rst
# py:function
sphinx.domains.python.PyFunction: eval_rst
# py:data
sphinx.domains.python.PyVariable: eval_rst
# py:class
# py:exception
sphinx.domains.python.PyClasslike: eval_rst
# py:method
sphinx.domains.python.PyMethod: eval_rst
# py:classmethod
sphinx.domains.python.PyClassMethod: eval_rst
# py:staticmethod
sphinx.domains.python.PyStaticMethod: eval_rst
# py:attribute
sphinx.domains.python.PyAttribute: eval_rst
# py:module
sphinx.domains.python.PyModule: eval_rst
# py:currentmodule
sphinx.domains.python.PyCurrentModule: eval_rst
# py:decorator
sphinx.domains.python.PyDecoratorFunction: eval_rst
# py:decoratormethod
sphinx.domains.python.PyDecoratorMethod: eval_rst
# rst:directive
sphinx.domains.rst.ReSTDirective: eval_rst
# rst:directive:option
sphinx.domains.rst.ReSTDirectiveOption: eval_rst
# rst:role
sphinx.domains.rst.ReSTRole: eval_rst
# std:program
sphinx.domains.std.Program: eval_rst
# std:cmdoption
# std:option
sphinx.domains.std.Cmdoption: eval_rst
# std:envvar
sphinx.domains.std.EnvVar: eval_rst
# std:glossary
sphinx.domains.std.Glossary: parse_content
# std:productionlist
sphinx.domains.std.ProductionList: eval_rst

# third-party directives
sphinxcontrib.bibtex.directives.BibliographyDirective: direct
sphinx_panels.dropdown.DropdownDirective: parse_all

Additional Functionality#

Listing available directives/roles#

List available directives/roles:

$ rst2myst directives list
acks admonition ...

$ rst2myst roles list
abbr abbreviation ...

Show details of a specific directive/role:

$ rst2myst directives show admonition
class: docutils.parsers.rst.directives.admonitions.Admonition
description: ''
has_content: true
name: admonition
optional_arguments: 0
options:
  class: class_option
  name: unchanged
required_arguments: 1

$ rst2myst roles show abbreviation
description: |-
  Generic interpreted text role, where the interpreted text is simply
  wrapped with the provided node class.
module: docutils.parsers.rst.roles
name: abbreviation