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

Viewed 69

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 Answers

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.

Related