Skip to content
Permalink
Browse files

Merge pull request databio#35 from databio/dev

Release 0.4.0
  • Loading branch information...
vreuter committed Jun 7, 2019
2 parents ce5330f + d9c6aae commit 8be77ec5a95fc9ce9230a32cf68752498d035702
@@ -1,9 +1,22 @@
# Changelog

## [0.3.1] - 2019-04-15
## [0.4.0] -- 2019-06-06
### Added
- `__aliases__` (mapping from name to list of names) as hook for package to declare what exports are equivalent, and therefore which to use just one of in the documentation.
- Anchor tags to class headers

### Changed
- Improved object signature renditions in API docs.
- Improved overall styling of API docs.

### Fixed
- Take measures to ensure objects are repeatedly documented if aliased for export, even
if the documented package doesn't declare `__aliases__`

## [0.3.1] -- 2019-04-15
- Depend on patched `logmuse`

## [0.3] - 2019-04-11
## [0.3.0] -- 2019-04-11
### Added
- By default, print version info for documentation target and for this package in footer of each docs page; this may be omitted with `--omit-meta`.
### Changed
@@ -13,13 +26,13 @@
- Make `--whitelist` and `--blacklist` CLI options functional.
- Improve docs rendition of nested classes

## [0.2] - 2019-03-20
## [0.2.0] -- 2019-03-20
### Added
- Support for specific inclusion/exclusion of objects for documentation.
- Support specification of grouping objects into separate documentation files.
### Changed
- Make signature for property match that of function with empty parameter list.

## [0.1] - 2019-03-11
## [0.1.0] -- 2019-03-11
- First release version

@@ -1,2 +1,2 @@
__version__ = "0.3.1"
__version__ = "0.4.0"

