Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
77 commits
Select commit Hold shift + click to select a range
557763e
Fix: pass kwargs on to OGR write
theroggy Apr 2, 2022
90cf78a
Merge remote-tracking branch 'upstream/main'
theroggy Apr 4, 2022
14c01c5
Merge remote-tracking branch 'upstream/main'
theroggy Apr 26, 2022
a924eb0
Merge remote-tracking branch 'upstream/main'
theroggy May 2, 2022
da65c46
Merge remote-tracking branch 'upstream/main'
theroggy May 11, 2022
cd1cfd8
Merge remote-tracking branch 'upstream/main'
theroggy Jun 8, 2022
22434dd
Merge remote-tracking branch 'upstream/main'
theroggy Aug 17, 2022
050df67
Merge remote-tracking branch 'upstream/main'
theroggy Jan 12, 2023
d999ec3
Merge remote-tracking branch 'upstream/main'
theroggy Mar 18, 2023
b92b3c3
Merge remote-tracking branch 'upstream/main'
theroggy Mar 19, 2023
238f151
Merge remote-tracking branch 'upstream/main'
theroggy May 4, 2023
1127ad7
Merge remote-tracking branch 'upstream/main'
theroggy Aug 14, 2023
664d8fe
Merge remote-tracking branch 'upstream/main'
theroggy Aug 14, 2023
ca87d9b
Merge remote-tracking branch 'upstream/main'
theroggy Aug 25, 2023
813c3f6
Merge remote-tracking branch 'upstream/main'
theroggy Sep 6, 2023
f53a0ad
Merge remote-tracking branch 'upstream/main'
theroggy Sep 23, 2023
987f47e
Merge remote-tracking branch 'upstream/main'
theroggy Sep 30, 2023
1ee3968
Merge remote-tracking branch 'upstream/main'
theroggy Oct 2, 2023
ed6432a
Merge remote-tracking branch 'upstream/main'
theroggy Oct 9, 2023
d89ed55
Merge remote-tracking branch 'upstream/main'
theroggy Oct 13, 2023
b050651
Merge remote-tracking branch 'upstream/main'
theroggy Oct 15, 2023
6d52e83
Merge remote-tracking branch 'upstream/main'
theroggy Oct 21, 2023
ff9dec9
Merge remote-tracking branch 'upstream/main'
theroggy Oct 28, 2023
32b7580
Merge remote-tracking branch 'upstream/main'
theroggy Oct 30, 2023
cf99380
Merge remote-tracking branch 'upstream/main'
theroggy Dec 2, 2023
482373f
Merge remote-tracking branch 'upstream/main'
theroggy Dec 4, 2023
fe56ddb
Merge remote-tracking branch 'upstream/main'
theroggy Dec 15, 2023
fb856c0
Merge remote-tracking branch 'upstream/main'
theroggy Jan 23, 2024
cd7ea7f
Merge remote-tracking branch 'upstream/main'
theroggy Jan 23, 2024
6c0a4c9
Merge branch 'main' of https://github.com/theroggy/pyogrio
theroggy Jan 23, 2024
263d87a
Merge remote-tracking branch 'upstream/main'
theroggy Jan 23, 2024
46602dc
Merge remote-tracking branch 'upstream/main'
theroggy Jan 27, 2024
8e30556
Merge remote-tracking branch 'upstream/main'
theroggy Jan 27, 2024
6b9b690
Merge remote-tracking branch 'upstream/main'
theroggy Feb 23, 2024
3b069df
Merge remote-tracking branch 'upstream/main'
theroggy Mar 5, 2024
2951f0e
Merge remote-tracking branch 'upstream/main'
theroggy Apr 4, 2024
7386097
Merge remote-tracking branch 'upstream/main'
theroggy Apr 17, 2024
2c29995
Merge remote-tracking branch 'upstream/main'
theroggy Apr 17, 2024
b7575ac
Merge remote-tracking branch 'upstream/main'
theroggy Apr 17, 2024
d7967cc
Merge remote-tracking branch 'upstream/main'
theroggy Apr 17, 2024
0e5d5bc
Merge remote-tracking branch 'upstream/main'
theroggy Apr 19, 2024
89647b4
Merge remote-tracking branch 'upstream/main'
theroggy May 13, 2024
84f6874
Merge remote-tracking branch 'upstream/main'
theroggy Jun 17, 2024
ff8b1f4
Merge remote-tracking branch 'upstream/main'
theroggy Jul 18, 2024
dc26a4e
Merge remote-tracking branch 'upstream/main'
theroggy Aug 20, 2024
8652bd3
Merge remote-tracking branch 'upstream/main'
theroggy Aug 20, 2024
53a8295
Merge remote-tracking branch 'upstream/main'
theroggy Aug 20, 2024
696cf6c
Merge remote-tracking branch 'upstream/main'
theroggy Aug 21, 2024
807a7a8
Merge remote-tracking branch 'upstream/main'
theroggy Aug 27, 2024
c199715
Revert "MAINT: avoid tests to run if linting fails (#459)"
theroggy Aug 27, 2024
c6af1c8
Merge remote-tracking branch 'upstream/main'
theroggy Aug 27, 2024
94a6b9e
Merge remote-tracking branch 'upstream/main'
theroggy Sep 10, 2024
f0fcb25
Merge remote-tracking branch 'upstream/main'
theroggy Sep 27, 2024
9485eca
Merge remote-tracking branch 'upstream/main'
theroggy Oct 2, 2024
9c66fc6
Merge remote-tracking branch 'upstream/main'
theroggy Nov 14, 2024
b2055bb
Merge remote-tracking branch 'upstream/main'
theroggy Dec 6, 2024
4f91c36
Merge remote-tracking branch 'upstream/main'
theroggy Jan 8, 2025
9dd7c64
Merge remote-tracking branch 'upstream/main'
theroggy Jan 19, 2025
4b8edd3
Merge remote-tracking branch 'upstream/main'
theroggy Jan 27, 2025
c2c38f3
Merge remote-tracking branch 'upstream/main'
theroggy Jan 30, 2025
c2d2306
Merge remote-tracking branch 'upstream/main'
theroggy Mar 30, 2025
647d0e8
Merge remote-tracking branch 'upstream/main'
theroggy Apr 10, 2025
b2f6b1a
Merge remote-tracking branch 'upstream/main'
theroggy Apr 15, 2025
2ac0044
Merge remote-tracking branch 'upstream/main'
theroggy Apr 22, 2025
e614d34
Merge remote-tracking branch 'upstream/main'
theroggy Apr 24, 2025
b425e83
Merge remote-tracking branch 'upstream/main'
theroggy May 13, 2025
951529a
Merge remote-tracking branch 'upstream/main'
theroggy Aug 8, 2025
a153ec3
Merge remote-tracking branch 'upstream/main'
theroggy Aug 26, 2025
80f5a4c
Merge remote-tracking branch 'upstream/main'
theroggy Aug 27, 2025
6f24d6e
Merge remote-tracking branch 'upstream/main'
theroggy Aug 29, 2025
b8514e4
Merge remote-tracking branch 'upstream/main'
theroggy Aug 29, 2025
b34b903
Merge remote-tracking branch 'upstream/main'
theroggy Sep 1, 2025
6dbef2c
ENH: unlock the gil during execute of SQL
theroggy Sep 4, 2025
d833903
Use check_pointer outside nogil
theroggy Sep 5, 2025
7841b82
Update CHANGES.md
theroggy Sep 5, 2025
86b5b86
Use nogil on all GDAL functions that can take significant time
theroggy Sep 7, 2025
9b76da1
Update CHANGES.md
theroggy Sep 8, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

