Skip to content

Commit 7949dff

Browse files
authored
FIX: Raise NotImplementedError if user passes an open file handle to write (#442)
1 parent 664c437 commit 7949dff

File tree

6 files changed

+98
-4
lines changed

6 files changed

+98
-4
lines changed

CHANGES.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
### Bug fixes
66

77
- Silence warning from `write_dataframe` with `GeoSeries.notna()` (#435).
8-
- BUG: Enable mask & bbox filter when geometry column not read (#431).
8+
- Enable mask & bbox filter when geometry column not read (#431).
9+
- Raise NotImplmentedError when user attempts to write to an open file handle (#442).
910
- Prevent seek on read from compressed inputs (#443).
1011

1112
## 0.9.0 (2024-06-17)

pyogrio/geopandas.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -360,7 +360,8 @@ def write_dataframe(
360360
in the output file.
361361
path : str or io.BytesIO
362362
path to output file on writeable file system or an io.BytesIO object to
363-
allow writing to memory
363+
allow writing to memory. Will raise NotImplementedError if an open file
364+
handle is passed; use BytesIO instead.
364365
NOTE: support for writing to memory is limited to specific drivers.
365366
layer : str, optional (default: None)
366367
layer name to create. If writing to memory and layer name is not

pyogrio/raw.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from io import BytesIO
2+
from pathlib import Path
23
import warnings
34

45
from pyogrio._env import GDALEnv
@@ -522,7 +523,8 @@ def _get_write_path_driver(path, driver, append=False):
522523
----------
523524
path : str or io.BytesIO
524525
path to output file on writeable file system or an io.BytesIO object to
525-
allow writing to memory
526+
allow writing to memory. Will raise NotImplementedError if an open file
527+
handle is passed.
526528
driver : str, optional (default: None)
527529
The OGR format driver used to write the vector file. By default attempts
528530
to infer driver from path. Must be provided to write to a file-like
@@ -554,6 +556,11 @@ def _get_write_path_driver(path, driver, append=False):
554556
if append:
555557
raise NotImplementedError("append is not supported for in-memory files")
556558

559+
elif hasattr(path, "write") and not isinstance(path, Path):
560+
raise NotImplementedError(
561+
"writing to an open file handle is not yet supported; instead, write to a BytesIO instance and then read bytes from that to write to the file handle"
562+
)
563+
557564
else:
558565
path = vsi_path(str(path))
559566

@@ -605,7 +612,8 @@ def write(
605612
----------
606613
path : str or io.BytesIO
607614
path to output file on writeable file system or an io.BytesIO object to
608-
allow writing to memory
615+
allow writing to memory. Will raise NotImplementedError if an open file
616+
handle is passed; use BytesIO instead.
609617
NOTE: support for writing to memory is limited to specific drivers.
610618
geometry : ndarray of WKB encoded geometries or None
611619
If None, geometries will not be written to output file

pyogrio/tests/test_arrow.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import os
66
from packaging.version import Version
77
import sys
8+
from zipfile import ZipFile
89

910
import pytest
1011
import numpy as np
@@ -951,6 +952,45 @@ def test_write_memory_existing_unsupported(naturalearth_lowres):
951952
)
952953

953954

955+
@requires_arrow_write_api
956+
def test_write_open_file_handle(tmp_path, naturalearth_lowres):
957+
"""Verify that writing to an open file handle is not currently supported"""
958+
959+
meta, table = read_arrow(naturalearth_lowres, max_features=1)
960+
meta["geometry_type"] = "MultiPolygon"
961+
962+
# verify it fails for regular file handle
963+
with pytest.raises(
964+
NotImplementedError, match="writing to an open file handle is not yet supported"
965+
):
966+
with open(tmp_path / "test.geojson", "wb") as f:
967+
write_arrow(
968+
table,
969+
f,
970+
driver="GeoJSON",
971+
layer="test",
972+
crs=meta["crs"],
973+
geometry_type=meta["geometry_type"],
974+
geometry_name=meta["geometry_name"] or "wkb_geometry",
975+
)
976+
977+
# verify it fails for ZipFile
978+
with pytest.raises(
979+
NotImplementedError, match="writing to an open file handle is not yet supported"
980+
):
981+
with ZipFile(tmp_path / "test.geojson.zip", "w") as z:
982+
with z.open("test.geojson", "w") as f:
983+
write_arrow(
984+
table,
985+
f,
986+
driver="GeoJSON",
987+
layer="test",
988+
crs=meta["crs"],
989+
geometry_type=meta["geometry_type"],
990+
geometry_name=meta["geometry_name"] or "wkb_geometry",
991+
)
992+
993+
954994
@requires_arrow_write_api
955995
def test_non_utf8_encoding_io_shapefile(tmp_path, encoded_text):
956996
encoding, text = encoded_text

pyogrio/tests/test_geopandas_io.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from io import BytesIO
44
import locale
55
import warnings
6+
from zipfile import ZipFile
67

78
import numpy as np
89
import pytest
@@ -1971,6 +1972,27 @@ def test_write_memory_existing_unsupported(naturalearth_lowres):
19711972
write_dataframe(df.head(1), buffer, driver="GeoJSON", layer="test")
19721973

19731974

1975+
def test_write_open_file_handle(tmp_path, naturalearth_lowres):
1976+
"""Verify that writing to an open file handle is not currently supported"""
1977+
1978+
df = read_dataframe(naturalearth_lowres)
1979+
1980+
# verify it fails for regular file handle
1981+
with pytest.raises(
1982+
NotImplementedError, match="writing to an open file handle is not yet supported"
1983+
):
1984+
with open(tmp_path / "test.geojson", "wb") as f:
1985+
write_dataframe(df.head(1), f)
1986+
1987+
# verify it fails for ZipFile
1988+
with pytest.raises(
1989+
NotImplementedError, match="writing to an open file handle is not yet supported"
1990+
):
1991+
with ZipFile(tmp_path / "test.geojson.zip", "w") as z:
1992+
with z.open("test.geojson", "w") as f:
1993+
write_dataframe(df.head(1), f)
1994+
1995+
19741996
@pytest.mark.parametrize("ext", ["gpkg", "geojson"])
19751997
def test_non_utf8_encoding_io(tmp_path, ext, encoded_text):
19761998
"""Verify that we write non-UTF data to the data source

pyogrio/tests/test_raw_io.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from io import BytesIO
44
import json
55
import sys
6+
from zipfile import ZipFile
67

78
import numpy as np
89
from numpy import array_equal
@@ -1177,6 +1178,27 @@ def test_write_memory_existing_unsupported(naturalearth_lowres):
11771178
write(buffer, geometry, field_data, driver="GeoJSON", layer="test", **meta)
11781179

11791180

1181+
def test_write_open_file_handle(tmp_path, naturalearth_lowres):
1182+
"""Verify that writing to an open file handle is not currently supported"""
1183+
1184+
meta, _, geometry, field_data = read(naturalearth_lowres)
1185+
1186+
# verify it fails for regular file handle
1187+
with pytest.raises(
1188+
NotImplementedError, match="writing to an open file handle is not yet supported"
1189+
):
1190+
with open(tmp_path / "test.geojson", "wb") as f:
1191+
write(f, geometry, field_data, driver="GeoJSON", layer="test", **meta)
1192+
1193+
# verify it fails for ZipFile
1194+
with pytest.raises(
1195+
NotImplementedError, match="writing to an open file handle is not yet supported"
1196+
):
1197+
with ZipFile(tmp_path / "test.geojson.zip", "w") as z:
1198+
with z.open("test.geojson", "w") as f:
1199+
write(f, geometry, field_data, driver="GeoJSON", layer="test", **meta)
1200+
1201+
11801202
@pytest.mark.parametrize("ext", ["fgb", "gpkg", "geojson"])
11811203
@pytest.mark.parametrize(
11821204
"read_encoding,write_encoding",

0 commit comments

Comments
 (0)