From 9737dab90d8e6da0b0789b948613f0abe122edce Mon Sep 17 00:00:00 2001 From: Kieran Leschinski Date: Thu, 2 Apr 2026 12:40:09 +0200 Subject: [PATCH 1/2] Add Poetry packaging for distributable HEEPS wheel Configure pyproject.toml with all dependencies, including PyPROPER3 sourced from the university wheel server. Add missing util/__init__.py, LICENSE, .gitignore updates for build artifacts, and build docs. Co-Authored-By: Claude Opus 4.6 (1M context) --- .gitignore | 2 + LICENSE | 21 +++++++++ docs/building_wheels.md | 94 +++++++++++++++++++++++++++++++++++++++++ heeps/util/__init__.py | 0 pyproject.toml | 48 +++++++++++++++++++++ 5 files changed, 165 insertions(+) create mode 100644 LICENSE create mode 100644 docs/building_wheels.md create mode 100644 heeps/util/__init__.py create mode 100644 pyproject.toml diff --git a/.gitignore b/.gitignore index b86203e..1fe0719 100644 --- a/.gitignore +++ b/.gitignore @@ -31,7 +31,9 @@ var/ *.egg-info/ .installed.cfg *.egg +*.whl _build/ +poetry.lock # PyInstaller # Usually these files are written by a python script from a template diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..ba590fd --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020-2026 Vortex Team, University of Liege + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/docs/building_wheels.md b/docs/building_wheels.md new file mode 100644 index 0000000..4ccd407 --- /dev/null +++ b/docs/building_wheels.md @@ -0,0 +1,94 @@ +# Building HEEPS and PyPROPER3 Wheels + +## Prerequisites + +- Python 3.10+ installed +- `poetry` installed (`pip install poetry`) +- `wheel` and `setuptools` installed (`pip install wheel setuptools`) + +## Building the HEEPS wheel + +From the repo root (`D:\Repos\HEEPS`): + +```bash +python -m poetry build +``` + +This produces two files in `dist/`: +- `heeps--py3-none-any.whl` (wheel) +- `heeps-.tar.gz` (sdist) + +The version is read from `heeps/__init__.py` (`__version__`) and must match the version in `pyproject.toml`. + +### What the HEEPS wheel contains + +All Python modules under `heeps/`, including the `util/` subpackage. No data files (FITS, config, etc.) are bundled — those are downloaded at runtime via Google Drive. + +## Building the PyPROPER3 3.3.4 wheel + +PROPER 3.3.4 is **not available on PyPI** (only 3.2.1 is there), so we build it from the SourceForge zip. + +```bash +# Extract the zip +mkdir /tmp/proper_build && cd /tmp/proper_build +unzip /path/to/proper_v3.3.4_python.zip +cd proper_v3.3.4_python + +# Build the wheel +python setup.py bdist_wheel +``` + +The wheel appears in `dist/`: +- `PyPROPER3-3.3.4-py3-none-any.whl` + +### Gotcha: `wheel` must be installed + +If you get `error: invalid command 'bdist_wheel'`, install the `wheel` package first: + +```bash +pip install wheel +``` + +### Gotcha: the wheel is much smaller than the zip (~160KB vs ~7MB) + +This is expected. The zip contains a 7MB PDF manual (`PROPER_manual_v3.3.4.pdf`) at the top level, outside the `proper/` package directory. The `setup.py` `package_data` only bundles files inside `proper/`, so the PDF is excluded from the wheel. The `.fits` data file (72KB, inside `proper/`) is included. + +## Hosting on a private PyPI server + +Both wheels need to be uploaded to your private PyPI server: + +1. `dist/heeps--py3-none-any.whl` +2. `PyPROPER3-3.3.4-py3-none-any.whl` + +HEEPS declares `PyPROPER3>=3.3.3` as a dependency sourced from the university wheel server. The `pyproject.toml` includes a `[[tool.poetry.source]]` entry pointing to `https://scopesim.univie.ac.at/wheels/`. + +For Poetry users, this is automatic. For pip users, pass the extra index: + +```bash +pip install heeps --extra-index-url https://scopesim.univie.ac.at/wheels/ +``` + +Using `--extra-index-url` (not `--index-url`) lets pip fall back to public PyPI for other dependencies like `vip-hci`, `numpy`, etc. + +## Testing locally (without a PyPI server) + +```bash +python -m uv venv .venv +.venv\Scripts\activate # Windows +# source .venv/bin/activate # Linux/macOS + +python -m uv pip install /path/to/PyPROPER3-3.3.4-py3-none-any.whl +python -m uv pip install -e /path/to/HEEPS # editable install for dev +# or: python -m uv pip install /path/to/HEEPS/dist/heeps-1.0.0-py3-none-any.whl +``` + +## Gotchas summary + +| Issue | Cause | Fix | +|---|---|---| +| `bdist_wheel` command not found | `wheel` package not installed | `pip install wheel` | +| PROPER wheel is tiny vs zip | PDF manual excluded from wheel | Expected — not a problem | +| `pip install heeps` can't find PyPROPER3>=3.3.3 | Only 3.2.1 on public PyPI | Host 3.3.4 wheel on private server, use `--extra-index-url` | +| `uv` command not found on Windows | Installed to user Scripts dir, not on PATH | Use `python -m uv` instead | +| `heeps.util` import fails | `heeps/util/__init__.py` was missing | Already fixed — file was added | +| Version mismatch | `__init__.py` and `pyproject.toml` versions can drift | Keep both in sync when bumping | diff --git a/heeps/util/__init__.py b/heeps/util/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..da94b2f --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,48 @@ +[tool.poetry] +name = "heeps" +version = "1.0.0" +description = "The HCI End-to-End Performance Simulator for ELT/METIS" +authors = ["Vortex Team "] +license = "MIT" +readme = "README.md" +homepage = "https://github.com/vortex-exoplanet/HEEPS" +repository = "https://github.com/vortex-exoplanet/HEEPS" +keywords = ["astronomy", "coronagraph", "high-contrast-imaging", "METIS", "ELT"] +classifiers = [ + "Development Status :: 4 - Beta", + "Intended Audience :: Science/Research", + "Topic :: Scientific/Engineering :: Astronomy", + "Programming Language :: Python :: 3", +] +packages = [{include = "heeps"}] + +[tool.poetry.dependencies] +python = ">=3.10,<3.13" +numpy = ">=1.24" +scipy = ">=1.10" +matplotlib = ">=3.7" +astropy = ">=5.3" +photutils = ">=1.8" +scikit-image = ">=0.20" +vip-hci = ">=1.6.0" +PyPROPER3 = {version = ">=3.3.3", source = "scopesim-wheels"} +requests = ">=2.28" +beautifulsoup4 = ">=4.11" +packaging = ">=21.0" + +[tool.poetry.group.dev.dependencies] +jupyter = ">=1.0" +ipython = ">=8.0" + +[tool.poetry.group.scopesim.dependencies] +scopesim = ">=0.8" +synphot = ">=1.1" + +[[tool.poetry.source]] +name = "scopesim-wheels" +url = "https://scopesim.univie.ac.at/wheels/" +priority = "supplemental" + +[build-system] +requires = ["poetry-core"] +build-backend = "poetry.core.masonry.api" From 1a98797bfefbb58379cede02beabef0a08568297 Mon Sep 17 00:00:00 2001 From: Kieran Leschinski Date: Wed, 8 Apr 2026 10:10:32 +0200 Subject: [PATCH 2/2] Fix f-string quoting in update_config.py Use double quotes for f-strings that contain single-quoted dict key access, fixing a syntax error on Python < 3.12. Co-Authored-By: Claude Opus 4.6 (1M context) --- heeps/config/update_config.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/heeps/config/update_config.py b/heeps/config/update_config.py index 341f180..e582c76 100644 --- a/heeps/config/update_config.py +++ b/heeps/config/update_config.py @@ -112,9 +112,9 @@ def update_config(band='L', band_specs={'L':{}}, mode='RAVC', lam=3.8e-6, print(' [ WARNING ] no file for auto-select Lyot stop not found at %s'%conf['f_lyot_stop']) elif conf['select_lyot'] != '': # string is not empty - print(f'\n Selecting Lyot stop name {conf['select_lyot']}') + print(f"\n Selecting Lyot stop name {conf['select_lyot']}") if Path(conf['f_lyot_stop']).is_file(): - print(f' [ WARNING ] Lyot stop name is set via select_lyot and f_lyot_stop file ({conf['f_lyot_stop']})is also found. Using select_lyot.') + print(f" [ WARNING ] Lyot stop name is set via select_lyot and f_lyot_stop file ({conf['f_lyot_stop']})is also found. Using select_lyot.") conf['f_lyot_stop'] = Path(conf['f_lyot_stop']) / \ COLD_STOPS[conf['select_lyot']]['f_lyot_stop']