Skip to content

Commit e63ceb4

Browse files
authored
Merge pull request #286 from clamsproject/283-pyproject-migration
pyproject-based build processes
2 parents a8d97fc + b5e20e2 commit e63ceb4

23 files changed

Lines changed: 682 additions & 288 deletions

.github/workflows/codecov.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ on:
1212
jobs:
1313
test-and-codecov:
1414
name: "🤙 Call SDK test workflow"
15-
uses: clamsproject/.github/.github/workflows/sdk-codecov.yml@main
15+
uses: clamsproject/.github/.github/workflows/sdk-codecov-pyproj.yml@main
1616
secrets:
1717
CC_REPO_UPLOAD_TOKEN: ${{ secrets.CODECOV_UPLOAD_TOKEN_CLAMS_PYTHON }}
1818

.github/workflows/publish.yml

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ jobs:
3636
name: "📦 Build and upload to PyPI"
3737
needs: check-pypi
3838
if: needs.check-pypi.outputs.exists == 'false'
39-
uses: clamsproject/.github/.github/workflows/sdk-publish.yml@main
39+
uses: clamsproject/.github/.github/workflows/sdk-publish-pyproj.yml@main
4040
secrets: inherit
4141

4242
publish-docs:
@@ -48,7 +48,4 @@ jobs:
4848
source_repo: clamsproject/clams-python
4949
source_ref: ${{ needs.check-pypi.outputs.version }}
5050
project_name: clams-python
51-
build_command: 'echo "${{ needs.check-pypi.outputs.version }}" > VERSION && python3 build-tools/docs.py --output-dir docs'
52-
docs_output_dir: 'docs'
53-
python_version: '3.11'
5451
secrets: inherit

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44

55
# build time temp files
66
VERSION*
7-
clams/ver
87

98
# linux
109
.*.sw?
@@ -67,6 +66,7 @@ hs_err_pid* # virtual machine crash logs, see http://www.java.com/en/download/he
6766
build/
6867
dist/
6968
*.egg-info
69+
clams_python-*/
7070
coverage.xml
7171

7272
# shared folders
@@ -84,3 +84,5 @@ tags
8484

8585
# sphinx
8686
docs-test/
87+
documentation/appmetadata.jsonschema
88+
documentation/whatsnew.md

CONTRIBUTING.md

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
# Contributing to clams-python
2+
3+
## Prerequisites
4+
5+
- Python 3.10+
6+
- `gh` CLI (for changelog generation)
7+
8+
## Setup
9+
10+
```bash
11+
pip install -e ".[dev]"
12+
```
13+
14+
Unlike the old `setup.py`-based workflow, an editable install
15+
(`pip install -e .`) is now required before running tests or building
16+
docs. The package uses `importlib.metadata` for version resolution at
17+
runtime, which only works when the package is registered in the
18+
environment. You can no longer run `pytest` or `pytype` directly
19+
against the source tree without installing first. If you want to avoid
20+
pulling in all dependencies, `pip install -e . --no-deps` is sufficient
21+
to register the package metadata.
22+
23+
## Local Development
24+
25+
All build tasks are handled by scripts in `build-tools/`. Each script
26+
is self-contained and installs its own dependencies as needed.
27+
28+
| Task | Command |
29+
|------|---------|
30+
| Build (sdist + wheel) | `python build-tools/build.py` |
31+
| Run tests | `python build-tools/test.py` |
32+
| Build docs | `python build-tools/docs.py` |
33+
| Clean artifacts | `python build-tools/clean.py` |
34+
| Publish | `python build-tools/publish.py` |
35+
36+
All scripts support `--help` for full usage details.
37+
38+
### Build
39+
40+
```bash
41+
python build-tools/build.py
42+
```
43+
44+
Produces sdist and wheel in `dist/`.
45+
46+
### Test
47+
48+
```bash
49+
python build-tools/test.py
50+
```
51+
52+
Runs pytest with coverage. Use `--skip-install` if you already have the
53+
package installed in editable mode.
54+
55+
### Documentation
56+
57+
```bash
58+
python build-tools/docs.py
59+
```
60+
61+
Builds Sphinx HTML docs into `docs-test/` (override with `--output-dir`).
62+
The `--build-ver` flag is accepted for CI compatibility but has no effect
63+
— clams-python uses unversioned documentation.
64+
65+
### Versioning
66+
67+
Versions are derived automatically from git tags via `setuptools-scm`.
68+
There is no `VERSION` file to manage. At runtime, the version is
69+
accessed through `importlib.metadata`:
70+
71+
```python
72+
from clams.ver import __version__
73+
```
74+
75+
For a dev install without a matching tag, `setuptools-scm` generates a
76+
version like `1.4.1.dev20+gaf551a4e4.d20260325`.
77+
78+
## Migration from Makefile
79+
80+
The old `Makefile` and `setup.py` have been removed. If you are
81+
accustomed to the old workflow, here is a mapping:
82+
83+
| Old command | New equivalent |
84+
|-------------|----------------|
85+
| `make package` / `python setup.py sdist` | `python build-tools/build.py` |
86+
| `make develop` / `python setup.py develop` | `pip install -e ".[dev]"` |
87+
| `make test` | `python build-tools/test.py` |
88+
| `make doc` | `python build-tools/docs.py` |
89+
| `make version` / `make devversion` | Automatic via `setuptools-scm` (tag-based) |
90+
| `make clean` | `python build-tools/clean.py` |
91+
| `make publish` | `python build-tools/publish.py` |

