Skip to content
Permalink
Browse files

ENH: Deprecate rarely-used CLI options in favor of new --config

Deprecations:
* --overwrite  →  -f, --force
* --html-dir   →  -o, --output
* --html-no-source
* --external-links
* --link-prefix

New:
* --config TEMPLATE_OPTION=VALUE
  • Loading branch information...
kernc committed Apr 24, 2019
1 parent d1d19d3 commit da4e82292bc0dc34566483e29efb01c16b1a89c1
Showing with 132 additions and 84 deletions.
  1. +4 −30 pdoc/__init__.py
  2. +100 −44 pdoc/cli.py
  3. +9 −0 pdoc/templates/config.mako
  4. +1 −1 pdoc/templates/html.mako
  5. +18 −9 pdoc/test/__init__.py
@@ -454,14 +454,7 @@ def _render_template(template_name, **kwargs):
raise


def html(
module_name,
docfilter=None,
external_links=False,
link_prefix="",
source=True,
**kwargs
) -> str:
def html(module_name, docfilter=None, **kwargs) -> str:
"""
Returns the documentation for the module `module_name` in HTML
format. The module must be a module or an importable string.
@@ -470,24 +463,10 @@ def html(
documentation objects are shown in the output. It is a function
that takes a single argument (a documentation object) and returns
`True` or `False`. If `False`, that object will not be documented.
If `external_links` is `True`, then identifiers to external modules
are always turned into links.
If `link_prefix` is a non-empty string, all links will be relative
to the top module and will have that prefix.
Otherwise, all links will be relative.
If `source` is `True`, then source code will be retrieved,
and outputted, for every Python object whenever possible. This can
dramatically decrease performance when documenting large modules.
"""
mod = Module(import_module(module_name), docfilter=docfilter)
link_inheritance()
return mod.html(external_links=external_links,
link_prefix=link_prefix,
source=source,
**kwargs)
return mod.html(**kwargs)


def text(module_name, docfilter=None, **kwargs) -> str:
@@ -1037,7 +1016,7 @@ def text(self, **kwargs) -> str:
txt = _render_template('/text.mako', module=self, **kwargs)
return re.sub("\n\n\n+", "\n\n", txt)

def html(self, external_links=False, link_prefix="", source=True, minify=True, **kwargs) -> str:
def html(self, minify=True, **kwargs) -> str:
"""
Returns the documentation for this module as
self-contained HTML.
@@ -1048,12 +1027,7 @@ def html(self, external_links=False, link_prefix="", source=True, minify=True, *
`kwargs` is passed to the `mako` render function.
"""
html = _render_template('/html.mako',
module=self,
external_links=external_links,
link_prefix=link_prefix,
show_source_code=source,
**kwargs)
html = _render_template('/html.mako', module=self, **kwargs)
if minify:
from pdoc.html_helpers import minify_html
html = minify_html(html)
@@ -7,16 +7,21 @@
import os
import os.path as path
import sys
import warnings
from http.server import BaseHTTPRequestHandler, HTTPServer
from typing import Sequence
from warnings import warn

import pdoc

parser = argparse.ArgumentParser(
description="Automatically generate API docs for Python modules.",
)
aa = parser.add_argument
aa('--version', action='version', version='%(prog)s ' + pdoc.__version__)
mode_aa = parser.add_mutually_exclusive_group().add_argument