- Add listing of GDAL data types and subtypes to `read_info` (#556).
- Add support to read list fields without arrow (#558).
- Unlock the gil during GDAL functions that can take significant time (#572).

### Bug fixes

Expand Down
4 changes: 3 additions & 1 deletion pyogrio/_geometry.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,9 @@ cdef str get_geometry_type(void *ogr_layer):
cdef OGRwkbGeometryType ogr_type

try:
ogr_featuredef = check_pointer(OGR_L_GetLayerDefn(ogr_layer))
with nogil:
ogr_featuredef = OGR_L_GetLayerDefn(ogr_layer)
ogr_featuredef = check_pointer(ogr_featuredef)
except NullPointerError:
raise DataLayerError("Could not get layer definition")

Expand Down
131 changes: 92 additions & 39 deletions pyogrio/_io.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ from pathlib import Path

from libc.stdint cimport uint8_t, uintptr_t
from libc.stdlib cimport malloc, free
from libc.string cimport strlen
from libc.math cimport isnan
from cpython.pycapsule cimport PyCapsule_GetPointer

Expand Down Expand Up @@ -226,6 +225,7 @@ cdef void* ogr_open(const char* path_c, int mode, char** options) except NULL:
"""
cdef void *ogr_dataset = NULL
cdef ErrorHandler errors
cdef int flags

# Force linear approximations in all cases
OGRSetNonLinearGeometriesEnabledFlag(0)
Expand All @@ -240,9 +240,10 @@ cdef void* ogr_open(const char* path_c, int mode, char** options) except NULL:
# WARNING: GDAL logs warnings about invalid open options to stderr
# instead of raising an error
with capture_errors() as errors:
ogr_dataset = GDALOpenEx(
path_c, flags, NULL, <const char *const *>options, NULL
)
with nogil:
ogr_dataset = GDALOpenEx(
path_c, flags, NULL, <const char *const *>options, NULL
)
return errors.check_pointer(ogr_dataset, True)

except NullPointerError:
Expand Down Expand Up @@ -295,15 +296,23 @@ cdef OGRLayerH get_ogr_layer(GDALDatasetH ogr_dataset, layer) except NULL:
pointer to OGR layer
"""
cdef OGRLayerH ogr_layer = NULL
cdef char *layer_c
cdef int layer_int
cdef char *sql_c

try:
if isinstance(layer, str):
name_b = layer.encode("utf-8")
name_c = name_b
ogr_layer = check_pointer(GDALDatasetGetLayerByName(ogr_dataset, name_c))
layer_b = layer.encode("utf-8")
layer_c = layer_b
with nogil:
ogr_layer = GDALDatasetGetLayerByName(ogr_dataset, layer_c)
ogr_layer = check_pointer(ogr_layer)

elif isinstance(layer, int):
ogr_layer = check_pointer(GDALDatasetGetLayer(ogr_dataset, layer))
layer_int = layer
with nogil:
ogr_layer = GDALDatasetGetLayer(ogr_dataset, layer_int)
ogr_layer = check_pointer(ogr_layer)
else:
raise ValueError(
f"'layer' parameter must be a str or int, got {type(layer)}"
Expand All @@ -325,7 +334,8 @@ cdef OGRLayerH get_ogr_layer(GDALDatasetH ogr_dataset, layer) except NULL:
sql_b = f"SET interest_layers = {layer_name}".encode("utf-8")
sql_c = sql_b

GDALDatasetExecuteSQL(ogr_dataset, sql_c, NULL, NULL)
with nogil:
GDALDatasetExecuteSQL(ogr_dataset, sql_c, NULL, NULL)

return ogr_layer

Expand All @@ -347,18 +357,23 @@ cdef OGRLayerH execute_sql(
-------
pointer to OGR layer
"""
cdef char * sql_c
cdef char * sql_dialect_c
cdef OGRLayerH retval = NULL

try:
sql_b = sql.encode("utf-8")
sql_c = sql_b
if sql_dialect is None:
return check_pointer(GDALDatasetExecuteSQL(ogr_dataset, sql_c, NULL, NULL))
with nogil:
retval = GDALDatasetExecuteSQL(ogr_dataset, sql_c, NULL, NULL)
return check_pointer(retval)

sql_dialect_b = sql_dialect.encode("utf-8")
sql_dialect_c = sql_dialect_b
return check_pointer(GDALDatasetExecuteSQL(
ogr_dataset, sql_c, NULL, sql_dialect_c)
)
with nogil:
retval = GDALDatasetExecuteSQL(ogr_dataset, sql_c, NULL, sql_dialect_c)
return check_pointer(retval)

# GDAL does not always raise exception messages in this case
except NullPointerError:
Expand Down Expand Up @@ -386,7 +401,9 @@ cdef str get_crs(OGRLayerH ogr_layer):
cdef char *ogr_wkt = NULL

try:
ogr_crs = check_pointer(OGR_L_GetSpatialRef(ogr_layer))
with nogil:
ogr_crs = OGR_L_GetSpatialRef(ogr_layer)
ogr_crs = check_pointer(ogr_crs)

except NullPointerError:
# No coordinate system defined.
Expand Down Expand Up @@ -433,7 +450,9 @@ cdef get_driver(OGRDataSourceH ogr_dataset):
cdef void *ogr_driver

try:
ogr_driver = check_pointer(GDALGetDatasetDriver(ogr_dataset))
with nogil:
ogr_driver = GDALGetDatasetDriver(ogr_dataset)
ogr_driver = check_pointer(ogr_driver)

except NullPointerError:
raise DataLayerError(f"Could not detect driver of dataset") from None
Expand Down Expand Up @@ -464,19 +483,25 @@ cdef get_feature_count(OGRLayerH ogr_layer, int force):
"""

cdef OGRFeatureH ogr_feature = NULL
cdef int feature_count = OGR_L_GetFeatureCount(ogr_layer, force)
cdef int feature_count

with nogil:
feature_count = OGR_L_GetFeatureCount(ogr_layer, force)

# if GDAL refuses to give us the feature count, we have to loop over all
# features ourselves and get the count. This can happen for some drivers
# (e.g., OSM) or if a where clause is invalid but not rejected as error
if force and feature_count == -1:
# make sure layer is read from beginning
OGR_L_ResetReading(ogr_layer)
with nogil:
OGR_L_ResetReading(ogr_layer)

feature_count = 0
while True:
try:
ogr_feature = check_pointer(OGR_L_GetNextFeature(ogr_layer))
with nogil:
ogr_feature = OGR_L_GetNextFeature(ogr_layer)
ogr_feature = check_pointer(ogr_feature)
feature_count +=1

except NullPointerError:
Expand Down Expand Up @@ -519,10 +544,11 @@ cdef get_total_bounds(OGRLayerH ogr_layer, int force):
tuple of (xmin, ymin, xmax, ymax) or None
The total bounds of the layer, or None if they could not be determined.
"""

cdef OGREnvelope ogr_envelope

if OGR_L_GetExtent(ogr_layer, &ogr_envelope, force) == OGRERR_NONE:
with nogil:
err = OGR_L_GetExtent(ogr_layer, &ogr_envelope, force)
if err == OGRERR_NONE:
bounds = (
ogr_envelope.MinX, ogr_envelope.MinY, ogr_envelope.MaxX, ogr_envelope.MaxY
)
Expand Down Expand Up @@ -677,7 +703,9 @@ cdef get_fields(OGRLayerH ogr_layer, str encoding, use_arrow=False):
cdef const char *key_c

try:
ogr_featuredef = check_pointer(OGR_L_GetLayerDefn(ogr_layer))
with nogil:
ogr_featuredef = OGR_L_GetLayerDefn(ogr_layer)
ogr_featuredef = check_pointer(ogr_featuredef)

except NullPointerError:
raise DataLayerError("Could not get layer definition") from None
Expand Down Expand Up @@ -751,10 +779,14 @@ cdef apply_where_filter(OGRLayerH ogr_layer, str where):
------
ValueError: if SQL query is not valid
"""
cdef const char* where_c
cdef int err

where_b = where.encode("utf-8")
where_c = where_b
err = OGR_L_SetAttributeFilter(ogr_layer, where_c)
with nogil:
err = OGR_L_SetAttributeFilter(ogr_layer, where_c)

# WARNING: GDAL does not raise this error for GPKG but instead only
# logs to stderr
if err != OGRERR_NONE:
Expand All @@ -781,12 +813,14 @@ cdef apply_bbox_filter(OGRLayerH ogr_layer, bbox):
ValueError: if bbox is not a list or tuple or does not have proper number of
items
"""
cdef double xmin, ymin, xmax, ymax

if not (isinstance(bbox, (tuple, list)) and len(bbox) == 4):
raise ValueError(f"Invalid bbox: {bbox}")

xmin, ymin, xmax, ymax = bbox
OGR_L_SetSpatialFilterRect(ogr_layer, xmin, ymin, xmax, ymax)
with nogil:
OGR_L_SetSpatialFilterRect(ogr_layer, xmin, ymin, xmax, ymax)


cdef apply_geometry_filter(OGRLayerH ogr_layer, wkb):
Expand All @@ -807,7 +841,8 @@ cdef apply_geometry_filter(OGRLayerH ogr_layer, wkb):
OGR_G_DestroyGeometry(ogr_geometry)
raise GeometryError("Could not create mask geometry") from None

OGR_L_SetSpatialFilter(ogr_layer, ogr_geometry)
with nogil:
OGR_L_SetSpatialFilter(ogr_layer, ogr_geometry)
OGR_G_DestroyGeometry(ogr_geometry)


Expand All @@ -819,7 +854,9 @@ cdef apply_skip_features(OGRLayerH ogr_layer, int skip_features):
ogr_layer : pointer to open OGR layer
wskip_features : int
"""
err = OGR_L_SetNextByIndex(ogr_layer, skip_features)
with nogil:
err = OGR_L_SetNextByIndex(ogr_layer, skip_features)

# GDAL can raise an error (depending on the format) for out-of-bound index,
# but `validate_feature_range()` should ensure we only pass a valid number
if err != OGRERR_NONE:
Expand Down Expand Up @@ -1076,14 +1113,14 @@ cdef get_features(
uint8_t return_fids,
bint datetime_as_string
):

cdef OGRFeatureH ogr_feature = NULL
cdef int n_fields
cdef int i
cdef int field_index

# make sure layer is read from beginning
OGR_L_ResetReading(ogr_layer)
with nogil:
OGR_L_ResetReading(ogr_layer)

if skip_features > 0:
apply_skip_features(ogr_layer, skip_features)
Expand Down Expand Up @@ -1128,7 +1165,9 @@ cdef get_features(
break

try:
ogr_feature = check_pointer(OGR_L_GetNextFeature(ogr_layer))
with nogil:
ogr_feature = OGR_L_GetNextFeature(ogr_layer)
ogr_feature = check_pointer(ogr_feature)

except NullPointerError:
# No more rows available, so stop reading
Expand Down Expand Up @@ -1196,7 +1235,8 @@ cdef get_features_by_fid(
cdef int count = len(fids)

# make sure layer is read from beginning
OGR_L_ResetReading(ogr_layer)
with nogil:
OGR_L_ResetReading(ogr_layer)

if read_geometry:
geometries = np.empty(shape=(count, ), dtype="object")
Expand Down Expand Up @@ -1226,7 +1266,9 @@ cdef get_features_by_fid(
fid = fids[i]

try:
ogr_feature = check_pointer(OGR_L_GetFeature(ogr_layer, fid))
with nogil:
ogr_feature = OGR_L_GetFeature(ogr_layer, fid)
ogr_feature = check_pointer(ogr_feature)

except NullPointerError:
raise FeatureError(f"Could not read feature with fid {fid}") from None
Expand Down Expand Up @@ -1258,7 +1300,8 @@ cdef get_bounds(OGRLayerH ogr_layer, int skip_features, int num_features):
cdef int i

# make sure layer is read from beginning
OGR_L_ResetReading(ogr_layer)
with nogil:
OGR_L_ResetReading(ogr_layer)

if skip_features > 0:
apply_skip_features(ogr_layer, skip_features)
Expand All @@ -1276,7 +1319,9 @@ cdef get_bounds(OGRLayerH ogr_layer, int skip_features, int num_features):
break

try:
ogr_feature = check_pointer(OGR_L_GetNextFeature(ogr_layer))
with nogil:
ogr_feature = OGR_L_GetNextFeature(ogr_layer)
ogr_feature = check_pointer(ogr_feature)

except NullPointerError:
# No more rows available, so stop reading
Expand Down Expand Up @@ -1457,7 +1502,8 @@ def ogr_read(
field_c = field_b
fields_c = CSLAddString(fields_c, field_c)

OGR_L_SetIgnoredFields(ogr_layer, <const char**>fields_c)
with nogil:
OGR_L_SetIgnoredFields(ogr_layer, <const char**>fields_c)

geometry_type = get_geometry_type(ogr_layer)

Expand Down Expand Up @@ -1802,7 +1848,8 @@ def ogr_open_arrow(
field_c = field_b
fields_c = CSLAddString(fields_c, field_c)

OGR_L_SetIgnoredFields(ogr_layer, <const char**>fields_c)
with nogil:
OGR_L_SetIgnoredFields(ogr_layer, <const char**>fields_c)

if not return_fids:
options = CSLSetNameValue(options, "INCLUDE_FID", "NO")
Expand All @@ -1823,20 +1870,24 @@ def ogr_open_arrow(
)

# make sure layer is read from beginning
OGR_L_ResetReading(ogr_layer)
with nogil:
OGR_L_ResetReading(ogr_layer)

# allocate the stream struct and wrap in capsule to ensure clean-up on error
capsule = alloc_c_stream(&stream)

if not OGR_L_GetArrowStream(ogr_layer, stream, options):
with nogil:
reval = OGR_L_GetArrowStream(ogr_layer, stream, options)
if not reval:
raise RuntimeError("Failed to open ArrowArrayStream from Layer")

if skip_features > 0:
# only supported for GDAL >= 3.8.0; have to do this after getting
# the Arrow stream
# use `OGR_L_SetNextByIndex` directly and not `apply_skip_features`
# to ignore errors in case skip_features == feature_count
OGR_L_SetNextByIndex(ogr_layer, skip_features)
with nogil:
OGR_L_SetNextByIndex(ogr_layer, skip_features)

if use_pyarrow:
import pyarrow as pa
Expand Down Expand Up @@ -2145,12 +2196,14 @@ cdef get_layer_names(OGRDataSourceH ogr_dataset):
"""
cdef OGRLayerH ogr_layer = NULL

layer_count = GDALDatasetGetLayerCount(ogr_dataset)
with nogil:
layer_count = GDALDatasetGetLayerCount(ogr_dataset)

data = np.empty(shape=(layer_count, 2), dtype=object)
data_view = data[:]
for i in range(layer_count):
ogr_layer = GDALDatasetGetLayer(ogr_dataset, i)
with nogil:
ogr_layer = GDALDatasetGetLayer(ogr_dataset, i)

data_view[i, 0] = get_string(OGR_L_GetName(ogr_layer))
data_view[i, 1] = get_geometry_type(ogr_layer)
Expand Down
Loading