MANIFEST.in

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

Makefile

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

build-tools/build.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
"""
2+
Build the clams-python package.
3+
4+
Installs dependencies and runs `python -m build` to produce sdist + wheel.
5+
"""
6+
import argparse
7+
import subprocess
8+
import sys
9+
from pathlib import Path
10+
11+
SCRIPT_DIR = Path(__file__).parent
12+
13+
14+
def run_command(command, cwd=None, check=True):
15+
"""Helper to run a shell command."""
16+
print(f"Running: {' '.join(str(c) for c in command)}")
17+
result = subprocess.run(command, cwd=cwd)
18+
if check and result.returncode != 0:
19+
print(
20+
f"Error: Command failed with exit code "
21+
f"{result.returncode}"
22+
)
23+
sys.exit(result.returncode)
24+
return result
25+
26+
27+
def main():
28+
parser = argparse.ArgumentParser(
29+
description="Build the clams-python package."
30+
)
31+
parser.parse_args()
32+
33+
project_root = SCRIPT_DIR.parent
34+
35+
# Install dev + build dependencies
36+
print("--- Installing dependencies ---")
37+
run_command(
38+
[sys.executable, "-m", "pip",
39+
"install", "-e", ".[dev]", "build"],
40+
cwd=project_root,
41+
)
42+
43+
# Build sdist + wheel
44+
print("\n--- Building sdist + wheel ---")
45+
run_command(
46+
[sys.executable, "-m", "build"],
47+
cwd=project_root,
48+
)
49+
50+
print("\nBuild complete. Output in: dist/")
51+
52+
53+
if __name__ == "__main__":
54+
main()

build-tools/clean.py

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
"""
2+
Clean build artifacts, caches, and generated files.
3+
4+
Replaces ``make clean`` / ``make distclean`` from the old Makefile.
5+
"""
6+
import argparse
7+
import shutil
8+
from pathlib import Path
9+
10+
SCRIPT_DIR = Path(__file__).parent
11+
PROJECT_ROOT = SCRIPT_DIR.parent
12+
13+
# Directories to remove
14+
CLEAN_DIRS = [
15+
"build", "dist", "*.egg-info", "clams_python-*",
16+
".pytest_cache", ".pytype", ".hypothesis",
17+
"tests/.hypothesis", "htmlcov",
18+
"docs-test",
19+
]
20+
21+
# Files to remove
22+
CLEAN_FILES = [
23+
"coverage.xml", ".coverage",
24+
]
25+
26+
# Glob patterns for recursive removal
27+
CLEAN_GLOBS = [
28+
"**/__pycache__",
29+
]
30+
31+
32+
def clean(root: Path):
33+
removed = []
34+
35+
for pattern in CLEAN_DIRS:
36+
for p in root.glob(pattern):
37+
if p.is_dir():
38+
shutil.rmtree(p)
39+
removed.append(str(p.relative_to(root)))
40+
41+
for name in CLEAN_FILES:
42+
p = root / name
43+
if p.exists():
44+
p.unlink()
45+
removed.append(str(p.relative_to(root)))
46+
47+
for pattern in CLEAN_GLOBS:
48+
for p in root.glob(pattern):
49+
if p.is_dir():
50+
shutil.rmtree(p)
51+
removed.append(str(p.relative_to(root)))
52+
53+
if removed:
54+
print(f"Removed {len(removed)} items:")
55+
for item in sorted(removed):
56+
print(f" {item}")
57+
else:
58+
print("Nothing to clean.")
59+
60+
61+
def main():
62+
parser = argparse.ArgumentParser(
63+
description="Clean build artifacts and caches."
64+
)
65+
parser.parse_args()
66+
clean(PROJECT_ROOT)
67+
68+
69+
if __name__ == "__main__":
70+
main()

0 commit comments

Comments
 (0)