diff --git a/.github/workflows/lockfiles.yml b/.github/workflows/lockfiles.yml index 4e73eac..296acfe 100644 --- a/.github/workflows/lockfiles.yml +++ b/.github/workflows/lockfiles.yml @@ -18,15 +18,21 @@ jobs: - uses: actions/setup-python@v5 with: - python-version-file: .python-version + python-version: "3.11" - - name: "Update Lockfiles and Open PR" - uses: tedivm/action-python-lockfile-update@v2 - with: - pip_extras: "dev" - # This key will bypass workflow limitations to ensure tests are run. - # deploy_key: ${{ secrets.WRITEABLE_DEPLOY_KEY }} + - name: Install Poetry + run: pip install "poetry==1.8.3" + + - name: Update Lockfile + run: poetry update + - name: Open PR with updated lockfile + uses: peter-evans/create-pull-request@v7 + with: + commit-message: "Update poetry.lock" + branch: chore/update-lockfile + title: "Update poetry.lock" + body: "Automated dependency update via `poetry update`." + delete-branch: true env: - # Needed to open pull request- automatically set for all actions. GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/pypi.yml b/.github/workflows/pypi.yml index 799289a..df61fd7 100644 --- a/.github/workflows/pypi.yml +++ b/.github/workflows/pypi.yml @@ -17,7 +17,9 @@ jobs: fetch-tags: true - uses: actions/setup-python@v5 with: - python-version-file: .python-version + python-version: "3.11" + - name: Install Poetry + run: pip install poetry==1.8.3 - name: Install Dependencies run: make install - name: Build Wheel diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f349ee5..de8deef 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -29,7 +29,9 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: - python-version-file: .python-version + python-version: "3.11" + - name: Install Poetry + run: pip install poetry==1.8.3 - name: Install Dependencies run: make install - name: Test diff --git a/.gitignore b/.gitignore index 6a7978e..e46b043 100644 --- a/.gitignore +++ b/.gitignore @@ -100,7 +100,7 @@ Pipfile.lock # This is especially recommended for binary packages to ensure reproducibility, and is more # commonly ignored for libraries. # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control -#poetry.lock +poetry.lock # pdm # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. diff --git a/.python-version b/.python-version deleted file mode 100644 index e4fba21..0000000 --- a/.python-version +++ /dev/null @@ -1 +0,0 @@ -3.12 diff --git a/README.md b/README.md index 6d51dae..f19b815 100644 --- a/README.md +++ b/README.md @@ -15,24 +15,41 @@ postprocessing utilities. Development is a little weird if you're not used to modern python projects, especially because [python development and packaging evolves so quickly](https://dev.to/farcellier/i-migrate-to-poetry-in-2023-am-i-right--115). -To isolate the development environment, `pyenv` and `pip` -install a toolchain locally. +To isolate the development environment, this project uses +[Poetry](https://python-poetry.org/) to manage dependencies, virtual +environments, and lockfiles. ## Setting up External dependencies (easily installed through [Homebrew](https://brew.sh/) or another package manager): -- [pyenv](https://github.com/pyenv/pyenv), which will install its own python versions in an isolated environment +- [Poetry](https://python-poetry.org/) +- Python 3.11 or newer After cloning the repository, run `make pre-commit` to: -- Install the development version of python specified in `.python-version` to - your `pyenv` prefix (default: `~/.pyenv`, configurable with the `PYENV_ROOT` - variable) -- Set up a virtual environment in `.venv` that will contain all the development - dependencies, including a `celerpy` symlink in its environment that will - point to your working copy -- Install pre-commit hooks that use the tools just installed in your virtual - environment. +- Create and/or update the Poetry-managed virtual environment +- Install all project and development dependencies from `poetry.lock` +- Install pre-commit hooks. + +If you have multiple Python versions installed, you can select one explicitly +for the project: +```console +$ poetry env use python3.11 +``` + +## Versioning + +Package versions are generated dynamically from git tags using +`poetry-dynamic-versioning`. + +- Tagged commits produce release versions (for example `v0.2.0` -> `0.2.0`) +- Untagged commits use a derived pre-release/dev form from git history + +To create a new release tag: +```console +$ git tag v0.2.0 +$ git push origin v0.2.0 +``` ## Testing and committing @@ -43,14 +60,12 @@ automatically. (Use `git commit --no-verify` to disable.) The makefile specifies a few useful targets: - `style`: apply style fixups to all the python files in development - `test`: run tests -- `pip`: reinstall all the dependencies in your virtual environment -- `rebuild_dependencies`: update the `requirements` file if you add a new - dependency to `pyproject.toml` +- `install`: install/update dependencies with Poetry +- `rebuild_dependencies`: update dependencies and refresh `poetry.lock` -You can also test independently once your virtual environment is set up. For -example, to run a single python test function from a single python test, with -the most verbose output and sending stdout/stderr to the console, run: +You can also run tools directly through Poetry. For example, to run a single +python test function from a single python test, with verbose output and sending +stdout/stderr to the console, run: ```console -$ . .venv/bin/activate -$ pytest -vv -s test/test_process.py -k test_context +$ poetry run pytest -vv -s test/test_process.py -k test_context ``` diff --git a/celerpy/__init__.py b/celerpy/__init__.py index 437a732..9b50f71 100644 --- a/celerpy/__init__.py +++ b/celerpy/__init__.py @@ -1,12 +1,12 @@ # Copyright 2024 UT-Battelle, LLC, and other Celeritas developers. # See the top-level LICENSE file for details. # SPDX-License-Identifier: Apache-2.0 -try: - from . import _version # type: ignore[attr-defined] +from importlib.metadata import PackageNotFoundError, version - __version__ = _version.__version__ -except: # noqa: E722 - __version__ = "0.0.0-dev" +try: + __version__ = version("celerpy") +except PackageNotFoundError: + __version__ = "0.1.0-dev" # Expose __version__ for type checkers __all__ = ["__version__"] diff --git a/celerpy/cli.py b/celerpy/cli.py index 9bb5cf4..4f50765 100644 --- a/celerpy/cli.py +++ b/celerpy/cli.py @@ -22,9 +22,9 @@ def load_settings(): def print_version(value: bool): if value: - from . import _version + from . import __version__ - typer.echo(_version.version) + typer.echo(__version__) raise typer.Exit() diff --git a/makefile b/makefile index 9ebcba0..2cec080 100644 --- a/makefile +++ b/makefile @@ -1,59 +1,22 @@ PACKAGE_SLUG=celerpy -ifdef CI - PYENV_TARGET:= - PYENV_PYTHON=python - PYTHON_VERSION:=$(shell $(PYENV_PYTHON) --version|cut -d" " -f2) -else - PYENV_TARGET:=pyenv - PYENV_PYTHON:=pyenv exec python - PYTHON_VERSION:=$(shell cat .python-version) -endif - -ifeq ($(USE_SYSTEM_PYTHON), true) - PYTHON_PACKAGE_PATH:=$(shell $(PYENV_PYTHON) -c "import sys; print(sys.path[-1])") - PYTHON_ENV:= - VENV_TARGET:= -else - PYTHON_SHORT_VERSION:=$(shell echo $(PYTHON_VERSION) | grep -o '[0-9].[0-9]*') - PYTHON_PACKAGE_PATH:=.venv/lib/python$(PYTHON_SHORT_VERSION)/site-packages - PYTHON_ENV:= . .venv/bin/activate && - VENV_TARGET:= .venv -endif - -PYTHON:=$(PYTHON_ENV) python - -# Used to confirm that pip has run at least once -BUILD_DIR:=$(PYTHON_PACKAGE_PATH)/build +PYTHON:=poetry run python +PYTHON_ENV:=poetry run .PHONY: all -all: $(PYENV_TARGET) $(BUILD_DIR) +all: poetry.lock .PHONY: install -install: $(PYENV_TARGET) $(VENV_TARGET) pip +install: poetry-install -.PHONY: pyenv -pyenv: - pyenv install --skip-existing $(PYTHON_VERSION) +.PHONY: poetry-install +poetry-install: + poetry install -.venv: - $(PYENV_PYTHON) -m venv .venv +poetry.lock: pyproject.toml + poetry lock - -# Note that CI is defined when running through github actions -.PHONY: pip -pip: $(VENV_TARGET) pip-install - -.PHONY: pip-install -pip-install: -ifdef CI - $(PYTHON) -m pip install -r requirements-dev.txt -else - $(PYTHON) -m pip install -e .[dev] -endif - -$(BUILD_DIR): $(VENV_TARGET) pip-install .PHONY: pre-commit -pre-commit: $(BUILD_DIR) +pre-commit: poetry.lock $(PYTHON_ENV) pre-commit install # @@ -77,13 +40,13 @@ style/dapperdata: .PHONY: style/tomlsort style/tomlsort: - $(PYTHON_ENV) toml-sort $$(find . -not -path "./.venv/*" -name "*.toml") -i + $(PYTHON_ENV) toml-sort $$(find . -name "*.toml") -i # # Testing # .PHONY: test -test: $(BUILD_DIR) test/all +test: poetry.lock test/all .PHONY: test/all test/all: test/pytest test/ruff test/black test/mypy test/dapperdata test/tomlsort @@ -114,28 +77,18 @@ test/dapperdata: .PHONY: test/tomlsort test/tomlsort: - $(PYTHON_ENV) toml-sort $$(find . -not -path "./.venv/*" -name "*.toml") --check + $(PYTHON_ENV) toml-sort $$(find . -name "*.toml") --check # # Dependencies # .PHONY: rebuild_dependencies rebuild_dependencies: - $(PYTHON) -m uv pip compile --output-file=requirements.txt pyproject.toml - $(PYTHON) -m uv pip compile --output-file=requirements-dev.txt --extra=dev pyproject.toml - -.PHONY: dependencies -dependencies: requirements.txt requirements-dev.txt - -requirements.txt: $(BUILD_DIR) pyproject.toml - $(PYTHON) -m uv pip compile --upgrade --output-file=requirements.txt pyproject.toml - -requirements-dev.txt: $(BUILD_DIR) pyproject.toml - $(PYTHON) -m uv pip compile --upgrade --output-file=requirements-dev.txt --extra=dev pyproject.toml + poetry update # # Packaging # .PHONY: build -build: $(BUILD_DIR) - $(PYTHON) -m build +build: poetry.lock + poetry build diff --git a/pyproject.toml b/pyproject.toml index dff11c6..3d67be1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,48 +1,54 @@ [build-system] -build-backend = "setuptools.build_meta" -requires = ["setuptools>=67.0", "setuptools_scm[toml]>=7.1"] +build-backend = "poetry_dynamic_versioning.backend" +requires = [ + "poetry-core>=2.0.0,<3.0.0", + "poetry-dynamic-versioning>=1.9.1,<2.0.0" +] -[project] -authors = [{"name" = "Seth R. Johnson et al."}] +[tool.mypy] +plugins = [ + "pydantic.mypy" +] + +[tool.poetry] +authors = ["Seth R. Johnson et al."] description = "" -dynamic = ["version"] -license = {"file" = "LICENSE"} +license = "Apache-2.0" name = "celerpy" -readme = {file = "README.md", content-type = "text/markdown"} -dependencies = [ - "pydantic~=2.0", - "pydantic-settings", - "matplotlib>=3.7", - "numpy>=1.20", - "typer" -] +packages = [{include = "celerpy"}] +readme = "README.md" +requires-plugins = {poetry-dynamic-versioning = {version = ">=1.9.1,<2.0.0", extras = ["plugin"]}} +version = "0.0.0" -[project.optional-dependencies] -dev = [ - "build", - "dapperdata", - "glom", - "mypy", - "pre-commit", - "pytest", - "pytest-cov", - "pytest-pretty", - "ruamel.yaml", - "ruff", - "toml-sort", - "uv" -] +[tool.poetry.dependencies] +matplotlib = ">=3.7" +numpy = ">=1.20" +pydantic = ">=2.0,<3.0" +pydantic-settings = ">=2.0,<3.0" +python = ">=3.11,<4.0" +typer = "*" + +[tool.poetry.group.dev.dependencies] +dapperdata = "0.4.0" +glom = "24.11.0" +mypy = "1.18.2" +pre-commit = "4.3.0" +pytest = "8.4.2" +pytest-cov = "7.0.0" +pytest-pretty = "1.3.0" +"ruamel.yaml" = "0.18.15" +ruff = "0.13.3" +toml-sort = "0.24.3" -[project.scripts] +[tool.poetry.scripts] celerpy = "celerpy.cli:app" -[tool.mypy] -plugins = [ - "pydantic.mypy" -] +[tool.poetry-dynamic-versioning] +enable = true +vcs = "git" +style = "semver" [tool.ruff] -exclude = [".venv", "./celerpy/_version.py"] line-length = 80 indent-width = 4 @@ -61,16 +67,3 @@ select = [ # isort "I" ] - -[tool.setuptools.dynamic] -readme = {file = ["README.md"]} - -[tool.setuptools.package-data] -celerpy = ["py.typed"] - -[tool.setuptools.packages] -find = {} - -[tool.setuptools_scm] -fallback_version = "0.0.0-dev" -write_to = "celerpy/_version.py" diff --git a/requirements-dev.txt b/requirements-dev.txt deleted file mode 100644 index acb677c..0000000 --- a/requirements-dev.txt +++ /dev/null @@ -1,147 +0,0 @@ -# This file was autogenerated by uv via the following command: -# uv pip compile --output-file=requirements-dev.txt --extra=dev pyproject.toml -annotated-types==0.7.0 - # via pydantic -attrs==25.3.0 - # via glom -boltons==25.0.0 - # via - # face - # glom -build==1.3.0 - # via celerpy (pyproject.toml) -cfgv==3.4.0 - # via pre-commit -click==8.3.0 - # via typer -contourpy==1.3.3 - # via matplotlib -coverage==7.10.7 - # via pytest-cov -cycler==0.12.1 - # via matplotlib -dapperdata==0.4.0 - # via celerpy (pyproject.toml) -distlib==0.4.0 - # via virtualenv -face==24.0.0 - # via glom -filelock==3.19.1 - # via virtualenv -fonttools==4.60.1 - # via matplotlib -glom==24.11.0 - # via celerpy (pyproject.toml) -identify==2.6.15 - # via pre-commit -iniconfig==2.1.0 - # via pytest -kiwisolver==1.4.9 - # via matplotlib -markdown-it-py==4.0.0 - # via rich -matplotlib==3.10.6 - # via celerpy (pyproject.toml) -mdurl==0.1.2 - # via markdown-it-py -mypy==1.18.2 - # via celerpy (pyproject.toml) -mypy-extensions==1.1.0 - # via mypy -nodeenv==1.9.1 - # via pre-commit -numpy==2.3.3 - # via - # celerpy (pyproject.toml) - # contourpy - # matplotlib -packaging==25.0 - # via - # build - # matplotlib - # pytest -pathspec==0.12.1 - # via mypy -pillow==11.3.0 - # via matplotlib -platformdirs==4.4.0 - # via virtualenv -pluggy==1.6.0 - # via - # pytest - # pytest-cov -pre-commit==4.3.0 - # via celerpy (pyproject.toml) -pydantic==2.11.10 - # via - # celerpy (pyproject.toml) - # dapperdata - # pydantic-settings -pydantic-core==2.33.2 - # via pydantic -pydantic-settings==2.11.0 - # via - # celerpy (pyproject.toml) - # dapperdata -pygments==2.19.2 - # via - # pytest - # rich -pyparsing==3.2.5 - # via matplotlib -pyproject-hooks==1.2.0 - # via build -pytest==8.4.2 - # via - # celerpy (pyproject.toml) - # pytest-cov - # pytest-pretty -pytest-cov==7.0.0 - # via celerpy (pyproject.toml) -pytest-pretty==1.3.0 - # via celerpy (pyproject.toml) -python-dateutil==2.9.0.post0 - # via matplotlib -python-dotenv==1.1.1 - # via pydantic-settings -pyyaml==6.0.3 - # via pre-commit -rich==14.1.0 - # via - # pytest-pretty - # typer -ruamel-yaml==0.18.15 - # via - # celerpy (pyproject.toml) - # dapperdata -ruamel-yaml-clib==0.2.14 - # via ruamel-yaml -ruff==0.13.3 - # via celerpy (pyproject.toml) -shellingham==1.5.4 - # via typer -six==1.17.0 - # via python-dateutil -toml-sort==0.24.3 - # via celerpy (pyproject.toml) -tomlkit==0.13.3 - # via toml-sort -typer==0.19.2 - # via - # celerpy (pyproject.toml) - # dapperdata -typing-extensions==4.15.0 - # via - # mypy - # pydantic - # pydantic-core - # typer - # typing-inspection -typing-inspection==0.4.2 - # via - # pydantic - # pydantic-settings -uv==0.8.23 - # via celerpy (pyproject.toml) -virtualenv==20.34.0 - # via pre-commit diff --git a/requirements.txt b/requirements.txt deleted file mode 100644 index 93d75e0..0000000 --- a/requirements.txt +++ /dev/null @@ -1,63 +0,0 @@ -# This file was autogenerated by uv via the following command: -# uv pip compile --output-file=requirements.txt pyproject.toml -annotated-types==0.7.0 - # via pydantic -click==8.3.0 - # via typer -contourpy==1.3.3 - # via matplotlib -cycler==0.12.1 - # via matplotlib -fonttools==4.60.1 - # via matplotlib -kiwisolver==1.4.9 - # via matplotlib -markdown-it-py==4.0.0 - # via rich -matplotlib==3.10.6 - # via celerpy (pyproject.toml) -mdurl==0.1.2 - # via markdown-it-py -numpy==2.3.3 - # via - # celerpy (pyproject.toml) - # contourpy - # matplotlib -packaging==25.0 - # via matplotlib -pillow==11.3.0 - # via matplotlib -pydantic==2.11.10 - # via - # celerpy (pyproject.toml) - # pydantic-settings -pydantic-core==2.33.2 - # via pydantic -pydantic-settings==2.11.0 - # via celerpy (pyproject.toml) -pygments==2.19.2 - # via rich -pyparsing==3.2.5 - # via matplotlib -python-dateutil==2.9.0.post0 - # via matplotlib -python-dotenv==1.1.1 - # via pydantic-settings -rich==14.1.0 - # via typer -shellingham==1.5.4 - # via typer -six==1.17.0 - # via python-dateutil -typer==0.19.2 - # via celerpy (pyproject.toml) -typing-extensions==4.15.0 - # via - # pydantic - # pydantic-core - # typer - # typing-inspection -typing-inspection==0.4.2 - # via - # pydantic - # pydantic-settings