diff --git a/doc/pdoc_template/config.mako b/doc/pdoc_template/config.mako new file mode 100644 index 00000000..577094b8 --- /dev/null +++ b/doc/pdoc_template/config.mako @@ -0,0 +1,5 @@ +<%! + +git_link_template = 'https://github.com/pdoc3/pdoc/blob/{commit}/{path}#L{start_line}-L{end_line}' + +%> diff --git a/pdoc/documentation.md b/pdoc/documentation.md index abbccb2a..62f2a9ac 100644 --- a/pdoc/documentation.md +++ b/pdoc/documentation.md @@ -192,7 +192,7 @@ or, alternatively, use [raw string literals]. [Google-style]: http://google.github.io/styleguide/pyguide.html#38-comments-and-docstrings [reST directives]: #supported-rest-directives [template config]: #custom-templates -[recognized delimiters]: http://docs.mathjax.org/en/latest/tex.html#tex-and-latex-math-delimiters +[recognized delimiters]: https://docs.mathjax.org/en/latest/input/tex/delimiters.html [raw string literals]: https://www.journaldev.com/23598/python-raw-string diff --git a/pdoc/html_helpers.py b/pdoc/html_helpers.py index b5ad9812..2580d74d 100644 --- a/pdoc/html_helpers.py +++ b/pdoc/html_helpers.py @@ -2,8 +2,10 @@ Helper functions for HTML output. """ import inspect -import os.path +import os import re +import subprocess +import traceback from functools import partial, lru_cache from typing import Callable, Match from warnings import warn @@ -430,3 +432,96 @@ def extract_toc(text: str): if toc.endswith('
'): # CUT was put into its own paragraph
toc = toc[:-3].rstrip()
return toc
+
+
+def format_git_link(template: str, dobj: pdoc.Doc):
+ """
+ Interpolate `template` as a formatted string literal using values extracted
+ from `dobj` and the working environment.
+ """
+ if not template:
+ return None
+ try:
+ if 'commit' in _str_template_fields(template):
+ commit = _git_head_commit()
+ abs_path = inspect.getfile(inspect.unwrap(dobj.obj))
+ path = _project_relative_path(abs_path)
+ lines, start_line = inspect.getsourcelines(dobj.obj)
+ end_line = start_line + len(lines) - 1
+ url = template.format(**locals())
+ return url
+ except Exception:
+ warn('format_git_link for {} failed:\n{}'.format(dobj.obj, traceback.format_exc()))
+ return None
+
+
+@lru_cache()
+def _git_head_commit():
+ """
+ If the working directory is part of a git repository, return the
+ head git commit hash. Otherwise, raise a CalledProcessError.
+ """
+ process_args = ['git', 'rev-parse', 'HEAD']
+ try:
+ commit = subprocess.check_output(process_args, universal_newlines=True).strip()
+ return commit
+ except OSError as error:
+ warn("git executable not found on system:\n{}".format(error))
+ except subprocess.CalledProcessError as error:
+ warn(
+ "Ensure pdoc is run within a git repository.\n"
+ "`{}` failed with output:\n{}"
+ .format(' '.join(process_args), error.output)
+ )
+ return None
+
+
+@lru_cache()
+def _git_project_root():
+ """
+ Return the path to project root directory or None if indeterminate.
+ """
+ path = None
+ for cmd in (['git', 'rev-parse', '--show-superproject-working-tree'],
+ ['git', 'rev-parse', '--show-toplevel']):
+ try:
+ path = subprocess.check_output(cmd, universal_newlines=True).rstrip('\r\n')
+ if path:
+ break
+ except (subprocess.CalledProcessError, OSError):
+ pass
+ return path
+
+
+@lru_cache()
+def _project_relative_path(absolute_path):
+ """
+ Convert an absolute path of a python source file to a project-relative path.
+ Assumes the project's path is either the current working directory or
+ Python library installation.
+ """
+ from distutils.sysconfig import get_python_lib
+ for prefix_path in (_git_project_root() or os.getcwd(),
+ get_python_lib()):
+ common_path = os.path.commonpath([prefix_path, absolute_path])
+ if common_path == prefix_path:
+ # absolute_path is a descendant of prefix_path
+ return os.path.relpath(absolute_path, prefix_path)
+ raise RuntimeError(
+ "absolute path {!r} is not a descendant of the current working directory "
+ "or of the system's python library."
+ .format(absolute_path)
+ )
+
+
+@lru_cache()
+def _str_template_fields(template):
+ """
+ Return a list of `str.format` field names in a template string.
+ """
+ from string import Formatter
+ return [
+ field_name
+ for _, field_name, _, _ in Formatter().parse(template)
+ if field_name is not None
+ ]
diff --git a/pdoc/templates/config.mako b/pdoc/templates/config.mako
index 48c0b61a..6e48c097 100644
--- a/pdoc/templates/config.mako
+++ b/pdoc/templates/config.mako
@@ -12,6 +12,15 @@
# Disabling this can improve rendering speed of large modules.
show_source_code = True
+ # If set, format links to objects in online source code repository
+ # according to this template. Supported keywords for interpolation
+ # are: commit, path, start_line, end_line.
+ #git_link_template = 'https://github.com/USER/PROJECT/blob/{commit}/{path}#L{start_line}-L{end_line}'
+ #git_link_template = 'https://gitlab.com/USER/PROJECT/blob/{commit}/{path}#L{start_line}-L{end_line}'
+ #git_link_template = 'https://bitbucket.org/USER/PROJECT/src/{commit}/{path}#lines-{start_line}:{end_line}'
+ #git_link_template = 'https://CGIT_HOSTNAME/PROJECT/tree/{path}?id={commit}#n{start-line}'
+ git_link_template = None
+
# A prefix to use for every HTML hyperlink in the generated documentation.
# No prefix results in all links being relative.
link_prefix = ''
diff --git a/pdoc/templates/css.mako b/pdoc/templates/css.mako
index 29c9a66d..f82ffc9e 100644
--- a/pdoc/templates/css.mako
+++ b/pdoc/templates/css.mako
@@ -196,14 +196,22 @@
background: inherit; /* Don't grey-back parameters */
}
- .source summary {
+ .source summary,
+ .git-link-div {
color: #666;
text-align: right;
font-weight: 400;
font-size: .8em;
text-transform: uppercase;
- cursor: pointer;
}
+ .source summary > * {
+ white-space: nowrap;
+ cursor: pointer;
+ }
+ .git-link {
+ color: inherit;
+ margin-left: 1em;
+ }
.source pre {
max-height: 500px;
overflow: auto;
diff --git a/pdoc/templates/html.mako b/pdoc/templates/html.mako
index ef0ce266..a108153a 100644
--- a/pdoc/templates/html.mako
+++ b/pdoc/templates/html.mako
@@ -2,7 +2,7 @@
import os
import pdoc
- from pdoc.html_helpers import extract_toc, glimpse, to_html as _to_html
+ from pdoc.html_helpers import extract_toc, glimpse, to_html as _to_html, format_git_link
def link(d, name=None, fmt='{}'):
@@ -21,12 +21,22 @@
<%def name="ident(name)">${name}%def>
<%def name="show_source(d)">
- % if show_source_code and d.source and d.obj is not getattr(d.inherits, 'obj', None):
- Source code
-
- ${d.source | h}
+ Expand source code
+ % if git_link:
+ Browse git
+ %endif
+
+
+ ${d.source | h}