feat: scaffold publishable sdk foundation - #7
Conversation
There was a problem hiding this comment.
Code Review
This pull request establishes the initial scaffold for the Convert Python SDK, including project metadata, build configuration via Hatchling, and a stable public API boundary. The feedback focuses on improving the project's maintainability and portability: specifically, adopting dynamic versioning to avoid duplication, removing redundant build configurations in pyproject.toml, ensuring tests do not hardcode version strings, and making local development instructions in the README more portable by removing environment-specific cache overrides.
| @@ -0,0 +1,36 @@ | |||
| [project] | |||
| name = "convert-python-sdk" | |||
| version = "0.1.0" | |||
There was a problem hiding this comment.
The package version is currently hardcoded in both pyproject.toml and src/convert_sdk/version.py. To avoid duplication and potential inconsistencies, it is recommended to use Hatch's dynamic versioning, treating the source code as the single source of truth.
| version = "0.1.0" | |
| dynamic = ["version"] |
| [tool.pytest.ini_options] | ||
| testpaths = ["tests"] |
There was a problem hiding this comment.
When using dynamic versioning, you need to specify the source of the version metadata for Hatch. This ensures that the version defined in src/convert_sdk/version.py is used as the package version.
| [tool.pytest.ini_options] | |
| testpaths = ["tests"] | |
| [tool.pytest.ini_options] | |
| testpaths = ["tests"] | |
| [tool.hatch.version] | |
| path = "src/convert_sdk/version.py" |
| from importlib import resources | ||
|
|
||
| from convert_sdk import Context, Core, __version__ | ||
|
|
||
|
|
||
| def test_public_import_boundary_is_stable() -> None: | ||
| assert Core.__module__ == "convert_sdk.core" | ||
| assert Context.__module__ == "convert_sdk.context" | ||
| assert __version__ == "0.1.0" |
There was a problem hiding this comment.
Avoid hardcoding the version string in tests. It is more robust to compare the exported __version__ against the package metadata to ensure they are in sync.
| from importlib import resources | |
| from convert_sdk import Context, Core, __version__ | |
| def test_public_import_boundary_is_stable() -> None: | |
| assert Core.__module__ == "convert_sdk.core" | |
| assert Context.__module__ == "convert_sdk.context" | |
| assert __version__ == "0.1.0" | |
| from importlib import metadata, resources | |
| from convert_sdk import Context, Core, __version__ | |
| def test_public_import_boundary_is_stable() -> None: | |
| assert Core.__module__ == "convert_sdk.core" | |
| assert Context.__module__ == "convert_sdk.context" | |
| assert __version__ == metadata.version("convert-python-sdk") |
| UV_CACHE_DIR=/tmp/uv-cache uv sync --group dev | ||
| UV_CACHE_DIR=/tmp/uv-cache uv run pytest | ||
| UV_CACHE_DIR=/tmp/uv-cache uv build |
There was a problem hiding this comment.
Hardcoding UV_CACHE_DIR=/tmp/uv-cache is not portable across different operating systems (e.g., Windows) and may fail if the directory is not writable. uv automatically manages its cache in a platform-appropriate location, so this override is generally unnecessary for local development instructions.
| UV_CACHE_DIR=/tmp/uv-cache uv sync --group dev | |
| UV_CACHE_DIR=/tmp/uv-cache uv run pytest | |
| UV_CACHE_DIR=/tmp/uv-cache uv build | |
| uv sync --group dev | |
| uv run pytest | |
| uv build |
| [tool.hatch.build.targets.wheel.force-include] | ||
| "src/convert_sdk/py.typed" = "convert_sdk/py.typed" |
There was a problem hiding this comment.
All eight open Dependabot alerts on this repo sit in `yarn.lock` — the dev-only semantic-release tooling. None of them reach the published wheel/sdist (httpx is still the only runtime dependency). Three of the four highs were already reachable inside the existing semver ranges; the fourth (sigstore GHSA-52v5-jr5w-gjxr, `certificateOIDs` verification constraints silently dropped) needed sigstore >= 4.1.1, which only arrives through a major bump: semantic-release 24.2.9 -> 25.0.9 @semantic-release/npm 12.0.2 -> 13.1.5 npm 10.9.8 -> 11.19.0 libnpmpublish 10 -> 11.2.0 }-> sigstore ^3 -> ^4 (4.1.1) pacote 19 -> 21.5.1 } @sigstore/core 2.0.0 -> 3.2.1 tar 7.5.16 -> 7.5.22 `@semantic-release/github` moves to ^12 to match what semantic-release 25 depends on — leaving it at ^11 would hoist the older copy to the project root and shadow the one core resolves. Resolved (4 high, 4 medium — no criticals were open): #14 high ip-address 10.2.0 -> 10.4.0 (needs >= 10.3.1) #9 high brace-expansion 2.1.1 -> 5.0.9 (needs >= 2.1.2) #7 high js-yaml 4.2.0 -> 4.3.1 (needs >= 4.3.0) #2 high sigstore 3.1.0 -> 4.1.1 (needs >= 4.1.1) #13 medium ip-address (same bump as #14) #12 medium ip-address (same bump as #14) #4 medium tar 7.5.16 -> 7.5.22 (needs >= 7.5.18) #1 medium @sigstore/core 2.0.0 -> 3.2.1 (needs >= 3.2.1) Node: semantic-release 25 requires ^22.14.0 || >= 24.10.0. release.yml installs `lts/*`, currently Node 24.19.0 — satisfied, and every future LTS line stays above the floor. Verified: `yarn install --immutable` (what release.yml runs) passes against the regenerated lockfile with the lockfile format unchanged (__metadata version 10), and `yarn npm audit --all --recursive` reports no suggestions. A `semantic-release --dry-run` against this branch loads all four configured plugins, passes verifyConditions for both exec and github (GitHub authentication + push permission), and analyzes commits to "no release" — correct, since `chore` is a non-releasing type in release.config.mjs. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Summary
Implements Story 1.1, "Scaffold the Publishable SDK Foundation," from the Python SDK planning set in epics.md and the execution artifact in 1-1-scaffold-the-publishable-sdk-foundation.md.
This PR creates the initial publishable package scaffold for the Python SDK and freezes the package boundary decisions for later stories:
convert-python-sdkconvert_sdkCore,Context, and__version__What Changed
uvlibrary project withhatchling>=3.9src/convert_sdk/Core,Context, and version metadatapy.typedso the package is typed for consumersuv.lockWhy
The planning artifacts explicitly call for Story 1.1 to freeze the canonical package name, import path, and public entry point before implementation accelerates. This PR establishes that baseline so later stories can add initialization, config loading, evaluation, and tracking behavior without renaming the package surface.
Validation
uv sync --group devuv run pytest -p no:cacheprovideruv buildValidation results:
convert_sdk/*andpy.typedNotes
This PR is intentionally scaffold-only. It does not implement SDK initialization, config loading, evaluation, tracking, or persistence yet. Those remain in follow-up stories from the same planning set.