Skip to content

Commit ad4a4c3

Browse files
AdamGlusteinclaude
andcommitted
Move numpy _import_array out of static initializers into Python module initialization
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Signed-off-by: Adam Glustein <Adam.Glustein@Point72.com>
1 parent 72c224b commit ad4a4c3

10 files changed

Lines changed: 28 additions & 43 deletions

cpp/csp/python/CMakeLists.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,12 @@ target_link_libraries(cspnpstatsimpl cspimpl npstatsimpl)
119119
target_include_directories(npstatsimpl PRIVATE ${NUMPY_INCLUDE_DIRS})
120120
target_include_directories(cspnpstatsimpl PRIVATE ${NUMPY_INCLUDE_DIRS})
121121

122+
# By default numpy declares PyArray_API as a TU-local `static` — each translation unit gets its own copy and must call import_array() itself
123+
# Setting PY_ARRAY_UNIQUE_SYMBOL turns it into a named extern symbol, so all TUs within one .so share a single pointer and a single import_array() from PyInit_* is enough
124+
target_compile_definitions(cspimpl PRIVATE PY_ARRAY_UNIQUE_SYMBOL=CSP_CSPIMPL_ARRAY_API)
125+
target_compile_definitions(npstatsimpl PRIVATE PY_ARRAY_UNIQUE_SYMBOL=CSP_CSPNPSTATSIMPL_ARRAY_API)
126+
target_compile_definitions(cspnpstatsimpl PRIVATE PY_ARRAY_UNIQUE_SYMBOL=CSP_CSPNPSTATSIMPL_ARRAY_API)
127+
122128
install(TARGETS csptypesimpl cspimpl cspbaselibimpl cspbasketlibimpl cspmathimpl cspstatsimpl csptestlibimpl cspnpstatsimpl
123129
PUBLIC_HEADER DESTINATION include/csp/python
124130
RUNTIME DESTINATION ${CSP_RUNTIME_INSTALL_SUBDIR}

cpp/csp/python/NumpyConversions.cpp

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
//this is included first so that we do include without NO_IMPORT_ARRAY defined, which is done in NumpyConversions.h
1+
#define NO_IMPORT_ARRAY
22
#include <numpy/ndarrayobject.h>
33
#include <numpy/npy_2_compat.h>
44

@@ -9,14 +9,6 @@
99
#include <locale>
1010
#include <codecvt>
1111

12-
static void * init_nparray()
13-
{
14-
csp::python::AcquireGIL gil;
15-
import_array();
16-
return nullptr;
17-
}
18-
static void * s_init_array = init_nparray();
19-
2012
namespace csp::python
2113
{
2214

cpp/csp/python/PyNumpyAdapter.cpp

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,17 @@
1-
//this is included first so that we do include without NO_IMPORT_ARRAY defined, which is done in NumpyInputAdapter.h
1+
#define NO_IMPORT_ARRAY
22
#include <numpy/ndarrayobject.h>
3-
//
4-
// BUT! really we can't have this here and in NumpyConversions.cpp....
5-
//
63

74
#include <csp/python/Conversions.h>
85
#include <csp/python/Exception.h>
96
#include <csp/python/NumpyInputAdapter.h>
107
#include <csp/python/PyEngine.h>
118
#include <csp/python/PyInputAdapterWrapper.h>
129

13-
static void * init_nparray()
14-
{
15-
csp::python::AcquireGIL gil;
16-
import_array();
17-
return nullptr;
18-
}
19-
static void * s_init_array;
20-
2110
namespace csp::python
2211
{
2312

2413
static InputAdapter * numpy_adapter_creator( csp::AdapterManager * manager, PyEngine * pyengine, PyObject * pyType, PushMode pushMode, PyObject * args )
2514
{
26-
s_init_array = init_nparray();
27-
2815
PyObject * type;
2916
PyArrayObject * pyDatetimes = nullptr;
3017
PyArrayObject * pyValues = nullptr;

cpp/csp/python/adapters/ArrowCppNodes.cpp

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
// RecordBatches are transported across the Python/C++ boundary as PyCapsules
55
// using the Arrow C Data Interface.
66

7-
// Must include numpy first without NO_IMPORT_ARRAY to define PyArray_API in this TU
7+
#define NO_IMPORT_ARRAY
88
#include <numpy/ndarrayobject.h>
99
#include <csp/adapters/arrow/RecordBatchToStruct.h>
1010
#include <csp/adapters/arrow/StructToRecordBatch.h>
@@ -268,11 +268,8 @@ EXPORT_CPPNODE( struct_to_record_batches );
268268
REGISTER_CPPNODE( csp::cppnodes, record_batches_to_struct );
269269
REGISTER_CPPNODE( csp::cppnodes, struct_to_record_batches );
270270

271-
// import_array() expands to `return NULL` on error, so the enclosing function must return a pointer type
272271
static void * _init_numpy = []() -> void *
273272
{
274-
csp::python::AcquireGIL gil;
275-
import_array();
276273
csp::python::registerNumpyListFieldReaderFactory();
277274
csp::python::registerNumpyListFieldWriterFactory();
278275
return nullptr;

cpp/csp/python/adapters/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ if(CSP_BUILD_ARROW_ADAPTER)
22
add_library(arrowadapterimpl SHARED PyArrowInputAdapter.cpp ArrowCppNodes.cpp ArrowInputAdapter.h)
33
target_link_libraries(arrowadapterimpl csp_core csp_engine cspimpl csp_arrow_adapter ${CSP_ARROW_LINK_LIBS})
44
target_include_directories(arrowadapterimpl PUBLIC ${ARROW_INCLUDE_DIR})
5+
target_compile_definitions(arrowadapterimpl PRIVATE PY_ARRAY_UNIQUE_SYMBOL=CSP_ARROWADAPTERIMPL_ARRAY_API)
56
install(TARGETS arrowadapterimpl RUNTIME DESTINATION ${CSP_RUNTIME_INSTALL_SUBDIR} )
67
endif()
78

cpp/csp/python/adapters/PyArrowInputAdapter.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
#include <numpy/ndarrayobject.h>
2+
13
#include <csp/python/adapters/ArrowInputAdapter.h>
24
#include <csp/python/Conversions.h>
35
#include <csp/python/Exception.h>
@@ -49,6 +51,9 @@ PyMODINIT_FUNC PyInit__arrowadapterimpl( void )
4951
return NULL;
5052
}
5153

54+
// Call _import_array from PyInit rather than a static initializer to avoid running the numpy import machinery at dlopen time, which could deadlock
55+
import_array();
56+
5257
if( !InitHelper::instance().execute( m ) )
5358
{
5459
return NULL;

cpp/csp/python/cspbaselibimpl.cpp

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,6 @@
66
#include <csp/engine/CppNode.h>
77
#include <csp/python/Conversions.h>
88

9-
static void * init_nparray()
10-
{
11-
csp::python::AcquireGIL gil;
12-
import_array();
13-
return nullptr;
14-
}
15-
static void * s_init_array = init_nparray();
16-
179
namespace csp::cppnodes
1810
{
1911
DECLARE_CPPNODE( exprtk_impl )
@@ -377,6 +369,9 @@ PyMODINIT_FUNC PyInit__cspbaselibimpl(void)
377369
if( m == NULL )
378370
return NULL;
379371

372+
// Call _import_array from PyInit rather than a static initializer to avoid running the numpy import machinery at dlopen time, which could deadlock
373+
import_array();
374+
380375
if( !csp::python::InitHelper::instance().execute( m ) )
381376
return NULL;
382377

cpp/csp/python/cspimpl.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
#include <numpy/ndarrayobject.h>
2+
13
#include <csp/engine/DynamicEngine.h>
24
#include <csp/python/Conversions.h>
35
#include <csp/python/InitHelper.h>
@@ -156,6 +158,9 @@ PyMODINIT_FUNC PyInit__cspimpl(void)
156158
if( m == NULL )
157159
return NULL;
158160

161+
// Call _import_array from PyInit rather than a static initializer to avoid running the numpy import machinery at dlopen time, which could deadlock
162+
import_array();
163+
159164
if( !InitHelper::instance().execute( m ) )
160165
return NULL;
161166

cpp/csp/python/cspnpstatsimpl.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,9 @@ PyMODINIT_FUNC PyInit__cspnpstatsimpl(void)
7171
if( m == NULL )
7272
return NULL;
7373

74+
// Call _import_array from PyInit rather than a static initializer to avoid running the numpy import machinery at dlopen time, which could deadlock
75+
import_array();
76+
7477
if( !csp::python::InitHelper::instance().execute( m ) )
7578
return NULL;
7679

cpp/csp/python/npstatsimpl.cpp

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11

22
#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
33
#include <Python.h> // needs to be included first
4+
5+
#define NO_IMPORT_ARRAY
46
#include <numpy/ndarrayobject.h>
57
#include <numpy/npy_math.h> // need to be included before csp
68

@@ -26,14 +28,6 @@ template<> struct NPY_TYPE<double> { static const int value = NPY_DOUBLE; };
2628

2729
using namespace csp::cppnodes;
2830

29-
static void * init_nparray()
30-
{
31-
csp::python::AcquireGIL gil;
32-
import_array();
33-
return nullptr;
34-
}
35-
static void * s_init_array = init_nparray();
36-
3731
// NumPy specific statistic functions
3832

3933
namespace csp::python

0 commit comments

Comments
 (0)