The Element API
For users who want a little more control over how their markdown is formatted, SnakeMD provides a low-level API constructed of elements.
Element Interface
Broadly speaking, anything that can be rendered as markdown is known as an element. Below is the element interface.
- class snakemd.Element
Bases:
ABC
A generic element interface which provides a framework for all types of elements in the collection. In short, elements must be able to be converted to their markdown representation using the built-in
str
constructor.
For consistency, element mutators all return self to allow
for method chaining. This is sometimes referred to as the
fluent interface pattern, and it’s particularly useful
for applying a series of changes to a single element. This
design choice most obviously shines in both snakemd.Paragraph
,
which allows different aspects of the text to be replaced
over a series of chained methods, and snakemd.Inline
,
which allows inline elements to be styled over a series of
chained methods.
For practical purposes, elements cannot be constructed directly. Instead, they are broken down into two main categories: block and inline.
Block Elements
SnakeMD block elements borrow from the idea of block-level elements
from HTML. And because Markdown documents are constructed from a
series of blocks, users of SnakeMD can seemlessly append their own
custom blocks using the snakemd.Document.add_block()
method. To make use
of this method, blocks must be imported and constructed manually,
like the following snakemd.Heading
example:
>>> from snakemd import Heading, new_doc
>>> doc = new_doc()
>>> heading = doc.add_block(Heading("Hello, World!", 2))
The remainder of this section introduces the Block interface as well as all of the Blocks currently available for use.
Block Interface
All markdown blocks inherit from the Block interface.
Code
- class snakemd.Code(code: str | Code, lang: str = 'generic')
Bases:
Block
A code block is a standalone block of syntax-highlighted code. Code blocks can have generic highlighting or highlighting based on their language.
- Parameters:
- __str__() str
Renders the code block as a markdown string. Markdown code blocks are returned with the fenced code block format using backticks:
```python x = 5 y = 2 + x ```
Code blocks can be nested and will be rendered with increasing numbers of backticks.
- Returns:
the code block as a markdown string
Heading
- class snakemd.Heading(text: str | Inline | Iterable[Inline | str], level: int)
Bases:
Block
A heading is a text block which serves as the title for a new section of a document. Headings come in six main sizes which correspond to the six headings sizes in HTML (e.g.,
<h1>
).- Raises:
ValueError – when level < 1 or level > 6
- Parameters:
text (str | Inline | Iterable[Inline | str]) –
the heading text
set to a string to render raw heading text
set to an Inline object to render a styled heading (e.g., bold, link, code, etc.)
set to a “list” of the prior options to render a header with more granular control over the individual inline elements
level (int) – the heading level between 1 and 6
- __str__() str
Renders the heading as a markdown string. Markdown headings are returned using the
#
syntax where the number of#
symbols corresponds to the heading level:# This is an H1 ## This is an H2 ### This is an H3
- Returns:
the heading as a markdown string
- demote() Heading
Demotes a heading down a level. Fails silently if the heading is already at the lowest level (i.e.,
<h6>
).>>> heading = Heading("This is an H2 heading", 1).demote() >>> str(heading) '## This is an H2 heading'
- Returns:
self
- get_text() str
Returns the heading text free of any styling. Useful when the heading is composed of various Inline elements, and the raw text is needed without styling or linking.
>>> heading = Heading("This is the heading text", 1) >>> heading.get_text() 'This is the heading text'
- Returns:
the heading as a string
HorizontalRule
MDList
- class snakemd.MDList(items: Iterable[str | Inline | Block], ordered: bool = False, checked: None | bool | Iterable[bool] = None)
Bases:
Block
A markdown list is a standalone list that comes in three varieties: ordered, unordered, and checked.
- Raises:
ValueError – when the checked argument is an Iterable[bool] that does not match the number of top-level elements in the list
- Parameters:
items (Iterable[str | Inline | Block]) – a “list” of objects to be rendered as a list
ordered (bool) –
the ordered state of the list
defaults to
False
which renders an unordered list (i.e.,-
)set to
True
to render an ordered list (i.e.,1.
)
checked (None | bool | Iterable[bool]) –
the checked state of the list
defaults to
None
which excludes checkboxes from being renderedset to
False
to render a series of unchecked boxes (i.e.,- [ ]
)set to
True
to render a series of checked boxes (i.e.,- [x]
)set to
Iterable[bool]
to render the checked status of the top-level list elements directly
- __str__() str
Renders the markdown list as a markdown string. Markdown lists come in a variety of flavors and are customized according to the settings provided. For example, if the the ordered flag is set, an ordered list will be rendered in markdown. Unordered lists and checklists both use the hyphen syntax for markdown (i.e.,
-
) to avoid clashes with horizontal rules:- This is an unordered list item - So, is this
Ordered lists use numbers for each list item:
1. This is an ordered list item 2. So, is this
- Returns:
the list as a markdown string
Paragraph
- class snakemd.Paragraph(content: str | Iterable[Inline | str])
Bases:
Block
A paragraph is a standalone block of text.
- Parameters:
content (str | Iterable[str | Inline]) –
the text to be rendered as a paragraph where whitespace is not respected (see
snakemd.Raw
for whitespace sensitive applications)set to a string to render a single line of unformatted text
set to a “list” of text objects to render a paragraph with more granular control over the individual text objects (e.g., linking, styling, etc.)
- __str__() str
Renders the paragraph as a markdown string. Markdown paragraphs are returned as a singular line of text with all of the underlying elements rendered as expected:
This is an example of a **paragraph** with _formatting_
- Returns:
the paragraph as a markdown string
- add(text: str | Inline) Paragraph
Adds a text object to the paragraph.
>>> paragraph = Paragraph("Hello! ").add("I come in peace") >>> str(paragraph) 'Hello! I come in peace'
- insert_link(target: str, link: str, count: int = -1) Paragraph
A convenience method which inserts links in the paragraph for all matching instances of a target string. This method is modeled after
str.replace()
, so a count can be provided to limit the number of insertions. This method will not replace links of text that have already been linked. Seesnakemd.Paragraph.replace_link()
for that behavior.>>> paragraph = Paragraph("Go here for docs").insert_link("here", "https://snakemd.io") >>> str(paragraph) 'Go [here](https://snakemd.io) for docs'
- replace(target: str, replacement: str, count: int = -1) Paragraph
A convenience method which replaces a target string with a string of the users choice. Like
insert_link()
, this method is modeled afterstr.replace()
of the standard library. As a result, a count can be provided to limit the number of strings replaced in the paragraph.>>> paragraph = Paragraph("I come in piece").replace("piece", "peace") >>> str(paragraph) 'I come in peace'
- replace_link(target_link: str, replacement_link: str, count: int = -1) Paragraph
A convenience method which replaces matching URLs in the paragraph with a new url. Like
insert_link()
andreplace()
, this method is also modeled afterstr.replace()
, so a count can be provided to limit the number of links replaced in the paragraph. This method is useful if you want to replace existing URLs but don’t necessarily care what the anchor text is.>>> old = "https://therenegadecoder.com" >>> new = "https://snakemd.io" >>> paragraph = Paragraph("Go here for docs").insert_link("here", old).replace_link(old, new) >>> str(paragraph) 'Go [here](https://snakemd.io) for docs'
Quote
- class snakemd.Quote(content: str | Iterable[str | Inline | Block])
Bases:
Block
A quote is a standalone block of emphasized text. Quotes can be nested and can contain other blocks.
- Parameters:
content (str | Iterable[str | Inline | Block]) –
the text to be formatted as a Markdown quote
set to a string to render a whitespace respected quote (similar to
snakemd.Code
)set to a “list” of text objects to render a document-like quote (i.e., all items will be separated by newlines)
- __str__() str
Renders the quote as a markdown string. Markdown quotes vary in syntax, but the general approach in this repo is to apply the quote symbol (i.e.,
>
) to the front of each line in the quote:> this > is > a quote
Quotes can also be nested. To make this possible, nested quotes are padded by empty quote lines:
> Outer quote > > > Inner quote > > Outer quote
It’s unclear what is the correct way to handle nested quotes, but this format seems to be the most friendly for GitHub markdown. Future work may involve including the option to removing the padding.
- Returns:
the quote formatted as a markdown string
Raw
Table
- class snakemd.Table(header: Iterable[str | Inline | Paragraph], body: Iterable[Iterable[str | Inline | Paragraph]] = [], align: None | Iterable[Align] = None, indent: int = 0)
Bases:
Block
A table is a standalone block of rows and columns. Data is rendered according to underlying Inline items.
- Raises:
when rows of table are of varying lengths
when lengths of header and rows of table do not match
- Parameters:
header (Iterable[str | Inline | Paragraph]) – the header row of labels
body (Iterable[Iterable[str | Inline | Paragraph]]) – the collection of rows of data; defaults to an empty list
align (None | Iterable[Align]) – the column alignment; defaults to None
indent (int) – indent size for the whole table; defaults to 0
- class Align(value)
Bases:
Enum
Align is an enum only used by the Table class to specify the alignment of various columns in the table.
- CENTER = 3
- LEFT = 1
- RIGHT = 2
- __str__() str
Renders the table as a markdown string. Table markdown follows the standard pipe syntax:
| Header 1 | Header 2 | | -------- | -------- | | Item 1A | Item 2A | | Item 1B | Item 2B |
Alignment code adds colons in the appropriate locations. Final tables are rendered according to the widest items in each column for readability.
- Returns:
a table as a markdown string
- add_row(row: Iterable[str | Inline | Paragraph]) Table
A convenience method which adds a row to the end of table. Use this method to build a table row-by-row rather than constructing the table rows upfront.
>>> table = Table(["Rank", "Player"], [["1st", "Crosby"], ["2nd", "McDavid"]]) >>> table.add_row(["3rd", "Matthews"]) <snakemd.elements.Table object at ...> >>> str(table) '| Rank | Player |\n| ---- | -------- |\n| 1st | Crosby |\n| 2nd | McDavid |\n| 3rd | Matthews |'
- Raises:
ValueError – when row is not the same width as the table header
- Parameters:
- Returns:
self
Inline Elements
One of the benefits of creating Block elements over using the Document methods is the control users now have over the underlying structure and style. Instead of being bound to the string inputs, users can provide Inline elements directly. For example, there is often a need to link Headings. This is not exactly possible through the Document methods as even the returned Heading object has no support for linking. However, with Inline elements, we can create a custom Heading to our standards:
>>> from snakemd import Heading, Inline, new_doc
>>> doc = new_doc()
>>> heading = doc.add_block(Heading(Inline("Hello, World!", "https://snakemd.io"), 2))
The remainder of this section introduces the Inline class.
Inline
- class snakemd.Inline(text: str, image: None | str = None, link: None | str = None, bold: bool = False, italics: bool = False, strikethrough: bool = False, code: bool = False)
Bases:
Element
The basic unit of text in markdown. All components which contain text are built using this class instead of strings directly. That way, those elements capture all styling information.
Inline element parameters are in order of precedence. In other words, image markdown is applied to the text first while code markdown is applied last. Due to this design, some forms of inline text are not possible. For example, inline elements can be used to show inline markdown as an inline code element (e.g.,

). However, inline elements cannot be used to style inline code (e.g.,**`code`**
). If styled code is necessary, it’s possible to render the inline element as a string and pass the result to another inline element.- Parameters:
text (str) – the inline text to render
image (None | str) –
the source (either url or path) associated with an image
defaults to
None
set to a string representing a URL or path to render an image (i.e.,

)
link (None | str) –
the link (either url or path) associated with the inline element
defaults to
None
set to a string representing a URL or path to render a link (i.e.,
[text](link)
)
bold (bool) –
the bold state of the inline text
defaults to
False
set to
True
to render bold text (i.e.,**text**
)
italics (bool) –
the italics state of the inline element
defaults to
False
set to
True
to render text in italics (i.e.,_text_
)
strikethrough (bool) –
the strikethrough state of the inline text
defaults to
False
set to
True
to render text with a strikethrough (i.e.,~~text~~
)
code (bool) –
the code state of the inline text
defaults to
False
set to
True
to render text as code (i.e.,`text`
)
- __str__() str
Renders self as a string. In this case, inline can represent many different types of data from stylized text to code, links, and images.
>>> inline = Inline("This is formatted text", bold=True, italics=True) >>> str(inline) '_**This is formatted text**_'
- Returns:
the Inline object as a string
- bold() Inline
Adds bold styling to self.
>>> inline = Inline("This is bold text").bold() >>> str(inline) '**This is bold text**'
- Returns:
self
- code() Inline
Adds code style to self.
>>> inline = Inline("x = 5").code() >>> str(inline) '`x = 5`'
- Returns:
self
- is_link() bool
Checks if the Inline object represents a link.
>>> inline = Inline("This is not a link") >>> inline.is_link() False
- Returns:
True if the object has a link; False otherwise
- is_text() bool
Checks if this Inline element is a text-only element. If not, it must be an image, a link, or a code snippet.
>>> inline = Inline("This is text") >>> inline.is_text() True
- Returns:
True if this is a text-only element; False otherwise
- italicize() Inline
Adds italics styling to self.
>>> inline = Inline("This is italicized text").italicize() >>> str(inline) '_This is italicized text_'
- Returns:
self
- link(link: str) Inline
Adds link to self.
>>> inline = Inline("here").link("https://snakemd.io") >>> str(inline) '[here](https://snakemd.io)'
- Parameters:
link (str) – the URL or path to apply to this Inline element
- Returns:
self
- reset() Inline
Removes all settings from self (e.g., bold, code, italics, url, etc.). All that will remain is the text itself.
>>> inline = Inline("This is normal text", link="https://snakemd.io", bold=True).reset() >>> str(inline) 'This is normal text'
- Returns:
self
- strikethrough() Inline
Adds strikethrough styling to self.
>>> inline = Inline("This is striked text").strikethrough() >>> str(inline) '~~This is striked text~~'
- Returns:
self
- unbold() Inline
Removes bold styling from self.
>>> inline = Inline("This is normal text", bold=True).unbold() >>> str(inline) 'This is normal text'
- Returns:
self
- uncode() Inline
Removes code styling from self.
>>> inline = Inline("This is normal text", code=True).uncode() >>> str(inline) 'This is normal text'
- Returns:
self
- unitalicize() Inline
Removes italics styling from self.
>>> inline = Inline("This is normal text", italics=True).unitalicize() >>> str(inline) 'This is normal text'
- Returns:
self