Skip to content

User Guide#

PyPI - Version coverage code style: black Ruff Hatch project

Use includex to include anything from any file into your markdown documentation.

Installation#

pip install mkdocs-macros mkdocs-macros-includex

Usage#

includex can be configured as a pluglet for mkdocs-macros in the mkdocs.yml configuration file:

plugins:
  - search
  - macros:
      modules: ['includex']

Then you can use it to dynamically include file content in your documentation

### Versioning

The version number is defined in the `pyproject.toml` file:

{{ includex("pyproject.toml", start_match="[tool.hatch.version]", code=True, lines=2, caption=True) }}

which would be rendered as


Versioning#

The version number is defined in the pyproject.toml file:

[tool.hatch.version]
path = "includex.py"
pyproject.toml, lines 14-15

Comparison to other tools#

snippets (pymdown-extensions)#

tl;dr

  • use snippets if you want to recursively include content
  • use includex if you want to include content within macros
  • use includex if you want to include sections without special markers

The main use case this solves over snippets is that it includes partial content (i.e. a section or block) from a file as-is (i.e. without the need for special markers).

Snippets partially supports this now since v9.6 added support to include sections by lines.1 Further, v9.7 added to support to sections by name, given that they are marked as such using a special marker comment.2

What includex does additionally is to match the start and end of blocks and include them without the need for any markers or line numbers. While this makes the documentation more prone to break, e.g., when the line that is being matched is changed in a way that it no longer matches, it supports some additional use-cases and requires less custom syntax.

Snippets is implemented as a preprocessor, while includex is implemented as a mkdocs-macros pluglet. This means that snippets are evaluated earlier than includex and prohibits snippets to work with other macros, like this:

{% for file in get_files("docs/") %}
{{ includex(file) }}
{% endfor %}

However, sections included using includex are not evaluated themselves, so includex cannot be nested (yet). If you need nested includes, use snippets instead.

Examples#

Include complete file#

includex('LICENSE')
Result
MIT License

Copyright (c) 2023 Jannis Mainczyk

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

Include based on lines#

includex('LICENSE', end=3)
Result
MIT License

Copyright (c) 2023 Jannis Mainczyk
includex('LICENSE', start=1, lines=3)
Result
MIT License

Copyright (c) 2023 Jannis Mainczyk

Include based on content#

Include a content block by matching the start line and specifying the number of lines to include:

includex('mkdocs.yml', start_match='plugins:', lines=2)
Result
plugins:
  - search

Inlude a content block by matching the beginning of the next block:

includex('mkdocs.yml', start_match='features:', end_match='nav:')
Result
features:
  - content.code.copy
  - navigation.instant
  - navigation.tracking
  - toc.follow
  - toc.integrate
  - navigation.top

Inlude a content block by matching the line that starts the block:

includex('mkdocs.yml', start_match='features:', start_offset=1, end_match='nav:')
Result
- content.code.copy
- navigation.instant
- navigation.tracking
- toc.follow
- toc.integrate
- navigation.top

Inlude a content block by matching the end of the block:

includex('mkdocs.yml', start_match='site_name:', end_match='repo_name:', include_end_match=True)
Result
site_name: includex
site_url: http://jannismain.github.io/mkdocs-macros-includex
edit_uri: -/edit/main/doc/
site_dir: build/docs
site_author: Jannis Mainczyk
copyright: © Jannis Mainczyk
repo_url: https://github.com/jannismain/mkdocs-macros-includex
repo_name: mkdocs-macros-includex

Modifying indentation#

The included file content is dedented by default. However, you can also include content with its original indentation:

includex('mkdocs.yml', start_match='features:', end_match='nav:', dedent=False)
Result
  features:
    - content.code.copy
    - navigation.instant
    - navigation.tracking
    - toc.follow
    - toc.integrate
    - navigation.top

or set your own level of indentation:

includex('mkdocs.yml', start_match='features:', end_match='nav:', indent=8, indent_first=True)
Result
        features:
          - content.code.copy
          - navigation.instant
          - navigation.tracking
          - toc.follow
          - toc.integrate
          - navigation.top

You can also choose a custom character to be used for this indentation:

includex('mkdocs.yml', start_match='features:', end_match='nav:', indent=8, indent_first=True, indent_char='.')
Result
........features:
........  - content.code.copy
........  - navigation.instant
........  - navigation.tracking
........  - toc.follow
........  - toc.integrate
........  - navigation.top
........

Escape characters#

includex('mkdocs.yml', start_match='features:', end_match='nav:', start_offset=1, escape=['-'])
Result
\- content.code.copy
\- navigation.instant
\- navigation.tracking
\- toc.follow
\- toc.integrate
\- navigation.top
*In the above text, the following characters have been escaped: ` - `*{.caption}

The escape notice at the bottom can be disabled:

includex('mkdocs.yml', start_match='features:', end_match='nav:', start_offset=1, escape=['-'], escape_notice=False)
Result
\- content.code.copy
\- navigation.instant
\- navigation.tracking
\- toc.follow
\- toc.integrate
\- navigation.top

