diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index e80f9f06..acc240df 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -49,6 +49,8 @@ jobs: gdal-version: '3.6.4' - python-version: '3.11' gdal-version: '3.7.1' + - python-version: '3.12' + gdal-version: '3.7.1' steps: - uses: actions/checkout@v4 diff --git a/CHANGES.txt b/CHANGES.txt index 39f2fd3a..f0c2f028 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -3,6 +3,22 @@ Changes All issue numbers are relative to https://github.com/Toblerity/Fiona/issues. +1.9.5 (2023-10-11) +------------------ + +Bug fixes: + +- Expand keys in schema mismatch exception, resolving #1278. +- Preserve the null properties and geometry of a Feature when serializing + (#1276). + +Packaging: + +* Builds now require Cython >= 3.0.2 (#1276). +* PyPI wheels include GDAL 3.6.4, PROJ 9.0.1, and GEOS 3.11.2. +* PyPI wheels include curl 8.4.0, addressing CVE-2023-38545 and CVE-38546. +* PyPI wheels are now available for Python 3.12. + 1.9.4.post1 (2023-05-23) ------------------------ diff --git a/CITATION.cff b/CITATION.cff index f0879f3e..136eaa56 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -2,9 +2,9 @@ cff-version: 1.2.0 message: "Please cite this software using these metadata." type: software title: Fiona -version: "1.9.0" -date-released: "2023-01-30" -abstract: "OGR's neat, nimble, no-nonsense API." +version: "1.9.5" +date-released: "2023-10-11" +abstract: "Fiona streams simple feature data to and from GIS formats like GeoPackage and Shapefile." keywords: - cartography - GIS diff --git a/environment.yml b/environment.yml index 1dbe4e86..8c132109 100644 --- a/environment.yml +++ b/environment.yml @@ -7,5 +7,6 @@ dependencies: - python=3.9.* - libgdal=3.4.* - sphinx-click +- sphinx-rtd-theme - pip: - jinja2==3.0.3 diff --git a/fiona/model.py b/fiona/model.py index 81184c4b..4e575f05 100644 --- a/fiona/model.py +++ b/fiona/model.py @@ -383,15 +383,15 @@ class ObjectEncoder(JSONEncoder): """Encodes Geometry, Feature, and Properties.""" def default(self, o): - if isinstance(o, (Geometry, Properties)): - return {k: self.default(v) for k, v in o.items() if v is not None} - elif isinstance(o, Feature): - o_dict = dict(o) - o_dict["type"] = "Feature" - if o.geometry is not None: - o_dict["geometry"] = self.default(o.geometry) - if o.properties is not None: - o_dict["properties"] = self.default(o.properties) + if isinstance(o, Object): + o_dict = {k: self.default(v) for k, v in o.items()} + if isinstance(o, Geometry): + if o.type == "GeometryCollection": + _ = o_dict.pop("coordinates", None) + else: + _ = o_dict.pop("geometries", None) + elif isinstance(o, Feature): + o_dict["type"] = "Feature" return o_dict elif isinstance(o, bytes): return hexlify(o) diff --git a/fiona/ogrext.pyx b/fiona/ogrext.pyx index dfd6fe71..dcd5a65b 100644 --- a/fiona/ogrext.pyx +++ b/fiona/ogrext.pyx @@ -1397,7 +1397,7 @@ cdef class WritingSession(Session): if set(record.properties.keys()) != schema_props_keys: raise ValueError( "Record does not match collection schema: %r != %r" % ( - record.properties.keys(), + list(record.properties.keys()), list(schema_props_keys) )) if not validate_geometry_type(record): diff --git a/fiona/session.py b/fiona/session.py index 084481da..30ac0b5f 100644 --- a/fiona/session.py +++ b/fiona/session.py @@ -2,13 +2,16 @@ import logging import os +import warnings from fiona.path import parse_path, UnparsedPath log = logging.getLogger(__name__) try: - import boto3 + with warnings.catch_warnings(): + warnings.simplefilter("ignore") + import boto3 except ImportError: log.debug("Could not import boto3, continuing with reduced functionality.") boto3 = None diff --git a/pyproject.toml b/pyproject.toml index cb78880c..94f84af2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,12 +1,14 @@ [build-system] requires = [ - "cython>=3", - "setuptools>=61", + "cython~=3.0.2", + "oldest-supported-numpy", + "setuptools>=67.8", + "wheel", ] build-backend = "setuptools.build_meta" [project] -name = "Fiona" +name = "fiona" dynamic = ["readme", "version"] authors = [ {name = "Sean Gillies"}, diff --git a/pytest.ini b/pytest.ini index 6d58010f..522b93d8 100644 --- a/pytest.ini +++ b/pytest.ini @@ -6,6 +6,7 @@ filterwarnings = ignore:.*negative step size may be slow*:RuntimeWarning ignore:.*is buggy and will be removed in Fiona 2.0.* ignore:.*unclosed =3 pytest~=7.2 pytest-cov~=4.0 pytz==2022.6 +setuptools wheel diff --git a/tests/test_model.py b/tests/test_model.py index 838bdae5..cd3082c6 100644 --- a/tests/test_model.py +++ b/tests/test_model.py @@ -132,7 +132,7 @@ def test_geometry__props(): def test_geometry_gi(): """Geometry __geo_interface__""" - gi = Geometry(coordinates=(0, 0), type="Point").__geo_interface__ + gi = Geometry(coordinates=(0, 0), type="Point", geometries=[]).__geo_interface__ assert gi["type"] == "Point" assert gi["coordinates"] == (0, 0) @@ -314,3 +314,22 @@ def test_encode_bytes(): """Bytes are encoded using base64.""" assert ObjectEncoder().default(b"01234") == b'3031323334' + +def test_null_property_encoding(): + """A null feature property is retained.""" + # Verifies fix for gh-1270. + assert ObjectEncoder().default(Properties(a=1, b=None)) == {"a": 1, "b": None} + + +def test_null_geometry_encoding(): + """A null feature geometry is retained.""" + # Verifies fix for gh-1270. + o_dict = ObjectEncoder().default(Feature()) + assert o_dict["geometry"] is None + + +def test_geometry_collection_encoding(): + """No coordinates in a GeometryCollection.""" + assert "coordinates" not in ObjectEncoder().default( + Geometry(type="GeometryCollection", geometries=[]) + )