aa(
'--version', action='version', version='%(prog)s ' + pdoc.__version__)
aa(
"modules",
type=str,
@@ -26,6 +31,16 @@
"the current environment, or a file path to a Python module or "
"package.",
)
aa(
"-c", "--config",
type=str,
metavar='OPTION=VALUE',
action='append',
default=[],
help="Override template options. This is an alternative to using "
"a custom config.mako file in --template-dir. This option "
"can be specified multiple times.",
)
aa(
"--filter",
type=str,
@@ -36,55 +51,65 @@
"will be shown in the output. Search is case sensitive. "
"Has no effect when --http is set.",
)
aa("--html", action="store_true", help="When set, the output will be HTML formatted.")
aa(
"-f", "--force",
action="store_true",
help="Overwrite any existing generated (--output-dir) files.",
)
mode_aa(
"--html",
action="store_true",
help="When set, the output will be HTML formatted.",
)
mode_aa(
"--pdf",
action="store_true",
help="When set, the specified modules will be printed to standard output, "
"formatted in Markdown-Extra, compatible with most "
"Markdown-(to-HTML-)to-PDF converters.",
)
aa(
"--html-dir",
type=str,
help=argparse.SUPPRESS,
)
aa(
"-o", "--output-dir",
type=str,
metavar='DIR',
default="html",
help="The directory to output HTML files to (default: ./html). "
"Only in effect when --html is.",
default='html',
help="The directory to output generated HTML/text files to "
"(default: ./html for --html).",
)
aa(
"--html-no-source",
action="store_true",
help="When set, source code will not be viewable in the generated HTML. "
"This can speed up the time required to document large modules.",
help=argparse.SUPPRESS,
)
aa(
"--overwrite",
action="store_true",
help="Overwrites any existing HTML files instead of producing an error.",
help=argparse.SUPPRESS,
)
aa("--pdf",
action="store_true",
help="When set, the specified modules will be printed to standard output, "
"formatted in Markdown-Extra, compatible with most "
"Markdown-(to-HTML-)to-PDF converters.")
aa(
"--external-links",
action="store_true",
help="When set, identifiers to external modules are turned into links. "
"This is automatically set when --http is.",
help=argparse.SUPPRESS,
)
aa(
"--template-dir",
type=str,
metavar='DIR',
default=None,
help="Specify a directory containing Mako templates "
"(html.mako or text.mako, and any templates they include). "
"(html.mako, text.mako, config.mako and/or any templates they include). "
"Alternatively, put your templates in $XDG_CONFIG_HOME/pdoc and "
"pdoc will automatically find them.",
)
aa(
"--link-prefix",
type=str,
metavar='STRING',
default="",
help="A prefix to use for every link in the generated documentation. "
"No link prefix results in all links being relative. "
"No effect when combined with --http.",
help=argparse.SUPPRESS,
)
aa(
"--close-stdin",
@@ -119,6 +144,7 @@ def _check_host_port(s):

class WebDoc(BaseHTTPRequestHandler):
args = None # Set before server instantiated
template_config = None

def do_HEAD(self):
if self.path != "/":
@@ -146,7 +172,7 @@ def do_GET(self):
for module in modules)
out = pdoc._render_template('/html.mako',
modules=modules,
link_prefix=self.args.link_prefix)
**self.template_config)
elif self.path.endswith(".ext"):
# External links are a bit weird. You should view them as a giant
# hack. Basically, the idea is to "guess" where something lives
@@ -166,7 +192,7 @@ def do_GET(self):
if resolved is None: # Try to generate the HTML...
print("Generating HTML for %s on the fly..." % import_path, file=sys.stderr)
try:
out = pdoc.html(import_path.split(".")[0])
out = pdoc.html(import_path.split(".")[0], **self.template_config)
except Exception as e:
print('Error generating docs: {}'.format(e), file=sys.stderr)
# All hope is lost.
@@ -208,18 +234,19 @@ def html(self):
the source code.
"""
# TODO: pass extra pdoc.html() params
return pdoc.html(self.import_path_from_req_url, http_server=True)
return pdoc.html(self.import_path_from_req_url,
http_server=True, external_links=True, **self.template_config)

def resolve_ext(self, import_path):
def exists(p):
p = path.join(args.html_dir, p)
p = path.join(args.output_dir, p)
pkg = path.join(p, pdoc._URL_PACKAGE_SUFFIX.lstrip('/'))
mod = p + pdoc._URL_MODULE_SUFFIX

if path.isfile(pkg):
return pkg[len(args.html_dir):]
return pkg[len(args.output_dir):]
elif path.isfile(mod):
return mod[len(args.html_dir):]
return mod[len(args.output_dir):]
return None

parts = import_path.split(".")
@@ -244,11 +271,11 @@ def import_path_from_req_url(self):


def module_html_path(m: pdoc.Module):
return path.join(args.html_dir, *m.url().split('/'))
return path.join(args.output_dir, *m.url().split('/'))


def _quit_if_exists(m: pdoc.Module):
if args.overwrite:
if args.force:
return

paths = [module_html_path(m)]
@@ -257,12 +284,12 @@ def _quit_if_exists(m: pdoc.Module):

for pth in paths:
if path.lexists(pth):
print("File '%s' already exists. Delete it or run with --overwrite" % pth,
print("File '%s' already exists. Delete it, or run with --force" % pth,
file=sys.stderr)
sys.exit(1)


def write_html_files(m: pdoc.Module):
def write_html_files(m: pdoc.Module, **kwargs):
f = module_html_path(m)

dirpath = path.dirname(f)
@@ -271,11 +298,7 @@ def write_html_files(m: pdoc.Module):

try:
with open(f, 'w+', encoding='utf-8') as w:
w.write(m.html(
external_links=args.external_links,
link_prefix=args.link_prefix,
source=not args.html_no_source,
))
w.write(m.html(**kwargs))
except Exception:
try:
os.unlink(f)
@@ -284,7 +307,7 @@ def write_html_files(m: pdoc.Module):
raise

for submodule in m.submodules():
write_html_files(submodule)
write_html_files(submodule, **kwargs)


def _flatten_submodules(modules: Sequence[pdoc.Module]):
@@ -299,14 +322,49 @@ def print_pdf(modules, **kwargs):
print(pdoc._render_template('/pdf.mako', modules=modules, **kwargs))


def _warn_deprecated(option, alternative='', use_config_mako=False):
msg = 'Program option `{}` is deprecated.'.format(option)
if alternative:
msg += ' Use `' + alternative + '`'
if use_config_mako:
msg += ' or override config.mako template'
msg += '.'
warn(msg, DeprecationWarning, stacklevel=2)


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

warnings.simplefilter("once", DeprecationWarning)

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

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

try:
template_config = {opt.split('=', 1)[0]: eval(opt.split('=', 1)[1], {})
for opt in args.config}
except Exception as e:
raise RuntimeError('Error evaluating config values {}: {}\n'
'Make sure string values are quoted?'.format(args.config, e))
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('Error: Template dir {!r} is not a directory'.format(args.template_dir),
@@ -318,13 +376,11 @@ def main(_args=None):
sys.path.append(os.getcwd())

if args.http:
args.html = True
args.external_links = True
args.overwrite = True
args.link_prefix = "/"
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
@@ -357,7 +413,7 @@ def docfilter(obj, _filters=args.filter.strip().split(',')):
pdoc.link_inheritance()

if args.pdf:
print_pdf(modules)
print_pdf(modules, **template_config)
print("""
PDF-ready markdown written to standard output.
^^^^^^^^^^^^^^^
@@ -393,9 +449,9 @@ def docfilter(obj, _filters=args.filter.strip().split(',')):
for module in modules:
if args.html:
_quit_if_exists(module)
write_html_files(module)
write_html_files(module, **template_config)
else:
sys.stdout.write(module.text())
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])))

@@ -7,6 +7,15 @@
list_class_variables_in_index = True
sort_identifiers = True
show_type_annotations = False
# Show collapsed source code block next to each item.
# Disabling this can improve rendering speed of large modules.
show_source_code = True
# A prefix to use for every HTML hyperlink in the generated documentation.
# No prefix results in all links being relative.
link_prefix = ''
# Set the style keyword such as 'atom-one-light' or 'github-gist'
# Options: https://github.com/highlightjs/highlight.js/tree/master/src/styles
# Demo: https://highlightjs.org/static/demo/
Oops, something went wrong.

0 comments on commit da4e822

Please sign in to comment.
You can’t perform that action at this time.