API reference

Look up every public class and function of mkdeck.

You import everything on this page from the top-level mkdeck package. The one exception is the rollout converter, which lives in mkdeck.rollout. The public modules are mkdeck, mkdeck.errors, and mkdeck.rollout. Every other module is an implementation detail, and it can change between releases.

Deck.build and Deck.serve write and show a deck that you build in Python. load_source(...) returns a DeckSource with the same two methods for a deck written in Markdown. These are the only entry points. See Build a deck from Python for how they fit together.

The slide model

Markdown produces these classes, and the renderer reads them. You can also build them yourself.

mkdeck.Deck dataclass

A whole deck.

Attributes:

Name Type Description
title str

The deck title: the title of the page, and the text of the generated opening slide.

date str | None

The deck date, shown on the opening slide and in the chrome.

theme str

The name of the theme stylesheet to link.

slides list[Slide]

The slides, in the order they are shown.

units list[str]

The units the number highlighting recognises, in any order.

extra_css list[str]

Extra stylesheets, linked after the theme.

extra_js list[str]

Extra scripts, loaded after mkdeck.js.

reveal dict[str, Any]

Options merged into Reveal.initialize.

title_slide bool

Whether to generate the opening title slide from title and date. It is left out when the first slide is itself a title slide (layout="title"), which is then the opening slide.

Source code in src/mkdeck/model.py
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
@dataclass(slots=True)
class Deck:
    """A whole deck.

    Attributes:
        title: The deck title: the title of the page, and the text of the
            generated opening slide.
        date: The deck date, shown on the opening slide and in the chrome.
        theme: The name of the theme stylesheet to link.
        slides: The slides, in the order they are shown.
        units: The units the number highlighting recognises, in any order.
        extra_css: Extra stylesheets, linked after the theme.
        extra_js: Extra scripts, loaded after `mkdeck.js`.
        reveal: Options merged into `Reveal.initialize`.
        title_slide: Whether to generate the opening title slide from `title`
            and `date`. It is left out when the first slide is itself a title
            slide (`layout="title"`), which is then the opening slide.
    """

    title: str
    date: str | None = None
    theme: str = "minimal"
    slides: list[Slide] = field(default_factory=list)
    units: list[str] = field(default_factory=lambda: list(DEFAULT_UNITS))
    extra_css: list[str] = field(default_factory=list)
    extra_js: list[str] = field(default_factory=list)
    reveal: dict[str, Any] = field(default_factory=dict)
    title_slide: bool = True

    # build and serve import inside the method body on purpose: mkdeck.build and
    # mkdeck.server both import this module, so a module-scope import would be
    # circular,
    # and `import mkdeck` should not load the file watcher.

    def build(
        self,
        out: Path | str = "site",
        *,
        source: Path | str | None = None,
        single_file: bool = False,
    ) -> Path:
        """Write this deck to an output folder.

        Building again into the same folder brings it up to date: every file is
        copied afresh, and a file the previous build wrote that this one no
        longer needs is removed.

        Args:
            out: The output folder, or the HTML file when `single_file` is set
                and the path ends in `.html`.
            source: The folder the deck's assets live in; the current directory
                when omitted.
            single_file: True to inline every stylesheet and script.

        Returns:
            The path of the written HTML document.

        Raises:
            DeckError: If the deck or a slide breaks a rule of the model, the
                source folder does not exist, the output folder is the source
                folder or its `assets` folder, or a file the deck names is
                outside the source folder.
        """
        from mkdeck.build import build_deck

        return build_deck(self, out, source=source, single_file=single_file)

    def serve(
        self,
        *,
        source: Path | str | None = None,
        host: str = "127.0.0.1",
        port: int = 5020,
        open_browser: bool = False,
        reload: bool = True,
    ) -> None:
        """Serve this deck until interrupted.

        The deck is the one in hand: it is built again, unchanged, whenever a
        file under `source` changes. The Python code that made the slides is not
        run again.

        Args:
            source: The folder the deck's assets live in; the current directory
                when omitted. It is watched, and the page reloads when a file in
                it changes.
            host: The interface to bind.
            port: The port to bind.
            open_browser: True to open the deck in a browser once it is up.
            reload: True to watch `source` and push reloads.

        Raises:
            DeckError: If the deck cannot be rendered, the source folder does
                not exist, or the port is taken.
        """
        from mkdeck.build import build_deck
        from mkdeck.server import serve

        folder = Path(source) if source is not None else Path.cwd()

        def build(root: Path, live: bool) -> None:
            build_deck(self, root, source=folder, live_reload=live)

        serve(
            build,
            watch_paths=[folder],
            host=host,
            port=port,
            open_browser=open_browser,
            reload=reload,
        )

