Source code for xdoctest.__main__

#!/usr/bin/env python
"""
Provides a simple script for running module doctests.

This should work even if the target module is unaware of xdoctest.
"""

from __future__ import annotations

import sys

__tests__ = """
Ignore:
    xdoctest -m xdoctest.demo
    xdoctest ~/code/xdoctest/src/xdoctest/demo.py
    python -m xdoctest xdoctest all
    python -m xdoctest networkx all --options=+IGNORE_WHITESPACE
"""


[docs] def main(argv: list[str] | None = None) -> int: """ Args: argv (List[str] | None): """ import xdoctest if argv is None: argv = sys.argv version_info = { 'sys_version': sys.version, 'version': xdoctest.__version__, } if '--version' in argv: print(version_info['version']) return 0 if '--version-info' in argv: print('sys_version = {}'.format(version_info['sys_version'])) print('file = {}'.format(__file__)) print('version = {}'.format(version_info['version'])) return 0 import argparse import textwrap from os.path import exists from xdoctest import utils # FIXME: default values are reporting incorrectly or are missformated class RawDescriptionDefaultsHelpFormatter( argparse.RawDescriptionHelpFormatter, argparse.ArgumentDefaultsHelpFormatter, ): pass parser = argparse.ArgumentParser( prog='xdoctest', description=( 'Xdoctest {version} - on Python - {sys_version} - ' 'discover and run doctests within a python package' ).format(**version_info), formatter_class=RawDescriptionDefaultsHelpFormatter, ) # Ignored if optional arguments are specified, otherwise: # Defaults --modname to arg.pop(0). # Defaults --command to arg.pop(0). parser.add_argument( 'arg', nargs='*', help=utils.codeblock( """ If the `--command` key / value pair is unspecified, the first positional argument is used as the command. """ ), ) parser.add_argument( '--version', action='store_true', help='Display version and quit' ) parser.add_argument( '--version-info', action='store_true', help='Display version and other info and quit', ) # The bulk of the argparse CLI is defined in the doctest example from xdoctest import doctest_example, runner runner._update_argparse_cli(parser.add_argument) doctest_example.DoctestConfig()._update_argparse_cli(parser.add_argument) args, unknown = parser.parse_known_args(args=argv[1:]) ns = args.__dict__.copy() if ns['version']: print(xdoctest.__version__) return 0 # ... postprocess args modname = ns['modname'] command = ns['command'] arg = ns['arg'] style = ns['style'] durations = ns['durations'] analysis = ns['analysis'] if ns['time']: durations = 0 # --- # Allow for positional args to specify defaults for unspecified optionals errors = [] if modname is None: if len(arg) == 0: errors += ['you must specify modname or modpath'] else: modname = arg.pop(0) if command is None: if len(arg) == 0: # errors += ['you must specify a command e.g (list, all)'] command = 'all' else: command = arg.pop(0) if errors: if len(errors) == 1: errmsg = errors[0] else: listed_errors = ', '.join( ['({}) {}'.format(c, e) for c, e in enumerate(errors, start=1)] ) errmsg = '{} errors: {}'.format(len(errors), listed_errors) parser.error(errmsg) # --- options = ns['options'] if options is None: options = '' pyproject_fpath = 'pyproject.toml' if exists(pyproject_fpath): toml_loader = None try: import tomllib except ImportError: try: import tomli # type: ignore[unresolved-import] except ImportError: pass else: toml_loader = tomli else: toml_loader = tomllib if toml_loader is not None: with open(pyproject_fpath, 'rb') as file: pyproject_settings = toml_loader.load(file) try: options = pyproject_settings['tool']['xdoctest']['options'] except KeyError: pass if exists('pytest.ini'): import configparser config_parser = configparser.ConfigParser() config_parser.read('pytest.ini') try: options = config_parser.get('pytest', 'xdoctest_options') except configparser.NoOptionError: pass ns['options'] = options from xdoctest import doctest_example config = doctest_example.DoctestConfig()._populate_from_cli(ns) if config['verbose'] > 2: print( textwrap.dedent( r""" ===================================== _ _ ___ ____ ____ ___ ____ ____ ___ \/ | \ | | | | |___ [__ | _/\_ |__/ |__| |___ | |___ ___] | ===================================== """ ) ) run_summary = xdoctest.doctest_module( modname, argv=[], style=style, command=command, verbose=config['verbose'], config=config, durations=durations, analysis=analysis, ) n_failed = run_summary.get('n_failed', 0) if n_failed > 0: return 1 else: return 0
if __name__ == '__main__': retcode = main() sys.exit(retcode)