Repository: geopandas/geopandas

Issue:
MAINT: Fiona 1.10 support
In our CI builds our fiona tests are failing against the 1.10a wheels, as a new path opening API is introduced (https://github.com/Toblerity/Fiona/pull/1331/files). We use `fiona.parse_path`, `fiona.path.ParsedPath`, `fiona.path.UnparsedPath` and `fiona.path.ParsedPath.from_uri(path)` at a quick glance. Now I think if we'd imported `fiona.path` instead of getting it relative to `import fiona` we'd just hit a warning that these will be removed in fiona 2.0 instead of hard crashes, but we may as well look at solving the root problem right now.

Edit: had a bit of a look and it seems to keep the same level of convenience witth zipfiles we might need to either vendor some things from fiona (or use the private internals), or use fsspec or similar


Discussion:
@m-richards fiona's path module was never intended to be used by other projects. It's only public by accident. If I had known geopandas was using them, I would have warned you all sooner. Geopandas should try to rely only on passing URIs to its reader/writer.
Note that geopandas was not only using `fiona.path`, but also some objects that were exposed publicly in the top-level `fiona` namespace (i.e. `fiona.parse_path`). So if fiona wants to make those private, in geopandas we can of course update our code, but also for other users it might still be nice to first include a deprecation warning about the removal.
Temporarily pinning to fiona 1.9 to get green CI -> https://github.com/geopandas/geopandas/pull/3208
The main usage of `fiona.path` in our code is related to handling with zip files. It might also be that things have improved on fiona's side lately and not everything might still be needed.

We have this helper to check if a path points to a zip-file:

https://github.com/geopandas/geopandas/blob/fb8da80fb2f170bdcb6b635a783d9d1424222021/geopandas/io/file.py#L179-L186

and then if that is the case, we have the following piece of code to add "zip" to the path's scheme:

https://github.com/geopandas/geopandas/blob/fb8da80fb2f170bdcb6b635a783d9d1424222021/geopandas/io/file.py#L321-L339

This is to make sure a user you can just pass a path to a zipfile like `"path/to/file.zip"`, instead of needing to do `"zip://./path/to/file.zip"`.

Resolving patch:
diff --git a/geopandas/io/file.py b/geopandas/io/file.py
--- a/geopandas/io/file.py
+++ b/geopandas/io/file.py
@@ -178,6 +178,8 @@ def _is_url(url):
 
 def _is_zip(path):
     """Check if a given path is a zipfile"""
+    import fiona.path
+
     parsed = fiona.path.ParsedPath.from_uri(path)
     return (
         parsed.archive.endswith(".zip")
@@ -322,7 +324,12 @@ def _read_file_fiona(
         # zipped file. In order to match that behavior, attempt to add a zip scheme
         # if missing.
         if _is_zip(str(path_or_bytes)):
-            parsed = fiona.parse_path(str(path_or_bytes))
+            # TODO: disconnect GeoPandas from Fiona's URI/path parsing internals.
+            # Meanwhile parse_path is imported here to avoid adding more conditional
+            # statements at the top of the module.
+            from fiona.path import parse_path
+
+            parsed = parse_path(str(path_or_bytes))
             if isinstance(parsed, fiona.path.ParsedPath):
                 # If fiona is able to parse the path, we can safely look at the scheme
                 # and update it to have a zip scheme if necessary.


Test patch:
diff --git a/geopandas/io/tests/test_file.py b/geopandas/io/tests/test_file.py
--- a/geopandas/io/tests/test_file.py
+++ b/geopandas/io/tests/test_file.py
@@ -689,8 +689,10 @@ def test_infer_zipped_file(engine, nybb_filename):
 
     # Check that it can add a zip scheme for a path that includes a subpath
     # within the archive.
-    gdf = read_file(path + "!nybb.shp", engine=engine)
-    assert isinstance(gdf, geopandas.GeoDataFrame)
+    # TODO: fix for fiona
+    if engine == "pyogrio":
+        gdf = read_file(path + "!nybb.shp", engine=engine)
+        assert isinstance(gdf, geopandas.GeoDataFrame)
 
 
 def test_allow_legacy_gdal_path(engine, nybb_filename):