Development tools API version 1.4.0

Generate a pytest file.

phmdoctest.main.testfile(markdown_file: str = '', *, skips: Optional[List[str]] = None, fail_nocode: bool = False, setup: Optional[str] = None, teardown: Optional[str] = None, setup_doctest: bool = False, built_from: str = '') str

Run with callers keyword arguments and default values.

Return a string that contains the generated pytest file. The parameters are described by the command line help. Each string in the list skips is described by the –skip TEXT command line option.

Parameters

markdown_file – Path to the Markdown input file.

Keyword Arguments
  • skips – List[str]. Do not test blocks with substring TEXT.

  • fail_nocode – Markdown file with no code blocks generates a failing test.

  • setup – Run block with substring TEXT at test module setup time.

  • teardown – Run block with substring TEXT at test module teardown time.

  • setup_doctest – Make globals created by the setup Python code block or setup directive visible to Python interactive session >>> blocks. The globals are set at Pytest Session scope and are visible to all tests run by –doctest-modules.

  • built_from – Text that follows “built from” in test file’s docstring. When empty string the docstring built from text is derived from markdown_file.

Returns

String containing the contents of the generated pytest file.

Generate pytest files using a configuration file.

phmdoctest.main.generate_using(config_file: pathlib.Path) None

Generate test files as directed by configuration file.

See the “Using a configuration file” section of the documentation.

  • The markdown_globs key specifies Markdown files to select for test file generation.

  • The exclude_globs key specifies Markdown files that should not generate test files. Markdown files that don’t have any Python examples are automatically excluded.

  • The generated test files get written to the directory specified by output_directory.

  • The print key directs printing.

Parameters

config_file – Path to the .cfg, .ini, or .toml configuration file.

Test with Pytest fixtures.

phmdoctest.tester.testfile_creator(pytestconfig)

Fixture creates the pytest test file contents from the Markdown file.

A Markdown file, in a folder relative to the pytest command line invocation directory is processed by phmdoctest to create a pytest file which is returned as a string.

This fixture is needed to produce the pytest test file when using the fixture testfile_tester below because pytester changes the current working directory when it is activated.

The fixture injects a function with the following signature. Please consult the source in tester.py. The returned function calls phmdoctest.main.testfile() to generate the pytest test file from Markdown.

Parameters

markdown_file – Path to the Markdown input file. This file name is relative to the working directory of the command that invokes pytest. Typically this is the root of the repository.

Keyword Arguments
  • skips – List[str]. Do not test blocks with substring TEXT.

  • fail_nocode – Markdown file with no code blocks generates a failing test.

  • setup – Run block with substring TEXT at test module setup time.

  • teardown – Run block with substring TEXT at test module teardown time.

  • setup_doctest – Make globals created by the setup Python code block or setup directive visible to Python interactive session >>> blocks. Caution: The globals are set at Pytest Session scope and are visible to all tests run by –doctest-modules.

Returns

String containing the contents of the generated pytest file.

phmdoctest.tester.testfile_tester(pytester)

Fixture runs pytester.runpytest with the caller’s pytest file string.

Stores the caller’s pytest test file contents in a temporary directory hosted by pytest. Run pytest on it using pytest_options. Typically the caller runs testfile_creator to generate the test file. See example usage in the files tests/test_examples.py and tests/test_details.py.

The fixture injects a function with the following signature. Please consult the source in tester.py.

  • pytester requires conftest.py in tests folder with pytest_plugins = [“pytester”]

  • Requires pytest >= 6.2.

Parameters
  • contents – String containing the contents of a pytest test file.

  • testfile_name – Name given to the test file when it is stored in the pytester temporary directory.

  • pytest_options – List of strings of pytest command line options that are passed to pytester.

Returns

pytest RunResult returned by pytester.runpytest().

Simulate the command line.

phmdoctest.simulator.run_and_pytest(well_formed_command: str, pytest_options: Optional[List[str]] = None, junit_family: Optional[str] = None) phmdoctest.simulator.SimulatorStatus

Simulate a phmdoctest command, optionally run pytest.

If a filename is provided by the --outfile option, the command is rewritten replacing the OUTFILE with a path to a temporary directory and a synthesized filename.

To run pytest on an --outfile, pass a list of zero or more pytest_options. pytest is run in a subprocess.

The PYPI package pytest must be installed separately since pytest is not required to install phmdoctest. Use this command: pip install pytest

Returns SimulatorStatus object. SimulatorStatus.runner_status is the CliRunner.invoke return value.

If an outfile is streamed to stdout a copy of it is found in simulator_status.runner_status.stdout.

If calling run_and_pytest() from a pytest file, try adding the pytest option --capture=tee-sys to the command running pytest on the file.

For example on a checkout of phmdoctest the command line:

python -m pytest tests -v --capture=tee-sys

will print the outputs from the subprocess.run() invocations of pytest on the --outfile written to the temporary directory. A wild guess would be that the subprocess inherited changes made to the parent by –capture=tee-sys.

