diff --git a/.gitignore b/.gitignore index aabfdb0..cc493e6 100644 --- a/.gitignore +++ b/.gitignore @@ -141,3 +141,6 @@ dmypy.json # geoh5 locks *.geoh5.lock + +#version ignore +omf/_version.py diff --git a/omf/__init__.py b/omf/__init__.py index 33b2ee3..3adf17e 100644 --- a/omf/__init__.py +++ b/omf/__init__.py @@ -16,6 +16,7 @@ import logging import sys +from importlib.metadata import PackageNotFoundError, version from .base import Project from .data import ( @@ -44,7 +45,14 @@ from .volume import VolumeElement, VolumeGridGeometry -__version__ = "3.4.0a1" +try: + from ._version import __version__ +except ModuleNotFoundError: + from datetime import datetime + + __date_str = datetime.today().strftime("%Y%m%d") + __version__ = "0.0.0.dev0+" + __date_str + __author__ = "Global Mining Standards and Guidelines Group, Mira Geoscience Ltd." __license__ = "MIT License" __copyright__ = ( diff --git a/pyproject.toml b/pyproject.toml index d9828ee..dc9f125 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,16 +1,15 @@ [build-system] -requires = ["poetry-core>=1.8.0"] -build-backend = "poetry.core.masonry.api" +requires = ["poetry-core>=1.8.0", "poetry-dynamic-versioning>=1.9.0,<2.0.0"] +build-backend = "poetry_dynamic_versioning.backend" [project] name = "mira-omf" -version = "3.4.0a1" requires-python = ">=3.10,<4.0" description = "API Library for Open Mining Format" license = "MIT" keywords = ["geology", "geophysics", "earth sciences"] readme = "README.rst" -dynamic = ["dependencies", "classifiers"] +dynamic = ["version", "dependencies", "classifiers"] authors = [ { name = "Mira Geoscience", email = "support@mirageoscience.com" }, { name = "Global Mining Guidelines Group", email = "info@gmggroup.org" } @@ -54,6 +53,7 @@ include = [ { path = "THIRD_PARTY_SOFTWARE.rst" }, { path = "docs/**/THIRD_PARTY_SOFTWARE.rst" }, ] +version = "0.0.0.dev0" [tool.poetry.dependencies] numpy = "~1.26.0" # also in geoh5py @@ -79,6 +79,34 @@ jinja2 = '*' packaging = '*' tomli = "*" # for tests only +[tool.poetry.requires-plugins] +poetry-dynamic-versioning = { version = ">=1.0.0,<2.0.0", extras = ["plugin"] } + +[tool.poetry-dynamic-versioning] +bump = true +enable = true +fix-shallow-repository = true +strict = true +style = "pep440" +vcs = "git" + +[tool.poetry-dynamic-versioning.substitution] +files = ["omf/_version.py", "recipe.yaml"] +patterns = [ + { value = '''(^__version__\s*(?::.*?)?=\s*['"])[^'"]*(['"])''', mode = "str" }, + { value = '''(^\s*version\s*(?::.*?)?:\s*['"])[^'"]*(['"])''', mode = "str" }, +] + +[tool.poetry-dynamic-versioning.files."omf/_version.py"] +persistent-substitution = true +initial-content = """ + # Version placeholder that will be replaced during substitution + __version__ = "0.0.0" +""" + +[tool.poetry-dynamic-versioning.files."recipe.yaml"] +persistent-substitution = true + [tool.ruff] target-version = "py310" diff --git a/recipe.yaml b/recipe.yaml index 433ea44..bfa36a5 100644 --- a/recipe.yaml +++ b/recipe.yaml @@ -2,7 +2,7 @@ schema_version: 1 context: name: "mira-omf" - version: "3.4.0a1" + version: "0.0.0.dev0" # This will be replaced by the actual version in the build process python_min: "3.10" package: @@ -25,7 +25,8 @@ requirements: host: - pip - python 3.10.* - - poetry-core >=1.0.0 + - poetry-core >=1.8.0 + - poetry-dynamic-versioning >=1.9, <2.0.dev - setuptools run: - python >=${{ python_min }} @@ -42,6 +43,7 @@ tests: - python: imports: - omf + - omf._version pip_check: false # unclear why pip check reports that installed versions # of vectormath and properties are "not supported on this platform" # while they seem to work fine in the tests diff --git a/tests/version_test.py b/tests/version_test.py index d261db4..7bbb058 100644 --- a/tests/version_test.py +++ b/tests/version_test.py @@ -12,10 +12,11 @@ from __future__ import annotations +import importlib import re from pathlib import Path -import tomli as toml +import pytest import yaml from jinja2 import Template from packaging.version import InvalidVersion, Version @@ -23,15 +24,6 @@ import omf -def get_pyproject_version(): - path = Path(__file__).resolve().parents[1] / "pyproject.toml" - - with open(str(path), encoding="utf-8") as file: - pyproject = toml.loads(file.read()) - - return pyproject["project"]["version"] - - def get_conda_recipe_version(): path = Path(__file__).resolve().parents[1] / "recipe.yaml" @@ -47,10 +39,44 @@ def get_conda_recipe_version(): def test_version_is_consistent(): - assert omf.__version__ == get_pyproject_version() - normalized_conda_version = Version(get_conda_recipe_version()) - normalized_version = Version(omf.__version__) - assert normalized_conda_version == normalized_version + project_version = Version(omf.__version__) + conda_version = Version(get_conda_recipe_version()) + assert conda_version.base_version == project_version.base_version + + +def _version_module_exists(): + try: + importlib.import_module("omf._version") + return True + except ModuleNotFoundError: + return False + + +@pytest.mark.skipif( + _version_module_exists(), + reason="omf._version can be found: package is built", +) +def test_fallback_version_is_zero(): + project_version = Version(omf.__version__) + fallback_version = Version("0.0.0.dev0") + assert project_version.base_version == fallback_version.base_version + assert project_version.pre is None + assert project_version.post is None + assert project_version.dev == fallback_version.dev + + +@pytest.mark.skipif( + not _version_module_exists(), + reason="omf._version cannot be found: uses a fallback version", +) +def test_conda_version_is_consistent(): + project_version = Version(omf.__version__) + conda_version = Version(get_conda_recipe_version()) + + assert conda_version.is_devrelease == project_version.is_devrelease + assert conda_version.is_prerelease == project_version.is_prerelease + assert conda_version.is_postrelease == project_version.is_postrelease + assert conda_version == project_version def version_base_and_pre() -> tuple[str, str]: