Module pdoc.cli

pdoc's CLI interface and helper functions.

Functions

def main()
Expand source code Browse git
def main(_args=None):
    """ Command-line entry point """
    global args
    args = _args or parser.parse_args()

    # If warnings not externally managed, show deprecation warnings
    if not sys.warnoptions:
        warnings.simplefilter("once", DeprecationWarning)

    if args.close_stdin:
        sys.stdin.close()

    if (args.html or args.http) and not args.output_dir:
        args.output_dir = 'html'

    if args.html_dir:
        _warn_deprecated('--html-dir', '--output-dir')
        args.output_dir = args.html_dir
    if args.overwrite:
        _warn_deprecated('--overwrite', '--force')
        args.force = args.overwrite

    template_config = {}
    for config_str in args.config:
        try:
            key, value = config_str.split('=', 1)
            value = ast.literal_eval(value)
            template_config[key] = value
        except Exception:
            raise ValueError(
                f'Error evaluating --config statement "{config_str}". '
                'Make sure string values are quoted?'
            )

    if args.html_no_source:
        _warn_deprecated('--html-no-source', '-c show_source_code=False', True)
        template_config['show_source_code'] = False
    if args.link_prefix:
        _warn_deprecated('--link-prefix', '-c link_prefix="foo"', True)
        template_config['link_prefix'] = args.link_prefix
    if args.external_links:
        _warn_deprecated('--external-links')
        template_config['external_links'] = True

    if args.template_dir is not None:
        if not path.isdir(args.template_dir):
            print(f'Error: Template dir {args.template_dir!r} is not a directory', file=sys.stderr)
            sys.exit(1)
        pdoc.tpl_lookup.directories.insert(0, args.template_dir)

    # Support loading modules specified as python paths relative to cwd
    sys.path.append(os.getcwd())

    # Virtual environment handling for pdoc script run from system site
    try:
        os.environ['VIRTUAL_ENV']
    except KeyError:
        pass  # pdoc was not invoked while in a virtual environment
    else:
        from glob import glob
        from sysconfig import get_path
        libdir = get_path("platlib")
        sys.path.append(libdir)
        # Resolve egg-links from `setup.py develop` or `pip install -e`
        # XXX: Welcome a more canonical approach
        for pth in glob(path.join(libdir, '*.egg-link')):
            try:
                with open(pth) as f:
                    sys.path.append(path.join(libdir, f.readline().rstrip()))
            except IOError:
                warn(f'Invalid egg-link in venv: {pth!r}')

    if args.http:
        template_config['link_prefix'] = "/"

        # Run the HTTP server.
        _WebDoc.args = args  # Pass params to HTTPServer xP
        _WebDoc.template_config = template_config

        host, _, port = args.http.partition(':')
        host = host or DEFAULT_HOST
        port = int(port or DEFAULT_PORT)

        print(f'Starting pdoc server on {host}:{port}', file=sys.stderr)
        httpd = HTTPServer((host, port), _WebDoc)
        print(f"pdoc server ready at http://{host}:{port}", file=sys.stderr)

        # Allow tests to perform `pdoc.cli._httpd.shutdown()`
        global _httpd
        _httpd = httpd

        try:
            httpd.serve_forever()
        finally:
            httpd.server_close()
            sys.exit(0)

    if args.filter and args.filter.strip():
        def docfilter(obj, _filters=args.filter.strip().split(',')):
            return any(f in obj.refname or
                       isinstance(obj, pdoc.Class) and f in obj.doc
                       for f in _filters)
    else:
        docfilter = None

    modules = [pdoc.Module(module, docfilter=docfilter,
                           skip_errors=args.skip_errors)
               for module in args.modules]
    pdoc.link_inheritance()

    # Loading is done. Output stage ...
    config = pdoc._get_config(**template_config)

    # Load configured global markdown extensions
    # XXX: This is hereby enabled only for CLI usage as for
    #  API use I couldn't figure out where reliably to put it.
    if config.get('md_extensions'):
        from .html_helpers import _md
        _kwargs = {'extensions': [], 'configs': {}}
        _kwargs.update(config.get('md_extensions', {}))
        _md.registerExtensions(**_kwargs)

    if args.pdf:
        _print_pdf(modules, **template_config)
        import textwrap
        PANDOC_CMD = textwrap.indent(_PANDOC_COMMAND, '    ')
        print(f"""
PDF-ready markdown written to standard output.
                              ^^^^^^^^^^^^^^^
Convert this file to PDF using e.g. Pandoc:

{PANDOC_CMD}

or using Python-Markdown and Chrome/Chromium/WkHtmlToPDF:

    markdown_py --extension=meta         \\
                --extension=abbr         \\
                --extension=attr_list    \\
                --extension=def_list     \\
                --extension=fenced_code  \\
                --extension=footnotes    \\
                --extension=tables       \\
                --extension=admonition   \\
                --extension=smarty       \\
                --extension=toc          \\
                pdf.md > pdf.html

    chromium --headless --disable-gpu --print-to-pdf=pdf.pdf pdf.html

    wkhtmltopdf --encoding utf8 -s A4 --print-media-type pdf.html pdf.pdf

or similar, at your own discretion.""",
              file=sys.stderr)
        sys.exit(0)

    for module in modules:
        if args.html:
            _quit_if_exists(module, ext='.html')
            recursive_write_files(module, ext='.html', **template_config)
        elif args.output_dir:  # Generate text files
            _quit_if_exists(module, ext='.md')
            recursive_write_files(module, ext='.md', **template_config)
        else:
            sys.stdout.write(module.text(**template_config))
            # Two blank lines between two modules' texts
            sys.stdout.write(os.linesep * (1 + 2 * int(module != modules[-1])))

    if args.html:
        lunr_config = config.get('lunr_search')
        if lunr_config is not None:
            _generate_lunr_search(
                modules, lunr_config.get("index_docstrings", True), template_config)

Command-line entry point

def module_path(m: Module, ext: str)
Expand source code Browse git
def module_path(m: pdoc.Module, ext: str):
    return path.join(args.output_dir, *re.sub(r'\.html$', ext, m.url()).split('/'))
def recursive_write_files(m: Module,
ext: str,
**kwargs)
Expand source code Browse git
def recursive_write_files(m: pdoc.Module, ext: str, **kwargs):
    assert ext in ('.html', '.md')
    filepath = module_path(m, ext=ext)

    dirpath = path.dirname(filepath)
    if not os.access(dirpath, os.R_OK):
        os.makedirs(dirpath)

    with _open_write_file(filepath) as f:
        if ext == '.html':
            f.write(m.html(**kwargs))
        elif ext == '.md':
            f.write(m.text(**kwargs))

    for submodule in m.submodules():
        recursive_write_files(submodule, ext=ext, **kwargs)