build(out: Path | str = 'site', *, source: Path | str | None = None, single_file: bool = False) -> Path

Write this deck to an output folder.

Building again into the same folder brings it up to date: every file is copied afresh, and a file the previous build wrote that this one no longer needs is removed.

Parameters:

Name Type Description Default
out Path | str

The output folder, or the HTML file when single_file is set and the path ends in .html.

'site'
source Path | str | None

The folder the deck's assets live in; the current directory when omitted.

None
single_file bool

True to inline every stylesheet and script.

False

Returns:

Type Description
Path

The path of the written HTML document.

Raises:

Type Description
DeckError

If the deck or a slide breaks a rule of the model, the source folder does not exist, the output folder is the source folder or its assets folder, or a file the deck names is outside the source folder.

Source code in src/mkdeck/model.py
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
def build(
    self,
    out: Path | str = "site",
    *,
    source: Path | str | None = None,
    single_file: bool = False,
) -> Path:
    """Write this deck to an output folder.

    Building again into the same folder brings it up to date: every file is
    copied afresh, and a file the previous build wrote that this one no
    longer needs is removed.

    Args:
        out: The output folder, or the HTML file when `single_file` is set
            and the path ends in `.html`.
        source: The folder the deck's assets live in; the current directory
            when omitted.
        single_file: True to inline every stylesheet and script.

    Returns:
        The path of the written HTML document.

    Raises:
        DeckError: If the deck or a slide breaks a rule of the model, the
            source folder does not exist, the output folder is the source
            folder or its `assets` folder, or a file the deck names is
            outside the source folder.
    """
    from mkdeck.build import build_deck

    return build_deck(self, out, source=source, single_file=single_file)

serve(*, source: Path | str | None = None, host: str = '127.0.0.1', port: int = 5020, open_browser: bool = False, reload: bool = True) -> None

Serve this deck until interrupted.

The deck is the one in hand: it is built again, unchanged, whenever a file under source changes. The Python code that made the slides is not run again.

Parameters:

Name Type Description Default
source Path | str | None

The folder the deck's assets live in; the current directory when omitted. It is watched, and the page reloads when a file in it changes.

None
host str

The interface to bind.

'127.0.0.1'
port int

The port to bind.

5020
open_browser bool

True to open the deck in a browser once it is up.

False
reload bool

True to watch source and push reloads.

True

Raises:

Type Description
DeckError

If the deck cannot be rendered, the source folder does not exist, or the port is taken.

Source code in src/mkdeck/model.py
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
def serve(
    self,
    *,
    source: Path | str | None = None,
    host: str = "127.0.0.1",
    port: int = 5020,
    open_browser: bool = False,
    reload: bool = True,
) -> None:
    """Serve this deck until interrupted.

    The deck is the one in hand: it is built again, unchanged, whenever a
    file under `source` changes. The Python code that made the slides is not
    run again.

    Args:
        source: The folder the deck's assets live in; the current directory
            when omitted. It is watched, and the page reloads when a file in
            it changes.
        host: The interface to bind.
        port: The port to bind.
        open_browser: True to open the deck in a browser once it is up.
        reload: True to watch `source` and push reloads.

    Raises:
        DeckError: If the deck cannot be rendered, the source folder does
            not exist, or the port is taken.
    """
    from mkdeck.build import build_deck
    from mkdeck.server import serve

    folder = Path(source) if source is not None else Path.cwd()

    def build(root: Path, live: bool) -> None:
        build_deck(self, root, source=folder, live_reload=live)

    serve(
        build,
        watch_paths=[folder],
        host=host,
        port=port,
        open_browser=open_browser,
        reload=reload,
    )

