Skip to content

Commit 775eeae

Browse files
committed
Merge branch 'main' into feat/beautify-cli-help
2 parents 7d94a3a + ad65ac8 commit 775eeae

File tree

13 files changed

+526
-116
lines changed

13 files changed

+526
-116
lines changed

.github/workflows/release-package.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ jobs:
3434
if: ${{ needs.release.outputs.previous_version != needs.release.outputs.current_version }}
3535
steps:
3636
- name: Harden the runner (Audit all outbound calls)
37-
uses: step-security/harden-runner@e3f713f2d8f53843e71c69a996d56f51aa9adfb9 # v2.14.1
37+
uses: step-security/harden-runner@a90bcbc6539c36a85cdfeb73f7e2f433735f215b # v2.15.0
3838
with:
3939
egress-policy: audit
4040

@@ -50,7 +50,7 @@ jobs:
5050
# - https://www.andrlik.org/dispatches/til-use-uv-for-build-and-publish-github-actions/
5151
# - https://github.com/astral-sh/trusted-publishing-examples
5252
- name: Set up uv
53-
uses: astral-sh/setup-uv@803947b9bd8e9f986429fa0c5a41c367cd732b41 # v7.2.1
53+
uses: astral-sh/setup-uv@5a095e7a2014a4212f075830d4f7277575a9d098 # v7.3.1
5454

5555
- name: Build distributions
5656
# Builds dists from source and stores them in the dist/ directory.

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,12 @@ individual release will not have many changes within it. Below is a list
1616
of the releases we've made so far, along with what was changed within
1717
each release.
1818

