Skip to content

Commit

Permalink
Add project automation, fix tests, update package setup, add contribu…
Browse files Browse the repository at this point in the history
…ting guidelines
  • Loading branch information
Florimond Manca committed Jun 4, 2020
1 parent 6e33c3a commit eddf26c
Show file tree
Hide file tree
Showing 21 changed files with 176 additions and 60 deletions.
25 changes: 25 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Contributing guidelines

To install development dependencies, run:

```bash
scripts/install
```

Once this is done, you should be able to run the test suite:

```bash
scripts/test
```

Code auto formatting can be run with:

```bash
scripts/format
```

To run style checks, use:

```bash
scripts/style
```
4 changes: 4 additions & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
include README.md
include CHANGELOG.md
include LICENSE
include mkdocs_click/py.typed
33 changes: 18 additions & 15 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,32 +1,35 @@
# mkdocs-ext-click
Extensions for mkdocs to read and parse click commands.
# mkdocs-click

## Usage
An MkDocs extension to generate documentation for Click command line applications.

Install the extension from the repo. At the moment there is no version of it on PyPI.
## Installation

```bash
git clone https://github.com/DataDog/mkdocs-click.git
cd mkdocs-click
pip install .
```
This package is not available on PyPI yet, but you can install it from git:

If you use tox or a dynamic environment, you can add the following line to your requirements:
```bash
git+https://github.com/DataDog/mkdocs-click.git
pip install git+https://github.com/DataDog/mkdocs-click.git
```

Adds this to the `markdown_extensions` in your mkdocs.yml file:
## Quickstart

Register the extension in your `mkdocs.yml` configuration:

```yaml
# mkdocs.yml
markdown_extensions:
- mkdocs-click
```
And finally to document a given click method, add this to any of your markdown file:
To document a given Click command, add this in the body of a Markdown file:
```markdown
:::click module=<MODULE_PATH>:<CLICK_FUNCTION>:::
:::click module=<MODULE_PATH>:<COMMAND>:::
```
replacing `<MODULE_PATH>` and `<CLICK_METHOD>` as needed.
Be sure to replace `<MODULE_PATH>` and `<COMMAND>` as appropriate.

Example:

```markdown
:::click module=app.cli:build:::
```
4 changes: 2 additions & 2 deletions mkdocs_click/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from .__version__ import __version__
from .extension import MKClickExtension, makeExtension

__version__ = "0.0.1"
__all__ = ["MKClickExtension", "makeExtension"]
__all__ = ["__version__", "MKClickExtension", "makeExtension"]
1 change: 1 addition & 0 deletions mkdocs_click/__version__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
__version__ = "0.1.0"
4 changes: 2 additions & 2 deletions mkdocs_click/extension.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@

class ClickProcessor(Preprocessor):

PATTERN_PLUGIN_IDENTIFIER = re.compile(r'^::: mkdocs-click')
PATTERN_PLUGIN_OPTIONS = re.compile(r'^(?:\t|\s{4}):(.+):(\s\S+|\s*)$')
PATTERN_PLUGIN_IDENTIFIER = re.compile(r"^::: mkdocs-click")
PATTERN_PLUGIN_OPTIONS = re.compile(r"^(?:\t|\s{4}):(.+):(\s\S+|\s*)$")

def run(self, lines: List[str]) -> List[str]:
new_lines = []
Expand Down
39 changes: 21 additions & 18 deletions mkdocs_click/parser.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import logging
import traceback
from typing import Dict, List, Union
from typing import cast, Dict, List, Optional

import click
from click import Command
Expand Down Expand Up @@ -44,17 +44,17 @@ def _load_command(module_path: str, module_name: str) -> click.BaseCommand:


def _make_header(text: str, level: int) -> str:
"""Creates a markdown header at a given level"""
"""Create a markdown header at a given level"""
return f"{'#' * (level + 1)} {text}"


def _make_title(prog_name: str, level: int) -> List[str]:
"""Creates the first markdown lines describing a command."""
"""Create the first markdown lines describing a command."""
return [_make_header(prog_name, level), ""]