mkdeck.Slide dataclass

One slide of a deck.

Attributes:

Name Type Description
id str | None

A stable identifier, emitted as data-id, or None.

layout Layout

How the slide is laid out; "auto" picks from the content.

title str | None

The heading, drawn above the rest of the slide. A slide that holds nothing else is a title slide.

sentence str | None

The visible headline. Inline Markdown and $...$ math are allowed, and so is an HTML element, which is kept as written.

bullets list[str]

A compact list drawn under the sentence, with the same inline syntax.

math list[str]

Display formulas, one per line, drawn above the sentence.

embeds list[Embed]

The figures on the slide, at most MAX_EMBEDS of them. A slide takes figures or a table, not both.

table Table | None

The table on the slide, or None.

html str | None

Raw HTML drawn with the rest of the slide. The escape hatch.

notes str | None

Speaker notes, shown in reveal's presenter view.

date str | None

On layout="title", the date that restamps every later slide.

classes list[str]

Extra CSS classes put on the <section>.

Source code in src/mkdeck/model.py
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
@dataclass(slots=True)
class Slide:
    """One slide of a deck.

    Attributes:
        id: A stable identifier, emitted as `data-id`, or `None`.
        layout: How the slide is laid out; `"auto"` picks from the content.
        title: The heading, drawn above the rest of the slide. A slide that
            holds nothing else is a title slide.
        sentence: The visible headline. Inline Markdown and `$...$` math are
            allowed, and so is an HTML element, which is kept as written.
        bullets: A compact list drawn under the sentence, with the same inline
            syntax.
        math: Display formulas, one per line, drawn above the sentence.
        embeds: The figures on the slide, at most `MAX_EMBEDS` of them. A slide
            takes figures or a table, not both.
        table: The table on the slide, or `None`.
        html: Raw HTML drawn with the rest of the slide. The escape hatch.
        notes: Speaker notes, shown in reveal's presenter view.
        date: On `layout="title"`, the date that restamps every later slide.
        classes: Extra CSS classes put on the `<section>`.
    """

    id: str | None = None
    layout: Layout = "auto"
    title: str | None = None
    sentence: str | None = None
    bullets: list[str] = field(default_factory=list)
    math: list[str] = field(default_factory=list)
    embeds: list[Embed] = field(default_factory=list)
    table: Table | None = None
    html: str | None = None
    notes: str | None = None
    date: str | None = None
    classes: list[str] = field(default_factory=list)

mkdeck.Embed dataclass

A figure on a slide: an iframe page, an image or an animation.

Attributes:

Name Type Description
src str

The page or image, relative to the deck source folder. An http or https URL is also accepted; an absolute path is not. A query or fragment, as in plot.html?seed=2, is kept in the document and left off the file name.

label str | None

The caption drawn above the frame, or None for no caption.

kind EmbedKind

"iframe", "image" or "rollout", or "auto" to pick from the suffix of src.

Source code in src/mkdeck/model.py
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
@dataclass(slots=True)
class Embed:
    """A figure on a slide: an iframe page, an image or an animation.

    Attributes:
        src: The page or image, relative to the deck source folder. An `http` or
            `https` URL is also accepted; an absolute path is not. A query or
            fragment, as in `plot.html?seed=2`, is kept in the document and left
            off the file name.
        label: The caption drawn above the frame, or `None` for no caption.
        kind: `"iframe"`, `"image"` or `"rollout"`, or `"auto"` to pick from the
            suffix of `src`.
    """

    src: str
    label: str | None = None
    kind: EmbedKind = "auto"

mkdeck.Table dataclass

A table on a slide.

Attributes:

Name Type Description
columns list[str]

The header cells, at least one. Each is shown with str().

rows list[list[str]]

