Skip to content

Commit 6edd261

Browse files
authored
[MAINT] Refactor and add test suite (#1)
* update version and name in pyproject * commit old tests * remove old tests * add tests for run_argdump * add clickdump as dependency * add tests for run_clickdump.py * add tests for update_descriptor.py * refactor into a create_descriptor.sh script and add tests * add pre-commit * lint * rename PR branch * add CI workflow for tests * rename test workflow
1 parent ff2c86c commit 6edd261

23 files changed

Lines changed: 1022 additions & 1785 deletions

‎.github/workflows/run_tests.yml‎

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
name: Run tests
2+
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
branches: [main]
8+
9+
jobs:
10+
test:
11+
runs-on: ubuntu-latest
12+
strategy:
13+
matrix:
14+
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"]
15+
16+
steps:
17+
- uses: actions/checkout@v6
18+
19+
- name: Set up Python ${{ matrix.python-version }}
20+
uses: actions/setup-python@v6
21+
with:
22+
python-version: ${{ matrix.python-version }}
23+
24+
- name: Install dependencies
25+
run: |
26+
pip install --upgrade pip
27+
pip install . --group test
28+
29+
- name: Run tests
30+
run: pytest

‎.pre-commit-config.yaml‎

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
---
2+
# See https://pre-commit.com for more information
3+
# See https://pre-commit.com/hooks.html for more hooks
4+
5+
# To use to configure behavior of pre-commit ci
6+
# ci:
7+
# skip: [hadolint-docker]
8+
9+
repos:
10+
- repo: https://github.com/pre-commit/pre-commit-hooks
11+
rev: v6.0.0
12+
hooks:
13+
- id: check-ast
14+
- id: check-case-conflict
15+
- id: check-json
16+
- id: check-merge-conflict
17+
- id: check-yaml
18+
- id: end-of-file-fixer
19+
- id: trailing-whitespace
20+
- id: check-toml
21+
- repo: https://github.com/astral-sh/ruff-pre-commit
22+
# Ruff version.
23+
rev: v0.15.22
24+
hooks:
25+
# Run the linter.
26+
- id: ruff-check
27+
args: [ --fix ]
28+
# Run the formatter.
29+
- id: ruff-format
30+
- repo: https://github.com/codespell-project/codespell
31+
rev: v2.4.2
32+
hooks:
33+
- id: codespell
34+
args: [--toml=pyproject.toml]
35+
additional_dependencies: [tomli]

‎__init__.py‎

Whitespace-only changes.

‎action.yml‎

Lines changed: 3 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -70,63 +70,15 @@ runs:
7070
7171
pip install -U pip
7272
73-
echo "::endgroup::"
74-
75-
echo "::group::Creating the dump file"
7673
if [[ "$PARSER_TYPE" == "click" ]]; then
77-
78-
# install clickdump
7974
pip install clickdump
80-
81-
# create dump from click command
82-
CLICK_ARGS=(--output "$DUMP_FILE")
83-
if [[ -n "$CLICK_PROG_NAME" ]]; then
84-
CLICK_ARGS+=(--prog "$CLICK_PROG_NAME")
85-
fi
86-
if [[ -n "$CLICK_PARENT_LOCATION" ]]; then
87-
CLICK_ARGS+=(--parent "$CLICK_PARENT_LOCATION")
88-
fi
89-
python ./scripts/run_clickdump.py "${CLICK_ARGS[@]}" $PARSER_LOCATION
9075
else
91-
92-
# install argdump
9376
pip install argdump
94-
95-
# create dump from argparse
96-
python ./scripts/run_argdump.py --output $DUMP_FILE $PARSER_LOCATION
9777
fi
98-
cat $DUMP_FILE
99-
echo "::endgroup::"
100-
101-
echo "::group::Creating Boutiques descriptor from dump file"
102-
npx -y --package @styx-api/cli styx build $DUMP_FILE --out styx-out --backend boutiques
103-
echo "::endgroup::"
104-
105-
echo "::group::Moving descriptor to output path"
106-
# move descriptor to output path
107-
mkdir -p $(dirname $OUTPUT_PATH)
108-
mv ./styx-out/boutiques/descriptor.json $OUTPUT_PATH
109-
echo "::endgroup::"
11078
111-
echo "::group::Applying user-specified updates"
112-
UPDATES_DESCRIPTOR_ARGS=()
113-
if [[ "${EXCLUDE_VERSION}" == "true" ]]; then
114-
UPDATES_DESCRIPTOR_ARGS+=(--remove-version)
115-
fi
116-
if [[ -n "${UPDATES_FILE}" ]]; then
117-
UPDATES_DESCRIPTOR_ARGS+=(--updates-file ${{ github.workspace }}/${UPDATES_FILE})
118-
fi
119-
if [[ -n "${UPDATES_STR}" ]]; then
120-
UPDATES_DESCRIPTOR_ARGS+=(--updates-str "${UPDATES_STR}")
121-
fi
122-
if [[ "${#UPDATES_DESCRIPTOR_ARGS[@]}" -gt 0 ]]; then
123-
python ./scripts/update_descriptor.py --descriptor $OUTPUT_PATH "${UPDATES_DESCRIPTOR_ARGS[@]}"
124-
fi
12579
echo "::endgroup::"
12680
127-
echo "::group::Final descriptor"
128-
cat $OUTPUT_PATH
129-
echo "::endgroup::"
81+
bash ./scripts/create_descriptor.sh
13082
env:
13183
PARSER_TYPE: ${{ inputs.parser-type }}
13284
PARSER_LOCATION: ${{ inputs.parser-location }}
@@ -135,7 +87,7 @@ runs:
13587
CLICK_PROG_NAME: ${{ inputs.click-prog-name }}
13688
CLICK_PARENT_LOCATION: ${{ inputs.click-parent-location }}
13789
EXCLUDE_VERSION: ${{ inputs.exclude-version }}
138-
UPDATES_FILE: ${{ inputs.updates-file }}
90+
UPDATES_FILE: ${{ inputs.updates-file && format('{0}/{1}', github.workspace, inputs.updates-file) || '' }}
13991
UPDATES_STR: ${{ inputs.updates-str }}
14092
shell: bash
14193

@@ -155,7 +107,7 @@ runs:
155107
SANITIZED="${{ inputs.parser-location }}"
156108
SANITIZED="${SANITIZED//./__}"
157109
SANITIZED="${SANITIZED//:/-}"
158-
echo "branch=argparse-to-boutiques/${SANITIZED}" >> "$GITHUB_OUTPUT"
110+
echo "branch=python-cli-to-boutiques/${SANITIZED}" >> "$GITHUB_OUTPUT"
159111
shell: bash
160112

161113
- name: Open pull request

‎conftest.py‎

Lines changed: 0 additions & 4 deletions
This file was deleted.

‎pyproject.toml‎

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,24 @@
11
[project]
2-
name = "argparse-to-boutiques"
3-
version = "0.1.0"
2+
name = "python-cli-to-boutiques"
3+
version = "1.1.0"
44
description = "Add your description here"
55
readme = "README.md"
66
requires-python = ">=3.9"
77
dependencies = [
88
"argdump>=0.1.2",
9+
"clickdump>=0.1.0",
910
]
1011

1112
[project.optional-dependencies]
1213
validation = [
1314
"boutiques>=0.5.33",
1415
]
1516

17+
[tool.pytest.ini_options]
18+
pythonpath = ["scripts", "tests"]
19+
1620
[dependency-groups]
17-
dev = [
21+
test = [
1822
"pytest>=8.4.2",
1923
]
24+
dev = ["pre-commit", {include-group = "test"}]

‎scripts/create_descriptor.sh‎

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
# -- Create dump file --
5+
if [[ "$PARSER_TYPE" == "click" ]]; then
6+
CLICK_ARGS=(--output "$DUMP_FILE")
7+
if [[ -n "$CLICK_PROG_NAME" ]]; then
8+
CLICK_ARGS+=(--prog "$CLICK_PROG_NAME")
9+
fi
10+
if [[ -n "$CLICK_PARENT_LOCATION" ]]; then
11+
CLICK_ARGS+=(--parent "$CLICK_PARENT_LOCATION")
12+
fi
13+
python ./scripts/run_clickdump.py "${CLICK_ARGS[@]}" "$PARSER_LOCATION"
14+
elif [[ "$PARSER_TYPE" == "argparse" ]]; then
15+
python ./scripts/run_argdump.py --output "$DUMP_FILE" "$PARSER_LOCATION"
16+
else
17+
echo "Error: PARSER_TYPE must be 'argparse' or 'click', got '$PARSER_TYPE'" >&2
18+
exit 1
19+
fi
20+
cat "$DUMP_FILE"
21+
22+
# -- Build Boutiques descriptor --
23+
npx -y --package @styx-api/cli styx build "$DUMP_FILE" --out styx-out --backend boutiques
24+
25+
# -- Move to output path --
26+
mkdir -p "$(dirname "$OUTPUT_PATH")"
27+
mv ./styx-out/boutiques/descriptor.json "$OUTPUT_PATH"
28+
29+
# -- Apply updates --
30+
UPDATES_DESCRIPTOR_ARGS=()
31+
if [[ "${EXCLUDE_VERSION}" == "true" ]]; then
32+
UPDATES_DESCRIPTOR_ARGS+=(--remove-version)
33+
fi
34+
if [[ -n "${UPDATES_FILE}" ]]; then
35+
UPDATES_DESCRIPTOR_ARGS+=(--updates-file "$UPDATES_FILE")
36+
fi
37+
if [[ -n "${UPDATES_STR}" ]]; then
38+
UPDATES_DESCRIPTOR_ARGS+=(--updates-str "${UPDATES_STR}")
39+
fi
40+
if [[ "${#UPDATES_DESCRIPTOR_ARGS[@]}" -gt 0 ]]; then
41+
python ./scripts/update_descriptor.py --descriptor "$OUTPUT_PATH" "${UPDATES_DESCRIPTOR_ARGS[@]}"
42+
fi
43+
44+
# -- Print final descriptor --
45+
cat "$OUTPUT_PATH"

‎scripts/update_descriptor.py‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
#!/usr/bin/env python
22
"""Update a Boutiques descriptor JSON using dot/bracket path notation."""
33

4+
from __future__ import annotations
5+
46
import argparse
57
import json
68
import re
@@ -25,11 +27,15 @@ def set_at_path(obj: dict | list, path_parts: list[str | int], value) -> None:
2527
"""Navigate *path_parts* through *obj* and set *value* at the leaf."""
2628
for part in path_parts[:-1]:
2729
if isinstance(part, int):
30+
if not isinstance(obj, list):
31+
raise TypeError(f"Expected list, got {type(obj).__name__}")
2832
obj = obj[part]
2933
else:
3034
if part not in obj:
3135
obj[part] = {}
3236
obj = obj[part]
37+
if isinstance(path_parts[-1], int) and not isinstance(obj, list):
38+
raise TypeError(f"Expected list, got {type(obj).__name__}")
3339
obj[path_parts[-1]] = value
3440

3541

‎tests/__init__.py‎

Whitespace-only changes.

‎tests/acceptance/__init__.py‎

Whitespace-only changes.

0 commit comments

Comments
 (0)