def _make_description(ctx: click.Context) -> List[str]:
"""Creates markdown lines based on the command's own description."""
"""Create markdown lines based on the command's own description."""
lines = []
help_string = ctx.command.help or ctx.command.short_help
if help_string:
Expand All @@ -65,7 +65,7 @@ def _make_description(ctx: click.Context) -> List[str]:


def _make_usage(ctx: click.Context) -> List[str]:
"""Creates the markdown lines from the command usage string."""
"""Create the markdown lines from the command usage string."""

# Gets the usual 'Usage' string without the prefix.
formatter = ctx.make_formatter()
Expand All @@ -75,7 +75,7 @@ def _make_usage(ctx: click.Context) -> List[str]:

# Generate the full usage string based on parents if any i.e. `ddev meta snmp ...`
full_path = []
current = ctx
current: Optional[click.Context] = ctx
while current is not None:
full_path.append(current.command.name)
current = current.parent
Expand All @@ -86,7 +86,7 @@ def _make_usage(ctx: click.Context) -> List[str]:


def _make_options(ctx: click.Context) -> List[str]:
"""Creates the markdown lines describing the options for the command"""
"""Create the markdown lines describing the options for the command."""
formatter = ctx.make_formatter()
Command.format_options(ctx.command, ctx, formatter)
# First line is redundant "Options"
Expand All @@ -98,19 +98,22 @@ def _make_options(ctx: click.Context) -> List[str]:
return ["Options:", "```code", *option_lines, "```"]


def _get_lazyload_commands(multicommand: click.MultiCommand) -> Dict[str, click.Command]:
"""Obtains click.Command references to the subcommands of a given command"""
def _get_lazyload_commands(multicommand: click.MultiCommand, ctx: click.Context) -> Dict[str, click.Command]:
"""Obtain click.Command references to the subcommands of a given command."""
commands = {}
for command in multicommand.list_commands(multicommand):
commands[command] = multicommand.get_command(multicommand, command)

for name in multicommand.list_commands(ctx):
command = multicommand.get_command(ctx, name)
assert command is not None
commands[name] = command

return commands


