From e29330832a78e87b0a8bef5558fb889b8e8a6b27 Mon Sep 17 00:00:00 2001 From: Martin Yeo Date: Tue, 24 Sep 2024 19:16:05 +0100 Subject: [PATCH 01/16] Transfer project metadata to pyproject.toml. --- pyproject.toml | 64 +++++++++++++++++++++++++++++++++++++++++++++++++- setup.cfg | 63 ------------------------------------------------- 2 files changed, 63 insertions(+), 64 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index a0b9188b..76805cd3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,11 +5,70 @@ requires = [ "setuptools_scm[toml]>=7.0", "wheel", "oldest-supported-numpy", - "Cython>=3.0" + "Cython>=3.0", ] # Defined by PEP 517 build-backend = "setuptools.build_meta" +[project] +name = "cf-units" +authors = [ + {name = "cf-units Contributors", email = "scitools.pub@gmail.com"} +] +classifiers = [ + "Development Status :: 5 - Production/Stable", + "Intended Audience :: Science/Research", + "License :: OSI Approved :: BSD License", + "Operating System :: OS Independent", + "Programming Language :: Python", + "Programming Language :: Python :: 3 :: Only", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: Implementation :: CPython", + "Topic :: Scientific/Engineering", + "Topic :: Scientific/Engineering :: Atmospheric Science", + "Topic :: Scientific/Engineering :: Oceanography", +] +dependencies = [ + "antlr4-python3-runtime ==4.11.1", # To update this, see cf_units/_udunits2_parser/README.md + "cftime >=1.2", + "jinja2", + "numpy ==1.26.4" + # udunits2 cannot be installed with pip, and it is expected to be + # installed separately. +] +description = "Units of measure as required by the Climate and Forecast (CF) metadata conventions" +dynamic = [ + "version", +] +keywords = [ + "units", + "cf", + "cf-metadata", + "netcdf", + "science", + "earth-science", + "oceanography", + "meteorology", + "climate", + "python", +] +license.file = "LICENSE" +readme = "README.md" +requires-python = ">=3.9" + +[project.optional-dependencies] +docs = ["sphinx"] +test = ["codecov", "cython", "jinja2", "pip", "pytest", "pytest-cov"] +all = ["pre-commit", "sphinx", "codecov", "cython", "jinja2", "pip", "pytest", "pytest-cov"] + +[project.urls] +Code = "https://github.com/SciTools/cf-units" +Discussions = "https://github.com/SciTools/cf-units/discussions" +Issues = "https://github.com/SciTools/cf-units/issues" +Documentation = "https://cf-units.readthedocs.io" + [tool.coverage.run] branch = true plugins = [ @@ -41,6 +100,9 @@ doctest_optionflags = "NORMALIZE_WHITESPACE ELLIPSIS NUMBER" minversion = "6.0" testpaths = "cf_units" +[tool.setuptools] +license-files = ["LICENSE"] + [tool.setuptools_scm] write_to = "cf_units/_version.py" local_scheme = "dirty-tag" diff --git a/setup.cfg b/setup.cfg index 9face95d..69aa7925 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,66 +1,3 @@ -[metadata] -author = SciTools Developers -classifiers = - Development Status :: 5 - Production/Stable - Intended Audience :: Science/Research - License :: OSI Approved :: BSD License - Operating System :: OS Independent - Programming Language :: Python :: 3 :: Only - Programming Language :: Python :: 3.10 - Programming Language :: Python :: 3.11 - Programming Language :: Python :: 3.12 - Topic :: Scientific/Engineering -description = Units of measure as required by the Climate and Forecast (CF) metadata conventions -download_url = https://github.com/SciTools/cf-units -keywords = - units - cf - netcdf - science - oceanography - meteorology - climate -license = BSD -license_files = LICENSE -long_description = file: README.md -long_description_content_type = text/markdown -name = cf-units -project_urls = - Code = https://github.com/SciTools/cf-units - Discussions = https://github.com/SciTools/cf-units/discussions - Issues = https://github.com/SciTools/cf-units/issues -url = https://github.com/SciTools/cf-units -version = attr: cf_units.__version__ - -[options] -include_package_data = True -install_requires = - antlr4-python3-runtime ==4.11.1 # To update this, see cf_units/_udunits2_parser/README.md - cftime >=1.2 - jinja2 - numpy ==1.26.4 - # udunits2 cannot be installed with pip, and it is expected to be - # installed separately. -packages = find_namespace: -python_requires = - >=3.9 -zip_safe = False - -[options.extras_require] -docs = - sphinx -test = - codecov - cython - jinja2 - pip - pytest - pytest-cov -all = - pre-commit - %(docs)s - %(test)s - [flake8] # References: # https://flake8.readthedocs.io/en/latest/user/configuration.html From 31bd4972cf6d40bd711a736957f46eb90136ed68 Mon Sep 17 00:00:00 2001 From: Martin Yeo Date: Tue, 24 Sep 2024 19:59:41 +0100 Subject: [PATCH 02/16] Cythonize is now implicit thanks to build-system. --- setup.py | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/setup.py b/setup.py index 9745ede4..c7993960 100644 --- a/setup.py +++ b/setup.py @@ -6,12 +6,6 @@ from setuptools import Command, Extension, setup -# Default to using cython, but use the .c files if it doesn't exist -try: - from Cython.Build import cythonize -except ImportError: - cythonize = False - COMPILER_DIRECTIVES = {} # This Cython macro disables a build warning, obsolete with Cython>=3 # see : https://cython.readthedocs.io/en/latest/src/userguide/migrating_to_cy30.html#numpy-c-api @@ -130,7 +124,7 @@ def _set_builtin(name, value): udunits_ext = Extension( f"{PACKAGE}._udunits2", - [str(Path(f"{PACKAGE}") / f"_udunits2.{'pyx' if cythonize else 'c'}")], + [str(Path(f"{PACKAGE}") / "_udunits2.pyx")], include_dirs=get_include_dirs(), library_dirs=library_dirs, libraries=["udunits2"], @@ -140,15 +134,6 @@ def _set_builtin(name, value): ), ) -if cythonize: - [udunits_ext] = cythonize( - udunits_ext, - compiler_directives=COMPILER_DIRECTIVES, - # Assert python 3 source syntax: Currently required to suppress a warning, - # even though this is now the default (as-of Cython v3). - language_level="3str", - ) - cmdclass = {"clean_cython": CleanCython, "build_ext": numpy_build_ext} kwargs = dict( From 4b907b6caf4d24be104d07ef9edc5cc7f2b6522c Mon Sep 17 00:00:00 2001 From: Martin Yeo Date: Wed, 25 Sep 2024 14:01:46 +0100 Subject: [PATCH 03/16] Revert "Cythonize is now implicit thanks to build-system." This reverts commit 31bd4972cf6d40bd711a736957f46eb90136ed68. --- setup.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index c7993960..9745ede4 100644 --- a/setup.py +++ b/setup.py @@ -6,6 +6,12 @@ from setuptools import Command, Extension, setup +# Default to using cython, but use the .c files if it doesn't exist +try: + from Cython.Build import cythonize +except ImportError: + cythonize = False + COMPILER_DIRECTIVES = {} # This Cython macro disables a build warning, obsolete with Cython>=3 # see : https://cython.readthedocs.io/en/latest/src/userguide/migrating_to_cy30.html#numpy-c-api @@ -124,7 +130,7 @@ def _set_builtin(name, value): udunits_ext = Extension( f"{PACKAGE}._udunits2", - [str(Path(f"{PACKAGE}") / "_udunits2.pyx")], + [str(Path(f"{PACKAGE}") / f"_udunits2.{'pyx' if cythonize else 'c'}")], include_dirs=get_include_dirs(), library_dirs=library_dirs, libraries=["udunits2"], @@ -134,6 +140,15 @@ def _set_builtin(name, value): ), ) +if cythonize: + [udunits_ext] = cythonize( + udunits_ext, + compiler_directives=COMPILER_DIRECTIVES, + # Assert python 3 source syntax: Currently required to suppress a warning, + # even though this is now the default (as-of Cython v3). + language_level="3str", + ) + cmdclass = {"clean_cython": CleanCython, "build_ext": numpy_build_ext} kwargs = dict( From 9618c7637e9f15060c3e27f796d5596ff6b1f4de Mon Sep 17 00:00:00 2001 From: Martin Yeo Date: Wed, 25 Sep 2024 14:25:46 +0100 Subject: [PATCH 04/16] Commit to existing structure of setup.py. --- setup.py | 41 ++++++++++++++++++++--------------------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/setup.py b/setup.py index 9745ede4..97aaf8d2 100644 --- a/setup.py +++ b/setup.py @@ -1,3 +1,8 @@ +"""Setup routines to enable cf-units' Cython elements. + +All other setup configuration is in `pyproject.toml`. +""" + import sys from distutils.sysconfig import get_config_var from os import environ @@ -6,7 +11,8 @@ from setuptools import Command, Extension, setup -# Default to using cython, but use the .c files if it doesn't exist +# Default to using cython, but use the .c files if it doesn't exist. +# Supports the widest possible range of developer setups. try: from Cython.Build import cythonize except ImportError: @@ -40,27 +46,14 @@ def run(self): path.unlink() -def get_include_dirs(): - include_dirs = [] - include_dir = environ.get("UDUNITS2_INCDIR") - if include_dir is None: - include_dir = get_config_var("INCLUDEDIR") - if include_dir is not None: - include_dirs.append(include_dir) - return include_dirs - - -def get_library_dirs(): - library_dirs = [] - library_dir = environ.get("UDUNITS2_LIBDIR") - if library_dir is None: - library_dir = get_config_var("LIBDIR") - if library_dir is not None: - library_dirs.append(library_dir) - return library_dirs +def get_dirs(env_var: str, config_var: str): + """Get a directory from an environment variable or a distutils config variable.""" + dir = environ.get(env_var) or get_config_var(config_var) + return [dir] if dir else [] def get_package_data(): + """Find and correctly package the UDUNITS2 XML files for a wheel build.""" package_data = {} # Determine whether we're building a wheel. if "bdist_wheel" in sys.argv: @@ -99,6 +92,7 @@ def get_package_data(): def numpy_build_ext(pars): + """Make the NumPy headers available for the Cython layer.""" from setuptools.command.build_ext import build_ext as _build_ext class build_ext(_build_ext): @@ -126,12 +120,16 @@ def _set_builtin(name, value): sys.argv.remove(FLAG_COVERAGE) print('enable: "linetrace" Cython compiler directive') -library_dirs = get_library_dirs() +include_dirs = get_dirs("UDUNITS2_INCDIR", "INCLUDEDIR") +library_dirs = get_dirs("UDUNITS2_LIBDIR", "LIBDIR") +# Some of the complexity MUST remain in setup.py due to its dynamic nature. To +# reduce confusion, the Extension is 100% defined here, rather than splitting +# between setup.py and pyproject.toml `ext-modules`. udunits_ext = Extension( f"{PACKAGE}._udunits2", [str(Path(f"{PACKAGE}") / f"_udunits2.{'pyx' if cythonize else 'c'}")], - include_dirs=get_include_dirs(), + include_dirs=include_dirs, library_dirs=library_dirs, libraries=["udunits2"], define_macros=DEFINE_MACROS, @@ -141,6 +139,7 @@ def _set_builtin(name, value): ) if cythonize: + # https://docs.cython.org/en/latest/src/userguide/source_files_and_compilation.html#distributing-cython-modules [udunits_ext] = cythonize( udunits_ext, compiler_directives=COMPILER_DIRECTIVES, From 11a10ef83ff8111d443726e60f4541f5029dcd0f Mon Sep 17 00:00:00 2001 From: Martin Yeo Date: Wed, 25 Sep 2024 14:37:46 +0100 Subject: [PATCH 05/16] Remove unnecessary alias from setup.cfg. --- setup.cfg | 3 --- 1 file changed, 3 deletions(-) diff --git a/setup.cfg b/setup.cfg index 69aa7925..c74cb2d4 100644 --- a/setup.cfg +++ b/setup.cfg @@ -30,6 +30,3 @@ exclude = doc, etc, cf_units/_udunits2_parser/parser/* - -[aliases] -test = pytest From 562a8332e2a0f165b36eab76516ba3de832a50f8 Mon Sep 17 00:00:00 2001 From: Martin Yeo Date: Wed, 25 Sep 2024 15:08:43 +0100 Subject: [PATCH 06/16] Attempt fix package discovery. --- pyproject.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index 76805cd3..b1ec6c2d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -103,6 +103,9 @@ testpaths = "cf_units" [tool.setuptools] license-files = ["LICENSE"] +[tool.setuptools.packages.find] +include = ["cf_units"] + [tool.setuptools_scm] write_to = "cf_units/_version.py" local_scheme = "dirty-tag" From c7f288f8175f53dc6daf434117174fd5382e91f2 Mon Sep 17 00:00:00 2001 From: Martin Yeo Date: Wed, 25 Sep 2024 15:16:40 +0100 Subject: [PATCH 07/16] Fix test_python_versions. --- cf_units/tests/test_coding_standards.py | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/cf_units/tests/test_coding_standards.py b/cf_units/tests/test_coding_standards.py index e1a74d9d..9e98b639 100644 --- a/cf_units/tests/test_coding_standards.py +++ b/cf_units/tests/test_coding_standards.py @@ -128,7 +128,6 @@ def test_python_versions(): # Places that are checked: pyproject_toml_file = REPO_DIR / "pyproject.toml" - setup_cfg_file = REPO_DIR / "setup.cfg" tox_file = REPO_DIR / "tox.ini" ci_locks_file = workflows_dir / "ci-locks.yml" ci_tests_file = workflows_dir / "ci-tests.yml" @@ -137,19 +136,19 @@ def test_python_versions(): text_searches: list[tuple[Path, str]] = [ ( pyproject_toml_file, - "target-version = [" - + ", ".join([f'"py{p}"' for p in supported_strip]) - + "]", - ), - ( - setup_cfg_file, "\n ".join( [ - f"Programming Language :: Python :: {ver}" + f'"Programming Language :: Python :: {ver}",' for ver in supported ] ), ), + ( + pyproject_toml_file, + "target-version = [" + + ", ".join([f'"py{p}"' for p in supported_strip]) + + "]", + ), ( tox_file, "[testenv:py{" + ",".join(supported_strip) + "}-lock]", From 3102a8a9f01bf42823f779fa6d5597e652bbaf0a Mon Sep 17 00:00:00 2001 From: Martin Yeo Date: Wed, 25 Sep 2024 15:20:46 +0100 Subject: [PATCH 08/16] Don't override dir. --- setup.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index 97aaf8d2..b565b1e4 100644 --- a/setup.py +++ b/setup.py @@ -48,8 +48,8 @@ def run(self): def get_dirs(env_var: str, config_var: str): """Get a directory from an environment variable or a distutils config variable.""" - dir = environ.get(env_var) or get_config_var(config_var) - return [dir] if dir else [] + result = environ.get(env_var) or get_config_var(config_var) + return [result] if result else [] def get_package_data(): From 7cdfe95134502915822d72a45e491e73ebc0bfd6 Mon Sep 17 00:00:00 2001 From: Martin Yeo Date: Wed, 25 Sep 2024 15:21:12 +0100 Subject: [PATCH 09/16] Test with Test PyPI. --- .github/workflows/ci-wheels.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci-wheels.yml b/.github/workflows/ci-wheels.yml index 860ea22c..9a8f25cb 100644 --- a/.github/workflows/ci-wheels.yml +++ b/.github/workflows/ci-wheels.yml @@ -132,7 +132,7 @@ jobs: name: "Publish to Test PyPI" runs-on: ubuntu-latest # upload to Test PyPI for every commit on main branch - if: github.event_name == 'push' && github.event.ref == 'refs/heads/main' + if: github.event_name == 'push' steps: - uses: actions/download-artifact@v3 with: From 42d17586c7d8435349c1c1e6e732fc806401f8ab Mon Sep 17 00:00:00 2001 From: Martin Yeo Date: Wed, 25 Sep 2024 16:57:35 +0100 Subject: [PATCH 10/16] Revert "Test with Test PyPI." This reverts commit 7cdfe95134502915822d72a45e491e73ebc0bfd6. --- .github/workflows/ci-wheels.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci-wheels.yml b/.github/workflows/ci-wheels.yml index 9a8f25cb..860ea22c 100644 --- a/.github/workflows/ci-wheels.yml +++ b/.github/workflows/ci-wheels.yml @@ -132,7 +132,7 @@ jobs: name: "Publish to Test PyPI" runs-on: ubuntu-latest # upload to Test PyPI for every commit on main branch - if: github.event_name == 'push' + if: github.event_name == 'push' && github.event.ref == 'refs/heads/main' steps: - uses: actions/download-artifact@v3 with: From dab4560c80227d6f6b805e151e50df41999b987a Mon Sep 17 00:00:00 2001 From: Martin Yeo Date: Wed, 25 Sep 2024 17:08:11 +0100 Subject: [PATCH 11/16] Pre-commit compliance. --- cf_units/tests/test_coding_standards.py | 2 +- pyproject.toml | 3 +-- setup.py | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/cf_units/tests/test_coding_standards.py b/cf_units/tests/test_coding_standards.py index de7d19f3..e0caa0fb 100644 --- a/cf_units/tests/test_coding_standards.py +++ b/cf_units/tests/test_coding_standards.py @@ -145,7 +145,7 @@ def test_python_versions(): ), ( pyproject_toml_file, - f'target-version = "py{supported_strip[0]}"', + f'requires-python = ">={supported[0]}"', ), ( tox_file, diff --git a/pyproject.toml b/pyproject.toml index 4de5cb16..d727e898 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -56,7 +56,7 @@ keywords = [ ] license.file = "LICENSE" readme = "README.md" -requires-python = ">=3.9" +requires-python = ">=3.10" [project.optional-dependencies] docs = ["sphinx"] @@ -116,7 +116,6 @@ exclude = [ "cf_units/_udunits2_parser/parser", ] line-length = 79 -target-version = "py310" [tool.ruff.lint] select = [ diff --git a/setup.py b/setup.py index e68089cb..2f7c3587 100644 --- a/setup.py +++ b/setup.py @@ -47,7 +47,7 @@ def run(self): def get_dirs(env_var: str, config_var: str): - """Get a directory from an environment variable or a distutils config variable.""" + """Get a directory from an env variable or a distutils config variable.""" result = environ.get(env_var) or get_config_var(config_var) return [result] if result else [] From 5c8ac913bbca953b7597886739549a2af484b9de Mon Sep 17 00:00:00 2001 From: Martin Yeo Date: Wed, 25 Sep 2024 17:08:33 +0100 Subject: [PATCH 12/16] Remove empty setup.cfg. --- setup.cfg | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 setup.cfg diff --git a/setup.cfg b/setup.cfg deleted file mode 100644 index e69de29b..00000000 From f4ee03a0b9642039564b4147fe396965e5141411 Mon Sep 17 00:00:00 2001 From: Martin Yeo Date: Thu, 26 Sep 2024 09:17:05 +0100 Subject: [PATCH 13/16] Rely on setuptools defaults for license-files. --- pyproject.toml | 3 --- 1 file changed, 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index d727e898..d594d828 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -100,9 +100,6 @@ doctest_optionflags = "NORMALIZE_WHITESPACE ELLIPSIS NUMBER" minversion = "6.0" testpaths = "cf_units" -[tool.setuptools] -license-files = ["LICENSE"] - [tool.setuptools.packages.find] include = ["cf_units"] From 9cf0ace63d4532cfdaa555de3097fbd315367af7 Mon Sep 17 00:00:00 2001 From: Martin Yeo Date: Thu, 26 Sep 2024 14:21:14 +0100 Subject: [PATCH 14/16] Use recommended syntax for optional dependencies. --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index d594d828..fcc04cce 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -59,9 +59,9 @@ readme = "README.md" requires-python = ">=3.10" [project.optional-dependencies] +all = ["cf-units[docs]", "cf-units[test]"] docs = ["sphinx"] test = ["codecov", "cython", "jinja2", "pip", "pytest", "pytest-cov"] -all = ["pre-commit", "sphinx", "codecov", "cython", "jinja2", "pip", "pytest", "pytest-cov"] [project.urls] Code = "https://github.com/SciTools/cf-units" From 05ba860494406f4f1fea70123cfc9e209eff7c1e Mon Sep 17 00:00:00 2001 From: Martin Yeo Date: Thu, 26 Sep 2024 14:34:04 +0100 Subject: [PATCH 15/16] Ruff compliance. --- cf_units/tests/integration/parse/test_graph.py | 1 + cf_units/tests/integration/parse/test_parse.py | 1 + cf_units/tests/test_tex.py | 1 + 3 files changed, 3 insertions(+) diff --git a/cf_units/tests/integration/parse/test_graph.py b/cf_units/tests/integration/parse/test_graph.py index 9743b8a0..fb1ad67f 100644 --- a/cf_units/tests/integration/parse/test_graph.py +++ b/cf_units/tests/integration/parse/test_graph.py @@ -2,6 +2,7 @@ # # This file is part of cf-units and is released under the BSD license. # See LICENSE in the root of the repository for full licensing details. +# ruff: noqa: E402 import pytest antlr4 = pytest.importorskip("antlr4") diff --git a/cf_units/tests/integration/parse/test_parse.py b/cf_units/tests/integration/parse/test_parse.py index b76fadbf..5e91fd5b 100644 --- a/cf_units/tests/integration/parse/test_parse.py +++ b/cf_units/tests/integration/parse/test_parse.py @@ -2,6 +2,7 @@ # # This file is part of cf-units and is released under the BSD license. # See LICENSE in the root of the repository for full licensing details. +# ruff: noqa: E402 import pytest antlr4 = pytest.importorskip("antlr4") diff --git a/cf_units/tests/test_tex.py b/cf_units/tests/test_tex.py index 966c887a..ffb91b6e 100644 --- a/cf_units/tests/test_tex.py +++ b/cf_units/tests/test_tex.py @@ -2,6 +2,7 @@ # # This file is part of cf-units and is released under the BSD license. # See LICENSE in the root of the repository for full licensing details. +# ruff: noqa: E402 import pytest antlr4 = pytest.importorskip("antlr4") From e34a68ece77ecc39c946933c1bbf14916317dbfb Mon Sep 17 00:00:00 2001 From: Martin Yeo Date: Thu, 26 Sep 2024 15:09:50 +0100 Subject: [PATCH 16/16] Correct NumPy pin. --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 6acdbc05..c3d4cb16 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -33,7 +33,7 @@ classifiers = [ dependencies = [ "cftime >=1.2", "jinja2", - "numpy ==1.26.4" + "numpy <2" # udunits2 cannot be installed with pip, and it is expected to be # installed separately. ]