In doctest, is there a way to treat a yaml code-block as a variable?

178 Views Asked by At

I'm using doctest to test code snippets in .rst files. In many cases, the most important tests are simple validity checks on yaml configs.

For example:

>>> my_yaml = """
... foo:
...   bar:
...    - baz1
...    - baz2
... """
>>> my_yaml_dict = yaml.load(my_yaml_string, Loader=yaml.FullLoader)
>>> assert "baz_2" in my_yaml_dict["foo"]["bar"]

To make code more readable and copy-pasteable, I'd like to only display the yaml block, and put the test itself in a hidden code block.

This works:

>>> my_yaml = """
... foo:
...   bar:
...    - baz1
...    - baz2
... """

.. invisible-code-block: python

    >>> my_yaml_dict = yaml.load(my_yaml_string, Loader=yaml.FullLoader)
    >>> assert "baz_2" in my_yaml_dict["foo"]["bar"]

However, the yaml block is still encapsulated in my_yaml="""...""", which makes it hard to copy-paste.

Is there a way to treat a code-block itself as a variable? I'm imagining something along these lines:

.. code-block:: yaml # doctest: +varname=my_yaml_string

    foo:
      bar:
       - baz1
       - baz2

.. invisible-code-block: python

    >>> my_yaml_dict = yaml.load(my_yaml_string, Loader=yaml.FullLoader)
    >>> assert "baz_2" in my_yaml_dict["foo"]["bar"]
1

There are 1 best solutions below

1
On

Based on the documentation, I do not think changing / skipping docstring delimiters (>>> and ... ) is possible:

Any expected output must immediately follow the final '>>> ' or '... ' line containing the code, and the expected output (if any) extends to the next '>>> ' or all-whitespace line. https://docs.python.org/3/library/doctest.html#how-are-docstring-examples-recognized

An alternate solution could be to move said Yaml data to an external variable, a variable that is not defined in the doctest block:

my_yaml_string = """
foo:
    bar:
      - baz1
      - baz2
"""

Above variable then could be used from the doctest block directly:

>>> my_yaml_dict = yaml.load(my_yaml_string, Loader=yaml.FullLoader)
>>> assert "baz_2" in my_yaml_dict["foo"]["bar"]

Additionally my_yaml_string variable could be defined in another module to not impact runtime performance:

>>> import test_constants
>>> my_yaml_dict = yaml.load(test_constants.my_yaml_string, Loader=yaml.FullLoader)
>>> assert "baz_2" in my_yaml_dict["foo"]["bar"]

Also, the test data could be moved to a separate file entirely. This has the added benefit of enabling static analyzer coverage for the data-serialization language of choice (in this case Yaml):

# test_data.yaml
foo:
    bar:
      - baz1
      - baz2
>>> my_yaml_dict = yaml.load(open("test_data.yaml"), Loader=yaml.FullLoader)
>>> assert "baz_2" in my_yaml_dict["foo"]["bar"]

Note that "test_data.yaml" might need to be turned into an absolute path, depending on how doctests are executed.