Skip to content
Merged
2 changes: 1 addition & 1 deletion .github/workflows/ci-locks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ jobs:
shell: bash -l {0}
env:
SHELLOPTS: "errexit:pipefail"
NAME: "cf-units-py313"
NAME: "cf-units-py314"
steps:
- name: "Checkout"
uses: actions/checkout@v6
Expand Down
14 changes: 9 additions & 5 deletions .github/workflows/ci-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,23 +31,24 @@ jobs:
strategy:
matrix:
os: [ubuntu-latest]
version: [py310, py311, py312, py313]
version: [py311, py312, py313, py314]
gitpath-prepend: [""]
include:
- os: ubuntu-latest
platform: linux
- os: ubuntu-latest
version: py313
version: py314
posargs: "--cov-report=xml --cov"
- os: macos-latest
version: py313
version: py314
platform: osx
# On macos, the up-to-date git may not be first on the path
# N.B. setting includes a final ":", to simplify the path setting command
gitpath-prepend: "/opt/homebrew/bin:"
- os: windows-latest
version: py313
version: py314
platform: win
fail-fast: false
steps:
- name: "Checkout"
uses: actions/checkout@v6
Expand Down Expand Up @@ -81,7 +82,10 @@ jobs:
export PATH=${{ matrix.gitpath-prepend }}$PATH
which git
git --version
pixi run --frozen pytest ${{ matrix.posargs }}
pip install -e . --no-build-isolation --no-deps
pushd requirements/
pixi run --frozen pytest --pyargs cf_units ${{ matrix.posargs }}
popd

- name: "Upload coverage report to Codecov"
if: contains(matrix.posargs, '--cov')
Expand Down
4 changes: 3 additions & 1 deletion .github/workflows/ci-wheels.yml
Original file line number Diff line number Diff line change
Expand Up @@ -93,8 +93,10 @@ jobs:
- name: "Building ${{ matrix.os }} (${{ matrix.arch }}) wheels"
uses: pypa/cibuildwheel@v3.3.1
env:
CIBW_SKIP: "cp39-* pp* *-musllinux*"
CIBW_ENVIRONMENT: CF_UNITS_LIMITED_API="1"
CIBW_SKIP: "pp* *-musllinux*"
CIBW_ARCHS: ${{ matrix.arch }}
CIBW_BUILD: "cp311-* cp314-*"
CIBW_BUILD_FRONTEND: build
CIBW_MANYLINUX_X86_64_IMAGE: manylinux2014
CIBW_BEFORE_BUILD_LINUX: yum install -y udunits2-devel
Expand Down
10 changes: 7 additions & 3 deletions cf_units/tests/test_coding_standards.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ def test_license_headers(self):
@pytest.mark.skipif(not IS_GIT_REPO, reason="Not a git repository.")
def test_python_versions():
"""Confirm alignment of ALL files listing supported Python versions."""
supported = ["3.10", "3.11", "3.12", "3.13"]
supported = ["3.11", "3.12", "3.13", "3.14"]
supported_strip = [ver.replace(".", "") for ver in supported]
_parsed = [Version(v) for v in supported]
supported_latest = str(max(_parsed)).replace(".", "")
Expand Down Expand Up @@ -139,8 +139,12 @@ def test_python_versions():
assert search in path.read_text()

ci_wheels_text = ci_wheels_file.read_text()
(cibw_line,) = (line for line in ci_wheels_text.splitlines() if "CIBW_SKIP" in line)
assert all(p not in cibw_line for p in supported_strip)
(cibw_line,) = (
line for line in ci_wheels_text.splitlines() if "CIBW_BUILD:" in line
)
# Only check if min (ABI3) and latest are tested as wheels.
min_and_max = supported_strip[:: len(supported_strip) - 1]
assert all(p in cibw_line for p in min_and_max)

with pyproject_toml_file.open("rb") as f:
data = tomli.load(f)
Expand Down
3,700 changes: 2,141 additions & 1,559 deletions pixi.lock

Large diffs are not rendered by default.

