Skip to main content

Inject code snippets into your Markdown docs

Project description

license_markplates

MarkPlates

A templating utility for keeping code included in Markdown documents in sync with the original source.

CircleCI black License: MIT pyup.io PyPI version Coverage Status

The problem I hope to solve is to simplify keeping external files up to date with markdown documents that contain them. This happens to me frequently when an editor makes a suggestion to an article that will modify the underlying code it is quoting.

Installing

You can download and install the latest version of MarkPlates from the PyPI with this command:

$ python3 -m pip install --upgrade markplates

MarkPlates is currently tested against Python 3.6, 3.7, 3.8, and 3.9.

Usage

Running markplates is as simple as handing it a file:

$ markplates template.mdt

This will process the template in template.mdt, filling it in with data specified in the template.

The examples directory has the simple.mdt template:

# Sample MarkPlates Template
{{ set_path("./examples") }}

This is an example of importing an entire file. Without a range specified, the first line is skipped to not include the shell specifier. Double blank lines are condensed into single lines:
{{ import_source("testfile.py") }}

If you want to include the shell specifier, you can include it explicitly in a range. This silly example imports some of the lines from the file, demonstrating different ranges. Note that the ranges are compiled into a single list, so over-lapping ranges are only shown once:
{{ import_source("testfile.py", [1, 5, "2", 3, "8-$", "$", "$-2"]) }}

Functions can be imported from a file by name:

{{ import_function("testfile.py", "flying_pig_menu") }}

The import_repl tag captures stdout and stderr from a REPL session:
{{ import_repl(
"""
def func(x):
    if x:
        print(x)

func(True)
func(False) """) }}

This demonstrates setting the path and pulling in some of the lines of a file. You can also examine the README.mdt file in this library which is used to create this README.md.

To use on your own project create a markdown document with special tags to indicate a markplates function call. The delimiter for these tags is {{ function goes here }}.

