This is an automated email from the ASF dual-hosted git repository.

Jefffrey pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/arrow-rs.git


The following commit(s) were added to refs/heads/main by this push:
     new 078cf60f40 nit: arrow-pyarrow: Use string interning (#10224)
078cf60f40 is described below

commit 078cf60f40ead9321c2facf8c31bc1b142d2816c
Author: Thomas Tanon <[email protected]>
AuthorDate: Sat Jun 27 23:19:30 2026 +0200

    nit: arrow-pyarrow: Use string interning (#10224)
    
    Avoids allocating Python strings each time a method is called but only
    once
    
    Internal only change, should not have any effect outside of a small
    speedup
---
 arrow-pyarrow/src/lib.rs | 72 ++++++++++++++++++++++++++++++++++--------------
 1 file changed, 52 insertions(+), 20 deletions(-)

diff --git a/arrow-pyarrow/src/lib.rs b/arrow-pyarrow/src/lib.rs
index a82256d9ed..cfe51b807a 100644
--- a/arrow-pyarrow/src/lib.rs
+++ b/arrow-pyarrow/src/lib.rs
@@ -77,8 +77,8 @@ use pyo3::exceptions::{PyTypeError, PyValueError};
 use pyo3::ffi::Py_uintptr_t;
 use pyo3::prelude::*;
 use pyo3::sync::PyOnceLock;
-use pyo3::types::{PyCapsule, PyDict, PyList, PyType};
-use pyo3::{CastError, import_exception};
+use pyo3::types::{PyCapsule, PyDict, PyList, PyString, PyType};
+use pyo3::{CastError, import_exception, intern};
 
 import_exception!(pyarrow, ArrowException);
 /// Represents an exception raised by PyArrow.
@@ -130,7 +130,9 @@ impl FromPyArrow for DataType {
         // Newer versions of PyArrow as well as other libraries with Arrow 
data implement this
         // method, so prefer it over _export_to_c.
         // See 
https://arrow.apache.org/docs/format/CDataInterface/PyCapsuleInterface.html
-        if let Some(capsule) = call_capsule_method_if_exists(value, 
"__arrow_c_schema__")? {
+        if let Some(capsule) =
+            call_capsule_method_if_exists(value, intern!(value.py(), 
"__arrow_c_schema__"))?
+        {
             let schema_ptr = extract_capsule::<FFI_ArrowSchema>(
                 &capsule,
                 c"arrow_schema",
@@ -142,7 +144,10 @@ impl FromPyArrow for DataType {
         validate_class(data_type_class(value.py())?, value)?;
 
         let mut c_schema = FFI_ArrowSchema::empty();
-        value.call_method1("_export_to_c", (&raw mut c_schema as 
Py_uintptr_t,))?;
+        value.call_method1(
+            intern!(value.py(), "_export_to_c"),
+            (&raw mut c_schema as Py_uintptr_t,),
+        )?;
         DataType::try_from(&c_schema).map_err(to_py_err)
     }
 }
@@ -150,7 +155,10 @@ impl FromPyArrow for DataType {
 impl ToPyArrow for DataType {
     fn to_pyarrow<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyAny>> {
         let c_schema = FFI_ArrowSchema::try_from(self).map_err(to_py_err)?;
-        data_type_class(py)?.call_method1("_import_from_c", (&raw const 
c_schema as Py_uintptr_t,))
+        data_type_class(py)?.call_method1(
+            intern!(py, "_import_from_c"),
+            (&raw const c_schema as Py_uintptr_t,),
+        )
     }
 }
 
@@ -159,7 +167,9 @@ impl FromPyArrow for Field {
         // Newer versions of PyArrow as well as other libraries with Arrow 
data implement this
         // method, so prefer it over _export_to_c.
         // See 
https://arrow.apache.org/docs/format/CDataInterface/PyCapsuleInterface.html
-        if let Some(capsule) = call_capsule_method_if_exists(value, 
"__arrow_c_schema__")? {
+        if let Some(capsule) =
+            call_capsule_method_if_exists(value, intern!(value.py(), 
"__arrow_c_schema__"))?
+        {
             let schema_ptr = extract_capsule::<FFI_ArrowSchema>(
                 &capsule,
                 c"arrow_schema",
@@ -171,7 +181,10 @@ impl FromPyArrow for Field {
         validate_class(field_class(value.py())?, value)?;
 
         let mut c_schema = FFI_ArrowSchema::empty();
-        value.call_method1("_export_to_c", (&raw mut c_schema as 
Py_uintptr_t,))?;
+        value.call_method1(
+            intern!(value.py(), "_export_to_c"),
+            (&raw mut c_schema as Py_uintptr_t,),
+        )?;
         Field::try_from(&c_schema).map_err(to_py_err)
     }
 }
@@ -179,7 +192,10 @@ impl FromPyArrow for Field {
 impl ToPyArrow for Field {
     fn to_pyarrow<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyAny>> {
         let c_schema = FFI_ArrowSchema::try_from(self).map_err(to_py_err)?;
-        field_class(py)?.call_method1("_import_from_c", (&raw const c_schema 
as Py_uintptr_t,))
+        field_class(py)?.call_method1(
+            intern!(py, "_import_from_c"),
+            (&raw const c_schema as Py_uintptr_t,),
+        )
     }
 }
 
@@ -188,7 +204,9 @@ impl FromPyArrow for Schema {
         // Newer versions of PyArrow as well as other libraries with Arrow 
data implement this
         // method, so prefer it over _export_to_c.
         // See 
https://arrow.apache.org/docs/format/CDataInterface/PyCapsuleInterface.html
-        if let Some(capsule) = call_capsule_method_if_exists(value, 
"__arrow_c_schema__")? {
+        if let Some(capsule) =
+            call_capsule_method_if_exists(value, intern!(value.py(), 
"__arrow_c_schema__"))?
+        {
             let schema_ptr = extract_capsule::<FFI_ArrowSchema>(
                 &capsule,
                 c"arrow_schema",
@@ -200,7 +218,10 @@ impl FromPyArrow for Schema {
         validate_class(schema_class(value.py())?, value)?;
 
         let mut c_schema = FFI_ArrowSchema::empty();
-        value.call_method1("_export_to_c", (&raw mut c_schema as 
Py_uintptr_t,))?;
+        value.call_method1(
+            intern!(value.py(), "_export_to_c"),
+            (&raw mut c_schema as Py_uintptr_t,),
+        )?;
         Schema::try_from(&c_schema).map_err(to_py_err)
     }
 }
@@ -208,7 +229,10 @@ impl FromPyArrow for Schema {
 impl ToPyArrow for Schema {
     fn to_pyarrow<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyAny>> {
         let c_schema = FFI_ArrowSchema::try_from(self).map_err(to_py_err)?;
-        schema_class(py)?.call_method1("_import_from_c", (&raw const c_schema 
as Py_uintptr_t,))
+        schema_class(py)?.call_method1(
+            intern!(py, "_import_from_c"),
+            (&raw const c_schema as Py_uintptr_t,),
+        )
     }
 }
 
@@ -235,7 +259,7 @@ impl FromPyArrow for ArrayData {
         // this changes the pointer's memory and is thus unsafe.
         // In particular, `_export_to_c` can go out of bounds
         value.call_method1(
-            "_export_to_c",
+            intern!(value.py(), "_export_to_c"),
             (
                 &raw mut array as Py_uintptr_t,
                 &raw mut schema as Py_uintptr_t,
@@ -251,7 +275,7 @@ impl ToPyArrow for ArrayData {
         let array = FFI_ArrowArray::new(self);
         let schema = 
FFI_ArrowSchema::try_from(self.data_type()).map_err(to_py_err)?;
         array_class(py)?.call_method1(
-            "_import_from_c",
+            intern!(py, "_import_from_c"),
             (
                 &raw const array as Py_uintptr_t,
                 &raw const schema as Py_uintptr_t,
@@ -343,7 +367,7 @@ impl ToPyArrow for RecordBatch {
         let reader = RecordBatchIterator::new(vec![Ok(self.clone())], 
self.schema());
         let reader: Box<dyn RecordBatchReader + Send> = Box::new(reader);
         let py_reader = reader.into_pyarrow(py)?;
-        py_reader.call_method0("read_next_batch")
+        py_reader.call_method0(intern!(py, "read_next_batch"))
     }
 }
 
@@ -353,7 +377,9 @@ impl FromPyArrow for ArrowArrayStreamReader {
         // Newer versions of PyArrow as well as other libraries with Arrow 
data implement this
         // method, so prefer it over _export_to_c.
         // See 
https://arrow.apache.org/docs/format/CDataInterface/PyCapsuleInterface.html
-        if let Some(capsule) = call_capsule_method_if_exists(value, 
"__arrow_c_stream__")? {
+        if let Some(capsule) =
+            call_capsule_method_if_exists(value, intern!(value.py(), 
"__arrow_c_stream__"))?
+        {
             let stream_ptr =
                 extract_capsule(&capsule, c"arrow_array_stream", 
"__arrow_c_stream__")?;
             let stream = unsafe { 
FFI_ArrowArrayStream::from_raw(stream_ptr.as_ptr()) };
@@ -372,7 +398,10 @@ impl FromPyArrow for ArrowArrayStreamReader {
         // make the conversion through PyArrow's private API
         // this changes the pointer's memory and is thus unsafe.
         // In particular, `_export_to_c` can go out of bounds
-        value.call_method1("_export_to_c", (&raw mut stream as 
Py_uintptr_t,))?;
+        value.call_method1(
+            intern!(value.py(), "_export_to_c"),
+            (&raw mut stream as Py_uintptr_t,),
+        )?;
 
         ArrowArrayStreamReader::try_new(stream)
             .map_err(|err| PyValueError::new_err(err.to_string()))
@@ -385,8 +414,10 @@ impl IntoPyArrow for Box<dyn RecordBatchReader + Send> {
     // there is already a blanket implementation for `T: ToPyArrow`.
     fn into_pyarrow<'py>(self, py: Python<'py>) -> PyResult<Bound<'py, PyAny>> 
{
         let stream = FFI_ArrowArrayStream::new(self);
-        record_batch_reader_class(py)?
-            .call_method1("_import_from_c", (&raw const stream as 
Py_uintptr_t,))
+        record_batch_reader_class(py)?.call_method1(
+            intern!(py, "_import_from_c"),
+            (&raw const stream as Py_uintptr_t,),
+        )
     }
 }
 
@@ -501,6 +532,7 @@ fn record_batch_reader_class(py: Python<'_>) -> 
PyResult<&Bound<'_, PyType>> {
     static TYPE: PyOnceLock<Py<PyType>> = PyOnceLock::new();
     TYPE.import(py, "pyarrow", "RecordBatchReader")
 }
+
 fn data_type_class(py: Python<'_>) -> PyResult<&Bound<'_, PyType>> {
     static TYPE: PyOnceLock<Py<PyType>> = PyOnceLock::new();
     TYPE.import(py, "pyarrow", "DataType")
@@ -557,7 +589,7 @@ impl<T> From<T> for PyArrowType<T> {
 
 fn call_capsule_method_if_exists<'py>(
     object: &Bound<'py, PyAny>,
-    method_name: &'static str,
+    method_name: &Bound<'py, PyString>,
 ) -> PyResult<Option<Bound<'py, PyCapsule>>> {
     let Some(method) = object.getattr_opt(method_name)? else {
         return Ok(None);
@@ -576,7 +608,7 @@ fn call_capsule_method_if_exists<'py>(
 fn call_arrow_c_array_method_if_exists<'py>(
     object: &Bound<'py, PyAny>,
 ) -> PyResult<Option<(Bound<'py, PyCapsule>, Bound<'py, PyCapsule>)>> {
-    let Some(method) = object.getattr_opt("__arrow_c_array__")? else {
+    let Some(method) = object.getattr_opt(intern!(object.py(), 
"__arrow_c_array__"))? else {
         return Ok(None);
     };
     Ok(Some(method.call0()?.extract().map_err(|e| {

Reply via email to