Replace characters#

includex('mkdocs.yml', start_match='features:', end_match='nav:', start_offset=1, replace=[('-', '~')])
Result
~ content.code.copy
~ navigation.instant
~ navigation.tracking
~ toc.follow
~ toc.integrate
~ navigation.top

For replaced chraracters, a replace notice can be added:

includex('mkdocs.yml', start_match='features:', end_match='nav:', start_offset=1, replace=[('-', '~')], replace_notice=True)
Result
~ content.code.copy
~ navigation.instant
~ navigation.tracking
~ toc.follow
~ toc.integrate
~ navigation.top
*In the above text, the following substrings have been replaced: - --> ~*{.caption}

Wrap in code block#

includex('mkdocs.yml', lines=3, lang='yml')
Result
'''yml
site_name: includex
site_url: http://jannismain.github.io/mkdocs-macros-includex
edit_uri: -/edit/main/doc/
'''

site_name: includex
site_url: http://jannismain.github.io/mkdocs-macros-includex
edit_uri: -/edit/main/doc/

Override code language#

As the code language detection mainly works on the file extension of the included file, it will produce unwanted results if including snippets in another language (e.g. including shell instructions or python snippets from a Markdown file). In these cases, the intended language can be set manually:

includex('README.md', start_match='pip install', lines=1, code='sh')
Result
'''sh
pip install mkdocs-macros mkdocs-macros-includex
'''

pip install mkdocs-macros mkdocs-macros-includex

Do not set code language#

If you want to include a code block but do not want to include the code language for highlighting, simply set code to an empty string:

includex('mkdocs.yml', lines=3, code='')
Result
'''
site_name: includex
site_url: http://jannismain.github.io/mkdocs-macros-includex
edit_uri: -/edit/main/doc/
'''

site_name: includex
site_url: http://jannismain.github.io/mkdocs-macros-includex
edit_uri: -/edit/main/doc/

Include a caption#

includex('mkdocs.yml', lines=3, code=True, caption=True)
Result
'''yaml
site_name: includex
site_url: http://jannismain.github.io/mkdocs-macros-includex
edit_uri: -/edit/main/doc/
'''
*mkdocs.yml, lines 1-3*{.caption}

site_name: includex
site_url: http://jannismain.github.io/mkdocs-macros-includex
edit_uri: -/edit/main/doc/
mkdocs.yml, lines 1-3


Custom caption#

includex('mkdocs.yml', lines=3, code=True, caption='*Excerpt from MkDocs configuration file*{.caption}')
Result
'''yaml
site_name: includex
site_url: http://jannismain.github.io/mkdocs-macros-includex
edit_uri: -/edit/main/doc/
'''
*Excerpt from MkDocs configuration file*{.caption}

site_name: includex
site_url: http://jannismain.github.io/mkdocs-macros-includex
edit_uri: -/edit/main/doc/
Excerpt from MkDocs configuration file


includex('mkdocs.yml', lines=3, code=True, caption='*Excerpt from %(filepath)s*{.caption}')
Result
'''yaml
site_name: includex
site_url: http://jannismain.github.io/mkdocs-macros-includex
edit_uri: -/edit/main/doc/
'''
*Excerpt from mkdocs.yml*{.caption}

site_name: includex
site_url: http://jannismain.github.io/mkdocs-macros-includex
edit_uri: -/edit/main/doc/
Excerpt from mkdocs.yml


Wrap in raw tags#

You can wrap included content in {\% raw \%} tags to prevent any further macro syntax from being executed:

includex('mkdocs.yml', lines=3, raw=True)
Result
{% raw %}
site_name: includex
site_url: http://jannismain.github.io/mkdocs-macros-includex
edit_uri: -/edit/main/doc/
{% endraw %}

Error Handling#

By default, an exception raised by includex will be raised, which invokes the default macros error handling. This means the whole page will be replaced by an error message and the corresponding traceback:

includex("foo.txt")
Example of rendered error output

File: index.md

FileNotFoundError: [Errno 2] No such file or directory: 'foo.txt'

Traceback (most recent call last):
...
File "/Users/mkj/Developer/mkdocs-macros-includex/includex.py", line XXX, in includex
    raise e
...
FileNotFoundError: [Errno 2] No such file or directory: 'foo.txt'

If you'd rather have the error message inserted into the document, so that the remainder of the document is unaffected by the error in any single macro, you can set raise_errors=False.

includex('foo.txt', raise_errors=False)
Result
<span class="error" style="color:red">FileNotFoundError: [Errno 2] No such file or directory: 'foo.txt'</span>

FileNotFoundError: [Errno 2] No such file or directory: 'foo.txt'


You can also choose to silence errors (silence_errors=True) completely. If errors are silenced, no content is being included at all.

includex('foo.txt', silence_errors=True)
Result


  1. https://facelessuser.github.io/pymdown-extensions/extensions/snippets/#snippet-lines 

  2. https://facelessuser.github.io/pymdown-extensions/extensions/snippets/#snippet-sections