Parameters
  • well_formed_command

    • starts with phmdoctest

    • followed by MARKDOWN_FILE

    • ends with --outfile OUTFILE (if needed)

    • all other options are between MARKDOWN_FILE and --outfile for example: phmdoctest MARKDOWN_FILE --skip FIRST --outfile OUTFILE

  • pytest_options – List of strings like this: ["--doctest-modules", "-v"]. Set to empty list to run pytest with no options. Set to None to skip pytest.

  • junit_family – Configures the format of the Pytest generated JUnit XML string returned in SimulatorStatus. The value is used for the Pytest configuration option of the same name. Set to None or the empty string to skip XML generation.

Returns

SimulatorStatus containing runner_status, outfile, pytest_exit_code, and generated JUnit XML.

Read contents of Markdown fenced code blocks.

class phmdoctest.tool.FCBChooser(markdown_filename: str)

Select labeled fenced code block from the Markdown file.

FCBChooser.__init__(markdown_filename: str)

Gather labelled Markdown fenced code blocks in the file.

Parameters

markdown_filename – Path to the Markdown file as a string.

FCBChooser.contents(label: str = '') str

Return contents of the labeled fenced code block with label.

Parameters

label – Value of label directive placed on the fenced code block in the Markdown file.

Returns

Contents of the labeled fenced code block as a string or empty string if the label is not found. Fenced code block strings typically end with a newline.

class phmdoctest.tool.LabeledFCB(label, line, contents)

Describes a fenced code block that has a label directive. (collections.namedtuple).

Parameters
  • label – The label directive’s value.

  • line – Markdown file line number of block contents.

  • contents – Fenced code block contents.

phmdoctest.tool.labeled_fenced_code_blocks(markdown_filename: str) List[phmdoctest.tool.LabeledFCB]

Return Markdown fenced code blocks that have label directives.

Label directives are placed immediately before a fenced code block in the Markdown source file. The directive can be placed before any fenced code block. The label directive is the HTML comment <!--phmdoctest-label VALUE--> where VALUE is typically a legal Python identifier. The space must be present before VALUE. The label directive is also used to name the test function in generated code. When used that way, it must be a valid Python identifier. If there is more than one label directive on the block, the label value that occurs earliest in the file is used.

Parameters

markdown_filename – Path to the Markdown file as a string.

Returns

List of LabeledFCB objects.

LabeledFCB is a NamedTuple with these fields:

  • label is the value of a label directive placed in a HTML comment before the fenced code block.

  • line is the line number in the Markdown file where the block starts.

  • contents is the fenced code block contents as a string.

phmdoctest.tool.fenced_code_blocks(markdown_filename: str) List[str]

Return Markdown fenced code block contents as a list of strings.

Parameters

markdown_filename – Path to the Markdown file as a string.

Returns

List of strings, one for the contents of each Markdown fenced code block.

phmdoctest.tool.fenced_block_nodes(fp: IO[str]) List[commonmark.node.Node]

Get markdown fenced code blocks as list of Node objects.

Deprecation Watch: This function may be labelled as deprecated in a future version of phmdoctest. It returns a data type defined by the commonmark package.

Parameters

fp – file object returned by open().

Returns

List of commonmark.node.Node objects.

Get elements from test suite JUnit XML output.

phmdoctest.tool.extract_testsuite(junit_xml_string: str) Tuple[Optional[xml.etree.ElementTree.Element], List[xml.etree.ElementTree.Element]]

Return testsuite tree and list of failing trees from JUnit XML.

Parameters

junit_xml_string – String containing JUnit xml returned by pytest or phmdoctest.simulator.run_and_pytest().

Returns

tuple testsuite tree, list of failed test case trees

Check a Markdown file for Python examples.

class phmdoctest.tool.PythonExamples(has_code, has_session)

Presence of Python fenced code blocks in Markdown. (collections.namedtuple)

Parameters
  • has_code – True if detected at least one fenced code block with Python code.

  • has_session – True if detected at least one fenced code block with Python interactive session (doctest).

phmdoctest.tool.detect_python_examples(markdown_path: pathlib.Path) phmdoctest.tool.PythonExamples

Return whether .md has any Python highlighted fenced code blocks.

This includes Python code blocks and Python doctest interactive session blocks. These blocks may or may not generate test cases once processed by phmdoctest.test_file() and collected.

  • We don’t care here if the code block is followed by expected output.

  • This logic does not check if the block has any phmdoctest skip, mark.skip, or mark.skipif directives.

  • This logic does not check if the block would be skipped by a phmdoctest command line –skip option.

Parameters

markdown_path – pathlib.Path of input Markdown file.

Prepare directory for generated test files.

phmdoctest.tool.wipe_testfile_directory(target_dir: pathlib.Path) None

Create and/or clean target_dir directory to receive generated testfiles.

Create target_dir if needed for writing generated pytest files. Prevent future use of pre-existing .py files in target_dir.

  • The FILENAME.py files found in target_dir are renamed to noFILENAME.sav.

  • If a noFILENAME.sav already exists it is not modified.

  • Files in target_dir with other extensions are not modified.

  • A FILENAME.py pre-existing in target_dir is only renamed and not deleted. This allows for recovery of .py files when target_dir gets pointed by mistake to a directory with Python source files.

Parameters

target_dir – pathlib.Path of destination directory for generated test files.