def _parse_recursively(
prog_name: str, command: click.BaseCommand, parent: click.Context = None, level: int = 0
) -> List[str]:
ctx = click.Context(command, parent=parent)
ctx = click.Context(cast(click.Command, command), parent=parent)
lines = []
lines.extend(_make_title(prog_name, level))
lines.extend(_make_description(ctx))
Expand All @@ -120,7 +123,7 @@ def _parse_recursively(
# Get subcommands
lookup = getattr(ctx.command, "commands", {})
if not lookup and isinstance(ctx.command, click.MultiCommand):
lookup = _get_lazyload_commands(ctx.command)
lookup = _get_lazyload_commands(ctx.command, ctx)
commands = sorted(lookup.values(), key=lambda item: item.name)

for command in commands:
Expand All @@ -131,15 +134,15 @@ def _parse_recursively(
def generate_command_docs(block_options: Dict[str, str]) -> List[str]:
"""Entry point for generating markdown doumentation for a given command."""

required_options = ('module', 'command')
required_options = ("module", "command")
for option in required_options:
if option not in block_options:
raise MKClickConfigException(
'Parameter {} is required for mkdocs-click. Provided configuration was {}'.format(option, block_options)
"Parameter {} is required for mkdocs-click. Provided configuration was {}".format(option, block_options)
)

module_path = block_options['module']
command = block_options['command']
depth = int(block_options.get('depth', 0))
module_path = block_options["module"]
command = block_options["command"]
depth = int(block_options.get("depth", 0))
click_command = _load_command(module_path, command)
return _parse_recursively(command, click_command, level=depth)
Empty file added mkdocs_click/py.typed
Empty file.
3 changes: 3 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[tool.black]
line-length = 120
target-version = ["py37"]
2 changes: 0 additions & 2 deletions requirements.in

This file was deleted.

10 changes: 10 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
-e .

# Linters
black
flake8
mypy

# Testing
mock==4.*
pytest==5.*
8 changes: 8 additions & 0 deletions scripts/format
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#! /bin/sh -e

BIN="venv/bin/"
FILES="mkdocs_click tests"

set -x

${BIN}black $FILES
11 changes: 11 additions & 0 deletions scripts/install
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#! /bin/sh -e

VENV="venv"
BIN="$VENV/bin/"

set -x

python -m venv $VENV

${BIN}pip install -U pip
${BIN}pip install -r requirements.txt
10 changes: 10 additions & 0 deletions scripts/style
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#! /bin/sh -e

BIN="venv/bin/"
FILES="mkdocs_click tests"

set -x

${BIN}black --check --diff $FILES
${BIN}flake8 $FILES
${BIN}mypy $FILES
8 changes: 8 additions & 0 deletions scripts/test
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#! /bin/sh -e

VENV="venv"
BIN="$VENV/bin/"

set -x

${BIN}pytest "$@"
14 changes: 14 additions & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[flake8]
ignore = E203,E722,E741,W503
max-line-length = 120

[mypy]
disallow_untyped_defs = True
ignore_missing_imports = True

[mypy-tests.*]
disallow_untyped_defs = False
check_untyped_defs = True

[tool:pytest]
addopts = -rxXs
36 changes: 29 additions & 7 deletions setup.py
Original file line number Diff line number Diff line change
@@ -1,25 +1,47 @@
# (C) Datadog, Inc. 2020-present
# All rights reserved
# Licensed under the Apache license (see LICENSE)
import re
from pathlib import Path
from setuptools import setup

VERSION = "0.1.0"

with open("requirements.in", 'r') as req:
REQUIRES = req.read().splitlines()
def get_version(package: str) -> str:
text = Path(package, "__version__.py").read_text()
match = re.search('__version__ = "([^"]+)"', text)
assert match is not None
return match.group(1)


def get_long_description() -> str:
readme = Path("README.md").read_text()
changelog = Path("CHANGELOG.md").read_text()
return f"{readme}\n\n{changelog}"


setup(
name="mkdocs_click",
version=VERSION,
description="An mkdocs extension to document click methods",
version=get_version("mkdocs_click"),
description="An MkDocs extension to generate documentation for Click command line applications",
long_description=get_long_description(),
long_description_content_type="text/markdown",
keywords="mkdocs datadog click",
url="https://github.com/DataDog/mkdocs-click",
author="Datadog",
author_email="[email protected]",
license="Apache",
packages=["mkdocs_click"],
install_requires=REQUIRES,
python_requires=">=3.0",
install_requires=["click", "markdown"],
python_requires=">=3.7",
include_package_data=True,
zip_safe=False,
entry_points={"markdown.extensions": ["mkdocs-click = mkdocs_click:MKClickExtension"]},
classifiers=[
"Development Status :: 4 - Beta",
"Intended Audience :: Developers",
"Operating System :: OS Independent",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
]
)
7 changes: 3 additions & 4 deletions tests/click/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,12 @@


@click.command()
@click.option('--count', default=1, help='Number of greetings.')
@click.option('--name', prompt='Your name',
help='The person to greet.')
@click.option("--count", default=1, help="Number of greetings.")
@click.option("--name", prompt="Your name", help="The person to greet.")
def hello(count, name):
"""Simple program that greets NAME for a total of COUNT times."""
for x in range(count):
click.echo('Hello %s!' % name)
click.echo("Hello %s!" % name)


@click.group()
Expand Down
3 changes: 1 addition & 2 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,5 @@

@pytest.fixture
def generate_docs():
with mock.patch('mkdocs_click.extension.generate_command_docs') as generate_docs:
with mock.patch("mkdocs_click.extension.generate_command_docs") as generate_docs:
yield generate_docs

Loading

0 comments on commit eddf26c

Please sign in to comment.