Skip to content

Commit

Permalink
Implement linksmith inventory and linksmith output-formats
Browse files Browse the repository at this point in the history
... based on `sphobjinv` and others.
  • Loading branch information
amotl committed Apr 1, 2024
1 parent b09120a commit cdf8399
Show file tree
Hide file tree
Showing 27 changed files with 794 additions and 15 deletions.
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
*.inv binary
3 changes: 2 additions & 1 deletion CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@
## Unreleased

## v0.0.0 - 2024-xx-xx

- Implement `linksmith inventory` and `linksmith output-formats`
subcommands, based on `sphobjinv` and others. Thanks, @bskinn.
19 changes: 11 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,17 +38,19 @@ pip install 'linksmith @ git+https://github.com/tech-writing/linksmith.git'


## Usage
Nothing works yet. All just sketched out.

sphobjinv call delegation ftw.
```shell
linksmith inventory https://linksmith.readthedocs.io/en/latest/objects.inv
```
# Shorthand command ...
anansi suggest matplotlib draw

# ... for:
sphobjinv suggest -u https://matplotlib.org/stable/ draw
```
Read more at the [Linksmith Usage] documentation.

The `linksmith inventory` subsystem is heavily based on
`sphinx.ext.intersphinx` and `sphobjinv`.

> [!WARNING]
> Here be dragons. Please note the program is pre-alpha, and a work in
> progress, so everything may change while we go.

## Development
Expand Down Expand Up @@ -89,7 +91,7 @@ please let us know._

## Acknowledgements

Kudos to [Sviatoslav Sydorenko], [Brian Skinn], [Chris Sewell], and all other
Kudos to [Brian Skinn], [Sviatoslav Sydorenko], [Chris Sewell], and all other
lovely people around Sphinx and Read the Docs.


Expand All @@ -103,6 +105,7 @@ lovely people around Sphinx and Read the Docs.
[Hyperlinks]: https://en.wikipedia.org/wiki/Hyperlink
[linksmith]: https://linksmith.readthedocs.io/
[`linksmith`]: https://pypi.org/project/linksmith/
[Linksmith Usage]: https://linksmith.readthedocs.io/en/latest/usage.html
[rfc]: https://linksmith.readthedocs.io/en/latest/rfc.html
[Sphinx]: https://www.sphinx-doc.org/
[sphobjinv]: https://sphobjinv.readthedocs.io/
Expand Down
19 changes: 19 additions & 0 deletions docs/backlog.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Backlog

## Iteration +1
- Docs: Based on sphobjinv.
- Response caching to buffer subsequent invocations
- Add output flavor, like `--details=compact,full`.
**Full details**, well, should display **full URLs**, ready for
navigational consumption (clicking).
- Improve HTML output. (sticky breadcrumb/navbar, etc.)

## Iteration +2
sphobjinv call delegation ftw.
```
# Shorthand command ...
anansi suggest matplotlib draw
# ... for:
sphobjinv suggest -u https://matplotlib.org/stable/ draw
```
32 changes: 29 additions & 3 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

A program for processing Hyperlinks, Sphinx references, and inventories.

:::::{grid} 1 3 3 3
::::::{grid} 1 3 3 3
:margin: 4 4 0 0
:padding: 0
:gutter: 2
Expand All @@ -26,15 +26,41 @@ Just the proposal, nothing more.
- [](#rfc-community-operations)
::::

:::::
::::{grid-item}
:::{card} Setup
:margin: 0 2 0 0
:link: setup
:link-type: ref
`pip install ...`
:::
:::{card} Usage
:margin: 0 2 0 0
:link: usage
:link-type: ref
`linksmith inventory ...`
:::
::::

::::::


:::{toctree}
:caption: Handbook
:hidden:

rfc
sandbox
setup
usage
:::


:::{toctree}
:caption: Workbench
:hidden:

project
sandbox
backlog
:::


Expand Down
12 changes: 12 additions & 0 deletions docs/setup.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
(setup)=
# Setup

Up until published on PyPI, please install the package that way. Thank you.

```bash
pip install 'linksmith @ git+https://github.com/tech-writing/linksmith.git'
```

:::{note}
This command will need an installation of Git on your system.
:::
48 changes: 48 additions & 0 deletions docs/usage.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
(usage)=
# Usage

Linksmith provides the `linksmith` command line program. It harbours
different subsystems, accessible by using corresponding subcommands,
like `linksmith inventory`.

:::{warning}
Here be dragons. Please note the program is pre-alpha, and a work in
progress, so everything may change while we go.
:::


## Output Formats
Display all the available output formats at a glance.
```shell
linksmith output-formats
```


## Sphinx Inventories
The `linksmith inventory` subsystem supports working with Sphinx inventories,
it is heavily based on `sphinx.ext.intersphinx` and `sphobjinv`.

:::{rubric} Single Inventory
:::
Refer to `objects.inv` on the local filesystem or on a remote location.
```shell
linksmith inventory /path/to/objects.inv
```
```shell
linksmith inventory https://linksmith.readthedocs.io/en/latest/objects.inv
```

```shell
linksmith inventory \
https://linksmith.readthedocs.io/en/latest/objects.inv \
--format=markdown+table
```

:::{rubric} Multiple Inventories
:::
Refer to multiple `objects.inv` resources.
```shell
linksmith inventory \
https://github.com/crate/crate-docs/raw/main/registry/sphinx-inventories.txt \
--format=html+table
```
33 changes: 33 additions & 0 deletions linksmith/cli.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import json

import rich_click as click
from pueblo.util.cli import boot_click

from linksmith.settings import help_config

from .model import OutputFormatRegistry
from .sphinx.cli import cli as inventory_cli


@click.group()
@click.rich_config(help_config=help_config)
@click.option("--verbose", is_flag=True, required=False, help="Turn on logging")
@click.option("--debug", is_flag=True, required=False, help="Turn on logging with debug level")
@click.version_option()
@click.pass_context
def cli(ctx: click.Context, verbose: bool, debug: bool):
return boot_click(ctx, verbose, debug)


@click.command()
@click.rich_config(help_config=help_config)
@click.pass_context
def output_formats(ctx: click.Context): # noqa: ARG001
"""
Display available output format aliases.
"""
print(json.dumps(sorted(OutputFormatRegistry.aliases()), indent=2))


cli.add_command(output_formats, name="output-formats")
cli.add_command(inventory_cli, name="inventory")
70 changes: 70 additions & 0 deletions linksmith/model.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import dataclasses
import io
import typing as t
from enum import auto
from pathlib import Path

from linksmith.util.python import AutoStrEnum


class OutputFormat(AutoStrEnum):
TEXT_INSPECT = auto()
TEXT_PLAIN = auto()
MARKDOWN = auto()
MARKDOWN_TABLE = auto()
RESTRUCTUREDTEXT = auto()
HTML = auto()
HTML_TABLE = auto()
JSON = auto()
YAML = auto()


@dataclasses.dataclass
class OutputFormatRule:
format: OutputFormat
aliases: t.List[str]


class OutputFormatRegistry:
rules = [
OutputFormatRule(format=OutputFormat.TEXT_INSPECT, aliases=["text"]),
OutputFormatRule(format=OutputFormat.TEXT_PLAIN, aliases=["text+plain"]),
OutputFormatRule(format=OutputFormat.MARKDOWN, aliases=["markdown", "md"]),
OutputFormatRule(format=OutputFormat.MARKDOWN_TABLE, aliases=["markdown+table", "md+table"]),
OutputFormatRule(format=OutputFormat.RESTRUCTUREDTEXT, aliases=["restructuredtext", "rst"]),
OutputFormatRule(format=OutputFormat.HTML, aliases=["html", "html+table"]),
OutputFormatRule(format=OutputFormat.JSON, aliases=["json"]),
OutputFormatRule(format=OutputFormat.YAML, aliases=["yaml"]),
]

@classmethod
def resolve(cls, format_: str) -> OutputFormat:
for rule in cls.rules:
if format_ in rule.aliases:
return rule.format
raise NotImplementedError(f"Output format not implemented: {format_}")

@classmethod
def aliases(cls) -> t.List[str]:
data = []
for rule in cls.rules:
data += rule.aliases
return data


class ResourceType(AutoStrEnum):
BUFFER = auto()
PATH = auto()
URL = auto()

@classmethod
def detect(cls, location):
if isinstance(location, io.IOBase):
return cls.BUFFER
path = Path(location)
if path.exists():
return cls.PATH
elif location.startswith("http://") or location.startswith("https://"):
return cls.URL
else:
raise NotImplementedError(f"Resource type not implemented: {location}")
10 changes: 10 additions & 0 deletions linksmith/settings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import rich_click as click

help_config = click.RichHelpConfiguration(
use_markdown=True,
width=100,
style_option="bold white",
style_argument="dim cyan",
style_command="bold yellow",
style_errors_suggestion_command="bold magenta",
)
Empty file added linksmith/sphinx/__init__.py
Empty file.
47 changes: 47 additions & 0 deletions linksmith/sphinx/cli.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import typing as t

import rich_click as click
from click import ClickException

from linksmith.settings import help_config
from linksmith.sphinx.core import inventories_to_text, inventory_to_text


@click.command()
@click.rich_config(help_config=help_config)
@click.argument("infiles", nargs=-1)
@click.option("--format", "format_", type=str, default="text", help="Output format")
@click.pass_context
def cli(ctx: click.Context, infiles: t.List[str], format_: str):
"""
Decode one or multiple intersphinx inventories and output in different formats.
Use `linksmith output-formats` to learn about available output formats.
Examples:
Refer to `objects.inv` on the local filesystem or on a remote location:
```bash
linksmith inventory /path/to/objects.inv --format=html
linksmith inventory https://linksmith.readthedocs.io/en/latest/objects.inv --format=markdown
```
Refer to **multiple** `objects.inv` resources:
```bash
linksmith inventory https://github.com/crate/crate-docs/raw/main/registry/sphinx-inventories.txt
```
"""
if not infiles:
raise click.ClickException("No input")
for infile in infiles:
try:
if infile.endswith(".inv"):
inventory_to_text(infile, format_=format_)
elif infile.endswith(".txt"):
inventories_to_text(infile, format_=format_)
else:
raise NotImplementedError(f"Unknown input file type: {infile}")
except Exception as ex:
if ctx.parent and ctx.parent.params.get("debug"):
raise
raise ClickException(f"{ex.__class__.__name__}: {ex}")
Loading

0 comments on commit cdf8399

Please sign in to comment.