diff --git a/proj93-cython3.patch b/proj93-cython3.patch new file mode 100644 index 0000000..1d74069 --- /dev/null +++ b/proj93-cython3.patch @@ -0,0 +1,468 @@ +diff -rupN --no-dereference pyproj-3.6.0/pyproj/_datadir.pxd pyproj-3.6.0-new/pyproj/_datadir.pxd +--- pyproj-3.6.0/pyproj/_datadir.pxd 2023-06-12 15:27:32.000000000 +0200 ++++ pyproj-3.6.0-new/pyproj/_datadir.pxd 2023-09-13 12:13:21.780093772 +0200 +@@ -1,7 +1,7 @@ + include "proj.pxi" + +-cpdef str _get_proj_error() +-cpdef void _clear_proj_error() ++cpdef str _get_proj_error() noexcept ++cpdef void _clear_proj_error() noexcept + cdef PJ_CONTEXT* PYPROJ_GLOBAL_CONTEXT + cdef PJ_CONTEXT* pyproj_context_create() except * + cdef void pyproj_context_destroy(PJ_CONTEXT* context) except * +diff -rupN --no-dereference pyproj-3.6.0/pyproj/_datadir.pyx pyproj-3.6.0-new/pyproj/_datadir.pyx +--- pyproj-3.6.0/pyproj/_datadir.pyx 2023-06-12 15:27:32.000000000 +0200 ++++ pyproj-3.6.0-new/pyproj/_datadir.pyx 2023-09-13 12:13:21.781093753 +0200 +@@ -6,7 +6,6 @@ from libc.stdlib cimport free, malloc + + from pyproj._compat cimport cstrencode + +-from pyproj.exceptions import DataDirError + from pyproj.utils import strtobool + + # for logging the internal PROJ messages +@@ -79,14 +78,14 @@ def get_user_data_dir(create=False): + ) + + +-cpdef str _get_proj_error(): ++cpdef str _get_proj_error() noexcept: + """ + Get the internal PROJ error message. Returns None if no error was set. + """ + return _INTERNAL_PROJ_ERROR + + +-cpdef void _clear_proj_error(): ++cpdef void _clear_proj_error() noexcept: + """ + Clear the internal PROJ error message. + """ +@@ -94,7 +93,7 @@ cpdef void _clear_proj_error(): + _INTERNAL_PROJ_ERROR = None + + +-cdef void pyproj_log_function(void *user_data, int level, const char *error_msg) nogil: ++cdef void pyproj_log_function(void *user_data, int level, const char *error_msg) nogil noexcept: + """ + Log function for catching PROJ errors. + """ +diff -rupN --no-dereference pyproj-3.6.0/pyproject.toml pyproj-3.6.0-new/pyproject.toml +--- pyproj-3.6.0/pyproject.toml 2023-06-12 15:27:32.000000000 +0200 ++++ pyproj-3.6.0-new/pyproject.toml 2023-09-13 12:13:21.781093753 +0200 +@@ -1,5 +1,5 @@ + [build-system] +-requires = ["setuptools>=61.0.0", "wheel", "cython>=0.28.4"] ++requires = ["setuptools>=61.0.0", "wheel", "cython>=3"] + build-backend = "setuptools.build_meta" + + [project] +diff -rupN --no-dereference pyproj-3.6.0/setup.py pyproj-3.6.0-new/setup.py +--- pyproj-3.6.0/setup.py 2023-06-12 15:27:32.000000000 +0200 ++++ pyproj-3.6.0-new/setup.py 2023-09-13 12:13:21.780093772 +0200 +@@ -7,20 +7,39 @@ import sys + from pathlib import Path + from typing import Optional + +-from pkg_resources import parse_version + from setuptools import Extension, setup + +-PROJ_MIN_VERSION = parse_version("9.0.0") ++PROJ_MIN_VERSION = (9, 0, 0) + CURRENT_FILE_PATH = Path(__file__).absolute().parent + BASE_INTERNAL_PROJ_DIR = Path("proj_dir") + INTERNAL_PROJ_DIR = CURRENT_FILE_PATH / "pyproj" / BASE_INTERNAL_PROJ_DIR + PROJ_VERSION_SEARCH = re.compile(r".*Rel\.\s+(?P\d+\.\d+\.\d+).*") ++VERSION_SEARCH = re.compile(r".*(?P\d+\.\d+\.\d+).*") + + +-def get_proj_version(proj_dir: Path) -> str: ++def _parse_version(version: str) -> tuple[int, int, int]: ++ """Convert a version string to a tuple of integers.""" ++ match = VERSION_SEARCH.search(version) ++ if not match: ++ raise SystemExit( ++ f"PROJ version unable to be determined from {version}. " ++ "Please set the PROJ_VERSION environment variable." ++ ) ++ return tuple( ++ int(ver) for ver in match.groupdict()["version"].split(".", maxsplit=2) ++ ) ++ ++ ++def get_proj_version(proj_dir: Path) -> tuple[int, int, int]: ++ """ ++ Determine PROJ version. ++ ++ Prefer PROJ_VERSION environment variable. ++ If PROJ_VERSION is not set, try to determine the version from the PROJ executable. ++ """ + proj_version = os.environ.get("PROJ_VERSION") + if proj_version: +- return proj_version ++ return _parse_version(proj_version) + proj = proj_dir / "bin" / "proj" + proj_ver = subprocess.check_output(str(proj), stderr=subprocess.STDOUT).decode( + "ascii" +@@ -31,15 +50,17 @@ def get_proj_version(proj_dir: Path) -> + "PROJ version unable to be determined. " + "Please set the PROJ_VERSION environment variable." + ) +- return match.groupdict()["version"] ++ return _parse_version(match.groupdict()["version"]) + + +-def check_proj_version(proj_version: str) -> None: ++def check_proj_version(proj_version: tuple[int, int, int]) -> None: + """checks that the PROJ library meets the minimum version""" +- if parse_version(proj_version) < PROJ_MIN_VERSION: ++ if proj_version < PROJ_MIN_VERSION: ++ proj_version_str = ".".join(str(ver) for ver in proj_version) ++ min_proj_version_str = ".".join(str(ver) for ver in PROJ_MIN_VERSION) + raise SystemExit( +- f"ERROR: Minimum supported PROJ version is {PROJ_MIN_VERSION}, installed " +- f"version is {proj_version}. For more information see: " ++ f"ERROR: Minimum supported PROJ version is {min_proj_version_str}, " ++ f"installed version is {proj_version_str}. For more information see: " + "https://pyproj4.github.io/pyproj/stable/installation.html" + ) + +@@ -154,11 +175,11 @@ def get_extension_modules(): + # make sure cython is available + try: + from Cython.Build import cythonize +- except ImportError: ++ except ImportError as error: + raise SystemExit( + "ERROR: Cython.Build.cythonize not found. " + "Cython is required to build pyproj." +- ) ++ ) from error + + # By default we'll try to get options PROJ_DIR or the local version of proj + proj_dir = get_proj_dir() +@@ -167,9 +188,7 @@ def get_extension_modules(): + + proj_version = get_proj_version(proj_dir) + check_proj_version(proj_version) +- proj_version_major, proj_version_minor, proj_version_patch = parse_version( +- proj_version +- ).base_version.split(".") ++ proj_version_major, proj_version_minor, proj_version_patch = proj_version + + # setup extension options + ext_options = { +@@ -197,9 +216,9 @@ def get_extension_modules(): + ], + quiet=True, + compile_time_env={ +- "CTE_PROJ_VERSION_MAJOR": int(proj_version_major), +- "CTE_PROJ_VERSION_MINOR": int(proj_version_minor), +- "CTE_PROJ_VERSION_PATCH": int(proj_version_patch), ++ "CTE_PROJ_VERSION_MAJOR": proj_version_major, ++ "CTE_PROJ_VERSION_MINOR": proj_version_minor, ++ "CTE_PROJ_VERSION_PATCH": proj_version_patch, + "CTE_PYTHON_IMPLEMENTATION": platform.python_implementation(), + }, + **get_cythonize_options(), +diff -rupN --no-dereference pyproj-3.6.0/test/conftest.py pyproj-3.6.0-new/test/conftest.py +--- pyproj-3.6.0/test/conftest.py 2023-06-12 15:27:32.000000000 +0200 ++++ pyproj-3.6.0-new/test/conftest.py 2023-09-13 12:13:21.781093753 +0200 +@@ -18,6 +18,7 @@ PROJ_GTE_91 = PROJ_LOOSE_VERSION >= vers + PROJ_GTE_911 = PROJ_LOOSE_VERSION >= version.parse("9.1.1") + PROJ_GTE_92 = PROJ_LOOSE_VERSION >= version.parse("9.2.0") + PROJ_GTE_921 = PROJ_LOOSE_VERSION >= version.parse("9.2.1") ++PROJ_GTE_93 = PROJ_LOOSE_VERSION >= version.parse("9.3.0") + + + def unset_data_dir(): +diff -rupN --no-dereference pyproj-3.6.0/test/test_proj.py pyproj-3.6.0-new/test/test_proj.py +--- pyproj-3.6.0/test/test_proj.py 2023-06-12 15:27:32.000000000 +0200 ++++ pyproj-3.6.0-new/test/test_proj.py 2023-09-13 12:13:21.782093733 +0200 +@@ -5,7 +5,7 @@ import sys + import unittest + from unittest.mock import patch + +-import numpy as np ++import numpy + import pytest + from numpy.testing import assert_almost_equal + +@@ -441,11 +441,11 @@ def test_reset_errno(): + @pytest.mark.parametrize("radians", [False, True]) + def test_get_factors__2d_input(radians): + transformer = Proj(3857) +- longitude = np.array([[0, 1], [2, 3]]) +- latitude = np.array([[1, 2], [3, 4]]) ++ longitude = numpy.array([[0, 1], [2, 3]]) ++ latitude = numpy.array([[1, 2], [3, 4]]) + if radians: +- longitude = np.radians(longitude) +- latitude = np.radians(latitude) ++ longitude = numpy.radians(longitude) ++ latitude = numpy.radians(latitude) + factors = transformer.get_factors( + longitude=longitude, latitude=latitude, radians=radians + ) +@@ -497,22 +497,36 @@ def test_get_factors(): + def test_get_factors__nan_inf(): + transformer = Proj(3857) + factors = transformer.get_factors( +- longitude=[0, np.nan, np.inf, 0], latitude=[np.nan, 2, 2, np.inf] ++ longitude=[0, numpy.nan, numpy.inf, 0], latitude=[numpy.nan, 2, 2, numpy.inf] + ) +- assert_almost_equal(factors.meridional_scale, [np.inf, np.inf, np.inf, np.inf]) +- assert_almost_equal(factors.parallel_scale, [np.inf, np.inf, np.inf, np.inf]) +- assert_almost_equal(factors.areal_scale, [np.inf, np.inf, np.inf, np.inf]) +- assert_almost_equal(factors.angular_distortion, [np.inf, np.inf, np.inf, np.inf]) +- assert_almost_equal( +- factors.meridian_parallel_angle, [np.inf, np.inf, np.inf, np.inf] +- ) +- assert_almost_equal(factors.meridian_convergence, [np.inf, np.inf, np.inf, np.inf]) +- assert_almost_equal(factors.tissot_semimajor, [np.inf, np.inf, np.inf, np.inf]) +- assert_almost_equal(factors.tissot_semiminor, [np.inf, np.inf, np.inf, np.inf]) +- assert_almost_equal(factors.dx_dlam, [np.inf, np.inf, np.inf, np.inf]) +- assert_almost_equal(factors.dx_dphi, [np.inf, np.inf, np.inf, np.inf]) +- assert_almost_equal(factors.dy_dlam, [np.inf, np.inf, np.inf, np.inf]) +- assert_almost_equal(factors.dy_dphi, [np.inf, np.inf, np.inf, np.inf]) ++ assert_almost_equal( ++ factors.meridional_scale, [numpy.inf, numpy.inf, numpy.inf, numpy.inf] ++ ) ++ assert_almost_equal( ++ factors.parallel_scale, [numpy.inf, numpy.inf, numpy.inf, numpy.inf] ++ ) ++ assert_almost_equal( ++ factors.areal_scale, [numpy.inf, numpy.inf, numpy.inf, numpy.inf] ++ ) ++ assert_almost_equal( ++ factors.angular_distortion, [numpy.inf, numpy.inf, numpy.inf, numpy.inf] ++ ) ++ assert_almost_equal( ++ factors.meridian_parallel_angle, [numpy.inf, numpy.inf, numpy.inf, numpy.inf] ++ ) ++ assert_almost_equal( ++ factors.meridian_convergence, [numpy.inf, numpy.inf, numpy.inf, numpy.inf] ++ ) ++ assert_almost_equal( ++ factors.tissot_semimajor, [numpy.inf, numpy.inf, numpy.inf, numpy.inf] ++ ) ++ assert_almost_equal( ++ factors.tissot_semiminor, [numpy.inf, numpy.inf, numpy.inf, numpy.inf] ++ ) ++ assert_almost_equal(factors.dx_dlam, [numpy.inf, numpy.inf, numpy.inf, numpy.inf]) ++ assert_almost_equal(factors.dx_dphi, [numpy.inf, numpy.inf, numpy.inf, numpy.inf]) ++ assert_almost_equal(factors.dy_dlam, [numpy.inf, numpy.inf, numpy.inf, numpy.inf]) ++ assert_almost_equal(factors.dy_dphi, [numpy.inf, numpy.inf, numpy.inf, numpy.inf]) + + + def test_get_factors__errcheck(): +@@ -523,7 +537,7 @@ def test_get_factors__errcheck(): + + def test_numpy_bool_kwarg_false(): + # Issue 564 +- south = np.array(50) < 0 ++ south = numpy.array(50) < 0 + proj = Proj( + proj="utm", zone=32, ellipsis="WGS84", datum="WGS84", units="m", south=south + ) +@@ -532,7 +546,7 @@ def test_numpy_bool_kwarg_false(): + + def test_numpy_bool_kwarg_true(): + # Issue 564 +- south = np.array(50) > 0 ++ south = numpy.array(50) > 0 + proj = Proj( + proj="utm", zone=32, ellipsis="WGS84", datum="WGS84", units="m", south=south + ) +diff -rupN --no-dereference pyproj-3.6.0/test/test_transformer.py pyproj-3.6.0-new/test/test_transformer.py +--- pyproj-3.6.0/test/test_transformer.py 2023-06-12 15:27:32.000000000 +0200 ++++ pyproj-3.6.0-new/test/test_transformer.py 2023-09-13 12:13:21.783093713 +0200 +@@ -9,7 +9,7 @@ from itertools import permutations + from pathlib import Path + from unittest.mock import call, patch + +-import numpy as np ++import numpy + import pytest + from numpy.testing import assert_almost_equal, assert_array_equal + +@@ -22,6 +22,7 @@ from pyproj.transformer import AreaOfInt + from test.conftest import ( + PROJ_GTE_91, + PROJ_GTE_92, ++ PROJ_GTE_93, + grids_available, + proj_env, + proj_network_env, +@@ -64,8 +65,8 @@ def test_illegal_transformation(): + xx, yy = pyproj.transform( + p1, p2, (-180, -180, 180, 180, -180), (-90, 90, 90, -90, -90) + ) +- assert np.all(np.isinf(xx)) +- assert np.all(np.isinf(yy)) ++ assert numpy.all(numpy.isinf(xx)) ++ assert numpy.all(numpy.isinf(yy)) + with pytest.warns(FutureWarning), pytest.raises(ProjError): + pyproj.transform( + p1, p2, (-180, -180, 180, 180, -180), (-90, 90, 90, -90, -90), errcheck=True +@@ -412,7 +413,7 @@ def test_always_xy__itransform(): + ) + + +-@pytest.mark.parametrize("empty_array", [(), [], np.array([])]) ++@pytest.mark.parametrize("empty_array", [(), [], numpy.array([])]) + def test_transform_empty_array_xy(empty_array): + transformer = Transformer.from_crs(2193, 4326) + assert_array_equal( +@@ -420,7 +421,7 @@ def test_transform_empty_array_xy(empty_ + ) + + +-@pytest.mark.parametrize("empty_array", [(), [], np.array([])]) ++@pytest.mark.parametrize("empty_array", [(), [], numpy.array([])]) + def test_transform_empty_array_xyzt(empty_array): + transformer = Transformer.from_pipeline("+init=ITRF2008:ITRF2000") + assert_array_equal( +@@ -540,7 +541,7 @@ def test_repr__conditional(): + "Description: unavailable until proj_trans is called\n" + "Area of Use:\n- undefined" + ) +- elif PROJ_GTE_92: ++ elif PROJ_GTE_92 and not PROJ_GTE_93: + assert trans_repr == ( + "\n" + "Description: Transformation from EGM2008 height to WGS 84 " +@@ -555,7 +556,7 @@ def test_repr__conditional(): + "(ballpark vertical transformation, without ellipsoid height " + "to vertical height correction)\n" + "Area of Use:\n" +- "- name: World\n" ++ f"- name: World{'.' if PROJ_GTE_93 else ''}\n" + "- bounds: (-180.0, -90.0, 180.0, 90.0)" + ) + +@@ -1205,7 +1206,7 @@ def test_transform_bounds_densify(densit + "+proj=laea +lat_0=45 +lon_0=-100 +x_0=0 +y_0=0 " + "+a=6370997 +b=6370997 +units=m +no_defs", + ) +- assert np.allclose( ++ assert numpy.allclose( + transformer.transform_bounds(40, -120, 64, -80, densify_pts=density), + expected, + ) +@@ -1222,7 +1223,15 @@ def test_transform_bounds_densify(densit + "input_bounds, radians", + [ + ((-120, 40, -80, 64), False), +- ((np.radians(-120), np.radians(40), np.radians(-80), np.radians(64)), True), ++ ( ++ ( ++ numpy.radians(-120), ++ numpy.radians(40), ++ numpy.radians(-80), ++ numpy.radians(64), ++ ), ++ True, ++ ), + ], + ) + def test_transform_bounds_densify__xy(density, expected, input_bounds, radians): +@@ -1232,7 +1241,7 @@ def test_transform_bounds_densify__xy(de + "+a=6370997 +b=6370997 +units=m +no_defs", + always_xy=True, + ) +- assert np.allclose( ++ assert numpy.allclose( + transformer.transform_bounds( + *input_bounds, densify_pts=density, radians=radians + ), +@@ -1459,8 +1468,8 @@ def test_transform_bounds__south_pole__x + + @pytest.mark.parametrize("inplace", [True, False]) + def test_transform__fortran_order(inplace): +- lons, lats = np.arange(-180, 180, 20), np.arange(-90, 90, 10) +- lats, lons = np.meshgrid(lats, lons) ++ lons, lats = numpy.arange(-180, 180, 20), numpy.arange(-90, 90, 10) ++ lats, lons = numpy.meshgrid(lats, lons) + f_lons, f_lats = lons.copy(order="F"), lats.copy(order="F") + transformer = Transformer.from_crs( + "EPSG:4326", +@@ -1519,10 +1528,10 @@ def test_4d_transform__inplace__array__i + + def test_4d_transform__inplace__numpy(): + transformer = Transformer.from_crs(7789, 8401) +- xarr = np.array([3496737.2679], dtype=np.float64) +- yarr = np.array([743254.4507], dtype=np.float64) +- zarr = np.array([5264462.9620], dtype=np.float64) +- tarr = np.array([2019.0], dtype=np.float64) ++ xarr = numpy.array([3496737.2679], dtype=numpy.float64) ++ yarr = numpy.array([743254.4507], dtype=numpy.float64) ++ zarr = numpy.array([5264462.9620], dtype=numpy.float64) ++ tarr = numpy.array([2019.0], dtype=numpy.float64) + t_xarr, t_yarr, t_zarr, t_tarr = transformer.transform( + xx=xarr, yy=yarr, zz=zarr, tt=tarr, inplace=True + ) +@@ -1538,10 +1547,10 @@ def test_4d_transform__inplace__numpy(): + + def test_4d_transform__inplace__numpy__int(): + transformer = Transformer.from_crs(7789, 8401) +- xarr = np.array([3496737], dtype=np.int32) +- yarr = np.array([743254], dtype=np.int32) +- zarr = np.array([5264462], dtype=np.int32) +- tarr = np.array([2019], dtype=np.int32) ++ xarr = numpy.array([3496737], dtype=numpy.int32) ++ yarr = numpy.array([743254], dtype=numpy.int32) ++ zarr = numpy.array([5264462], dtype=numpy.int32) ++ tarr = numpy.array([2019], dtype=numpy.int32) + t_xarr, t_yarr, t_zarr, t_tarr = transformer.transform( + xx=xarr, yy=yarr, zz=zarr, tt=tarr, inplace=True + ) +diff -rupN --no-dereference pyproj-3.6.0/test/test_utils.py pyproj-3.6.0-new/test/test_utils.py +--- pyproj-3.6.0/test/test_utils.py 2023-06-12 15:27:32.000000000 +0200 ++++ pyproj-3.6.0-new/test/test_utils.py 2023-09-13 12:13:21.783093713 +0200 +@@ -2,8 +2,6 @@ from array import array + + import numpy + import pytest +-from pandas import Series +-from xarray import DataArray + + from pyproj.utils import DataType, _copytobuffer, _copytobuffer_return_scalar + +@@ -22,7 +20,6 @@ def test__copytobuffer_return_scalar__in + "in_data, data_type", + [ + (numpy.array(1), DataType.FLOAT), +- (DataArray(numpy.array(1)), DataType.FLOAT), + (1, DataType.FLOAT), + ([1], DataType.LIST), + ((1,), DataType.TUPLE), +@@ -32,10 +29,23 @@ def test__copytobuffer(in_data, data_typ + assert _copytobuffer(in_data) == (array("d", [1]), data_type) + + +-@pytest.mark.parametrize( +- "in_arr", [numpy.array([1]), DataArray(numpy.array([1])), Series(numpy.array([1]))] +-) +-def test__copytobuffer__numpy_array(in_arr): ++def test__copytobuffer__xarray_scalar(): ++ xarray = pytest.importorskip("xarray") ++ assert _copytobuffer(xarray.DataArray(numpy.array(1))) == ( ++ array("d", [1]), ++ DataType.FLOAT, ++ ) ++ ++ ++@pytest.mark.parametrize("arr_type", ["numpy", "xarray", "pandas"]) ++def test__copytobuffer__array(arr_type): ++ in_arr = numpy.array([1]) ++ if arr_type == "xarray": ++ xarray = pytest.importorskip("xarray") ++ in_arr = xarray.DataArray(in_arr) ++ elif arr_type == "pandas": ++ pandas = pytest.importorskip("pandas") ++ in_arr = pandas.Series(in_arr) + assert _copytobuffer(in_arr) == ( + in_arr.astype("d").__array__(), + DataType.ARRAY, diff --git a/pyproj.spec b/pyproj.spec index 9fa0d82..e9e0f5f 100644 --- a/pyproj.spec +++ b/pyproj.spec @@ -4,13 +4,17 @@ Name: pyproj Version: 3.6.0 -Release: 2%{?dist} +Release: 3%{?dist} Summary: Cython wrapper to provide python interfaces to Proj # this software uses the "MIT:Modern Style with sublicense" license License: MIT URL: https://github.com/jswhit/%{name} Source0: https://files.pythonhosted.org/packages/source/p/%{name}/%{name}-%{version}.tar.gz +# Selected backports for proj-9.3 and cython-3 comptability +# From https://github.com/pyproj4/pyproj/compare/32565ddf266658aebc9787b7534fdbdd06762839..76b77c8586efa28565aaab2365fa459f75596043 +Patch0: proj93-cython3.patch + BuildRequires: make BuildRequires: gcc BuildRequires: proj-devel >= %{minimal_needed_proj_version} @@ -190,6 +194,9 @@ py.test-3 -m "not network and not grid" %changelog +* Wed Sep 13 2023 Sandro Mani - 3.6.0-3 +- Rebuild (proj) + * Wed Jul 19 2023 Elliott Sales de Andrade - 3.6.0-2 - Rebuild for Python 3.12b4