Directives

A cheat sheet of directives available in nbdev.

Directive comments start with #|. They control:

  1. Cell visibility in rendered documentation
  2. How source code is generated from notebook cells
  3. Execution of cells for tests and docs

nbdev supports all Quarto directives and adds its own. This page lists all nbdev directives and selected Quarto directives. See Quartoโ€™s directive reference for its full list.

To clarify the origin of directives we use the following emojis:

A directive accepts a value with or without a colon. These spellings are equivalent:

#| default_exp: core
#| default_exp:core
#| default_exp core

A bare directive means true: #| hide is equivalent to #| hide: true.

You can also put directives in cell metadata under an nbdev key. For example, {"nbdev": {"hide": "true", "eval": "false"}} is equivalent to #| hide and #| eval: false.

Metadata values must be strings. The string "true" represents a bare directive. JSON booleans true and false raise an error. Notebook-level directives such as default_exp can use the same format in notebook metadata. A comment takes precedence over metadata for the same directive.

When rendering docs, nbdev writes directives into cell sources as Quarto options. It uses name: value for values and name: true for bare directives. Quarto applies the options it recognizes and ignores the rest.

Cell Visibility

The following directives control cell visibility in rendered documentation:

๐Ÿ““ #| hide

Hide cell input and output.

The following will result in the contents of the cell and itโ€™s output from being hidden:

#| hide
print('you will not see this')

Note that using #| hide is equivalent to using the Quarto directive #| include: false:

#| include: false
print('you will not see this')

See the quarto docs for more information about #| include.

๐Ÿ”ต #| echo: <true|false>

Toggle the visibility of code-cell inputs.

#| echo: false
print('you can see the output but not the code!')

which results in:

you can see the output but not the code!

๐Ÿ”ต #| output: <true|false|asis>

Setting this to false hides the output of a cell. Setting this to asis renders the output as raw markdown.

The following cell will not display any output:

#| output: false
1 + 1

The following cell with #| output: asis will produce the output hello fastai rendered as markdown instead of a string:

#| output: asis
print("`hello fastai`")

๐Ÿ““ #| hide_line

Hide a specific line of code in an input cell.

def _secret(): ...

for i in range(3):
    _secret() #| hide_line
    print(i)

becomes this:

def _secret(): ...

for i in range(3):
    print(i)
0
1
2

๐Ÿ““ #| filter_stream <keyword> ...

Filter lines containing specific keywords in cell outputs.

#| filter_stream FutureWarning MultiIndex
print('\n'.join(['A line', 'Foobar baz FutureWarning blah', 
                 'zig zagMultiIndex zoom', 'Another line.']))

will output this:

A line
Another line.

๐Ÿ”ต #| code-fold: <show|true>

#| code-fold makes the code input collapsible. Use true to start collapsed or show to start expanded.

When you set #| code-fold: true, the input cell is collapsed:

Code
print('this is')
print('output')
print('that takes')
print('lots of vertical space')
this is
output
that takes
lots of vertical space

When you set #| code-fold: show the input cell is shown but still in a collapsible element:

Code
print('this is')
print('output')
print('that takes')
print('lots of vertical space')
this is
output
that takes
lots of vertical space

Generating Source Code

The following directives control how source code is exported from code cells.

๐Ÿ““ #| default_exp <name>

Names the module where cells with the #| export directive will be exported to by default.

#| default_exp baz

# In a new notebook cell:

#| export
def my_function(): pass

If our package is named: bitsnbytes then we can do:

from bitsnbytes.baz import my_function

The package name is defined by name in [project] in pyproject.toml.

๐Ÿ““ #| export

Exports the items in the cell into the generated module and documentation.

#| export
def say_hello(to:str # name of person to say hello to
             ):
    "Say hello to somebody"
    return f'Hello {to}!'

The above cell will get exported to the module specified by #| default_exp. These exports are automatically included in __all__ for the module. To export without including the names in __all__, use #| exporti.

Furthermore, the documentation for this function will automatically be rendered like this:


say_hello

def say_hello(
    to:str, # name of person to say hello to
):

Say hello to somebody

The docs are generated from this export using show_doc. See these docs for a detailed discussion of show_doc.

๐Ÿ““ #| exporti

An internal export. Not included in __all__ or the docs. Useful for a function that is called by other functions in this module but is not part of the public API.

Equivalently, you can prefix your function or method with _ e.g. def _private(): pass.

๐Ÿ““ #| exports

A source export. Like #| export but in addition to showing docs via showdoc.show_doc, it also shows the source code.

#| exports
def say_hello(to):
    "Say hello to somebody"
    return f'Hello {to}!'

this will produce the following output:

def say_hello(to):
    "Say hello to somebody"
    return f'Hello {to}!'

say_hello

def say_hello(
    to
):

Say hello to somebody

๐Ÿ““ #| exportd

Include the cell in the generated moduleโ€™s docstring. Code cells become fenced examples in the docstring, not executable module code. Markdown cells appear as written, as they do with #| export.

This lets you use tested examples in the docstring without copying them. The docstring starts with the title cellโ€™s > summary, followed by exported Markdown and exportd cells in notebook order.

Cell Execution

The following directives allow you to control how cells are executed during docs rendering and testing.

๐Ÿ““ #| exec_doc

Ensures that a cell is executed each time before generating docs. When a cell does not have this annotation, it is run according to the default rules described here.

datetime.datetime.now()
datetime.datetime(2022, 8, 18, 9, 1, 43, 907609)

However with the annotation:

#| exec_doc
datetime.datetime.now()

we can see that the time has been updated:

datetime.datetime.now()
datetime.datetime(2026, 9, 14, 9, 36, 4, 171863)

๐Ÿ”ต #| eval: <true|false>

When set to false, the cell is ignored during testing.

#| eval: false
raise Exception("I'm not raised because I'm not run")

๐Ÿ““ Notebook frontmatter: skip_exec and skip_showdoc

Two frontmatter keys control execution for a whole notebook, rather than a single cell. Set them in the notebookโ€™s frontmatter, e.g. as list items under the title:

# My notebook
> Needs a running database
- skip_exec: true
  • skip_exec: true makes nbdev-test skip the notebook and report it as passing. Use it when the notebook needs credentials or live services unavailable in the test environment.
  • skip_showdoc: true makes docs rendering use stored outputs without executing cells.

For example, nbdevโ€™s own api/19_diff.ipynb sets skip_exec: true, and tutorials/modular_nbdev.ipynb sets skip_showdoc: true.

Cell execution when there is no directive

When a cell has no directives, cells are run by nbdev according to the behavior described here.