From 7981a7a13756bc34acf441ddf04f138571b7076c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20H=C3=B6rnquist?= <hugo@lysator.liu.se> Date: Sat, 3 Jun 2023 22:26:56 +0200 Subject: [PATCH] Setup sphinx documentation. Documentation can now be generated to HTML through sphinx. Some docstrings have been updated to be on the correct format. --- .gitignore | 1 + Makefile | 12 ++++++++- README.md | 18 +++++++++++++ doc/.gitignore | 2 ++ doc/conf.py | 50 ++++++++++++++++++++++++++++++++++ doc/index.rst | 20 ++++++++++++++ muppet/data/__init__.py | 5 ++++ muppet/data/html.py | 2 +- muppet/format.py | 11 ++++---- muppet/lookup.py | 60 ++++++++++++++++++++++++----------------- 10 files changed, 150 insertions(+), 31 deletions(-) create mode 100644 doc/.gitignore create mode 100644 doc/conf.py create mode 100644 doc/index.rst diff --git a/.gitignore b/.gitignore index cb556ee..8012cbc 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ .cache .yardoc +doc.rendered diff --git a/Makefile b/Makefile index 2cbbe58..a0426b1 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,9 @@ -.PHONY: all output check test +.PHONY: all output check test sphinx-apidoc documentation all: output +DOC_OUTPUT = doc.rendered + output: python -m muppet --env ~/puppet/generated-environments/test/modules/ @@ -15,5 +17,13 @@ PYTEST_FLAGS = --cov=muppet --cov-branch --cov-report=html test: python -m pytest $(PYTEST_FLAGS) tests +sphinx-apidoc: + sphinx-apidoc --separate --force -o doc muppet + +$(DOC_OUTPUT)/index.html: sphinx-apidoc + sphinx-build -b dirhtml doc $(DOC_OUTPUT) + +documentation: $(DOC_OUTPUT)/index.html + clean: -rm -r output diff --git a/README.md b/README.md index cd2ffdf..3ad4256 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,24 @@ environments. Usage ----- +### Building the example (probably broken for you) + ```bash make clean all ``` + +### Running the tests + +```bash +make check # Run linters +make test # Run unit tests +``` + +Coverage information will end up in `htmlcov/`. + +### Building the documentation +```bash +make documentation +``` + +Documentation will end up in `doc.rendered/`. diff --git a/doc/.gitignore b/doc/.gitignore new file mode 100644 index 0000000..eebb7a8 --- /dev/null +++ b/doc/.gitignore @@ -0,0 +1,2 @@ +muppet*.rst +modules.rst diff --git a/doc/conf.py b/doc/conf.py new file mode 100644 index 0000000..895caba --- /dev/null +++ b/doc/conf.py @@ -0,0 +1,50 @@ +# Configuration file for the Sphinx documentation builder. +# +# For the full list of built-in configuration values, see the documentation: +# https://www.sphinx-doc.org/en/master/usage/configuration.html +# from __future__ import annotations + +import os +import sys + +sys.path.insert(0, os.path.abspath('..')) + +# -- Project information ----------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information + +project = 'Muppet Strings' +copyright = '2023, Hugo Hörnquist' +author = 'Hugo Hörnquist' + +# -- General configuration --------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration + +# Coverage not included, since flake8 already captures that + +extensions = [ + 'sphinx.ext.autodoc', # Automatically extract source from pypthon files + 'sphinx.ext.viewcode', # Adds source viewer to output +] + +# Fancy type aliases. +# For this to work, each module has to individually run +# from __future__ import annotations +# Which will prevent type aliases from being eagerly evaluated. + +autodoc_type_aliases = { + 'Markup': 'muppet.data.Markup', +} + +# Add type signatures in parameter list of description, instead of in +# signature. This reduces the clutter quite a bit. +autodoc_typehints = 'description' + +templates_path = ['_templates'] +exclude_patterns = [] + + +# -- Options for HTML output ------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output + +html_theme = 'pyramid' +html_static_path = ['_static'] diff --git a/doc/index.rst b/doc/index.rst new file mode 100644 index 0000000..17dc708 --- /dev/null +++ b/doc/index.rst @@ -0,0 +1,20 @@ +.. Muppet Strings documentation master file, created by + sphinx-quickstart on Sat Jun 3 20:13:35 2023. + You can adapt this file completely to your liking, but it should at least + contain the root `toctree` directive. + +Welcome to Muppet Strings's documentation! +========================================== + +.. toctree:: + :maxdepth: 2 + :caption: Contents: + +Here is some content. + +Indices and tables +================== + +* :ref:`genindex` +* :ref:`modindex` +* :ref:`search` diff --git a/muppet/data/__init__.py b/muppet/data/__init__.py index 92dcbc2..9e685be 100644 --- a/muppet/data/__init__.py +++ b/muppet/data/__init__.py @@ -8,6 +8,8 @@ Almost all of the datatypes have "bad" __repr__ implementations. This is to allow *much* easier ocular diffs when running pytest. """ +from __future__ import annotations + from dataclasses import dataclass from abc import ABC, abstractmethod from collections.abc import Sequence @@ -24,6 +26,9 @@ Markup: TypeAlias = Union[str, 'ID', 'Documentation', 'Indentation'] +""" +Documentation of Markup. +""" @dataclass diff --git a/muppet/data/html.py b/muppet/data/html.py index 1b155fe..67edef9 100644 --- a/muppet/data/html.py +++ b/muppet/data/html.py @@ -44,7 +44,7 @@ class HTMLRenderer(Renderer): The anchor will contain both the item, rendered as normally, and a div with class documentation. - The suggested CSS for this is + The suggested CSS for this is:: .documentation-anchor { display: relative; diff --git a/muppet/format.py b/muppet/format.py index e8f855e..43eaa7d 100644 --- a/muppet/format.py +++ b/muppet/format.py @@ -1,7 +1,7 @@ """ Pretty print a complete puppet documentation. -An `output.json`, as per produced by `./merge-json.py` should be +An ``output.json``, as per produced by ``./merge-json.py`` should be provided as the first element. This program goes through every definition in it, and outputs a complete index.html. """ @@ -101,9 +101,10 @@ def print_var(x: str, dollar: bool = True) -> Link: Print the given variable. If documentation exists, then add that documentation as hoover text. - :param: x + + :param x: The variable to print - :param: dollar + :param dollar: If there should be a dollar prefix. """ dol = '$' if dollar else '' @@ -155,9 +156,9 @@ def parse(form: Any, indent: int, context: list[str]) -> Tag: """ Print everything from a puppet parse tree. - :param: from + :param from: A puppet AST. - :param: indent + :param indent: How many levels deep in indentation the code is. Will get multiplied by the indentation width. """ diff --git a/muppet/lookup.py b/muppet/lookup.py index 0abb644..d51b1af 100644 --- a/muppet/lookup.py +++ b/muppet/lookup.py @@ -1,5 +1,5 @@ -""" -[Jq(1)](https://jqlang.github.io/jq/) like expressions for python. +r""" +Jq(1) <https://jqlang.github.io/jq/> like expressions for python. Something similar to Jq, but built on python objects. All procedures eventually return the expecetd value, or a @@ -8,17 +8,19 @@ user-supplied default value. Example ------- - lookup(i) \ - .ref('docstring') \ - .ref('tags') \ - .select(Ref('tag_name') == 'summary')) \ - .idx(0) - .ref('text') \ - .exec() +:: + + lookup(object) \ + .ref('docstring') \ + .ref('tags') \ + .select(Ref('tag_name') == 'summary')) \ + .idx(0) \ + .ref('text') \ + .exec() TODO ---- -- `select` +- ``select`` Selects all values from a list which matches a given expression. This would however require us to manage multiple values at once. """ @@ -26,11 +28,14 @@ TODO from typing import Any, Union -class _Expression: +class Expression: """ A test expression. + :: + x.find(Ref("key") == "summary") + Would focus in on the first list element which has the key "key" with a value of "summary". @@ -38,31 +43,39 @@ class _Expression: """ def run(self, value: Any) -> bool: + """Evaluate expression, returing true if the value matches the predicate.""" return False -class _RefEqExpr(_Expression): +class RefEqExpr(Expression): """ Equality expression. - Assumes that the left part is a _RefExpr and the right part is a value. + Assumes that the left part is a RefExpr and the right part is a value. Checks that the left reference exists in the given value, and that it's value is equal to the right one. """ - def __init__(self, left: '_RefExpr', right: Any): + def __init__(self, left: 'RefExpr', right: Any): self.left = left self.right = right def run(self, value: Any) -> bool: + """ + Evaluate the expression. + + Returns true if the left hand side key is present in *value*, + and if the value under that key in *value* matches the right + hand side. + """ if self.left.key not in value: return False else: return bool(value[self.left.key] == self.right) -class _RefExpr(_Expression): +class RefExpr(Expression): """ A key reference expression. @@ -74,12 +87,12 @@ class _RefExpr(_Expression): def __init__(self, key: str): self.key = key - def __eq__(self, other: Any) -> '_RefEqExpr': # type: ignore + def __eq__(self, other: Any) -> 'RefEqExpr': # type: ignore """ Return a new expression checking equality between left and right. Left side will be ourself, while the right side can in theory - be anything (see _RefEqExpr for details). + be anything (see RefEqExpr for details). Typing is removed here, since the base object specifies the type as def __eq__(self, x: Any) -> bool: @@ -90,9 +103,10 @@ class _RefExpr(_Expression): there is a president. """ - return _RefEqExpr(self, other) + return RefEqExpr(self, other) def run(self, value: Any) -> bool: + """Return true if our key is present in *value*.""" return self.key in value @@ -118,7 +132,7 @@ class _NullLookup: """Propagate null.""" return self - def find(self, _: _Expression) -> '_NullLookup': + def find(self, _: Expression) -> '_NullLookup': """Propagate null.""" return self @@ -162,7 +176,7 @@ class _TrueLookup: # Index out of range return _NullLookup() - def find(self, expr: _Expression) -> Union['_TrueLookup', '_NullLookup']: + def find(self, expr: Expression) -> Union['_TrueLookup', '_NullLookup']: """Find the first element in list matching expression.""" for item in self.object: if expr.run(item): @@ -190,11 +204,9 @@ def lookup(base: Any) -> Lookup: All queries should start here. - Parameters - ---------- - base - Can be anything which has meaningful subfields. + :param base: Can be anything which has meaningful subfields. """ return _TrueLookup(base) -Ref = _RefExpr +Ref = RefExpr -- GitLab