Note: if you need to add {{ characters which should not be processed as a template, you can put them in a {{ '' }} block to escape them. Template processing is done with Jinja2 so Markplates uses the same escape sequences.

Markplates supports these functions:

  • set_path("path/to/source/files", [show_skipped_section_marker])
  • import_source("source_file_name", [list of line number ranges], language=None, filename=False)
  • import_function("source_file_name", "function_name", language=None, filename=False)
  • import_repl("code to run in repl")

set_path()

The set_path() function allows you to specify the base directory to use when searching for source files. Each call to this function will apply from that point in the template down.

The path must be included in single or double qoutes. If not specified, the path defaults to ".", the current directory.

Examples:

{{set_path(".")}}  #sets path to the default
{{set_path("/home/user/my/project")}} # points the path to your project

The set_path() command is not required as all other functions will take full paths to files.

This command takes an optional show_skipped_section_marker parameter which defaults to False. When set to true, if a template expansion shows disjoint sections of a file which are separated by 2 or more lines, that gap will be shown in the output with a marker line:

# ...

This option defaults to false to not break backward compatibility.

import_source()

The import_source() function will pull in the contents of a source file. Optional line number ranges can be specified (see description below). The filename must be in quotes.

If no line number ranges are specified, the first line of the file will be omitted. This is to prevent the #!/usr/bin/env python line from cluttering the markdown article. If you want to include the first line, use the range: 1-$.

Examples:

{{import_source("__main__.py")}} # includes all but line 1 from `__main__.py` file
{{import_source("__main__.py", ["1-$",])}} # imports all lines from `__main__.py`
{{import_source("__main__.py", [1, "3", "5-$"])}} # imports all lines except lines 2 and 4 from `__main__.py`
{{import_source("__main__.py", language="python", filename=True)}}
# includes all but line 1 from `__main__.py` file, puts the
# contents in a python block with the filename as a comment in
# the first line of the block.

MarkPlates will display an error message to stderr if a file is not found.

import_function()

The import_function function searches a source file for the named function, class, class method or assigned variable. The function name supports dot-references, for example to get at the class method area() inside the class Sqaure, the function name would be "Square.area". To retrieve a nested function, name both the parent and child function, for example "outer.inner".

The first piece of code matching the given name is returned, (and you shouldn't have multiple things with the same name anyway!). The source file is parsed, not loaded, so no import side-effects take place.

Whitespace following the function will not be included.

Examples:

{{import_function("__main__.py", "condense_ranges")}} # imports the function named `condense_ranges` from `__main__.py`

The language and filename parameters are treated the same way they are in import_source().

import_repl()

The import_repl function takes the input parameter and splits it into separate lines. Each line of the input will be run in a REPL with the stdout and stderr captured to insert into the final output. The result should appear similar to a copy-paste from running the same commands manually.

There is an exception, however. Blank input lines are used for spacing and will not display the >>> prompt one would normally see in the REPL.

Example:

{{import_repl(
"""
def func(x):
    if x:
        print(x)

func(True)
func(False) """) }}

Output:

>>> def func(x):
...     if x:
...         print(x)

>>> func(True)
True
>>> func(False)

Line Number Ranges

Line number ranges allow you to specify which lines you want to include from the source file. Ranges can be in the following form:

  • 3 or "3" : an integer adds just that line from the input

  • "5-7" : a range adds lines between start and end inclusive (so 5, 6, 7)

  • "10-$" : an unlimited range includes start line to the end of the file

  • "$" : the last line

  • "$-3" : negative indexing, the last line and the three lines that proceed it

Note: LINE NUMBERING STARTS AT 1!

Copy to Clipboard

The -c option will copy most of the output to the clipboard. The first two lines are skipped, which generally are the h1 title and the following blank line. This is done to simplify copying the output to the Real Python CMS system.

Features to Come

I'd like to add:

  • Developer Instructions
  • Capturing the results of a shell command and inserting into the file
  • Running black over the included Python source
  • Windows and Mac testing/support

Interested?

Let me know! If you're interested in the results or would like to help out, please raise an issue and I'll be in touch!

Release History

  • 1.6.0 Add show_skipped option to set_path() and improved error reporting
  • 1.5.0 Added $ and $-n as valid line ranges. Fixed several bugs
  • 1.4.0 Added -c option, bug fixes
  • 1.3.0 Minor bug fixes
  • 1.2.0 Added language and filename options for import_source and import_repl methods
  • 1.1.0 Added import_repl functionality
  • 1.0.0 Initial release

License plate graphic thanks to ACME License Maker

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

markplates-1.7.0.tar.gz (9.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

markplates-1.7.0-py2.py3-none-any.whl (10.0 kB view details)

Uploaded Python 2Python 3

File details

Details for the file markplates-1.7.0.tar.gz.

File metadata

  • Download URL: markplates-1.7.0.tar.gz
  • Upload date:
  • Size: 9.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/1.13.0 pkginfo/1.5.0.1 requests/2.21.0 setuptools/40.8.0 requests-toolbelt/0.9.1 tqdm/4.31.1 CPython/3.7.2

File hashes

Hashes for markplates-1.7.0.tar.gz
Algorithm Hash digest
SHA256 03ceb8455899f7b1055c873158f24402d266d0dce9f09bda6eb793966a17294c
MD5 222ab85c8f7506b8fd8d3d8453bbaed4
BLAKE2b-256 b3ad7c864588d42758567f90acffb731d2ae0107677cff51fcb872a02a130878

See more details on using hashes here.

File details

Details for the file markplates-1.7.0-py2.py3-none-any.whl.

File metadata

  • Download URL: markplates-1.7.0-py2.py3-none-any.whl
  • Upload date:
  • Size: 10.0 kB
  • Tags: Python 2, Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/1.13.0 pkginfo/1.5.0.1 requests/2.21.0 setuptools/40.8.0 requests-toolbelt/0.9.1 tqdm/4.31.1 CPython/3.7.2

File hashes

Hashes for markplates-1.7.0-py2.py3-none-any.whl
Algorithm Hash digest
SHA256 c7608f4ff93bd4b24d68b3526253552c08d75b99ab93aa5a6d394219518cc5dd
MD5 84fbcf00210132d7889cce2e8a03e5ab
BLAKE2b-256 684a77578f2009fae6d3e5ee2d4cfc68eb3b145ee621122c8db1405763985755

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page