Skip to content

Commit

Permalink
docs: Fill out some API docs (#98)
Browse files Browse the repository at this point in the history
Signed-off-by: Henry Schreiner <henryschreineriii@gmail.com>
  • Loading branch information
henryiii authored Jun 12, 2023
1 parent 97ae1ff commit 07f2951
Show file tree
Hide file tree
Showing 17 changed files with 354 additions and 58 deletions.
23 changes: 23 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,29 @@ jobs:

- uses: hynek/build-and-inspect-python-package@v1

docs:
name: Docs
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0

- uses: wntrblm/nox@2023.04.22
with:
python-versions: "3.11"

- name: Linkcheck
run: nox -s docs -- -b linkcheck

- name: Build docs with warnings as errors
run: nox -s docs -- -W

- name: Verify no changes required to API docs
run: |
nox -s build_api_docs
git diff --exit-code
action:
name: Action
runs-on: ubuntu-latest
Expand Down
58 changes: 58 additions & 0 deletions docs/api/repo_review.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
repo\_review package
====================

.. automodule:: repo_review
:members:
:undoc-members:
:show-inheritance:

Submodules
----------

repo\_review.checks module
--------------------------

.. automodule:: repo_review.checks
:members:
:undoc-members:
:show-inheritance:

repo\_review.families module
----------------------------

.. automodule:: repo_review.families
:members:
:undoc-members:
:show-inheritance:

repo\_review.fixtures module
----------------------------

.. automodule:: repo_review.fixtures
:members:
:undoc-members:
:show-inheritance:

repo\_review.ghpath module
--------------------------

.. automodule:: repo_review.ghpath
:members:
:undoc-members:
:show-inheritance:

repo\_review.html module
------------------------

.. automodule:: repo_review.html
:members:
:undoc-members:
:show-inheritance:

repo\_review.processor module
-----------------------------

.. automodule:: repo_review.processor
:members:
:undoc-members:
:show-inheritance:
4 changes: 2 additions & 2 deletions docs/checks.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ class Check:
```

You need to implement `family`, which is a string indicating which family it is
grouped under, and `check()`, which can take [](fixtures), and returns `True` if
grouped under, and `check()`, which can take [](./fixtures.md), and returns `True` if
the check passes, or `False` if the check fails. If you want a dynamic error
explanation instead of the `check()` docstring, you can return a non-empty
string from the check instead of `False`. Returning `None` makes a check
Expand Down Expand Up @@ -89,7 +89,7 @@ Key features:

You register checks with a function that returns a dict of checks, with the code
of the check (letters + number) as the key, and check instances as the values.
This function can take [](fixtures), as well, allowing customization of checks
This function can take [](./fixtures.md), as well, allowing customization of checks
based on repo properties.

Here is the suggested function for the above example:
Expand Down
17 changes: 16 additions & 1 deletion docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,12 @@

extensions = [
"myst_parser",
"sphinx.ext.autodoc",
"sphinx.ext.intersphinx",
"sphinx_autodoc_typehints",
"sphinx_copybutton",
"sphinxcontrib.programoutput",
"sphinxext.opengraph",
"sphinx_copybutton",
]

source_suffix = [".rst", ".md"]
Expand All @@ -30,3 +33,15 @@
"colon_fence",
"deflist",
]

intersphinx_mapping = {
"python": ("https://docs.python.org/3", None),
}

nitpick_ignore = [
("py:class", "_io.StringIO"),
("py:class", "_io.BytesIO"),
("py:class", "repo_review.fixtures.T"),
]

always_document_param_types = True
8 changes: 8 additions & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,14 @@ families
webapp
```

```{toctree}
:maxdepth: 2
:hidden:
:caption: API
api/repo_review
```

```{include} ../README.md
:start-after: <!-- SPHINX-START -->
```
2 changes: 1 addition & 1 deletion docs/intro.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ Plugins are also encouraged to support pre-commit and GitHub Actions.
## Running checks

You can run checks with (`pipx run`) `repo-review <path>` or `python -m
repo_review <path>`. See [](cli) for command-line options.
repo_review <path>`. See [](./cli.md) for command-line options.

## Configuring

Expand Down
4 changes: 2 additions & 2 deletions docs/plugins.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Writing a plugin

To write a plugin for repo-review, you should provide one or more [](checks).
You can also add new [](fixtures), and customize [](families) with sorting and
To write a plugin for repo-review, you should provide one or more [](./checks.md).
You can also add new [](./fixtures.md), and customize [](./families.md) with sorting and
nicer display names. When writing a plugin, you should also do a few things
when setting up the package. These suggestions assume you are using a
standardized backend, such as `hatchling`, `flit-core`, `pdm-backend`, or
Expand Down
44 changes: 42 additions & 2 deletions noxfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,12 +84,52 @@ def docs(session: nox.Session) -> None:

parser = argparse.ArgumentParser()
parser.add_argument("--serve", action="store_true", help="Serve after building")
args = parser.parse_args(session.posargs)
parser.add_argument(
"-b", dest="builder", default="html", help="Build target (default: html)"
)
args, posargs = parser.parse_known_args(session.posargs)

if args.builder != "html" and args.serve:
session.error("Must not specify non-HTML builder with --serve")

session.install(".[docs]")
session.chdir("docs")
session.run("sphinx-build", "-M", "html", ".", "_build")

if args.builder == "linkcheck":
session.run(
"sphinx-build", "-b", "linkcheck", ".", "_build/linkcheck", *posargs
)
return

session.run(
"sphinx-build",
"-n", # nitpicky mode
"-T", # full tracebacks
"-b",
args.builder,
".",
f"_build/{args.builder}",
*posargs,
)

if args.serve:
session.log("Launching docs at http://localhost:8000/ - use Ctrl-C to quit")
session.run("python", "-m", "http.server", "8000", "-d", "_build/html")


@nox.session
def build_api_docs(session: nox.Session) -> None:
"""
Build (regenerate) API docs.
"""
session.install("sphinx")
session.chdir("docs")
session.run(
"sphinx-apidoc",
"-o",
"api/",
"--module-first",
"--no-toc",
"--force",
"../src/repo_review",
)
2 changes: 2 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ docs = [
"myst_parser >=0.13",
"repo-review[cli]",
"sphinx >=4.0",
"sphinx-autodoc-typehints",
"sphinx-copybutton",
"sphinxcontrib-programoutput",
"sphinxext-opengraph",
Expand Down Expand Up @@ -125,6 +126,7 @@ messages_control.disable = [
"redefined-outer-name",
"no-member", # better handled by mypy, etc.
"used-before-assignment", # False positive on conditional import
"unnecessary-ellipsis", # Not correct for typing
]


Expand Down
2 changes: 1 addition & 1 deletion src/repo_review/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"""
Copyright (c) 2022 Henry Schreiner. All rights reserved.
repo-review: Review repos with a set of checks defined by plugins.
Review repos with a set of checks defined by plugins.
"""


Expand Down
6 changes: 4 additions & 2 deletions src/repo_review/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@
from pathlib import Path
from typing import Literal

import click as orig_click

if typing.TYPE_CHECKING:
import click
import click # pylint: disable=reimported
else:
import rich_click as click

Expand All @@ -36,7 +38,7 @@ def __dir__() -> list[str]:
return __all__


rich.traceback.install(suppress=[click, rich], show_locals=True, width=None)
rich.traceback.install(suppress=[click, rich, orig_click], show_locals=True, width=None)


def list_all(ctx: click.Context, _param: click.Parameter, value: bool) -> None:
Expand Down
58 changes: 48 additions & 10 deletions src/repo_review/checks.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,51 @@


class Check(Protocol):
family: str
requires: Set[str] = frozenset() # Optional
url: str = "" # Optional
"""
This is the check Protocol. Since Python doesn't support optional Protocol
members, the two optional members are required if you want to use this
Protocol in a type checker. The members can be specified as class
properties if you want.
"""

@property
def family(self) -> str:
"""
The family is a string that the checks will be grouped by.
"""

@property
def requires(self) -> Set[str]: # Optional
"""
Requires is an (optional) set of checks that must pass for this check
to run. Omitting this is like returning `set()`.
"""

@property
def url(self) -> str: # Optional
"""
This is an (optional) URL to link to for this check. An empty string is
identical to omitting this member.
"""

def check(self) -> bool | None | str:
"""
This is a check. The docstring is used as the failure message if
`False` is returned. Returning None is a skip. Returning `True` (or an
empty string) is a pass. Can be a :func:`classmethod` or
:func:`staticmethod`. Can take fixtures.
"""
...


def collect_checks(fixtures: Mapping[str, Any]) -> dict[str, Check]:
"""
Produces a list of checks based on installed entry points. You must provide
the evaluated fixtures so that the check functions have access to the
fixtures when they are running.
:param fixtures: Fully evaluated dict of fixtures.
"""
check_functions = (
ep.load() for ep in importlib.metadata.entry_points(group="repo_review.checks")
)
Expand All @@ -30,18 +66,20 @@ def collect_checks(fixtures: Mapping[str, Any]) -> dict[str, Check]:
}


def is_allowed(select_list: Set[str], ignore_list: Set[str], name: str) -> bool:
def is_allowed(select: Set[str], ignore: Set[str], name: str) -> bool:
"""
Skips the check if the name is in the ignore list or if the name without the
number is in the ignore list. If the select list is not empty, only runs the
check if the name or name without the number is in the select list.
:param select: A set of names or prefixes to include.
:param ignore: A set of names or prefixes to exclude.
:param name: The check to test.
:return: True if this check is allowed, False otherwise.
"""
if (
select_list
and name not in select_list
and name.rstrip("0123456789") not in select_list
):
if select and name not in select and name.rstrip("0123456789") not in select:
return False
if name in ignore_list or name.rstrip("0123456789") in ignore_list:
if name in ignore or name.rstrip("0123456789") in ignore:
return False
return True
19 changes: 17 additions & 2 deletions src/repo_review/families.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,26 @@ def __dir__() -> list[str]:


class Family(typing.TypedDict, total=False):
name: str # defaults to key
order: int # defaults to 0
"""
A typed Dict that is used to customize the display of families in reports.
"""

#: Optional nice name to display instead of family key. Treated like family
#: key if missing.
name: str

#: Checks are first sorted by this integer order, then alphabetically by
#: family key. Treated like 0 if missing.
order: int


def collect_families() -> dict[str, Family]:
"""
Produces a dict mapping family keys to :class:`Family` dicts based on installed
entry points. Unlike other similar functions, this one currently does not
expect a dict of fixtures; dynamic listing is not usually needed for
:class:`Family`'s.
"""
return {
name: family
for ep in importlib.metadata.entry_points(group="repo_review.families")
Expand Down
Loading

0 comments on commit 07f2951

Please sign in to comment.