DEP: GDAL 3.5+, numpy 1.24+ (#3166)

* DEP: Python 3.10+, GDAL 3.5+, numpy 1.24+

* Apply suggestions from code review

* DEP: GDAL 3.5+, numpy 1.24+

* Fix

* remove python 3.13 from conda matrix

---------

Co-authored-by: Sean Gillies <sean.gillies@gmail.com>
This commit is contained in:
Alan D. Snow 2024-09-09 22:59:11 -05:00 committed by GitHub
parent 9de88d370d
commit fd4163d4b5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
21 changed files with 40 additions and 207 deletions

View File

@ -44,7 +44,7 @@ jobs:
numpy_compat_test:
runs-on: ubuntu-latest
name: Build with Numpy 2.0.0, test with 1.23.5
name: Build with Numpy 2.0.0, test with 1.24
container: ghcr.io/osgeo/gdal:ubuntu-small-${{ matrix.gdal-version }}
env:
DEBIAN_FRONTEND: noninteractive
@ -80,7 +80,7 @@ jobs:
- name: run tests with Numpy 1
run: |
. testenv/bin/activate
python -m pip install numpy==1.23.5
python -m pip install numpy==1.24
python -m pip wheel -r requirements-dev.txt
python -m pip install dist/*.whl
python -m pip install aiohttp boto3 fsspec hypothesis packaging pytest shapely
@ -97,15 +97,17 @@ jobs:
strategy:
fail-fast: false
matrix:
python-version: ['3.9', '3.10', '3.11']
gdal-version: ['3.6.4', '3.7.0']
python-version: ['3.9', '3.10', '3.11', '3.12', '3.13']
gdal-version: ['3.9.2']
include:
- python-version: '3.9'
gdal-version: '3.8.5'
- python-version: '3.9'
gdal-version: '3.7.3'
- python-version: '3.9'
gdal-version: '3.6.4'
- python-version: '3.9'
gdal-version: '3.5.3'
- python-version: '3.9'
gdal-version: '3.4.3'
- python-version: '3.9'
gdal-version: '3.3.3'
steps:
- uses: actions/checkout@v4
@ -152,9 +154,8 @@ jobs:
python-version: ['3.9', '3.10', '3.11', '3.12']
include:
- os: ubuntu-latest
python-version: '3.10'
- os: ubuntu-latest
python-version: '3.11'
python-version: '*'
steps:
- uses: actions/checkout@v4

View File

@ -27,7 +27,7 @@ optional format drivers.
Many users find Anaconda and conda-forge a good way to install Rasterio and get
access to more optional format drivers (like TileDB and others).
Rasterio 1.4 requires Python 3.9 or higher and GDAL 3.3 or higher.
Rasterio 1.4 requires Python 3.9 or higher and GDAL 3.5 or higher.
Advanced installation
=====================

View File

@ -47,9 +47,6 @@ from libc.string cimport memcpy
log = logging.getLogger(__name__)
gdal33_version_checked = False
gdal33_version_met = False
# NOTE: This has to be defined outside of gdal.pxi or other C extensions will
# try to compile C++ only code included in this header.

View File

@ -50,21 +50,10 @@ np.import_array()
log = logging.getLogger(__name__)
gdal33_version_checked = False
gdal33_version_met = False
def validate_resampling(resampling):
"""Validate that the resampling method is compatible of reads/writes."""
if resampling == Resampling.rms:
global gdal33_version_checked
global gdal33_version_met
if not gdal33_version_checked:
gdal33_version_checked = True
gdal33_version_met = GDALVersion.runtime().at_least('3.3')
if not gdal33_version_met:
raise ResamplingAlgorithmError("{!r} requires GDAL 3.3".format(Resampling(resampling)))
elif resampling > 7:
if resampling != Resampling.rms and resampling > 7:
raise ResamplingAlgorithmError("{!r} can be used for warp operations but not for reads and writes".format(Resampling(resampling)))

View File

@ -46,10 +46,7 @@ np.import_array()
log = logging.getLogger(__name__)
# Gauss (7) is not supported for warp
SUPPORTED_RESAMPLING = [r for r in Resampling if r.value != 7 and r.value <= 13]
# rms supported since GDAL 3.3
if GDALVersion.runtime().at_least('3.3'):
SUPPORTED_RESAMPLING.append(Resampling.rms)
SUPPORTED_RESAMPLING = [r for r in Resampling if r.value != 7]
def recursive_round(val, precision):

View File

@ -4,7 +4,6 @@ import numpy
from rasterio.env import GDALVersion
_GDAL_AT_LEAST_35 = GDALVersion.runtime().at_least("3.5")
_GDAL_AT_LEAST_37 = GDALVersion.runtime().at_least("3.7")
bool_ = 'bool'
@ -36,12 +35,10 @@ dtype_fwd = {
9: complex64, # GDT_CInt32
10: complex64, # GDT_CFloat32
11: complex128, # GDT_CFloat64
12: uint64, # GDT_UInt64
13: int64, # GDT_Int64
}
if _GDAL_AT_LEAST_35:
dtype_fwd[13] = int64 # GDT_Int64
dtype_fwd[12] = uint64 # GDT_UInt64
if _GDAL_AT_LEAST_37:
dtype_fwd[14] = sbyte # GDT_Int8
@ -79,11 +76,11 @@ typename_fwd = {
8: 'CInt16',
9: 'CInt32',
10: 'CFloat32',
11: 'CFloat64'}
11: 'CFloat64',
12: 'UInt64',
13: 'Int64',
}
if _GDAL_AT_LEAST_35:
typename_fwd[13] = 'Int64'
typename_fwd[12] = 'UInt64'
if _GDAL_AT_LEAST_37:
typename_fwd[14] = "Int8"
@ -105,12 +102,10 @@ dtype_ranges = {
"int32": (-2147483648, 2147483647),
"float32": (float(f32i.min), float(f32i.max)),
"float64": (float(f64i.min), float(f64i.max)),
"int64": (-9223372036854775808, 9223372036854775807),
"uint64": (0, 18446744073709551615),
}
if _GDAL_AT_LEAST_35:
dtype_ranges['int64'] = (-9223372036854775808, 9223372036854775807)
dtype_ranges['uint64'] = (0, 18446744073709551615)
dtype_info_registry = {"c": numpy.finfo, "f": numpy.finfo, "i": numpy.iinfo, "u": numpy.iinfo}
@ -171,8 +166,6 @@ def get_minimum_dtype(values):
return uint16
elif max_value <= 4294967295:
return uint32
if not _GDAL_AT_LEAST_35:
raise ValueError("Values out of range for supported dtypes")
return uint64
elif min_value >= -128 and max_value <= 127:
return int8
@ -180,8 +173,6 @@ def get_minimum_dtype(values):
return int16
elif min_value >= -2147483648 and max_value <= 2147483647:
return int32
if not _GDAL_AT_LEAST_35:
raise ValueError("Values out of range for supported dtypes")
return int64
else:
if min_value >= -3.4028235e+38 and max_value <= 3.4028235e+38:

View File

@ -274,10 +274,8 @@ def rasterize(
GDAL_CACHEMAX is larger than the size of `out` or `out_shape`.
"""
valid_dtypes = (
'int16', 'int32', 'uint8', 'uint16', 'uint32', 'float32', 'float64'
'int16', 'int32', 'int64', 'uint8', 'uint16', 'uint32', 'uint64', 'float32', 'float64'
)
if GDALVersion.runtime().at_least("3.5"):
valid_dtypes += ("int64", "uint64")
if GDALVersion.runtime().at_least("3.7"):
valid_dtypes += ("int8",)

View File

@ -8,6 +8,6 @@ click~=8.0
click-plugins
cligj>=0.5
matplotlib
numpy>=2.0
numpy>=1.24
setuptools>=20.0
pyparsing~=3.1

View File

@ -133,6 +133,10 @@ if "clean" not in sys.argv:
int, re.findall("[0-9]+", gdalversion)[:3]
)
if (gdal_major_version, gdal_minor_version) < (3, 5):
raise SystemExit("ERROR: GDAL >= 3.5 is required for rasterio. "
"Please upgrade GDAL.")
# Conditionally copy the GDAL data. To be used in conjunction with
# the bdist_wheel command to make self-contained binary wheels.
if os.environ.get('PACKAGE_DATA'):
@ -248,7 +252,7 @@ inst_reqs = [
"click>=4.0",
"cligj>=0.5",
"importlib-metadata ; python_version < '3.10'",
"numpy",
"numpy>=1.24",
"click-plugins",
"pyparsing",
]

View File

@ -664,22 +664,10 @@ class MockGeoInterface:
# Define helpers to skip tests based on GDAL version
gdal_version = GDALVersion.runtime()
requires_gdal33 = pytest.mark.skipif(
not gdal_version.at_least('3.3'),
reason="Requires GDAL 3.3.x")
requires_gdal35 = pytest.mark.skipif(
not gdal_version.at_least('3.5'),
reason="Requires GDAL 3.5.x")
requires_gdal37 = pytest.mark.skipif(
not gdal_version.at_least('3.7'), reason="Requires GDAL 3.7.x"
)
requires_gdal_lt_35 = pytest.mark.skipif(
gdal_version.at_least('3.5'), reason="Requires GDAL before 3.5"
)
requires_gdal_lt_37 = pytest.mark.skipif(
gdal_version.at_least('3.7'), reason="Requires GDAL before 3.7"
)

View File

@ -34,8 +34,6 @@ def mock_fhs(tmpdir):
@pytest.fixture
def mock_debian(tmpdir):
"""A fake Debian multi-install system"""
tmpdir.ensure("share/gdal/3.3/gdalvrt.xsd")
tmpdir.ensure("share/gdal/3.4/gdalvrt.xsd")
tmpdir.ensure("share/gdal/3.5/gdalvrt.xsd")
tmpdir.ensure("share/gdal/3.6/gdalvrt.xsd")
tmpdir.ensure(f"share/gdal/{gdal_version.major}.{gdal_version.minor}/gdalvrt.xsd")

View File

@ -1,8 +1,6 @@
import numpy as np
import pytest
from .conftest import gdal_version
import rasterio
from rasterio import (
ubyte,
@ -88,19 +86,10 @@ def test_get_minimum_dtype():
def test_get_minimum_dtype__int64():
if gdal_version.at_least("3.5"):
assert get_minimum_dtype([-1, 0, 2147483648]) == int64
else:
with pytest.raises(ValueError, match="Values out of range for supported dtypes"):
get_minimum_dtype([-1, 0, 2147483648])
assert get_minimum_dtype([-1, 0, 2147483648]) == int64
def test_get_minimum_dtype__uint64():
if gdal_version.at_least("3.5"):
assert get_minimum_dtype([0, 4294967296]) == uint64
else:
with pytest.raises(ValueError, match="Values out of range for supported dtypes"):
get_minimum_dtype([0, 4294967296])
assert get_minimum_dtype([0, 4294967296]) == uint64
def test_can_cast_dtype():
@ -158,8 +147,4 @@ def test_get_npdtype():
def test__get_gdal_dtype__int64():
if gdal_version.at_least("3.5"):
assert _get_gdal_dtype("int64") == 13
else:
with pytest.raises(TypeError, match="Unsupported data type"):
_get_gdal_dtype("int64")
assert _get_gdal_dtype("int64") == 13

View File

@ -6,9 +6,6 @@ import pytest
import rasterio
from rasterio._err import CPLE_BaseError
from rasterio.errors import RasterioIOError
from rasterio.env import GDALVersion
from .conftest import gdal_version
def test_io_error(tmpdir):
@ -53,17 +50,3 @@ def test_issue2353(caplog, path_rgb_byte_tif):
5434894.885056,
)
_ = src.colorinterp
@pytest.mark.xfail(gdal_version < GDALVersion(3, 3), reason="GDAL <3.3 will not warn")
@pytest.mark.xfail(gdal_version > GDALVersion(3, 3), reason="GDAL > 3.3 will not warn")
def test_issue2353bis(caplog, path_rgb_byte_tif):
"""Ensure VRT doesn't leave errors behind."""
from rasterio.vrt import WarpedVRT
with caplog.at_level(logging.INFO):
with rasterio.open('tests/data/goes.tif') as src:
with WarpedVRT(src, dst_crs="EPSG:3857") as vrt:
pass
assert "Ignoring error" in caplog.text
_ = src.colorinterp

View File

@ -649,13 +649,9 @@ def test_rasterize_out_image(basic_geometry, basic_image_2x2):
assert np.array_equal(basic_image_2x2, out)
@pytest.mark.skipif(
gdal_version.at_least("3.5"),
reason="int64 supported at GDAL 3.5",
)
def test_rasterize_int64_out_dtype(basic_geometry):
def test_rasterize_unsupported_dtype(basic_geometry):
"""A non-supported data type for out should raise an exception."""
out = np.zeros(DEFAULT_SHAPE, dtype=np.int64)
out = np.zeros(DEFAULT_SHAPE, dtype=np.float16)
with pytest.raises(ValueError):
rasterize([basic_geometry], out=out)
@ -732,10 +728,6 @@ def test_rasterize_default_value_for_none(basic_geometry, basic_image_2x2):
)
@pytest.mark.xfail(
not gdal_version.at_least("3.5"),
reason="GDAL versions < 3.5 cannot rasterize to 64 bit integer arrays",
)
def test_rasterize_int64_default_value(basic_geometry):
"""A default value that requires an int64 succeeds for GDAL >= 3.5."""
rasterize([basic_geometry], out_shape=DEFAULT_SHAPE, default_value=1000000000000)
@ -798,34 +790,10 @@ def test_rasterize_value(basic_geometry, basic_image_2x2):
)
@pytest.mark.skipif(
gdal_version.at_least("3.5"),
reason="int64 supported at GDAL 3.5",
)
def test_rasterize_invalid_value(basic_geometry):
"""A shape value that requires an int64 should raise an exception with GDAL < 3.5."""
with pytest.raises(
ValueError, match="GDAL versions < 3.6 cannot rasterize int64 values."
):
rasterize([(basic_geometry, 1000000000000)], out_shape=DEFAULT_SHAPE)
@pytest.mark.parametrize(
"dtype",
[
"float16",
pytest.param(
"int64",
marks=pytest.mark.skipif(
gdal_version.at_least("3.5"), reason="int64 supported at GDAL 3.5"
),
),
pytest.param(
"uint64",
marks=pytest.mark.skipif(
gdal_version.at_least("3.5"), reason="uint64 supported at GDAL 3.5"
),
),
pytest.param(
"int8",
marks=pytest.mark.skipif(

View File

@ -176,9 +176,6 @@ def test_merge_destination_1(tmp_path):
assert numpy.allclose(data, result[:, : data.shape[1], : data.shape[2]])
@pytest.mark.skipif(
not gdal_version.at_least("3.4"), reason="Precise windowing requires 3.4"
)
def test_merge_destination_2(tmp_path):
"""Merge into an opened, target-aligned dataset."""
with rasterio.open("tests/data/RGB.byte.tif") as src:

View File

@ -3,8 +3,6 @@
import numpy as np
import pytest
from .conftest import requires_gdal33
import rasterio
from rasterio.enums import OverviewResampling
from rasterio.enums import Resampling
@ -103,7 +101,6 @@ def test_build_overviews_new_file(tmpdir, path_rgb_byte_tif):
@pytest.mark.parametrize("ovr_levels", [[2], [3], [2, 4, 8]])
@requires_gdal33
def test_ignore_overviews(data, ovr_levels):
"""open dataset with OVERVIEW_LEVEL=NONE, overviews should be ignored"""
inputfile = str(data.join('RGB.byte.tif'))
@ -118,7 +115,6 @@ def test_ignore_overviews(data, ovr_levels):
assert src.overviews(3) == []
@requires_gdal33
def test_decimated_no_use_overview(red_green):
"""Force ignore existing overviews when performing decimated read"""
# Corrupt overview of red file by replacing red.tif.ovr with
@ -141,9 +137,8 @@ def test_decimated_no_use_overview(red_green):
assert not np.array_equal(ovr_data, decimated_data)
@requires_gdal33
def test_build_overviews_rms(data):
"""Make sure RMS resampling works with gdal3.3."""
"""Make sure RMS resampling works"""
inputfile = str(data.join('RGB.byte.tif'))
with rasterio.open(inputfile, 'r+') as src:
overview_factors = [2, 4]

View File

@ -11,9 +11,6 @@ from rasterio.enums import Resampling
from rasterio.errors import ResamplingAlgorithmError
from rasterio.windows import Window
from .conftest import requires_gdal33
# Rasterio's test dataset is 718 rows by 791 columns.
def test_read_out_shape_resample_down():
@ -94,7 +91,6 @@ def test_resampling_alg_error():
src.read(1, out_shape=(1, 10, 10), resampling=Resampling.max)
@requires_gdal33
def test_resampling_rms():
"""Test Resampling.rms method"""
with rasterio.open('tests/data/float.tif') as s:

View File

@ -175,13 +175,6 @@ def test_rpcs_write_read_rpcs(tmpdir):
assert isinstance(rpcs, RPC)
expected = TEST_RPCS_FROM_GDAL.copy()
# GDAL < 3.3 does not ensure ERR_BIAS and ERR_RAND are written out
# so we wont either
expected.pop("ERR_BIAS")
expected.pop("ERR_RAND")
rpcs.err_bias = None
rpcs.err_rand = None
assert sorted(rpcs.to_gdal().keys()) == sorted(expected.keys())
rpcs.lat_off = 48

View File

@ -322,10 +322,7 @@ def test_transform_bounds_no_change():
def test_transform_bounds_densify_out_of_bounds():
error = ValueError
if gdal_version.at_least('3.4'):
error = CPLE_AppDefinedError
with pytest.raises(error):
with pytest.raises(CPLE_AppDefinedError):
transform_bounds(
CRS.from_epsg(4326),
CRS.from_epsg(32610),
@ -358,10 +355,6 @@ def test_calculate_default_transform():
assert height == 696
@pytest.mark.skipif(
not gdal_version.at_least("3.5"),
reason="Older GDAL versions require geotransform or GCPs",
)
def test_calculate_default_transform_geoloc_array():
target_transform = Affine(
0.0028535715391804096,
@ -2254,18 +2247,6 @@ def test_coordinate_pipeline(tmp_path):
@pytest.mark.skipif(
not gdal_version.at_least('3.4') or gdal_version.at_least("3.5"),
reason="Requires GDAL 3.4.x")
def test_issue2353bis(caplog):
"""Errors left by a successful transformation are cleaned up."""
caplog.set_level(logging.INFO)
bounds = [458872.4197335826, -2998046.478919534, 584059.8115540259, -2883810.102037343]
with rasterio.Env():
transform_bounds("EPSG:6931", "EPSG:4326", *bounds)
assert "Point outside of" in caplog.text
@pytest.mark.skipif(not gdal_version.at_least("3.6"), reason="Requires GDAL 3.6")
def test_geoloc_warp_dataset(data, tmp_path):
"""Warp a dataset using external geolocation arrays."""

View File

@ -15,7 +15,6 @@ from rasterio.crs import CRS
from rasterio.errors import CRSError
from rasterio.transform import from_bounds
from rasterio.warp import calculate_default_transform, transform_bounds
from tests.conftest import gdal_version
log = logging.getLogger(__name__)
@ -187,10 +186,7 @@ def test_transform_bounds_identity():
def test_transform_bounds_densify_out_of_bounds():
error = ValueError
if gdal_version.at_least('3.4'):
error = CPLE_AppDefinedError
with pytest.raises(error):
with pytest.raises(CPLE_AppDefinedError):
transform_bounds(
"EPSG:4326",
"+proj=laea +lat_0=45 +lon_0=-100 +x_0=0 +y_0=0 "
@ -204,10 +200,7 @@ def test_transform_bounds_densify_out_of_bounds():
def test_transform_bounds_densify_out_of_bounds__geographic_output():
error = ValueError
if gdal_version.at_least('3.4'):
error = CPLE_AppDefinedError
with pytest.raises(error):
with pytest.raises(CPLE_AppDefinedError):
transform_bounds(
"+proj=laea +lat_0=45 +lon_0=-100 +x_0=0 +y_0=0 "
"+a=6370997 +b=6370997 +units=m +no_defs",

View File

@ -7,7 +7,7 @@ import affine
import numpy as np
import pytest
from .conftest import requires_gdal35, gdal_version
from .conftest import gdal_version
import rasterio
from rasterio.drivers import blacklist
@ -533,7 +533,6 @@ def test_write_masked_nomask(tmp_path):
assert list(arr.flatten()) == [0, 1, 2]
@requires_gdal35
def test_write_int64(tmp_path):
test_file = tmp_path / "test.tif"
data = np.array([np.ones((100, 100), dtype=rasterio.int64) * 127])
@ -553,26 +552,6 @@ def test_write_int64(tmp_path):
assert file.dtypes == (rasterio.int64,)
@pytest.mark.skipif(
gdal_version.at_least('3.5'),
reason="Validate behavior before GDAL 3.5",
)
def test_write_int64__unsupported(tmp_path):
test_file = tmp_path / "test.tif"
data = np.array([np.ones((100, 100), dtype=rasterio.int64) * 127])
with pytest.raises(TypeError, match="invalid dtype"):
with rasterio.open(
test_file,
'w',
driver='GTiff',
width=100,
height=100,
count=1,
dtype=data.dtype
) as file:
file.write(data, [1])
def test_open_no_log(caplog, tmp_path):
"""See gh-2525."""
caplog.set_level(logging.DEBUG)