pep723-to-wheel 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pep723_to_wheel-0.1.0/.github/workflows/cd.yml +58 -0
- pep723_to_wheel-0.1.0/.github/workflows/cd_version.py +94 -0
- pep723_to_wheel-0.1.0/.github/workflows/ci.yml +39 -0
- pep723_to_wheel-0.1.0/.github/workflows/ruff-autofix.yml +35 -0
- pep723_to_wheel-0.1.0/.gitignore +65 -0
- pep723_to_wheel-0.1.0/AGENTS.md +28 -0
- pep723_to_wheel-0.1.0/LICENSE +21 -0
- pep723_to_wheel-0.1.0/Makefile +12 -0
- pep723_to_wheel-0.1.0/PKG-INFO +73 -0
- pep723_to_wheel-0.1.0/README.md +62 -0
- pep723_to_wheel-0.1.0/pyproject.toml +41 -0
- pep723_to_wheel-0.1.0/src/pep723_to_wheel/__init__.py +5 -0
- pep723_to_wheel-0.1.0/src/pep723_to_wheel/cli.py +45 -0
- pep723_to_wheel-0.1.0/src/pep723_to_wheel/core.py +352 -0
- pep723_to_wheel-0.1.0/tests/examples/marimo_notebook.py +57 -0
- pep723_to_wheel-0.1.0/tests/test_cd_version.py +91 -0
- pep723_to_wheel-0.1.0/tests/test_cli.py +43 -0
- pep723_to_wheel-0.1.0/tests/test_core.py +211 -0
- pep723_to_wheel-0.1.0/tests/test_core_additional.py +247 -0
- pep723_to_wheel-0.1.0/uv.lock +430 -0
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
name: CD
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
|
|
7
|
+
permissions:
|
|
8
|
+
contents: write
|
|
9
|
+
id-token: write
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
publish:
|
|
13
|
+
if: github.actor != 'github-actions[bot]'
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v4
|
|
17
|
+
with:
|
|
18
|
+
fetch-depth: 0
|
|
19
|
+
- uses: actions/setup-python@v5
|
|
20
|
+
with:
|
|
21
|
+
python-version: "3.12"
|
|
22
|
+
- uses: astral-sh/setup-uv@v6
|
|
23
|
+
- name: Install dependencies
|
|
24
|
+
run: uv sync --frozen
|
|
25
|
+
- name: Determine version
|
|
26
|
+
id: version
|
|
27
|
+
run: |
|
|
28
|
+
LATEST_TAG="$(git tag --list 'v*' --sort=-v:refname | head -n1)"
|
|
29
|
+
export LATEST_TAG
|
|
30
|
+
uv run python .github/workflows/cd_version.py
|
|
31
|
+
- name: Commit version bump
|
|
32
|
+
run: |
|
|
33
|
+
if git status --porcelain | grep -q "pyproject.toml"; then
|
|
34
|
+
git config user.name "github-actions[bot]"
|
|
35
|
+
git config user.email "github-actions[bot]@users.noreply.github.com"
|
|
36
|
+
git add pyproject.toml
|
|
37
|
+
git commit -m "chore: bump version to v${{ steps.version.outputs.version }}"
|
|
38
|
+
git push
|
|
39
|
+
fi
|
|
40
|
+
- name: Tag release
|
|
41
|
+
run: |
|
|
42
|
+
VERSION="v${{ steps.version.outputs.version }}"
|
|
43
|
+
if git tag -l "$VERSION" | grep -q "$VERSION"; then
|
|
44
|
+
echo "Tag $VERSION already exists."
|
|
45
|
+
else
|
|
46
|
+
git tag "$VERSION"
|
|
47
|
+
git push origin "$VERSION"
|
|
48
|
+
fi
|
|
49
|
+
- name: Build
|
|
50
|
+
run: uv build
|
|
51
|
+
- name: Publish to PyPI (Trusted Publishing)
|
|
52
|
+
run: uv publish
|
|
53
|
+
- name: Create GitHub release
|
|
54
|
+
uses: softprops/action-gh-release@v2
|
|
55
|
+
with:
|
|
56
|
+
files: dist/*.whl
|
|
57
|
+
tag_name: v${{ steps.version.outputs.version }}
|
|
58
|
+
name: v${{ steps.version.outputs.version }}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import pathlib
|
|
5
|
+
import re
|
|
6
|
+
import tomllib
|
|
7
|
+
import tomli_w
|
|
8
|
+
from dataclasses import dataclass
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
# Strict semantic versioning pattern: MAJOR.MINOR.PATCH
|
|
12
|
+
# - MAJOR is either 0 (pre-1.0 semantics) or a non-zero integer without leading zeros.
|
|
13
|
+
# - MINOR and PATCH are non-negative integers.
|
|
14
|
+
# - Pre-release identifiers (e.g. "-alpha") and build metadata (e.g. "+build.1") are
|
|
15
|
+
# intentionally not supported, because this script only needs to handle final release
|
|
16
|
+
# versions when resolving and bumping versions.
|
|
17
|
+
VERSION_PATTERN = re.compile(r"^(?P<major>0|[1-9]\d*)\.(?P<minor>\d+)\.(?P<patch>\d+)$")
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@dataclass(frozen=True)
|
|
21
|
+
class Version:
|
|
22
|
+
major: int
|
|
23
|
+
minor: int
|
|
24
|
+
patch: int
|
|
25
|
+
|
|
26
|
+
@classmethod
|
|
27
|
+
def parse(cls, value: str) -> "Version | None":
|
|
28
|
+
match = VERSION_PATTERN.match(value)
|
|
29
|
+
if not match:
|
|
30
|
+
return None
|
|
31
|
+
return cls(
|
|
32
|
+
major=int(match.group("major")),
|
|
33
|
+
minor=int(match.group("minor")),
|
|
34
|
+
patch=int(match.group("patch")),
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
def bump_patch(self) -> "Version":
|
|
38
|
+
return Version(self.major, self.minor, self.patch + 1)
|
|
39
|
+
|
|
40
|
+
def __str__(self) -> str:
|
|
41
|
+
return f"{self.major}.{self.minor}.{self.patch}"
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def read_current_version(pyproject_path: pathlib.Path) -> Version:
|
|
45
|
+
data = tomllib.loads(pyproject_path.read_text())
|
|
46
|
+
current = data["project"]["version"]
|
|
47
|
+
parsed = Version.parse(current)
|
|
48
|
+
if not parsed:
|
|
49
|
+
raise ValueError(f"Invalid project.version in {pyproject_path}: {current}")
|
|
50
|
+
return parsed
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def write_version(pyproject_path: pathlib.Path, version: Version) -> None:
|
|
54
|
+
data = tomllib.loads(pyproject_path.read_text())
|
|
55
|
+
project = data.get("project")
|
|
56
|
+
if not isinstance(project, dict):
|
|
57
|
+
raise ValueError(f"Missing [project] table in {pyproject_path}")
|
|
58
|
+
if "version" not in project:
|
|
59
|
+
raise ValueError(f"Missing project.version in {pyproject_path}")
|
|
60
|
+
project["version"] = str(version)
|
|
61
|
+
pyproject_path.write_text(tomli_w.dumps(data))
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def resolve_version(current: Version, latest_tag: str | None) -> Version:
|
|
65
|
+
if not latest_tag:
|
|
66
|
+
return current
|
|
67
|
+
parsed = Version.parse(latest_tag.lstrip("v"))
|
|
68
|
+
if not parsed:
|
|
69
|
+
return current
|
|
70
|
+
if current.major == parsed.major and current.minor == parsed.minor:
|
|
71
|
+
new_patch = max(current.patch, parsed.patch) + 1
|
|
72
|
+
return Version(current.major, current.minor, new_patch)
|
|
73
|
+
return current
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def main() -> None:
|
|
77
|
+
pyproject_path = pathlib.Path("pyproject.toml")
|
|
78
|
+
current = read_current_version(pyproject_path)
|
|
79
|
+
new_version = resolve_version(current, os.environ.get("LATEST_TAG"))
|
|
80
|
+
|
|
81
|
+
if new_version != current:
|
|
82
|
+
write_version(pyproject_path, new_version)
|
|
83
|
+
|
|
84
|
+
output_path = os.environ.get("GITHUB_OUTPUT")
|
|
85
|
+
if output_path:
|
|
86
|
+
with pathlib.Path(output_path).open("a") as handle:
|
|
87
|
+
handle.write(f"version={new_version}\n")
|
|
88
|
+
else:
|
|
89
|
+
raise RuntimeError("GITHUB_OUTPUT is not set")
|
|
90
|
+
print(f"Resolved version: {new_version}")
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
if __name__ == "__main__":
|
|
94
|
+
main()
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
tests:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
strategy:
|
|
12
|
+
fail-fast: false
|
|
13
|
+
matrix:
|
|
14
|
+
python-version: ["3.12", "3.14"]
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v4
|
|
17
|
+
- uses: actions/setup-python@v5
|
|
18
|
+
with:
|
|
19
|
+
python-version: ${{ matrix.python-version }}
|
|
20
|
+
- uses: astral-sh/setup-uv@v5
|
|
21
|
+
- name: Install dependencies
|
|
22
|
+
run: uv sync --dev
|
|
23
|
+
- name: Run tests
|
|
24
|
+
run: make test
|
|
25
|
+
- name: Upload coverage reports to Codecov
|
|
26
|
+
uses: codecov/codecov-action@v5
|
|
27
|
+
with:
|
|
28
|
+
token: ${{ secrets.CODECOV_TOKEN }}
|
|
29
|
+
files: coverage.xml
|
|
30
|
+
fail_ci_if_error: true
|
|
31
|
+
- name: Upload coverage report
|
|
32
|
+
uses: actions/upload-artifact@v4
|
|
33
|
+
with:
|
|
34
|
+
name: coverage-xml-${{ matrix.python-version }}
|
|
35
|
+
path: coverage.xml
|
|
36
|
+
- name: Run type checks
|
|
37
|
+
run: uv run ty check
|
|
38
|
+
- name: Run ruff
|
|
39
|
+
run: uv run ruff check .
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
name: Ruff Autofix
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches:
|
|
6
|
+
- "**"
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: write
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
autofix:
|
|
13
|
+
if: github.actor != 'github-actions[bot]'
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v4
|
|
17
|
+
with:
|
|
18
|
+
fetch-depth: 0
|
|
19
|
+
- uses: actions/setup-python@v5
|
|
20
|
+
with:
|
|
21
|
+
python-version: "3.12"
|
|
22
|
+
- uses: astral-sh/setup-uv@v5
|
|
23
|
+
- name: Install dependencies
|
|
24
|
+
run: uv sync --dev
|
|
25
|
+
- name: Run ruff autofix
|
|
26
|
+
run: uv run ruff check --fix .
|
|
27
|
+
- name: Commit and push changes
|
|
28
|
+
run: |
|
|
29
|
+
if [[ -n "$(git status --porcelain)" ]]; then
|
|
30
|
+
git config user.name "github-actions[bot]"
|
|
31
|
+
git config user.email "github-actions[bot]@users.noreply.github.com"
|
|
32
|
+
git add -A
|
|
33
|
+
git commit -m "chore: ruff autofix"
|
|
34
|
+
git push
|
|
35
|
+
fi
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# C extensions
|
|
7
|
+
*.so
|
|
8
|
+
|
|
9
|
+
# Distribution / packaging
|
|
10
|
+
.Python
|
|
11
|
+
build/
|
|
12
|
+
dist/
|
|
13
|
+
*.egg-info/
|
|
14
|
+
.eggs/
|
|
15
|
+
|
|
16
|
+
# Installer logs
|
|
17
|
+
pip-log.txt
|
|
18
|
+
pip-delete-this-directory.txt
|
|
19
|
+
|
|
20
|
+
# Unit test / coverage reports
|
|
21
|
+
htmlcov/
|
|
22
|
+
.tox/
|
|
23
|
+
.nox/
|
|
24
|
+
.coverage
|
|
25
|
+
.coverage.*
|
|
26
|
+
.cache
|
|
27
|
+
nosetests.xml
|
|
28
|
+
coverage.xml
|
|
29
|
+
*.cover
|
|
30
|
+
*.py,cover
|
|
31
|
+
.hypothesis/
|
|
32
|
+
.pytest_cache/
|
|
33
|
+
|
|
34
|
+
# mypy
|
|
35
|
+
.mypy_cache/
|
|
36
|
+
.dmypy.json
|
|
37
|
+
|
|
38
|
+
# Pyre
|
|
39
|
+
.pyre/
|
|
40
|
+
|
|
41
|
+
# Ruff
|
|
42
|
+
.ruff_cache/
|
|
43
|
+
|
|
44
|
+
# IDEs and editors
|
|
45
|
+
.vscode/
|
|
46
|
+
.idea/
|
|
47
|
+
|
|
48
|
+
# Environments
|
|
49
|
+
.env
|
|
50
|
+
.venv/
|
|
51
|
+
venv/
|
|
52
|
+
ENV/
|
|
53
|
+
|
|
54
|
+
# Pyenv
|
|
55
|
+
.python-version
|
|
56
|
+
|
|
57
|
+
# OS files
|
|
58
|
+
.DS_Store
|
|
59
|
+
Thumbs.db
|
|
60
|
+
|
|
61
|
+
# Jupyter
|
|
62
|
+
.ipynb_checkpoints/
|
|
63
|
+
|
|
64
|
+
# marimo
|
|
65
|
+
__marimo__/
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Agent guide for pep723-to-wheel
|
|
2
|
+
|
|
3
|
+
## Repository overview
|
|
4
|
+
- `src/pep723_to_wheel/` contains the library and CLI implementation.
|
|
5
|
+
- `tests/` holds pytest coverage for core behavior.
|
|
6
|
+
- `pyproject.toml` defines dependencies, entry points, and tooling.
|
|
7
|
+
|
|
8
|
+
## Development setup
|
|
9
|
+
- Requires Python 3.12+ (see `pyproject.toml`).
|
|
10
|
+
- Environment management is with uv.
|
|
11
|
+
- Run Python and related CLI tools via `uv run` so they use the uv virtualenv.
|
|
12
|
+
|
|
13
|
+
## Common commands
|
|
14
|
+
- Run tests: `make test`
|
|
15
|
+
- Run type checks: `make typecheck`
|
|
16
|
+
- Run ruff checks: `make ruff`
|
|
17
|
+
- Run all checks: `make all-tests`
|
|
18
|
+
|
|
19
|
+
## Style and conventions
|
|
20
|
+
- TDD for all code development - write test, then run to verify it fails, then develop, then verify the test passes.
|
|
21
|
+
- All tasks should end by running `make all-tests` and verifying it passes.
|
|
22
|
+
- Prefer updating or adding pytest tests in `tests/` for behavior changes.
|
|
23
|
+
- For CLI changes, update both `src/pep723_to_wheel/cli.py` and any relevant tests.
|
|
24
|
+
- Target modern Python 3.12+ syntax, no need to be backwards compatible.
|
|
25
|
+
|
|
26
|
+
## Tips
|
|
27
|
+
- Use `pep723_to_wheel.core` for the main build/import logic.
|
|
28
|
+
- The CLI entry point is `pep723_to_wheel.cli:app`.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Johan Carlin
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pep723-to-wheel
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: PoC for pep-723 script to wheel and back
|
|
5
|
+
License-File: LICENSE
|
|
6
|
+
Requires-Python: >=3.12
|
|
7
|
+
Requires-Dist: pydantic>=2.5
|
|
8
|
+
Requires-Dist: tomli-w>=1.1.0
|
|
9
|
+
Requires-Dist: typer>=0.21.1
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
|
|
12
|
+
# pep723-to-wheel
|
|
13
|
+
|
|
14
|
+
[](https://github.com/jooh/pep723-to-wheel/actions/workflows/ci.yml)
|
|
15
|
+
[](https://codecov.io/gh/jooh/pep723-to-wheel)
|
|
16
|
+
|
|
17
|
+
A small utility for converting [PEP 723](https://peps.python.org/pep-0723/) inline dependency scripts into wheels and reconstructing scripts from wheels. Especially useful for taking [reproducible Marimo notebooks](https://marimo.io/blog/sandboxed-notebooks) to production environments.
|
|
18
|
+
|
|
19
|
+
## CLI
|
|
20
|
+
|
|
21
|
+
Build a wheel from a script that has a PEP 723 inline block:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
pep723-to-wheel build path/to/script.py --output-dir dist
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Set an explicit wheel version (defaults to calendar versioning using the script mtime as the patch segment):
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
pep723-to-wheel build path/to/script.py --version 2024.12.25
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Reconstruct a script from a wheel or package name:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
pep723-to-wheel import path/to/package.whl --output reconstructed.py
|
|
37
|
+
pep723-to-wheel import requests --output reconstructed.py
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Library
|
|
41
|
+
|
|
42
|
+
```python
|
|
43
|
+
from pathlib import Path
|
|
44
|
+
from pep723_to_wheel import build_script_to_wheel, import_wheel_to_script
|
|
45
|
+
|
|
46
|
+
result = build_script_to_wheel(Path("script.py"))
|
|
47
|
+
print(result.wheel_path)
|
|
48
|
+
|
|
49
|
+
import_result = import_wheel_to_script("requests", Path("reconstructed.py"))
|
|
50
|
+
print(import_result.script_path)
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Development
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
make test
|
|
57
|
+
make typecheck
|
|
58
|
+
make ruff
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Release process
|
|
62
|
+
|
|
63
|
+
Releases are automated on pushes to `main` by the CD workflow in `.github/workflows/cd.yml`.
|
|
64
|
+
|
|
65
|
+
1. The workflow determines the latest `v*` Git tag and runs `.github/workflows/cd_version.py` to
|
|
66
|
+
resolve the next version. If the latest tag matches the current major/minor, it bumps the patch
|
|
67
|
+
to one higher than the max of the current patch and the tag patch.
|
|
68
|
+
2. If `pyproject.toml` changes, the workflow commits the version bump back to `main`.
|
|
69
|
+
3. It tags the release as `v<version>`, builds the wheel with `uv build --wheel`, publishes to
|
|
70
|
+
PyPI, and creates a GitHub release with the wheel attached.
|
|
71
|
+
|
|
72
|
+
To trigger a release, merge or push changes to `main` and ensure `PYPI_API_TOKEN` is configured in
|
|
73
|
+
the repository secrets.
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# pep723-to-wheel
|
|
2
|
+
|
|
3
|
+
[](https://github.com/jooh/pep723-to-wheel/actions/workflows/ci.yml)
|
|
4
|
+
[](https://codecov.io/gh/jooh/pep723-to-wheel)
|
|
5
|
+
|
|
6
|
+
A small utility for converting [PEP 723](https://peps.python.org/pep-0723/) inline dependency scripts into wheels and reconstructing scripts from wheels. Especially useful for taking [reproducible Marimo notebooks](https://marimo.io/blog/sandboxed-notebooks) to production environments.
|
|
7
|
+
|
|
8
|
+
## CLI
|
|
9
|
+
|
|
10
|
+
Build a wheel from a script that has a PEP 723 inline block:
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
pep723-to-wheel build path/to/script.py --output-dir dist
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Set an explicit wheel version (defaults to calendar versioning using the script mtime as the patch segment):
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
pep723-to-wheel build path/to/script.py --version 2024.12.25
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Reconstruct a script from a wheel or package name:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
pep723-to-wheel import path/to/package.whl --output reconstructed.py
|
|
26
|
+
pep723-to-wheel import requests --output reconstructed.py
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Library
|
|
30
|
+
|
|
31
|
+
```python
|
|
32
|
+
from pathlib import Path
|
|
33
|
+
from pep723_to_wheel import build_script_to_wheel, import_wheel_to_script
|
|
34
|
+
|
|
35
|
+
result = build_script_to_wheel(Path("script.py"))
|
|
36
|
+
print(result.wheel_path)
|
|
37
|
+
|
|
38
|
+
import_result = import_wheel_to_script("requests", Path("reconstructed.py"))
|
|
39
|
+
print(import_result.script_path)
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Development
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
make test
|
|
46
|
+
make typecheck
|
|
47
|
+
make ruff
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Release process
|
|
51
|
+
|
|
52
|
+
Releases are automated on pushes to `main` by the CD workflow in `.github/workflows/cd.yml`.
|
|
53
|
+
|
|
54
|
+
1. The workflow determines the latest `v*` Git tag and runs `.github/workflows/cd_version.py` to
|
|
55
|
+
resolve the next version. If the latest tag matches the current major/minor, it bumps the patch
|
|
56
|
+
to one higher than the max of the current patch and the tag patch.
|
|
57
|
+
2. If `pyproject.toml` changes, the workflow commits the version bump back to `main`.
|
|
58
|
+
3. It tags the release as `v<version>`, builds the wheel with `uv build --wheel`, publishes to
|
|
59
|
+
PyPI, and creates a GitHub release with the wheel attached.
|
|
60
|
+
|
|
61
|
+
To trigger a release, merge or push changes to `main` and ensure `PYPI_API_TOKEN` is configured in
|
|
62
|
+
the repository secrets.
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "pep723-to-wheel"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "PoC for pep-723 script to wheel and back"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.12"
|
|
7
|
+
dependencies = [
|
|
8
|
+
"pydantic>=2.5",
|
|
9
|
+
"tomli-w>=1.1.0",
|
|
10
|
+
"typer>=0.21.1",
|
|
11
|
+
]
|
|
12
|
+
|
|
13
|
+
[project.scripts]
|
|
14
|
+
pep723-to-wheel = "pep723_to_wheel.cli:app"
|
|
15
|
+
|
|
16
|
+
[build-system]
|
|
17
|
+
requires = ["hatchling>=1.27.0"]
|
|
18
|
+
build-backend = "hatchling.build"
|
|
19
|
+
|
|
20
|
+
[tool.hatch.build.targets.wheel]
|
|
21
|
+
packages = ["src/pep723_to_wheel"]
|
|
22
|
+
|
|
23
|
+
[dependency-groups]
|
|
24
|
+
dev = [
|
|
25
|
+
"pytest>=9.0.2",
|
|
26
|
+
"pytest-cov>=6.0.0",
|
|
27
|
+
"ruff>=0.9.10",
|
|
28
|
+
"ty>=0.0.14",
|
|
29
|
+
]
|
|
30
|
+
|
|
31
|
+
[tool.ty.src]
|
|
32
|
+
exclude = ["tests/examples/**"]
|
|
33
|
+
|
|
34
|
+
[tool.coverage.run]
|
|
35
|
+
branch = true
|
|
36
|
+
source = ["pep723_to_wheel"]
|
|
37
|
+
|
|
38
|
+
[tool.coverage.report]
|
|
39
|
+
fail_under = 100
|
|
40
|
+
show_missing = true
|
|
41
|
+
skip_covered = true
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"""Typer-based CLI entry points."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
import typer
|
|
8
|
+
|
|
9
|
+
from pep723_to_wheel.core import build_script_to_wheel, import_wheel_to_script
|
|
10
|
+
|
|
11
|
+
app = typer.Typer(add_completion=False, no_args_is_help=True)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@app.command("build")
|
|
15
|
+
def build_command(
|
|
16
|
+
script_path: Path = typer.Argument(..., help="Path to the PEP 723 script."),
|
|
17
|
+
output_dir: Path | None = typer.Option(
|
|
18
|
+
None, "--output-dir", "-o", help="Directory for the built wheel."
|
|
19
|
+
),
|
|
20
|
+
version: str | None = typer.Option(
|
|
21
|
+
None,
|
|
22
|
+
"--version",
|
|
23
|
+
"-v",
|
|
24
|
+
help="Wheel version (defaults to calendar versioning).",
|
|
25
|
+
),
|
|
26
|
+
) -> None:
|
|
27
|
+
"""Build a wheel from a PEP 723 script."""
|
|
28
|
+
|
|
29
|
+
result = build_script_to_wheel(script_path, output_dir, version)
|
|
30
|
+
typer.echo(str(result.wheel_path))
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@app.command("import")
|
|
34
|
+
def import_command(
|
|
35
|
+
wheel_or_package: str = typer.Argument(
|
|
36
|
+
..., help="Wheel path or package name to import."
|
|
37
|
+
),
|
|
38
|
+
output_path: Path = typer.Option(
|
|
39
|
+
..., "--output", "-o", help="Path to write the reconstructed script."
|
|
40
|
+
),
|
|
41
|
+
) -> None:
|
|
42
|
+
"""Reconstruct a script from a wheel or package name."""
|
|
43
|
+
|
|
44
|
+
result = import_wheel_to_script(wheel_or_package, output_path)
|
|
45
|
+
typer.echo(str(result.script_path))
|