From c90f548f3fb774f853ed337b76f993a7d4a63a92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20M=C3=BCller?= Date: Fri, 26 Dec 2025 13:46:45 +0100 Subject: [PATCH 01/13] CI: add py314, remove py39, update cibw, cleanup --- .github/workflows/main.yml | 14 ++++++-------- pyproject.toml | 17 ++++++++--------- 2 files changed, 14 insertions(+), 17 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 1a0c39c..260a635 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -59,7 +59,7 @@ jobs: fail-fast: false matrix: # macos-13 is an intel runner, macos-latest is apple silicon - os: [ubuntu-latest, ubuntu-24.04-arm, windows-latest, macos-13, macos-latest] + os: [ubuntu-latest, ubuntu-24.04-arm, windows-latest, macos-15-intel, macos-latest] steps: - uses: actions/checkout@v4 @@ -67,7 +67,7 @@ jobs: fetch-depth: "0" - name: Build wheels - uses: pypa/cibuildwheel@v2.22.0 + uses: pypa/cibuildwheel@v3.3.0 with: output-dir: dist-wheel-${{ matrix.os }} @@ -82,19 +82,17 @@ jobs: strategy: fail-fast: false matrix: - os: [ubuntu-latest, windows-latest, macos-13, macos-14] + os: [ubuntu-latest, windows-latest, macos-15-intel, macos-latest] # https://github.com/scipy/oldest-supported-numpy/blob/main/setup.cfg ver: - - { py: "3.9", np: "==1.20.0" } - { py: "3.10", np: "==1.21.6" } - { py: "3.11", np: "==1.23.2" } - { py: "3.12", np: "==1.26.2" } - { py: "3.13", np: "==2.1.0" } - - { py: "3.13", np: ">=2.1.0" } + - { py: "3.14", np: "==2.3.2" } + - { py: "3.14", np: ">=2.3.2" } exclude: - - os: macos-14 - ver: { py: "3.9", np: "==1.20.0" } - - os: macos-14 + - os: macos-latest ver: { py: "3.10", np: "==1.21.6" } steps: - uses: actions/checkout@v4 diff --git a/pyproject.toml b/pyproject.toml index 1650c9e..9325a8e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,16 +1,15 @@ [build-system] requires = [ "setuptools>=64", - "setuptools<72.2; implementation_name == 'pypy'", # https://github.com/pypa/distutils/issues/283 "setuptools_scm>=7", - "numpy>=2.0.0rc1", - "Cython>=3.0.10,<3.1.0", + "numpy>=2", + "Cython>=3", "extension-helpers>=1", ] build-backend = "setuptools.build_meta" [project] -requires-python = ">=3.9" +requires-python = ">=3.10" name = "gstools_cython" description = "Cython backend for GSTools." authors = [ @@ -33,11 +32,11 @@ classifiers = [ "Programming Language :: Python", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3 :: Only", - "Programming Language :: Python :: 3.9", "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", "Topic :: Scientific/Engineering", "Topic :: Scientific/Engineering :: GIS", "Topic :: Scientific/Engineering :: Hydrology", @@ -58,7 +57,7 @@ doc = [ ] test = [ "pytest-cov>=3", - "Cython>=3.0.10,<3.1.0", + "Cython>=3", ] lint = [ "black>=24", @@ -90,11 +89,11 @@ multi_line_output = 3 [tool.black] target-version = [ - "py39", "py310", "py311", "py312", "py313", + "py314", ] [tool.coverage] @@ -145,8 +144,8 @@ target-version = [ build-frontend = "build" # explicitly enable pypy enable = ["pypy"] -# Disable building py3.6/7/8, pp3.8, 32bit linux -skip = ["cp36-*", "cp37-*", "cp38-*", "pp38-*", "*_i686"] +# Disable building py3.8/9, 32bit linux +skip = ["cp38-*", "cp39-*", "*_i686"] # Run the package tests using `pytest` test-extras = "test" test-command = "pytest -v {package}/tests" From d147b872c434b8a0355b6442e50eafb177b46320 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20M=C3=BCller?= Date: Fri, 26 Dec 2025 13:54:39 +0100 Subject: [PATCH 02/13] variogram: fix error message bug --- src/gstools_cython/variogram.pyx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/gstools_cython/variogram.pyx b/src/gstools_cython/variogram.pyx index 195fdc2..1907c6a 100644 --- a/src/gstools_cython/variogram.pyx +++ b/src/gstools_cython/variogram.pyx @@ -253,7 +253,7 @@ def directional( counts of samples per bin and direciton """ if pos.shape[1] != f.shape[1]: - raise ValueError(f'len(pos) = {pos.shape[1]} != len(f) = {f.shape[1])}') + raise ValueError(f'len(pos) = {pos.shape[1]} != len(f) = {f.shape[1]}') if bin_edges.shape[0] < 2: raise ValueError('len(bin_edges) too small') @@ -359,7 +359,7 @@ def unstructured( raise ValueError(f'Haversine: dim = {dim} != 2') if pos.shape[1] != f.shape[1]: - raise ValueError(f'len(pos) = {pos.shape[1]} != len(f) = {f.shape[1])}') + raise ValueError(f'len(pos) = {pos.shape[1]} != len(f) = {f.shape[1]}') if bin_edges.shape[0] < 2: raise ValueError('len(bin_edges) too small') From 6e89eb14cad8ab100aa86a909633bcc494232f24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20M=C3=BCller?= Date: Fri, 26 Dec 2025 14:03:54 +0100 Subject: [PATCH 03/13] pyproject: update to setuptools>=77 --- pyproject.toml | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 9325a8e..9af8c03 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [build-system] requires = [ - "setuptools>=64", + "setuptools>=77", "setuptools_scm>=7", "numpy>=2", "Cython>=3", @@ -16,7 +16,8 @@ authors = [ {name = "Sebastian Müller, Lennart Schüler", email = "info@geostat-framework.org"}, ] readme = "README.md" -license = {text = "LGPL-3.0"} +license = "LGPL-3.0" +license-files = ["LICENSE"] dynamic = ["version"] classifiers = [ "Development Status :: 5 - Production/Stable", @@ -24,7 +25,6 @@ classifiers = [ "Intended Audience :: End Users/Desktop", "Intended Audience :: Science/Research", "Intended Audience :: Education", - "License :: OSI Approved :: GNU Lesser General Public License v3 (LGPLv3)", "Natural Language :: English", "Operating System :: Unix", "Operating System :: Microsoft", @@ -74,9 +74,6 @@ Homepage = "https://geostat-framework.org/#gstools" Source = "https://github.com/GeoStat-Framework/GSTools-Cython" Tracker = "https://github.com/GeoStat-Framework/GSTools-Cython/issues" -[tool.setuptools] -license-files = ["LICENSE"] - [tool.setuptools_scm] write_to = "src/gstools_cython/_version.py" write_to_template = "__version__ = '{version}'" From 1f34f3d7ee467a1f1248d20e0e24e740e9623fbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20M=C3=BCller?= Date: Fri, 26 Dec 2025 14:04:04 +0100 Subject: [PATCH 04/13] fix black preview issue --- docs/source/conf.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/source/conf.py b/docs/source/conf.py index 50b3611..232fc63 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -203,11 +203,13 @@ def setup(app): # latex_show_urls = 'footnote' # http://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-latex-output latex_elements = { - "preamble": r""" + "preamble": ( + r""" \setcounter{secnumdepth}{1} \setcounter{tocdepth}{2} \pagestyle{fancy} -""", +""" + ), "pointsize": "10pt", "papersize": "a4paper", "fncychap": "\\usepackage[Glenn]{fncychap}", From 5f388074804e4f057141477fdba5af957fc9082f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20M=C3=BCller?= Date: Fri, 26 Dec 2025 14:15:12 +0100 Subject: [PATCH 05/13] CI: try win arm64 wheels --- .github/workflows/main.yml | 2 +- pyproject.toml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 260a635..51bd59b 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -59,7 +59,7 @@ jobs: fail-fast: false matrix: # macos-13 is an intel runner, macos-latest is apple silicon - os: [ubuntu-latest, ubuntu-24.04-arm, windows-latest, macos-15-intel, macos-latest] + os: [ubuntu-latest, ubuntu-24.04-arm, windows-latest, windows-11-arm, macos-15-intel, macos-latest] steps: - uses: actions/checkout@v4 diff --git a/pyproject.toml b/pyproject.toml index 9af8c03..d61fc8b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -141,8 +141,8 @@ target-version = [ build-frontend = "build" # explicitly enable pypy enable = ["pypy"] -# Disable building py3.8/9, 32bit linux -skip = ["cp38-*", "cp39-*", "*_i686"] +# Disable building py3.8/9, 32bit linux, and arm64 windows wheels for python 3.10 (no numpy support) +skip = ["cp38-*", "cp39-*", "*_i686", "cp310-win_arm64"] # Run the package tests using `pytest` test-extras = "test" test-command = "pytest -v {package}/tests" From a56a90389e0599c440259550fcead10850ed5ede Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20M=C3=BCller?= Date: Fri, 26 Dec 2025 15:22:40 +0100 Subject: [PATCH 06/13] fix license specifier --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index d61fc8b..d4a2240 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -16,7 +16,7 @@ authors = [ {name = "Sebastian Müller, Lennart Schüler", email = "info@geostat-framework.org"}, ] readme = "README.md" -license = "LGPL-3.0" +license = "LGPL-3.0-or-later" license-files = ["LICENSE"] dynamic = ["version"] classifiers = [ From d33ba8b43a20a1b10fa8d0cc13fd368754614410 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20M=C3=BCller?= Date: Fri, 26 Dec 2025 15:23:25 +0100 Subject: [PATCH 07/13] CI: fix comment --- .github/workflows/main.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 51bd59b..a617a12 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -58,7 +58,7 @@ jobs: strategy: fail-fast: false matrix: - # macos-13 is an intel runner, macos-latest is apple silicon + # macos-15-intel is an intel runner, macos-latest is apple silicon os: [ubuntu-latest, ubuntu-24.04-arm, windows-latest, windows-11-arm, macos-15-intel, macos-latest] steps: From 72d22a10fe64d71e421dbe84669705173563c23f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20M=C3=BCller?= Date: Fri, 26 Dec 2025 15:42:51 +0100 Subject: [PATCH 08/13] increase coverage --- tests/test_variogram.py | 81 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 81 insertions(+) diff --git a/tests/test_variogram.py b/tests/test_variogram.py index c8b6631..25cba6f 100644 --- a/tests/test_variogram.py +++ b/tests/test_variogram.py @@ -34,6 +34,58 @@ def test_directional(self): self.assertAlmostEqual(gamma[1, len(gamma[0]) // 2], var, places=2) self.assertAlmostEqual(gamma[1, -1], var, places=2) + def test_directional_separate_dirs(self): + pos = np.array(((0.0, 1.0), (0.0, 0.0)), dtype=np.double) + dirs = np.array(((1.0, 0.0), (1.0, 0.0)), dtype=np.double) + field = np.array(((1.0, 2.0),), dtype=np.double) + bins = np.array((0.0, 2.0), dtype=np.double) + + _, counts = gs_cy.variogram.directional( + field, bins, pos, dirs, angles_tol=np.pi, separate_dirs=True + ) + + self.assertEqual(counts[0, 0], 1) + self.assertEqual(counts[1, 0], 0) + + def test_directional_bandwidth_cressie(self): + pos = np.array(((0.0, 0.0, 1.0), (0.0, 1.0, 0.0)), dtype=np.double) + dirs = np.array(((1.0, 0.0),), dtype=np.double) + field = np.array(((1.0, 2.0, 5.0),), dtype=np.double) + bins = np.array((0.0, 2.0), dtype=np.double) + + gamma, counts = gs_cy.variogram.directional( + field, + bins, + pos, + dirs, + angles_tol=np.pi, + bandwidth=0.5, + estimator_type="c", + ) + + self.assertEqual(counts[0, 0], 1) + f_diff = field[0, 2] - field[0, 0] + raw = np.sqrt(abs(f_diff)) + expected = 0.5 * raw**4 / (0.457 + 0.494 + 0.045) + self.assertAlmostEqual(gamma[0, 0], expected, places=6) + + def test_directional_error_checks(self): + pos = np.array(((0.0, 1.0), (0.0, 1.0)), dtype=np.double) + dirs = np.array(((1.0, 0.0),), dtype=np.double) + bins = np.array((0.0, 1.0), dtype=np.double) + field = np.array(((1.0, 2.0),), dtype=np.double) + + with self.assertRaises(ValueError): + gs_cy.variogram.directional(field[:, :1], bins, pos, dirs) + + with self.assertRaises(ValueError): + gs_cy.variogram.directional( + field, np.array((0.0,), dtype=np.double), pos, dirs + ) + + with self.assertRaises(ValueError): + gs_cy.variogram.directional(field, bins, pos, dirs, angles_tol=0.0) + def test_unstructured(self): x = np.arange(1, 11, 1, dtype=np.double) z = np.array( @@ -73,6 +125,35 @@ def test_unstructured(self): self.assertAlmostEqual(gamma[len(gamma) // 2], var, places=2) self.assertAlmostEqual(gamma[-1], var, places=2) + def test_unstructured_haversine(self): + pos = np.array(((0.0, 0.0), (0.0, 90.0)), dtype=np.double) + field = np.array(((1.0, 3.0),), dtype=np.double) + bins = np.array((0.0, 2.0), dtype=np.double) + + gamma, counts = gs_cy.variogram.unstructured( + field, bins, pos, distance_type="h" + ) + + self.assertEqual(counts[0], 1) + self.assertAlmostEqual(gamma[0], 2.0, places=6) + + def test_unstructured_error_checks(self): + pos = np.array(((0.0, 1.0), (0.0, 1.0)), dtype=np.double) + field = np.array(((1.0, 2.0),), dtype=np.double) + bins = np.array((0.0, 1.0), dtype=np.double) + + with self.assertRaises(ValueError): + gs_cy.variogram.unstructured(field[:, :1], bins, pos) + + with self.assertRaises(ValueError): + gs_cy.variogram.unstructured( + field, np.array((0.0,), dtype=np.double), pos + ) + + pos_bad = np.array(((0.0, 1.0), (0.0, 1.0), (0.0, 1.0)), dtype=np.double) + with self.assertRaises(ValueError): + gs_cy.variogram.unstructured(field, bins, pos_bad, distance_type="h") + def test_structured(self): z = np.array( (41.2, 40.2, 39.7, 39.2, 40.1, 38.3, 39.1, 40.0, 41.1, 40.3), From 9611f36d1d4aaf99b1fe9e27dd3f0d63e8bdb7b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20M=C3=BCller?= Date: Fri, 26 Dec 2025 15:50:43 +0100 Subject: [PATCH 09/13] apply black --- tests/test_variogram.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/test_variogram.py b/tests/test_variogram.py index 25cba6f..64fe2c7 100644 --- a/tests/test_variogram.py +++ b/tests/test_variogram.py @@ -146,9 +146,7 @@ def test_unstructured_error_checks(self): gs_cy.variogram.unstructured(field[:, :1], bins, pos) with self.assertRaises(ValueError): - gs_cy.variogram.unstructured( - field, np.array((0.0,), dtype=np.double), pos - ) + gs_cy.variogram.unstructured(field, np.array((0.0,), dtype=np.double), pos) pos_bad = np.array(((0.0, 1.0), (0.0, 1.0), (0.0, 1.0)), dtype=np.double) with self.assertRaises(ValueError): From 12aacd110ac9773a6222fa27014ab0a608583ef0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20M=C3=BCller?= Date: Fri, 26 Dec 2025 17:18:01 +0100 Subject: [PATCH 10/13] test: check omp handler --- .github/workflows/main.yml | 1 + tests/test_field.py | 2 ++ tests/test_krige.py | 5 +++++ tests/test_variogram.py | 13 +++++++++++++ 4 files changed, 21 insertions(+) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index a617a12..935fee2 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -153,6 +153,7 @@ jobs: - name: Install GSTools-Cython env: GSTOOLS_CY_COV: 1 + GSTOOLS_BUILD_PARALLEL: 1 run: | pip install -v --editable .[test] diff --git a/tests/test_field.py b/tests/test_field.py index bf18db1..7de0591 100644 --- a/tests/test_field.py +++ b/tests/test_field.py @@ -69,6 +69,8 @@ def test_summate(self): ) summed = gs_cy.field.summate(cov_samples, z_1, z_2, pos) np.testing.assert_allclose(summed_modes, summed) + summed_threads = gs_cy.field.summate(cov_samples, z_1, z_2, pos, num_threads=2) + np.testing.assert_allclose(summed_modes, summed_threads) def test_summate_incompr(self): # x = y = np.linspace(0,1,3) diff --git a/tests/test_krige.py b/tests/test_krige.py index e07fde3..b44c7a6 100644 --- a/tests/test_krige.py +++ b/tests/test_krige.py @@ -44,6 +44,11 @@ def test_calc_field_krige_and_variance(self): ) np.testing.assert_allclose(field, self.field_ref) np.testing.assert_allclose(error, self.error_ref) + field_threads, error_threads = gs_cy.krige.calc_field_krige_and_variance( + self.krig_mat, self.krig_vecs, self.cond, num_threads=2 + ) + np.testing.assert_allclose(field_threads, self.field_ref) + np.testing.assert_allclose(error_threads, self.error_ref) def test_calc_field_krige(self): field = gs_cy.krige.calc_field_krige(self.krig_mat, self.krig_vecs, self.cond) diff --git a/tests/test_variogram.py b/tests/test_variogram.py index 64fe2c7..32357b7 100644 --- a/tests/test_variogram.py +++ b/tests/test_variogram.py @@ -137,6 +137,19 @@ def test_unstructured_haversine(self): self.assertEqual(counts[0], 1) self.assertAlmostEqual(gamma[0], 2.0, places=6) + def test_unstructured_num_threads(self): + pos = np.array(((0.0, 1.0, 2.0),), dtype=np.double) + field = np.array(((1.0, 3.0, 2.0),), dtype=np.double) + bins = np.array((0.0, 2.0), dtype=np.double) + + gamma_default, counts_default = gs_cy.variogram.unstructured(field, bins, pos) + gamma_threads, counts_threads = gs_cy.variogram.unstructured( + field, bins, pos, num_threads=2 + ) + + np.testing.assert_allclose(gamma_threads, gamma_default) + np.testing.assert_array_equal(counts_threads, counts_default) + def test_unstructured_error_checks(self): pos = np.array(((0.0, 1.0), (0.0, 1.0)), dtype=np.double) field = np.array(((1.0, 2.0),), dtype=np.double) From 740c11fa9a23f7d1b513fc3f056ca51539375493 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20M=C3=BCller?= Date: Fri, 26 Dec 2025 17:20:37 +0100 Subject: [PATCH 11/13] add changelog for v1.2 --- CHANGELOG.md | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index de323f9..b0f301d 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,18 @@ All notable changes to **GSTools-Cython** will be documented in this file. +## [1.2.0] - 2025-12 + +### Changes + +- add support for Python 3.14 (incl. free-threaded support) +- move pypy version to 3.11 +- add win arm64 wheels (without Python 3.10, since there are no numpy wheels prior to 3.11) +- remove support for Python 3.9 (EOL) +- fix bug in error message in variogram.pyx (undetected by cython<3.1) +- update pyproject.toml and use setuptools>=77 +- increased coverage + ## [1.1.0] - 2025-04 See [#5](https://github.com/GeoStat-Framework/GSTools-Cython/pull/5) @@ -23,6 +35,7 @@ First release of GSTools-Cython - moved Cython files into this separate package -[Unreleased]: https://github.com/GeoStat-Framework/gstools-cython/compare/v1.1.0...HEAD +[Unreleased]: https://github.com/GeoStat-Framework/gstools-cython/compare/v1.2.0...HEAD +[1.2.0]: https://github.com/GeoStat-Framework/gstools-cython/compare/v1.1.0...v1.2.0 [1.1.0]: https://github.com/GeoStat-Framework/gstools-cython/compare/v1.0.0...v1.1.0 [1.0.0]: https://github.com/GeoStat-Framework/gstools-cython/releases/tag/v1.0.0 From 27d6cd59538fcb7078b996ced9599c6122be11ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20M=C3=BCller?= Date: Fri, 26 Dec 2025 17:22:59 +0100 Subject: [PATCH 12/13] CI: remove redundant numpy install --- .github/workflows/main.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 935fee2..019a3a9 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -161,7 +161,6 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | - pip install "numpy${{ matrix.ver.np }}" python -m pytest --cov gstools_cython --cov-report term-missing -v tests/ python -m coveralls --service=github From 769a19de52d646ba3d2cee56c1f2c29339d48e5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20M=C3=BCller?= Date: Fri, 26 Dec 2025 17:25:40 +0100 Subject: [PATCH 13/13] changelog: add link to related PR --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b0f301d..c3f304d 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ All notable changes to **GSTools-Cython** will be documented in this file. ## [1.2.0] - 2025-12 +See [#11](https://github.com/GeoStat-Framework/GSTools-Cython/pull/11) + ### Changes - add support for Python 3.14 (incl. free-threaded support)