The body rows; every row holds one cell per column, each shown with str().

Source code in src/mkdeck/model.py
109
110
111
112
113
114
115
116
117
118
119
120
@dataclass(slots=True)
class Table:
    """A table on a slide.

    Attributes:
        columns: The header cells, at least one. Each is shown with `str()`.
        rows: The body rows; every row holds one cell per column, each shown
            with `str()`.
    """

    columns: list[str]
    rows: list[list[str]]

mkdeck.DEFAULT_UNITS: tuple[str, ...] = ('N m s/rad', 'kg m^2', 'rad/s', 'body weights', 'N m', 'mm', 'ms', 'Hz', 'kg', 'm', 's', 'm/s', '%', 'percent', 'degrees') module-attribute

The units a number may carry and still be highlighted.

These apply when a deck does not set its own.

The order does not matter: the renderer tries the longest spelling first.

Load a deck from disk

mkdeck.load_source(path: Path | str) -> DeckSource

Load a deck and its settings from disk.

The deck.yml beside the Markdown and the frontmatter are read once and merged into the fields of the deck, the frontmatter winning.

Parameters:

Name Type Description Default
path Path | str

A Markdown file, or a folder holding deck.md or slides.md.

required

Returns:

Type Description
DeckSource

The loaded deck, with its source folder.

Raises:

Type Description
DeckError

If the path holds no deck, the file is not readable UTF-8 text, or the deck cannot be parsed.

Source code in src/mkdeck/source.py
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
def load_source(path: Path | str) -> DeckSource:
    """Load a deck and its settings from disk.

    The `deck.yml` beside the Markdown and the frontmatter are read once and
    merged into the fields of the deck, the frontmatter winning.

    Args:
        path: A Markdown file, or a folder holding `deck.md` or `slides.md`.

    Returns:
        The loaded deck, with its source folder.

    Raises:
        DeckError: If the path holds no deck, the file is not readable UTF-8
            text, or the deck cannot be parsed.
    """
    markdown = find_markdown(path)
    deck = parse_markdown(
        read_text(markdown),
        source=markdown,
        defaults=read_config_file(markdown),
    )
    return DeckSource(deck=deck, directory=markdown.parent, markdown=markdown)

mkdeck.DeckSource dataclass

A deck loaded from disk, ready to build or serve.

Attributes:

Name Type Description
deck Deck

The parsed deck. The frontmatter and deck.yml are already merged into its fields, so the deck alone carries the settings.

directory Path

The folder holding the Markdown and its assets.

markdown Path

The Markdown file the deck came from.

Source code in src/mkdeck/source.py
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
@dataclass(frozen=True, slots=True)
class DeckSource:
    """A deck loaded from disk, ready to build or serve.

    Attributes:
        deck: The parsed deck. The frontmatter and `deck.yml` are already merged
            into its fields, so the deck alone carries the settings.
        directory: The folder holding the Markdown and its assets.
        markdown: The Markdown file the deck came from.
    """

    deck: Deck
    directory: Path
    markdown: Path

    def build(
        self, out: Path | str = "site", *, single_file: bool = False
    ) -> Path:
        """Write the deck to an output folder, with the assets it names.

        Args:
            out: The output folder, or the HTML file when `single_file` is set
                and the path ends in `.html`.
            single_file: True to inline every stylesheet and script.

        Returns:
            The path of the written HTML document.

        Raises:
            DeckError: If a slide breaks a rule of the model, the output folder
                is the deck folder, or a file the deck names is outside the deck
                folder.
        """
        return self.deck.build(
            out, source=self.directory, single_file=single_file
        )

    def serve(
        self,
        *,
        host: str = "127.0.0.1",
        port: int = 5020,
        open_browser: bool = False,
        reload: bool = True,
    ) -> None:
        """Serve the deck until interrupted.

        Read it again whenever its folder changes. While it is served from
        this machine, the text of a slide can be edited on the page, and the
        edit is written into the Markdown file.

        The first build uses `deck` as it is, but every rebuild reads the
        Markdown and `deck.yml` from disk again, so a change made to `deck` in
        Python shows only until the first edit. To serve a deck that is changed
        in Python, serve that `Deck` with `Deck.serve` instead.

        The folder is watched apart from the ones `mkdeck build`, `check` and
        `export` write into by default (`site`, `report` and `deck.pdf`).

        Args:
            host: The interface to bind.
            port: The port to bind.
            open_browser: True to open the deck in a browser once it is up.
            reload: True to watch the folder and push reloads.

        Raises:
            DeckError: If the deck cannot be rendered or the port is taken.
        """
        from mkdeck.build import (
            build_deck,  # imported here, so that `import mkdeck` loads neither
        )
        from mkdeck.server import serve  # the renderer nor the file watcher

        pending: DeckSource | None = self

        def build(root: Path, live: bool) -> None:
            nonlocal pending
            current, pending = (
                pending or load_source(self.markdown),
                None,
            )  # the first build reuses the deck in hand
            build_deck(
                current.deck,
                root,
                source=current.directory,
                live_reload=live,
                editable=True,
            )

        serve(
            build,
            watch_paths=[self.directory],
            host=host,
            port=port,
            open_browser=open_browser,
            reload=reload,
            edit_file=self.markdown,
        )