@@ -104,9 +104,9 @@ def __call__(self, t, *args, **kwargs):
:return str: rendition of the given tag
"""
if isinstance(t, ParTag):
return "- `{}` -- `{}`: {}".format(t.name, t.typename, t.description)
return "- `{}` (`{}`): {}".format(t.name, t.typename, t.description)
elif isinstance(t, RetTag):
return "`{}`: {}".format(t.typename, t.description)
return "- `{}`: {}".format(t.typename, t.description)
elif isinstance(t, ErrTag):
return "- `{}`: {}".format(t.typename, t.description)
else:
@@ -1,6 +1,5 @@
""" Helper functions """

import os
import sys
if sys.version_info < (3, 3):
from collections import Iterable
@@ -10,17 +9,7 @@
__author__ = "Vince Reuter"
__email__ = "vreuter@virginia.edu"

__all__ = ["expandpath", "is_callable_like", "is_collection_like"]


def expandpath(p):
"""
Expand environment and user variables contained in filesystem path.
:param str p: Path in which to perform the variable expansion
:return str: The expansion of the input path
"""
return os.path.expanduser(os.path.expandvars(p))
__all__ = ["is_callable_like", "is_collection_like"]


def is_callable_like(obj):
@@ -35,18 +35,60 @@
from .doctags import MdTagRenderer
from .exceptions import LucidocError
from ._version import __version__

module_header = "# Package {} Documentation\n"
class_header = "## Class {}"
function_header = "### {}"
from ubiquerg import expandpath


__all__ = ["doc_class", "doc_callable", "doc_module", "run_lucidoc"]


_LOGGER = None


script_header = """<script>
document.addEventListener('DOMContentLoaded', (event) => {
document.querySelectorAll('h3 code').forEach((block) => {
hljs.highlightBlock(block);
});
});
</script>
<style>
h3 {
padding-left: 22px;
text-indent: -15px;
}
h3 .hljs {
padding-left: 20px;
margin-left: 0px;
text-indent: -15px;
martin-bottom: 0px;
}
h4, table, p, li{ margin-left: 30px; }
h4 {
font-style: italic;
font-size: 1em;
margin-bottom: 0px;
}
</style>"""
module_header = "# Package `{}` Documentation\n"
class_header = "## <a name=\"{name}\"></a> Class `{name}`"
#function_header = "### <a name=\"{name}\"></a> `{name}`"


def _fmt_fun_section(name):
return "#### {}".format(name)


def _func_sect_head(name, fmt=_fmt_fun_section, suffix=":", newline=True):
return fmt(name) + suffix + "\n" if newline else ""


par_header = partial(_func_sect_head, "Parameters")
ret_header = partial(_func_sect_head, "Returns")
err_header = partial(_func_sect_head, "Raises")
exs_header = partial(_func_sect_head, "Examples")


class _VersionInHelpParser(argparse.ArgumentParser):
def format_help(self):
""" Add version information to help text. """
@@ -197,14 +239,33 @@ def collect_targets_by_name():
if repeated:
raise LucidocError("Repeat target names:\n{}".format(
"\n".join("{}: {}".format(n, ts) for n, ts in repeated)))
if "__aliases__" in dir(mod):
all_targets = {k: v for k, v in all_targets.items() if k not in
set(itertools.chain(*mod.__aliases__.values()))}
used_objs = {}
final_targets = {}
for name, obj in all_targets.items():
try:
seen = obj in used_objs
except TypeError as e:
print("Could not determine whether object for '{}' is repeated; "
"skipping ({})".format(name, str(e)))
continue
if not seen:
final_targets[name] = obj
used_objs[obj] = name
else:
print("Object for {} is already bound to {} and will be documented "
"as such".format(name, used_objs[obj]))

missing_targets = declared - set(all_targets.keys())
if missing_targets:
_LOGGER.warning("{} target(s) missing: {}".format(
len(missing_targets), ", ".join(missing_targets)))
print("Final targets: {}".format(", ".join(sorted(final_targets.keys()))))

# Header and module docstring
output = [module_header.format(mod.__name__)]
output = [script_header, module_header.format(mod.__name__)]
if mod.__doc__ and not no_mod_docstr:
output.append(mod.__doc__)

@@ -232,7 +293,7 @@ def postproc(head, blocks):
_LOGGER.warning("No version info available: {}".format(mod.__name__))
footer = ""
else:
footer = "\n\n**Version Information**: `{}` v{}, generated by `lucidoc` v{}".\
footer = "\n\n*Version Information: `{}` v{}, generated by `lucidoc` v{}*".\
format(mod.__name__, v, __version__)
def postproc(head, blocks):
return basic_finish(head, blocks) + footer
@@ -245,7 +306,7 @@ def prepare_targets(named_targets):
f = get_target_fun(t)
f and res.append((n, t, f))
return res
chunk_groups = {g: [(n, all_targets[n]) for n in ns] for g, ns in groups}
chunk_groups = {g: [(n, final_targets[n]) for n in ns] for g, ns in groups}
grouped_results = {g: build_doc_block(nt_pairs, prepare_targets)
for g, nt_pairs in chunk_groups.items()}
return {g: postproc(head=output, blocks=chunks)
@@ -261,7 +322,7 @@ def prepare_targets(named_targets):
classes.append((n, t))
return [(n, t, doc_cls) for n, t in classes] + \
[(n, t, doc_fun) for n, t in functions]
chunks = build_doc_block(all_targets.items(), prepare_targets)
chunks = build_doc_block(final_targets.items(), prepare_targets)
return postproc(head=output, blocks=chunks)


@@ -288,7 +349,7 @@ def doc_class(cls, docstr_parser, render_tag, include_inherited, nested=False):

_LOGGER.info("Processing class: {}".format(cls.__name__))

head = class_header.format(cls.__name__)
head = class_header.format(name=cls.__name__)
if nested:
head = "#" + head
cls_doc = [head]
@@ -300,21 +361,21 @@ def doc_class(cls, docstr_parser, render_tag, include_inherited, nested=False):
err_tag_lines = [render_tag(t) for t in parsed_clsdoc.raises]
block_lines = []
if param_tag_lines:
block_lines.append("**Parameters:**\n")
block_lines.append(par_header())
block_lines.extend(param_tag_lines)
block_lines.append("\n")
if parsed_clsdoc.returns:
raise LucidocError("Class docstring has a return value: {}".
format(parsed_clsdoc.returns))
if err_tag_lines:
block_lines.append("**Raises:**\n")
block_lines.append(err_header())
block_lines.extend(err_tag_lines)
block_lines.append("\n")
if parsed_clsdoc.examples:
if not isinstance(parsed_clsdoc.examples, list):
raise TypeError("Example lines are {}, not list".
format(type(parsed_clsdoc.examples)))
block_lines.append("**Example(s):**\n")
block_lines.append(exs_header())
block_lines.extend(parsed_clsdoc.examples)
block_lines.append("\n")
block = "\n".join(block_lines)
@@ -401,35 +462,31 @@ def doc_callable(f, docstr_parser, render_tag, name=None):

_LOGGER.info("Processing function: {}".format(n))

head = function_header.format(n.replace('_', '\\_'))

signature = "```python\ndef {}{}\n```\n".format(
n, "(self)" if isinstance(f, property) else
pydoc.inspect.formatargspec(*args_spec(f)))

res = [head]
res = [signature]
ds = pydoc.inspect.getdoc(f)
if ds:
parsed = docstr_parser(ds)
param_tag_lines = [render_tag(t) for t in parsed.params]
err_tag_lines = [render_tag(t) for t in parsed.raises]
block_lines = []
if param_tag_lines:
block_lines.append("**Parameters:**\n")
block_lines.append(par_header())
block_lines.extend(param_tag_lines)
block_lines.append("\n")
if parsed.returns:
block_lines.append("**Returns:**\n")
block_lines.append(ret_header())
block_lines.append(render_tag(parsed.returns))
block_lines.append("\n")
if err_tag_lines:
block_lines.append("**Raises:**\n")
block_lines.append(err_header())
block_lines.extend(err_tag_lines)
block_lines.append("\n")
block = "\n".join(block_lines)
res.extend([docstr_parser.description(ds), signature, block])
else:
res.append(signature)
res.extend([docstr_parser.description(ds), block])
res.append("\n")
return res

@@ -1,2 +1,2 @@
logmuse>=0.0.2

logmuse>=0.2.0
ubiquerg>=0.0.5
@@ -1 +1,2 @@
pytest>=3.0.7
ubiquerg>=0.0.3
@@ -12,12 +12,13 @@
from collections.abc import Iterable
import pytest
from lucidoc.docparse import RST_EXAMPLE_TAG, RstDocstringParser
from tests.helpers import make_exports_declaration, powerset
from tests.helpers import make_exports_declaration
from ubiquerg import powerset


__author__ = "Vince Reuter"
__email__ = "vreuter@virginia.edu"


HEADLINE = "This is a short description."
DETAIL_LINES = ["This description provides more detail",
"split over multiple lines.",
@@ -40,18 +40,6 @@ def make_exports_declaration(names):
return "__all__ = [{}]".format(", ".join("\"{}\"".format(n) for n in names))


def powerset(items, nonempty=False):
"""
Powerset of a collection, optionally excluding the empty set.
:param Iterable[object] items: collection of objects to powerset
:param bool nonempty: whether to exclude the empty subset
:return list[object]: powerset of the given collection
"""
return [x for k in range(1 if nonempty else 0, 1 + len(items)) for x in
itertools.combinations(items, k)]


class SafeExec(object):
""" Run something that could erroneously write output; clean it if so. """
def __init__(self):
@@ -2,9 +2,9 @@

import pytest
import lucidoc

from tests.conftest import DESC_KEY, PAR_KEY, RET_KEY, ERR_KEY, EXS_KEY, TYPE_ERROR, \
VALUE_ERROR, RETURN, RETURN_MUTLI_LINE, build_args_space, powerset
VALUE_ERROR, RETURN, RETURN_MUTLI_LINE, build_args_space
from ubiquerg import powerset

__author__ = "Vince Reuter"
__email__ = "vreuter@virginia.edu"
@@ -3,8 +3,9 @@
import pytest
import lucidoc
from lucidoc.docparse import RST_EXAMPLE_TAG
from tests.conftest import build_args_space, powerset, CODE_EX1, CODE_EX2, DESC_KEY, \
from tests.conftest import build_args_space, CODE_EX1, CODE_EX2, DESC_KEY, \
EXS_KEY, HEADLINE, DETAIL_LINES, LONG_DESC_KEY, SHORT_DESC_KEY
from ubiquerg import powerset

__author__ = "Vince Reuter"
__email__ = "vreuter@virginia.edu"
@@ -4,9 +4,11 @@
import os
import random
import string
from lucidoc.docparse import PARSERS
from tests.helpers import exec_test, powerset
import pytest
from lucidoc.docparse import PARSERS
from tests.helpers import exec_test
from ubiquerg import powerset


__author__ = "Vince Reuter"
__email__ = "vreuter@virginia.edu"

0 comments on commit 8be77ec

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