26 changes: 13 additions & 13 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@ classifiers = [
"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 :: 3.13",
"Programming Language :: Python :: 3.14",
"Programming Language :: Python :: Implementation :: CPython",
"Topic :: Scientific/Engineering",
"Topic :: Scientific/Engineering :: Atmospheric Science",
Expand Down Expand Up @@ -53,7 +53,7 @@ keywords = [
]
name = "cf-units"
readme = "README.md"
requires-python = ">=3.10"
requires-python = ">=3.11"
license = "BSD-3-Clause"
license-files = ["LICENSE"]

Expand Down Expand Up @@ -202,12 +202,6 @@ docs = { features = ["docs"], solve-group = "default" }
test = { features = ["test", "dev"], solve-group = "default" }
cf-units = { features = ["test", "dev", "docs"], solve-group = "default" }

py310 = { features = ["py310"], solve-group = "py310" }
dev-py310 = { features = ["dev", "py310"], solve-group = "py310" }
docs-py310 = { features = ["docs", "py310"], solve-group = "py310" }
test-py310 = { features = ["test", "dev", "py310"], solve-group = "py310" }
cf-units-py310 = { features = ["test", "dev", "docs", "py310"], solve-group = "py310" }

py311 = { features = ["py311"], solve-group = "py311" }
dev-py311 = { features = ["dev", "py311"], solve-group = "py311" }
docs-py311 = { features = ["docs", "py311"], solve-group = "py311" }
Expand All @@ -226,6 +220,12 @@ docs-py313 = { features = ["docs", "py313"], solve-group = "py313" }
test-py313 = { features = ["test", "dev", "py313"], solve-group = "py313" }
cf-units-py313 = { features = ["test", "dev", "docs", "py313"], solve-group = "py313" }

py314 = { features = ["py314"], solve-group = "py314" }
dev-py314 = { features = ["dev", "py314"], solve-group = "py314" }
docs-py314 = { features = ["docs", "py314"], solve-group = "py314" }
test-py314 = { features = ["test", "dev", "py314"], solve-group = "py314" }
cf-units-py314 = { features = ["test", "dev", "docs", "py314"], solve-group = "py314" }

[tool.pixi.dependencies]
cftime = ">=1.2"
cython = ">=3.0"
Expand Down Expand Up @@ -267,11 +267,6 @@ pytest = "*"
pytest-cov = "*"
tomli = "*"

[tool.pixi.feature.py310.dependencies]
# Pip is in here because version-specific considerations are sometimes expected.
pip = "*"
python = "3.10.*"

[tool.pixi.feature.py311.dependencies]
pip = "*"
python = "3.11.*"
Expand All @@ -285,6 +280,11 @@ pip = "*"
# cp313 = new syntax available for 3.13. onwards.
python = { version= "3.13.*", build = "*_cp313" }

[tool.pixi.feature.py314.dependencies]
pip = "*"
python = { version= "3.14.*", build = "*_cp314" }


[tool.pytest.ini_options]
addopts = ["-ra", "-v", "--strict-config", "--strict-markers", "--doctest-modules"]
doctest_optionflags = "NORMALIZE_WHITESPACE ELLIPSIS NUMBER"
Expand Down
23 changes: 21 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@
"""

from distutils.sysconfig import get_config_var
from os import environ
from os import environ, getenv
from pathlib import Path
from shutil import copy
import sys
import sysconfig

from setuptools import Command, Extension, setup
from setuptools.command import build_ext
Expand All @@ -19,10 +20,21 @@
except ImportError:
cythonize = False

USE_PY_LIMITED_API = (
# Builds are not ABI3 default
getenv("CF_UNITS_LIMITED_API", "0") == "1"
and sys.version_info >= (3, 11)
# Free-threaded builds do not support ABI3
and not sysconfig.get_config_var("Py_GIL_DISABLED")
)

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
DEFINE_MACROS = [("NPY_NO_DEPRECATED_API", "NPY_1_7_API_VERSION")]
if USE_PY_LIMITED_API:
# 0x030B0000 -> 3.11
DEFINE_MACROS.append(("Py_LIMITED_API", "0x030B0000"))
FLAG_COVERAGE = "--cython-coverage" # custom flag enabling Cython line tracing
BASEDIR = Path(__file__).resolve().parent
PACKAGE = "cf_units"
Expand Down Expand Up @@ -128,8 +140,9 @@ def _set_builtin(name, value):
include_dirs=include_dirs,
library_dirs=library_dirs,
libraries=["udunits2"],
define_macros=DEFINE_MACROS,
runtime_library_dirs=(None if sys.platform.startswith("win") else library_dirs),
define_macros=DEFINE_MACROS,
py_limited_api=True,
)

if cythonize:
Expand All @@ -144,10 +157,16 @@ def _set_builtin(name, value):

cmdclass = {"clean_cython": CleanCython, "build_ext": NumpyBuildExt}

if USE_PY_LIMITED_API:
SETUP_OPTIONS = {"bdist_wheel": {"py_limited_api": "cp311"}}
else:
SETUP_OPTIONS = {}

kwargs = {
"cmdclass": cmdclass,
"ext_modules": [udunits_ext],
"package_data": get_package_data(),
"options": SETUP_OPTIONS,
}

setup(**kwargs)
Loading