build(out: Path | str = 'site', *, single_file: bool = False) -> Path

Write the deck to an output folder, with the assets it names.

Parameters:

Name Type Description Default
out Path | str

The output folder, or the HTML file when single_file is set and the path ends in .html.

'site'
single_file bool

True to inline every stylesheet and script.

False

Returns:

Type Description
Path

The path of the written HTML document.

Raises:

Type Description
DeckError

If a slide breaks a rule of the model, the output folder is the deck folder, or a file the deck names is outside the deck folder.

Source code in src/mkdeck/source.py
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
def build(
    self, out: Path | str = "site", *, single_file: bool = False
) -> Path:
    """Write the deck to an output folder, with the assets it names.

    Args:
        out: The output folder, or the HTML file when `single_file` is set
            and the path ends in `.html`.
        single_file: True to inline every stylesheet and script.

    Returns:
        The path of the written HTML document.

    Raises:
        DeckError: If a slide breaks a rule of the model, the output folder
            is the deck folder, or a file the deck names is outside the deck
            folder.
    """
    return self.deck.build(
        out, source=self.directory, single_file=single_file
    )

serve(*, host: str = '127.0.0.1', port: int = 5020, open_browser: bool = False, reload: bool = True) -> None

Serve the deck until interrupted.

Read it again whenever its folder changes. While it is served from this machine, the text of a slide can be edited on the page, and the edit is written into the Markdown file.

The first build uses deck as it is, but every rebuild reads the Markdown and deck.yml from disk again, so a change made to deck in Python shows only until the first edit. To serve a deck that is changed in Python, serve that Deck with Deck.serve instead.

The folder is watched apart from the ones mkdeck build, check and export write into by default (site, report and deck.pdf).

Parameters:

Name Type Description Default
host str

The interface to bind.

'127.0.0.1'
port int

The port to bind.

5020
open_browser bool

True to open the deck in a browser once it is up.

False
reload bool

True to watch the folder and push reloads.

True

Raises:

Type Description
DeckError

If the deck cannot be rendered or the port is taken.