19+
## 0.7.0 (2026-03-02)
20+
21+
### Feat
22+
23+
-`_parse_uri()` (#152)
24+
1925
## 0.6.0 (2026-02-23)
2026

2127
### Feat

docs/design/interface/cli.qmd

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,8 @@ There will be two commands available:
5353

5454
## {{< var wip >}} `build`
5555

56-
The `build` command has the following signature, that is positional as well as keyword-based:
56+
The `build` command has the following signature, that is positional as
57+
well as keyword-based:
5758

5859
``` {.bash filename="Terminal"}
5960
seedcase-flower build [URI] [STYLE] [TEMPLATE-DIR] [OUTPUT-DIR] [VERBOSE]
@@ -210,7 +211,8 @@ override the corresponding settings in the configuration file.
210211

211212
## {{< var planned >}} `view`
212213

213-
The `view` command has the following signature, that is positional as well as keyword-based:
214+
The `view` command has the following signature, that is positional as
215+
well as keyword-based:
214216

215217
``` {.bash filename="Terminal"}
216218
seedcase-flower view [URI] [STYLE]

docs/includes/_contributors.qmd

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
The following people have contributed to this project by submitting pull requests :tada:
22

3-
[\@lwjohnst86](https://github.com/lwjohnst86), [\@signekb](https://github.com/signekb), [\@joelostblom](https://github.com/joelostblom), [\@martonvago](https://github.com/martonvago)
3+
[\@lwjohnst86](https://github.com/lwjohnst86), [\@joelostblom](https://github.com/joelostblom), [\@signekb](https://github.com/signekb), [\@martonvago](https://github.com/martonvago)

justfile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,10 @@ build-readme:
137137
build-contributors:
138138
sh ./tools/get-contributors.sh seedcase-project/seedcase-flower > docs/includes/_contributors.qmd
139139

140+
# Generate updated help-output strings for copy-pasting into test_cli.py
141+
generate-help-strings:
142+
PYTHONPATH=. uv run python tools/generate-help-strings.py
143+
140144
# Preview the documentation website with automatic reload on changes
141145
preview-website: build-quartodoc
142146
uv run quarto preview --execute

pyproject.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "seedcase-flower"
3-
version = "0.6.0"
3+
version = "0.7.0"
44
# TODO: Add a description of the package.
55
description = ""
66
authors = [
@@ -56,6 +56,7 @@ dev = [
5656
"pre-commit>=4.5.0",
5757
"pytest>=9.0.1",
5858
"pytest-cov>=7.0.0",
59+
"pytest-mock>=3.14.0",
5960
"quartodoc>=0.11.1",
6061
"ruff>=0.14.7",
6162
"types-tabulate>=0.9.0.20241207",

src/seedcase_flower/cli.py

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,14 @@
99
# from seedcase_flower.config import Config as FlowerConfig
1010
from seedcase_flower.internals import (
1111
BuildStyle,
12+
Uri,
1213
_format_param_help,
14+
_parse_uri,
1315
_read_properties,
14-
_resolve_uri,
1516
)
1617

1718
app = App(
19+
name="seedcase-flower",
1820
help="Flower generates human-readable documentation from Data Packages.",
1921
help_formatter=DefaultFormatter(
2022
column_specs=(
@@ -31,7 +33,7 @@
3133
),
3234
config.Toml(
3335
"pyproject.toml",
34-
root_keys="tool.seedcase-flower",
36+
root_keys=["tool", "seedcase-flower"],
3537
search_parents=True,
3638
use_commands_as_keys=False,
3739
),
@@ -50,7 +52,12 @@ def build(
5052
"""Build human-readable documentation from a `datapackage.json` file.
5153
5254
Args:
53-
uri: The URI to a datapackage.json file.
55+
uri: The path to a local `datapackage.json` file or its parent folder.
56+
Can also be an `https:` URL to a remote `datapackage.json` or a
57+
`github:` / `gh:` URI pointing to a repo with a `datapackage.json`
58+
in the repo root (in the format `gh:org/repo`, which can also include
59+
reference to a tag or branch, such as `gh:org/repo@main` or
60+
`gh:org/repo@1.0.1).
5461
style: The style used to structure the output. If a template directory
5562
is given, this parameter will be ignored.
5663
template_dir: The directory that contains the Jinja template
@@ -59,8 +66,8 @@ def build(
5966
output_dir: The directory to save the generated files in.
6067
verbose: If True, prints additional information to the console.
6168
"""
62-
path: Path = _resolve_uri(uri)
63-
properties: dict[str, Any] = _read_properties(path)
69+
uri: Uri = _parse_uri(uri) # type: ignore # TODO fix in read_prop PR
70+
properties: dict[str, Any] = _read_properties(uri) # type: ignore # TODO fix in read_prop PR
6471

6572
# One item per section, rendered from template.
6673
# Internally uses Jinja2 to render templates with metadata, which

src/seedcase_flower/internals.py

Lines changed: 58 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
"""Helper functions for private use."""
22

33
import json
4+
from dataclasses import dataclass
45
from enum import Enum
56
from itertools import repeat
67
from pathlib import Path
78
from typing import Any
9+
from urllib import parse
810

911
from cyclopts.annotations import get_hint_name
1012
from cyclopts.help import HelpEntry
@@ -18,18 +20,54 @@ class BuildStyle(Enum):
1820
quarto_resource_tables = "quarto_resource_tables"
1921

2022

21-
# Output maybe str? Path?
22-
# Use `match` inside for strictness on URI types? Or use a library for URI parsing?
23-
# TODO Extend to parse strings and return either URL or Path
24-
def _resolve_uri(uri: str) -> Path:
25-
return Path(uri)
23+
@dataclass(frozen=True)
24+
class Uri:
25+
"""A parsed URI with its normalised value and locality flag."""
2626

27+
value: str
28+
local: bool
2729

28-
# TODO Extend to also read properties from URLs
29-
def _read_properties(path: Path) -> dict[str, Any]:
30-
with open(path) as properties_file:
31-
datapackage: dict[str, Any] = json.load(properties_file)
32-
return datapackage
30+
31+
def _parse_uri(uri: str) -> Uri:
32+
split_uri = parse.urlsplit(uri)
33+
if split_uri.scheme == "":
34+
split_uri = split_uri._replace(scheme="file")
35+
match split_uri.scheme:
36+
case "file":
37+
return _convert_to_file_uri(split_uri)
38+
case "https":
39+
return _convert_to_https_uri(split_uri)
40+
case "gh" | "github":
41+
return _convert_to_github_uri(split_uri)
42+
case _:
43+
raise ValueError(
44+
"The uri must be either a path to an existing file/folder "
45+
"or a URI with one of the following URI prefixes: "
46+
"`file:`, `https:`, `gh:`, `github:`"
47+
)
48+
49+
50+
def _convert_to_file_uri(split_file_uri: parse.SplitResult) -> Uri:
51+
path = Path(split_file_uri.path).resolve()
52+
if path.is_dir():
53+
path /= "datapackage.json"
54+
split_file_uri = split_file_uri._replace(path=path.as_posix())
55+
return Uri(value=split_file_uri.geturl(), local=True)
56+
57+
58+
def _convert_to_https_uri(split_https_uri: parse.SplitResult) -> Uri:
59+
return Uri(value=split_https_uri.geturl(), local=False)
60+
61+
62+
def _convert_to_github_uri(split_gh_uri: parse.SplitResult) -> Uri:
63+
return Uri(
64+
value=split_gh_uri._replace(
65+
scheme="https",
66+
netloc="raw.githubusercontent.com",
67+
path=f"/{split_gh_uri.path}/refs/heads/main/datapackage.json",
68+
).geturl(),
69+
local=False,
70+
)
3371

3472

3573
def _format_param_help(entry: HelpEntry) -> str:
@@ -51,3 +89,13 @@ def _add_highlight_syntax(name: str, entry_type: type | None) -> str:
5189
if get_hint_name(entry_type) == "bool":
5290
formatted_name = ""
5391
return formatted_name
92+
93+
94+
def _read_properties(uri: Uri) -> dict[str, Any]:
95+
if uri.local:
96+
path = Path(parse.urlsplit(uri.value).path)
97+
with open(path) as properties_file:
98+
return json.load(properties_file) # type: ignore # TODO fix in read_prop PR
99+
else:
100+
# TODO read from remote file
101+
return {"placeholder": uri.value}

0 commit comments

Comments
 (0)