Skip to content

math_spec.typeset.markdown

GitHub-flavoured Markdown. The format that renders where the docs already live.

Markdown has no math of its own — GitHub delegates to MathJax, which eats LaTeX — so this is not a third spelling: it forwards every math method to :class:LatexFormat and writes only the document layer.

Forwarding rather than subclassing, deliberately. Inheritance would silently inherit a method later added to LatexFormat, and the two differ precisely in the document methods, so the silent case is a \paragraph in a Markdown file. Written out, a new seam method is simply missing until someone decides which side it belongs on.

It exists because docs/examples/ would otherwise write its math by hand with nothing checking it against the model beside it — see test_the_gallery_math_is_current.

MarkdownFormat #

See :class:math_spec.typeset.format.Format. Math is LaTeX's; prose is not.

dash = '—' class-attribute #

notation = 'latex' class-attribute #

operators = {**LatexFormat.operators, 'forall': '\\forall\\thinspace', 'such_that': '\\thinspace:\\thinspace'} class-attribute #

suffix = '.md' class-attribute #

apply(function, argument) #

Source code in src/math_spec/typeset/markdown.py
def apply(self, function: str, argument: str) -> str:
    return _LATEX.apply(function, argument)

cardinality(inner) #

Source code in src/math_spec/typeset/markdown.py
def cardinality(self, inner: str) -> str:
    return _LATEX.cardinality(inner)

cases(arms) #

LaTeX's block, with TeX's own row primitive in place of \\.

Markdown's escape pass eats one of the two backslashes, so MathJax would receive a single one and never break the row. \\cr is what \\ expands to anyway, and carries no punctuation to escape.

Source code in src/math_spec/typeset/markdown.py
def cases(self, arms: list[tuple[str, str]]) -> str:
    r"""LaTeX's block, with TeX's own row primitive in place of ``\\``.

    Markdown's escape pass eats one of the two backslashes, so MathJax
    would receive a single one and never break the row. ``\\cr`` is what
    ``\\`` expands to anyway, and carries no punctuation to escape.
    """
    return cases_block(arms, r' \cr ')

document(blocks, *, standalone) #

Markdown has no preamble, so standalone only adds a heading.

A fragment is meant to be pasted under a heading the page already has.

Source code in src/math_spec/typeset/markdown.py
def document(self, blocks: list[str], *, standalone: bool) -> str:
    """Markdown has no preamble, so ``standalone`` only adds a heading.

    A fragment is meant to be pasted under a heading the page already has.
    """
    body = '\n\n'.join(blocks) + '\n'
    return f'## The math\n\n{body}' if standalone else body

equations(lines, *, numbered) #

One display block per equation, with the name outside the math.

Not LaTeX's aligned, for two reasons that only show up in a browser: a name is not math (\text{total\_cost} renders its \_ escape literally under MathJax, where a backtick span outside the math does not), and aligned columns align across rows, which a page showing one equation per heading has nothing to line up against.

numbered is accepted and ignored — aligned cannot carry numbers, and producing something that looks numbered and is not would be worse.

Source code in src/math_spec/typeset/markdown.py
def equations(self, lines: list[Line], *, numbered: bool) -> str:
    r"""One display block per equation, with the name *outside* the math.

    Not LaTeX's ``aligned``, for two reasons that only show up in a
    browser: a name is not math (``\text{total\_cost}`` renders its
    ``\_`` escape literally under MathJax, where a backtick span outside
    the math does not), and ``aligned`` columns align *across rows*, which
    a page showing one equation per heading has nothing to line up against.

    ``numbered`` is accepted and ignored — ``aligned`` cannot carry numbers,
    and producing something that looks numbered and is not would be worse.
    """
    del numbered
    blocks = []
    for line in lines:
        body = f'{line.left} {line.right}'.strip()
        if line.condition:
            body = f'{body} \\qquad {line.condition}'
        block = f'$${body}$$'
        if line.label:
            block = f'**{self.mono(line.label)}**\n\n{block}'
        blocks.append(block)
    return '\n\n'.join(blocks)

escape(prose) #

Markdown's text mode is prose, so author prose is already in it.

Source code in src/math_spec/typeset/markdown.py
def escape(self, prose: str) -> str:
    """Markdown's text mode *is* prose, so author prose is already in it."""
    return prose

fraction(numerator, denominator) #

Source code in src/math_spec/typeset/markdown.py
def fraction(self, numerator: str, denominator: str) -> str:
    return _LATEX.fraction(numerator, denominator)

glossary(title, entries) #

Source code in src/math_spec/typeset/markdown.py
def glossary(self, title: str, entries: list[Entry]) -> str:
    rows = '\n'.join(f'| {self.math(e.symbol)} | {e.meaning(self.dash)} |' for e in entries)
    return f'#### {title}\n\n| Symbol | Meaning |\n|---|---|\n{rows}'

greek(name) #

Source code in src/math_spec/typeset/markdown.py
def greek(self, name: str) -> str:
    return _LATEX.greek(name)

italic(name) #

Source code in src/math_spec/typeset/markdown.py
def italic(self, name: str) -> str:
    return _LATEX.italic(name)

joined(parts, operator) #

a op b op c, with \enspace as the bare separator.

LaTeX's ,\ would be safe here too — a backslash before a space is not a Markdown escape — but spelling it \enspace says why without the reader having to know that.

Source code in src/math_spec/typeset/markdown.py
def joined(self, parts: list[str], operator: str) -> str:
    r"""``a op b op c``, with ``\enspace`` as the bare separator.

    LaTeX's ``,\ `` would be safe here too — a backslash before a *space*
    is not a Markdown escape — but spelling it ``\enspace`` says why
    without the reader having to know that.
    """
    return f' {operator} '.join(parts) if operator else r',\enspace '.join(parts)

math(expression) #

Source code in src/math_spec/typeset/markdown.py
def math(self, expression: str) -> str:
    return _LATEX.math(expression)

mono(text) #

A backtick span — this one lands in prose, not in math.

Source code in src/math_spec/typeset/markdown.py
def mono(self, text: str) -> str:
    """A backtick span — this one lands in prose, not in math."""
    return f'`{text}`'

note(text) #

Source code in src/math_spec/typeset/markdown.py
def note(self, text: str) -> str:
    return text

parenthesise(inner) #

Source code in src/math_spec/typeset/markdown.py
def parenthesise(self, inner: str) -> str:
    return _LATEX.parenthesise(inner)

prose(text) #

Source code in src/math_spec/typeset/markdown.py
def prose(self, text: str) -> str:
    return _LATEX.prose(text)

script(letter) #

Source code in src/math_spec/typeset/markdown.py
def script(self, letter: str) -> str:
    return _LATEX.script(letter)

section(title, body) #

Source code in src/math_spec/typeset/markdown.py
def section(self, title: str, body: str) -> str:
    return f'#### {title}\n\n{body}'

subscript(base, indices) #

Source code in src/math_spec/typeset/markdown.py
def subscript(self, base: str, indices: list[str]) -> str:
    return _LATEX.subscript(base, indices)

summation(domain, body) #

Source code in src/math_spec/typeset/markdown.py
def summation(self, domain: str, body: str) -> str:
    return _LATEX.summation(domain, body)

superscript(base, tail) #

Source code in src/math_spec/typeset/markdown.py
def superscript(self, base: str, tail: str) -> str:
    return _LATEX.superscript(base, tail)

upright(name) #

Source code in src/math_spec/typeset/markdown.py
def upright(self, name: str) -> str:
    return _LATEX.upright(name)