Source code in src/mkdeck/source.py
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
def serve(
    self,
    *,
    host: str = "127.0.0.1",
    port: int = 5020,
    open_browser: bool = False,
    reload: bool = True,
) -> None:
    """Serve the deck until interrupted.

    Read it again whenever its folder changes. While it is served from
    this machine, the text of a slide can be edited on the page, and the
    edit is written into the Markdown file.

    The first build uses `deck` as it is, but every rebuild reads the
    Markdown and `deck.yml` from disk again, so a change made to `deck` in
    Python shows only until the first edit. To serve a deck that is changed
    in Python, serve that `Deck` with `Deck.serve` instead.

    The folder is watched apart from the ones `mkdeck build`, `check` and
    `export` write into by default (`site`, `report` and `deck.pdf`).

    Args:
        host: The interface to bind.
        port: The port to bind.
        open_browser: True to open the deck in a browser once it is up.
        reload: True to watch the folder and push reloads.

    Raises:
        DeckError: If the deck cannot be rendered or the port is taken.
    """
    from mkdeck.build import (
        build_deck,  # imported here, so that `import mkdeck` loads neither
    )
    from mkdeck.server import serve  # the renderer nor the file watcher

    pending: DeckSource | None = self

    def build(root: Path, live: bool) -> None:
        nonlocal pending
        current, pending = (
            pending or load_source(self.markdown),
            None,
        )  # the first build reuses the deck in hand
        build_deck(
            current.deck,
            root,
            source=current.directory,
            live_reload=live,
            editable=True,
        )

    serve(
        build,
        watch_paths=[self.directory],
        host=host,
        port=port,
        open_browser=open_browser,
        reload=reload,
        edit_file=self.markdown,
    )

Errors and warnings

mkdeck.DeckError

Bases: Exception

A deck could not be read, validated or rendered.

The string form of the error is <source>: <slide>: <message>, with the parts that are known. Name the slide with mkdeck.model.slide_name so that every message names it the same way.

Attributes:

Name Type Description
message

What went wrong and what the author should do about it.

slide

The offending slide, already named, or None for a deck-level problem.

source

The deck file or folder the problem came from, or None.

Source code in src/mkdeck/errors.py
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
class DeckError(Exception):
    """A deck could not be read, validated or rendered.

    The string form of the error is `<source>: <slide>: <message>`, with the
    parts that are known. Name the slide with `mkdeck.model.slide_name` so that
    every message names it the same way.

    Attributes:
        message: What went wrong and what the author should do about it.
        slide: The offending slide, already named, or `None` for a deck-level
            problem.
        source: The deck file or folder the problem came from, or `None`.
    """

    def __init__(
        self,
        message: str,
        *,
        slide: str | None = None,
        source: Path | str | None = None,
    ) -> None:
        """Create the error.

        Args:
            message: What went wrong and what the author should do about it.
                Write it as a complete sentence with its own subject, because it
                is also shown without the prefix, for example `"This slide has 3
                embeds, but a slide takes at most 2; move one onto a new
                slide."`.
            slide: The offending slide, named by `mkdeck.model.slide_name`.
            source: The deck file or folder the problem came from.
        """
        self.message = message
        self.slide = slide
        self.source = str(source) if source is not None else None
        prefix = ": ".join(part for part in (self.source, self.slide) if part)
        super().__init__(f"{prefix}: {message}" if prefix else message)

__init__(message: str, *, slide: str | None = None, source: Path | str | None = None) -> None

Create the error.

Parameters:

Name Type Description Default
message str

What went wrong and what the author should do about it. Write it as a complete sentence with its own subject, because it is also shown without the prefix, for example "This slide has 3 embeds, but a slide takes at most 2; move one onto a new slide.".

required
slide str | None

The offending slide, named by mkdeck.model.slide_name.

None
source Path | str | None

The deck file or folder the problem came from.

None
Source code in src/mkdeck/errors.py
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
def __init__(
    self,
    message: str,
    *,
    slide: str | None = None,
    source: Path | str | None = None,
) -> None:
    """Create the error.

    Args:
        message: What went wrong and what the author should do about it.
            Write it as a complete sentence with its own subject, because it
            is also shown without the prefix, for example `"This slide has 3
            embeds, but a slide takes at most 2; move one onto a new
            slide."`.
        slide: The offending slide, named by `mkdeck.model.slide_name`.
        source: The deck file or folder the problem came from.
    """
    self.message = message
    self.slide = slide
    self.source = str(source) if source is not None else None
    prefix = ": ".join(part for part in (self.source, self.slide) if part)
    super().__init__(f"{prefix}: {message}" if prefix else message)

mkdeck.DeckWarning

Bases: UserWarning

A problem that leaves the deck usable.

An embed that is not on disk is one such problem.

