Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Accept linksmith inventory without INFILES argument #6

Merged
merged 1 commit into from
Apr 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,5 @@
- Implement `linksmith anansi suggest`, also available as `anansi`,
to easily suggest terms of a few curated community projects.
Thanks, @bskinn.
- Accept `linksmith inventory` without `INFILES` argument, implementing
auto-discovery of `objects.inv` in local current working directory.
8 changes: 8 additions & 0 deletions docs/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,14 @@ linksmith inventory \
```


:::{rubric} Auto-Discovery
:::
Discover `objects.inv` in working directory.
```shell
linksmith inventory
```


## Anansi

Suggest references from curated intersphinx inventories.
Expand Down
31 changes: 15 additions & 16 deletions linksmith/sphinx/cli.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import logging
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
from linksmith.sphinx.core import dump_inventory_universal

logger = logging.getLogger(__name__)


@click.command()
Expand All @@ -30,18 +32,15 @@ def cli(ctx: click.Context, infiles: t.List[str], format_: str):
```bash
linksmith inventory https://github.com/crate/crate-docs/raw/main/registry/sphinx-inventories.txt
```

Discover `objects.inv` in working directory:
```bash
linksmith inventory
```
"""
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}")
try:
dump_inventory_universal(infiles, format_)
except Exception as ex:
if ctx.parent and ctx.parent.params.get("debug"):
raise
raise click.ClickException(str(ex))
22 changes: 22 additions & 0 deletions linksmith/sphinx/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,32 @@

from linksmith.model import OutputFormat, OutputFormatRegistry, ResourceType
from linksmith.sphinx.inventory import InventoryFormatter
from linksmith.sphinx.util import LocalObjectsInv

logger = logging.getLogger(__name__)


def dump_inventory_universal(infiles: t.List[str], format_: str = "text"):
"""
Decode one or multiple intersphinx inventories and output in different formats.
"""
if not infiles:
logger.info("No inventory specified, entering auto-discovery mode")
try:
local_objects_inv = LocalObjectsInv.discover(Path.cwd())
logger.info(f"Auto-discovered objects.inv: {local_objects_inv}")
infiles = [str(local_objects_inv)]
except Exception as ex:
raise FileNotFoundError(f"No inventory specified, and none discovered: {ex}")
for infile in infiles:
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}")


def inventory_to_text(url: str, format_: str = "text"):
"""
Display intersphinx inventory for individual project, using selected output format.
Expand Down
29 changes: 29 additions & 0 deletions linksmith/sphinx/util.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
from pathlib import Path


class LocalObjectsInv:
"""
Support discovering an `objects.inv` in current working directory.
"""

# Candidate paths where to look for `objects.inv` in current working directory.
objects_inv_candidates = [
Path("objects.inv"),
Path("doc") / "_build" / "objects.inv",
Path("docs") / "_build" / "objects.inv",
Path("doc") / "_build" / "html" / "objects.inv",
Path("docs") / "_build" / "html" / "objects.inv",
Path("doc") / "build" / "html" / "objects.inv",
Path("docs") / "build" / "html" / "objects.inv",
]

@classmethod
def discover(cls, project_root: Path) -> Path:
"""
Return `Path` instance of discovered `objects.inv` in current working directory.
"""
for candidate in cls.objects_inv_candidates:
path = project_root / candidate
if path.exists():
return path
raise FileNotFoundError("No objects.inv found in working directory")
13 changes: 0 additions & 13 deletions tests/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,19 +28,6 @@ def test_cli_output_formats(cli_runner):
assert result.exit_code == 0


def test_cli_inventory_no_input(cli_runner):
"""
CLI test: Invoke `linksmith inventory`.
"""
result = cli_runner.invoke(
cli,
args="inventory",
catch_exceptions=False,
)
assert result.exit_code == 1
assert "No input" in result.output


def test_cli_inventory_unknown_input(cli_runner):
"""
CLI test: Invoke `linksmith inventory example.foo`.
Expand Down
24 changes: 24 additions & 0 deletions tests/test_inventory.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
from unittest.mock import patch

import pytest

from linksmith.sphinx.core import dump_inventory_universal
from linksmith.sphinx.inventory import InventoryFormatter, InventoryManager
from tests.config import OBJECTS_INV_PATH

Expand All @@ -25,3 +28,24 @@ def test_inventory_manager_unknown():
with pytest.raises(NotImplementedError) as ex:
invman.soi_factory()
assert ex.match("Resource type not implemented: foo")


def test_cli_inventory_autodiscover(capsys):
"""
Verify local `objects.inv` auto-discovery works.
"""
with patch("linksmith.sphinx.util.LocalObjectsInv.objects_inv_candidates", ["tests/assets/linksmith.inv"]):
dump_inventory_universal([])
out, err = capsys.readouterr()
assert "std:doc" in out
assert "std:label" in out


def test_inventory_no_input():
"""
Exercise a failing auto-discovery, where absolutely no input files can be determined.
"""
with patch("linksmith.sphinx.util.LocalObjectsInv.objects_inv_candidates", []):
with pytest.raises(FileNotFoundError) as ex:
dump_inventory_universal([])
ex.match("No inventory specified, and none discovered: No objects.inv found in working directory")