Skip to main content

doctestcase

Evaluate doctests with configurable globals and setUp–tearDown. Export to Markdown and reST to include in docs.

license pypi python versions tests coverage tested with multipython uses docsub mypy uv Ruff

Features

  • Evaluate doctests
  • Configure doctest globals and setUp–tearDown
  • Relies on unittest.TestCase
  • Minimalistic decorator-based API
  • Format docstring as Markdown and reST to include in docs
  • Naturally fits docsub-based pipeline
  • No dependencies
  • Checked with mypy
  • 100% test coverage
  • Tested with Python 2.7+

Installation

$ pip install doctestcase

Usage

  • Decorated TestCase
  • Reuse __doctestcase__ from other TestCase
  • Parametrize test case
  • Inherit from decorated TestCase
  • Format docstring as Markdown or reStructuredText
  • Integration with docsub

See API Reference for details.

Decorated TestCase

from doctest import ELLIPSIS
from unittest import TestCase

from doctestcase import doctestcase


@doctestcase(globals={'X': 'yz'}, options=ELLIPSIS)
class SimpleCase(TestCase):
    """
    Title

    Paragraph.

    >>> X * 100
    'yzyz...'

    Another paragraph.

    >>> None
    >>> True
    True
    """

    def test_custom(self):  # called before 'test_docstring'
        self.assertTrue(True)

    def test_other(self):  # called after 'test_docstring'
        self.assertTrue(True)

All test methods are called by unittest in alphabetic order, including test_docstring, added by @doctestcase.

Reuse __doctestcase__ from other TestCase

Extending example above,

@SimpleCase.__doctestcase__
class AnotherCase(TestCase):
    """
    Title

    >>> X * 100
    'yzyz...'
    """

Now AnotherCase.__doctestcase__ holds shallow copy of globals, kwargs, and same doctest options, as SimpleCase. These copies are independent.

Inherit from decorated class

Inheriting from another test case decorated with @doctestcase allows to reuse and extend globals and kwargs and override doctest options of the base class.

Extending examples above,

@doctestcase(globals={'A': 'bc'})
class InheritedCase(SimpleCase):
    """
    Title

    >>> (X + A) * 100
    'yzbcyzbc...'
    """

Notice that global variable A was added to globals defined in SimpleCase, and the new class reuses doctest.ELLIPSIS option.

For more details on how doctestcase properties are updated, check the API Reference.

Parametrize doctest case

First, define base class parametrized with cwd:

from doctest import ELLIPSIS
import os.path
import shutil
import tempfile
from unittest import TestCase

from doctestcase import doctestcase


@doctestcase(options=ELLIPSIS, cwd='.')
class ChdirTestCase(TestCase):
    def setUp(self):
        if self.__class__ is ChdirTestCase:
            self.skipTest('base class')  # no tests of the base class itself
        self.temp = tempfile.mkdtemp()
        self.prev = os.getcwd()
        cwd = os.path.join(self.temp, self.__doctestcase__.kwargs['cwd'])
        if not os.path.exists(cwd):
            os.mkdir(cwd)
        os.chdir(cwd)

    def tearDown(self):
        os.chdir(self.prev)
        shutil.rmtree(self.temp)

Notice how the base class is skipped from testing.

In this example we use os.path module for compatibility with older Python versions only. If you use recent Python versions, use pathlib instead.

Now we can define test case parametrized with cwd:

@doctestcase(cwd='subdir')
class Case1(ChdirTestCase):
    """
    >>> import os
    >>> os.getcwd()
    '.../subdir'
    """

Inherit from decorated TestCase

Test cases, decorated with @doctestcase, can be used as base classes for other test cases. This is useful when inherited classes need to extend or change properties, passed to parent's @doctestcase. Parent properties will be copied and updated with values from child class decorator.

For the SimpleCase class above,

@doctestcase(globals={'A': 'bc'})
class InheritedCase(SimpleCase):
    """
    Title

    >>> (X + A) * 100
    'yzbcyzbc...'
    """

Format docstring as Markdown or reStructuredText

For the SimpleCase class above,

Markdown

>>> from doctestcase import to_markdown
>>> to_markdown(SimpleCase)
## Title

Paragraph.

```pycon
>>> X * 100
'yzyz...'
```

Another paragraph.

```pycon
>>> None
>>> True
True
```

reStructuredText

>>> from doctestcase import to_rest
>>> to_rest(SimpleCase)
Title
-----

Paragraph.

>>> X * 100
'yzyz...'

Another paragraph.

>>> None
>>> True
True

Integration with docsub

When documenting packages, "Usage" section often includes doctests. It is a good practice to test all documented use cases, so why not adopt test-driven documenting approach and write tests with docs in mind?

  1. Write tests with carefully crafted docstrings using doctests.
  2. Include generated Markdown or reST in docs.

With docsub, this can be achieved with some minimal configuration.

Just two commands to run tests and update docs:

$ pytest tests
$ docsub sync -i usage.md

usage.md

# Usage

<!-- docsub: begin -->
<!-- docsub: x case tests/test_usage.py:UseCase1 -->
## Use Case 1

Long description of the use case.

Usage example in doctest:

```pycon
>>> True
True
```
<!-- docsub: end -->

tests/test_usage.py

from unittest import TestCase

from doctestcase import doctestcase


@doctestcase()
class UseCase1(TestCase):
    """
    Use Case 1

    Long description of the use case.

    Usage example in doctest:

    >>> True
    True
    """

docsubfile.py

Docsub configuration file declaring project-local x-tension command:

from docsub import click
from doctestcase import to_markdown
from importloc import Location


@click.group()
def x() -> None:
    pass


@x.command()
@click.argument('case')
def case(case: str) -> None:
    text = to_markdown(Location(case).load(), title_depth=2)
    click.echo(text, nl=False)

See also

Metadata

Release files for doctestcase 0.2.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for doctestcase 0.2.2
File Size Uploaded Provenance Actions
doctestcase-0.2.2.tar.gz 102.6 kB Without provenance Details for doctestcase-0.2.2.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for doctestcase 0.2.2
File Interpreter ABI Platform Actions
doctestcase-0.2.2-py2.py3-none-any.whl Python 3, Python 2 none any Details for doctestcase-0.2.2-py2.py3-none-any.whl

Total release size: 112.9 kB

Release files / doctestcase-0.2.2.tar.gz

Download URL doctestcase-0.2.2.tar.gz
Size 102.6 kB
Tags Source
SHA-256 checksum
How to use checksums
bbf908b4d8752885d261b4086d6eb0981d08ff22822ff2daca058619aa20f561
BLAKE2b-256 checksum
How to use checksums
4fd35778881c0df782a9c8068ca20a58621c5c655afdb76e81db49cfe4aac767
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.5.13

Release files / doctestcase-0.2.2-py2.py3-none-any.whl

Download URL doctestcase-0.2.2-py2.py3-none-any.whl
Size 10.2 kB
Tags Python 2 Python 3
SHA-256 checksum
How to use checksums
81a5bb67d5f549a3575efe46c6c786644bd8cff0ef889843eb0ae25f2eda9c9e
BLAKE2b-256 checksum
How to use checksums
83811c6c53597b502245700eb7474e7f4517be483a613fc2aa0652e902384275
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.5.13

Security for doctestcase 0.2.2

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page