Source code in src/mkdeck/errors.py
56
57
58
59
60
class DeckWarning(UserWarning):
    """A problem that leaves the deck usable.

    An embed that is not on disk is one such problem.
    """

Rollouts

mkdeck rollout calls convert_brax_html. It reads one Brax playback page. It writes a .rollout file and, for a model it has not seen, a shared .meshes file. A page with no Brax scene raises NotBraxPage, which is a DeckError. A caller that converts a folder of pages can catch it and skip the page.

mkdeck.rollout.convert_brax_html(src: Path | str, out: Path | str) -> Converted

Convert one Brax page into a rollout and its shared meshes.

The mesh file is named after its own content, so converting a second run of the same model writes only the small per-run file and leaves the meshes alone. Both files are written whole or not at all.

Parameters:

Name Type Description Default
src Path | str

The Brax .html to read.

required
out Path | str

The folder to write into; it is created when missing.

required

Returns:

Type Description
Converted

Where the files went.

Raises:

Type Description
NotBraxPage

If the page holds no scene at all.

DeckError

If the page holds a scene that cannot be converted.

Source code in src/mkdeck/rollout.py
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
def convert_brax_html(src: Path | str, out: Path | str) -> Converted:
    """Convert one Brax page into a rollout and its shared meshes.

    The mesh file is named after its own content, so converting a second run of
    the same model writes only the small per-run file and leaves the meshes
    alone. Both files are written whole or not at all.

    Args:
        src: The Brax ``.html`` to read.
        out: The folder to write into; it is created when missing.

    Returns:
        Where the files went.

    Raises:
        NotBraxPage: If the page holds no scene at all.
        DeckError: If the page holds a scene that cannot be converted.
    """
    src, out = Path(src), Path(out)
    scene = read_brax_scene(src)
    try:
        rollout = to_rollout(scene, name=src.stem)
    except DeckError as exc:
        raise DeckError(exc.message, source=src) from exc
    except (
        KeyError,
        TypeError,
        ValueError,
        AttributeError,
        OverflowError,
    ) as exc:
        raise DeckError(
            f"holds a Brax scene this version cannot read "
            f"({type(exc).__name__}: {exc}).",
            source=src,
        ) from exc
    out.mkdir(parents=True, exist_ok=True)
    meshes = out / f"{rollout.meshes_hash}{MESHES_SUFFIX}"
    shared = meshes.is_file()
    if not shared:
        _write_atomic(meshes, gzip.compress(rollout.meshes, 6, mtime=0))
    target = out / f"{src.stem}{ROLLOUT_SUFFIX}"
    _write_atomic(target, dump_rollout(rollout, meshes_name=meshes.name))
    return Converted(rollout=target, meshes=meshes, shared=shared)

mkdeck.rollout.Converted dataclass

Where one conversion put its files.

Attributes:

Name Type Description
rollout Path

The written .rollout.

meshes Path

The shared .meshes the rollout points at.

shared bool

True when the mesh file was already there, written by an earlier run of the same model.

Source code in src/mkdeck/rollout.py
116
117
118
119
120
121
122
123
124
125
126
127
128
129
@dataclass(frozen=True)
class Converted:
    """Where one conversion put its files.

    Attributes:
        rollout: The written ``.rollout``.
        meshes: The shared ``.meshes`` the rollout points at.
        shared: True when the mesh file was already there, written by an earlier
            run of the same model.
    """

    rollout: Path
    meshes: Path
    shared: bool

mkdeck.rollout.NotBraxPage

Bases: DeckError

The page is not a Brax playback page, which is not always a mistake.

An assets folder normally mixes playback pages with plots, so a caller converting a batch may want to skip these and still fail on the rest.

Source code in src/mkdeck/rollout.py
88
89
90
91
92
93
class NotBraxPage(DeckError):  # noqa: N818 - a case for the caller to choose on, not a failure name
    """The page is not a Brax playback page, which is not always a mistake.

    An assets folder normally mixes playback pages with plots, so a caller
    converting a batch may want to skip these and still fail on the rest.
    """