Skip to content

Commit 1c52f84

Browse files
committed
Python bindings: implement __arrow_c_stream__() interface for ogr.Layer
1 parent ccb472a commit 1c52f84

File tree

3 files changed

+103
-0
lines changed

3 files changed

+103
-0
lines changed

autotest/ogr/ogr_mem.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -709,6 +709,39 @@ def test_ogr_mem_alter_geom_field_defn():
709709
assert lyr.GetSpatialRef() is None
710710

711711

712+
###############################################################################
713+
# Test ogr.Layer.__arrow_c_stream__() interface.
714+
# Cf https://arrow.apache.org/docs/format/CDataInterface/PyCapsuleInterface.html
715+
716+
717+
@gdaltest.enable_exceptions()
718+
def test_ogr_mem_arrow_stream_pycapsule_interface():
719+
720+
ds = ogr.GetDriverByName("Memory").CreateDataSource("")
721+
lyr = ds.CreateLayer("foo")
722+
723+
stream = lyr.__arrow_c_stream__()
724+
assert stream
725+
t = type(stream)
726+
assert t.__module__ == "builtins"
727+
assert t.__name__ == "PyCapsule"
728+
729+
with pytest.raises(
730+
Exception, match="An arrow Arrow Stream is in progress on that layer"
731+
):
732+
lyr.__arrow_c_stream__()
733+
734+
del stream
735+
736+
stream = lyr.__arrow_c_stream__()
737+
assert stream
738+
del stream
739+
740+
with pytest.raises(Exception, match="requested_schema != None not implemented"):
741+
# "something" should rather by a PyCapsule with an ArrowSchema...
742+
lyr.__arrow_c_stream__(requested_schema="something")
743+
744+
712745
###############################################################################
713746

714747

swig/include/ogr.i

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1143,6 +1143,22 @@ public:
11431143
}; /* class ArrowArrayStream */
11441144
#endif
11451145

1146+
#ifdef SWIGPYTHON
1147+
// Implements __arrow_c_stream__ export interface:
1148+
// https://arrow.apache.org/docs/format/CDataInterface/PyCapsuleInterface.html#create-a-pycapsule
1149+
%{
1150+
static void ReleaseArrowArrayStreamPyCapsule(PyObject* capsule) {
1151+
struct ArrowArrayStream* stream =
1152+
(struct ArrowArrayStream*)PyCapsule_GetPointer(capsule, "arrow_array_stream");
1153+
if (stream->release != NULL) {
1154+
stream->release(stream);
1155+
}
1156+
CPLFree(stream);
1157+
}
1158+
%}
1159+
1160+
#endif
1161+
11461162
/************************************************************************/
11471163
/* OGRLayer */
11481164
/************************************************************************/
@@ -1507,6 +1523,31 @@ public:
15071523

15081524
#ifdef SWIGPYTHON
15091525

1526+
PyObject* ExportArrowArrayStreamPyCapsule()
1527+
{
1528+
struct ArrowArrayStream* stream =
1529+
(struct ArrowArrayStream*)CPLMalloc(sizeof(struct ArrowArrayStream));
1530+
1531+
const int success = OGR_L_GetArrowStream(self, stream, NULL);
1532+
1533+
PyObject* ret;
1534+
SWIG_PYTHON_THREAD_BEGIN_BLOCK;
1535+
if( success )
1536+
{
1537+
ret = PyCapsule_New(stream, "arrow_array_stream", ReleaseArrowArrayStreamPyCapsule);
1538+
}
1539+
else
1540+
{
1541+
CPLFree(stream);
1542+
Py_INCREF(Py_None);
1543+
ret = Py_None;
1544+
}
1545+
1546+
SWIG_PYTHON_THREAD_END_BLOCK;
1547+
1548+
return ret;
1549+
}
1550+
15101551
%newobject GetArrowStream;
15111552
ArrowArrayStream* GetArrowStream(char** options = NULL) {
15121553
struct ArrowArrayStream* stream = (struct ArrowArrayStream* )malloc(sizeof(struct ArrowArrayStream));

swig/include/python/ogr_python.i

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -411,6 +411,35 @@ def ReleaseResultSet(self, sql_lyr):
411411
schema = property(schema)
412412

413413

414+
def __arrow_c_stream__(self, requested_schema=None):
415+
"""
416+
Export to a C ArrowArrayStream PyCapsule, according to
417+
https://arrow.apache.org/docs/format/CDataInterface/PyCapsuleInterface.html
418+
419+
Also note that only one active stream can be queried at a time for a
420+
given layer.
421+
422+
Parameters
423+
----------
424+
requested_schema : PyCapsule, default None
425+
The schema to which the stream should be casted, passed as a
426+
PyCapsule containing a C ArrowSchema representation of the
427+
requested schema.
428+
Currently, this is not supported and will raise a
429+
NotImplementedError if the schema is not None
430+
431+
Returns
432+
-------
433+
PyCapsule
434+
A capsule containing a C ArrowArrayStream struct.
435+
"""
436+
437+
if requested_schema is not None:
438+
raise NotImplementedError("requested_schema != None not implemented")
439+
440+
return self.ExportArrowArrayStreamPyCapsule()
441+
442+
414443
def GetArrowStreamAsPyArrow(self, options = []):
415444
""" Return an ArrowStream as PyArrow Schema and Array objects """
416445

0 commit comments

Comments
 (0)