Skip to main content

Build and test your Python examples

Project description

Exemplary

A micro library for testing the Python examples in your documentation.

Installation

To install Exemplary, use pip:

pip install exemplary

Exemplary requires Python 3.6 or later.

Note: In your projects, you'll probably want to install Exemplary as a "dev" dependency. It's a tool for updating and testing your documentation.

What problem does Exemplary solve?

Exemplary solves two main problems:

  • It makes sure that the examples in your documentation actually work.
  • It renders the output for your examples, so that your documentation shows what your users are really going to see.

How do you use Exemplary?

TLDR:

  • exemplary.run(pathnames) -- runs all the Python examples in your Markdown files.
  • exemplary.run(pathnames, render=True) -- runs all your Python examples, and also renders the output of any examples that start with >>>.

(Hint: Use glob('**/*.md', recursive=True) to run Exemplary on all the Markdown files in your project.)

Or, to run exemplary from the command line:

python3 -m exemplary --paths "**/*.md" --render

For testing:

Put some Python sections in your markdown files.

Then, in your tests:

from glob import glob
import exemplary

def test_docs():
    # Run all the examples in your markdown files:
    pathnames = glob('**/*.md', recursive=True)
    exemplary.run(pathnames)

This raises an exception if any of your examples fail.

For rendering:

In this context, "rendering" means, "Taking an example and showing what it looks like when you run it in Python's interactive interpreter."

So, let's say you have some markdown like this:

# How to use deque

```python
>>> from collections import deque
>>> d = deque([1, 2, 3])
>>> d.popleft()
>>> d.pop()
```

In your build script, run Exemplary with render=True:

from glob import glob
import exemplary

def render_docs():
    # Render all the examples in your markdown files:
    pathnames = glob('**/*.md', recursive=True)
    exemplary.run(pathnames, render=True)

Or run from a shell script:

python3 -m exemplary --paths "**/*.md" --render

Aftwards, the example will look like this:

# How to use deque

```python
>>> from collections import deque
>>> d = deque([1, 2, 3])
>>> d.popleft()
1

>>> d.pop()
3
```

When Exemplary sees an example that starts with >>>, it runs the example in Python's iteractive interpreter, and adds the interpreter's output to your documentation.

(Exemplary adds an extra newline after the interpreter's output, to improve readability.)

If you run Exemplary again, it will render the example again, ignoring any output that may already appear in the example. This lets you run Exemplary multiple times as you edit your documentation.

Take care: Because Exemplary modifies your files, make sure they are committed to git before you render them.

(Or, as part of your build, copy your Markdown files to a build directory, and then run Exemplary on the copies.)

What if I have multiple examples in one markdown file?

Exemplary runs each example in the same file in the same interpreter. This allows you to break up your examples with text sections.

For example, if you have:

# My example

```python
x = "hello"
```

Now use x:

```python
>>> print(x)
```

When you render this example, Exemplary will add hello at the end. The point is that the second python section can see x, which is defined in the first section.

If you need an example to start fresh in its own namespace, you can put a special HTML comment in the line before your example:

<!-- fresh example -->
```python
# Exmplary will run this example in a fresh environment.
import foo

foo.bar('baz')
```

When Exemplary sees the "fresh example" comment, it essentially restarts the interpreter that it's using to test and render your examples.

How can I hide some of the tests for my examples?

Exemplary looks for code sections even in HTML comments. This lets you write additional tests for your examples (to make sure they really work), without cluttering up your documentation.

For example:

# Some Example

Setup:

```python
import something
foo = do_something()
```

<!--
```python
assert foo.some_property
assert some_other_predicate(foo)
```
-->

Exemplary will run both Python sections -- the one before the comment and the one inside the comment.

This way you can:

  • thoroughly test your examples.
  • keep examples and tests together in the same file.
  • hide the tests so that they don't detract from the documentation.

Taken to the extreme, you could treat all of your unit tests as part of your documentation, and structure them this way.

What if I don't want Exemplary to test an example?

Put the HTML comment <!-- skip example --> on the line above each example that you want Exemplary to ignore.

# My bad example

Exemplary will never know...

<!-- skip example -->
```python
>>> assert False
```

Exemplary won't try to test or render this example, because it's guarded by the <!-- skip example --> comment.

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

exemplary-0.0.9.tar.gz (14.8 kB view details)

Uploaded Source

File details

Details for the file exemplary-0.0.9.tar.gz.

File metadata

  • Download URL: exemplary-0.0.9.tar.gz
  • Upload date:
  • Size: 14.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.7.1 importlib_metadata/4.10.0 pkginfo/1.8.2 requests/2.26.0 requests-toolbelt/0.9.1 tqdm/4.62.3 CPython/3.8.2

File hashes

Hashes for exemplary-0.0.9.tar.gz
Algorithm Hash digest
SHA256 e08e074c869b6c455f0268cdad2a7fe5477fad08bf63c346232067ab74167fae
MD5 f48aa1fe03cc5324e144241cb6dc01ca
BLAKE2b-256 139ec63d1172b61c2f53b07401d84ef735a9198d2f6c6630ff8c167ec6df2488

See more details on using hashes here.

Supported by

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