Skip to content

Commit b19fe80

Browse files
committed
NumPy 2.0 build support
Signed-off-by: Tim Paine <3105306+timkpaine@users.noreply.github.com>
1 parent 323122e commit b19fe80

File tree

8 files changed

+124
-50
lines changed

8 files changed

+124
-50
lines changed

.github/actions/setup-dependencies/action.yml

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,6 @@ runs:
2525

2626
################
2727
# Linux # NOTE: skip for manylinux image
28-
# - name: Linux init steps
29-
# shell: bash
30-
# run: make dependencies-vcpkg
31-
# if: ${{ runner.os == 'Linux' }} # skip
3228

3329
################
3430
# Mac
@@ -37,16 +33,6 @@ runs:
3733
run: make dependencies-mac
3834
if: ${{ runner.os == 'macOS' }}
3935

40-
# - name: Setup vcpkg cache in shell
41-
# shell: bash
42-
# run: |
43-
# which -a gcc-12
44-
# echo "CC=/usr/local/bin/gcc-12" >> $GITHUB_ENV
45-
# echo "CMAKE_C_COMPILER=/usr/local/bin/gcc-12" >> $GITHUB_ENV
46-
# echo "CXX=/usr/local/bin/g++-12" >> $GITHUB_ENV
47-
# echo "CMAKE_CXX_COMPILER=/usr/local/bin/g++-12" >> $GITHUB_ENV
48-
# if: ${{ runner.os == 'macOS' }}
49-
5036
################
5137
# Windows
5238
- name: Windows init steps (vc143)

.github/workflows/build.yml

Lines changed: 79 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -648,7 +648,68 @@ jobs:
648648
####################################################
649649
# Test Dependencies/Regressions #
650650
####################################################
651-
test_dependencies:
651+
test_buildtime_dependencies:
652+
needs:
653+
- initialize
654+
strategy:
655+
matrix:
656+
os:
657+
- ubuntu-20.04
658+
python-version:
659+
- 3.9
660+
packages:
661+
- '"numpy>=2" "pandas>=2.2" "pyarrow>=16.1"'
662+
663+
runs-on: ${{ matrix.os }}
664+
665+
steps:
666+
- name: Checkout
667+
uses: actions/checkout@v4
668+
with:
669+
submodules: recursive
670+
fetch-depth: 0
671+
672+
- name: Set up Python ${{ matrix.python-version }}
673+
uses: ./.github/actions/setup-python
674+
with:
675+
version: '${{ matrix.python-version }}'
676+
677+
- name: Set up Caches
678+
uses: ./.github/actions/setup-caches
679+
680+
- name: Install python dependencies
681+
run: make requirements
682+
683+
- name: Install test dependencies
684+
shell: bash
685+
run: sudo apt-get install graphviz
686+
687+
# If we're checking a build-time dependency, install
688+
# the dependency, and then try to build
689+
- name: Install packages - ${{ matrix.packages }} (build time dependency check)
690+
run: python -m pip install -U ${{ matrix.packages }}
691+
692+
- name: Python Wheel Steps - ${{ matrix.packages }} (build time dependency check)
693+
run: make dist-py-cibw
694+
env:
695+
CIBW_BUILD: "cp39-manylinux*"
696+
CIBW_ENVIRONMENT_LINUX: CSP_MANYLINUX="ON" CCACHE_DIR="/host/home/runner/work/csp/csp/.ccache" VCPKG_DEFAULT_BINARY_CACHE="/host${{ env.VCPKG_DEFAULT_BINARY_CACHE }}" VCPKG_DOWNLOADS="/host${{ env.VCPKG_DOWNLOADS }}"
697+
CIBW_BUILD_VERBOSITY: 3
698+
699+
- name: Move Wheel
700+
run: mv dist/*.whl .
701+
702+
- name: Install wheel (build time dependency check)
703+
run: python -m pip install -U *manylinux*.whl --target .
704+
705+
- name: Install packages - ${{ matrix.packages }} (build time dependency check)
706+
run: python -m pip install -U ${{ matrix.packages }}
707+
708+
# Run tests to check dependencies
709+
- name: Python Test Steps (build time dependency check)
710+
run: make test
711+
712+
test_runtime_dependencies:
652713
needs:
653714
- initialize
654715
- build
@@ -659,10 +720,10 @@ jobs:
659720
- ubuntu-20.04
660721
python-version:
661722
- 3.9
662-
package:
663-
- "sqlalchemy>=2"
664-
- "sqlalchemy<2"
665-
- "numpy==1.19.5"
723+
packages:
724+
- '"sqlalchemy>=2"'
725+
- '"sqlalchemy<2"'
726+
- '"numpy==1.19.5"'
666727

667728
runs-on: ${{ matrix.os }}
668729

@@ -671,6 +732,7 @@ jobs:
671732
uses: actions/checkout@v4
672733
with:
673734
submodules: recursive
735+
fetch-depth: 0
674736

675737
- name: Set up Python ${{ matrix.python-version }}
676738
uses: ./.github/actions/setup-python
@@ -684,24 +746,26 @@ jobs:
684746
shell: bash
685747
run: sudo apt-get install graphviz
686748

687-
- name: Download wheel
749+
- name: Download wheel (run time dependency check)
688750
uses: actions/download-artifact@v4
689751
with:
690752
name: csp-dist-${{ runner.os }}-${{ runner.arch }}-${{ matrix.python-version }}
691753

692-
- name: Install wheel
754+
- name: Install wheel (run time dependency check)
693755
run: python -m pip install -U *manylinux*.whl --target .
694756

695-
- name: Install package - ${{ matrix.package }}
696-
run: python -m pip install -U "${{ matrix.package }}"
757+
- name: Install packages - ${{ matrix.packages }} (run time dependency check)
758+
run: python -m pip install -U ${{ matrix.packages }}
697759

698-
- name: Python Test Steps
760+
# Run tests to check dependencies
761+
- name: Python Test Steps (run time dependency check)
699762
run: make test TEST_ARGS="-k TestDBReader"
700-
if: ${{ contains( 'sqlalchemy', matrix.package )}}
763+
if: ${{ contains( matrix.packages, 'sqlalchemy' )}}
701764

702-
- name: Python Test Steps
765+
# For e.g. numpy dep changes, run all tests
766+
- name: Python Test Steps (run time dependency check)
703767
run: make test
704-
if: ${{ contains( 'numpy', matrix.package )}}
768+
if: ${{ contains( matrix.packages, 'numpy' )}}
705769

706770
###########################################################################################################
707771
#.........................................................................................................#
@@ -750,7 +814,8 @@ jobs:
750814
- build
751815
- test
752816
- test_sdist
753-
- test_dependencies
817+
- test_buildtime_dependencies
818+
- test_runtime_dependencies
754819

755820
if: startsWith(github.ref, 'refs/tags/v')
756821
runs-on: ubuntu-22.04

cpp/csp/python/Common.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,22 @@
77

88
#define INIT_PYDATETIME if( !PyDateTimeAPI ) { PyDateTime_IMPORT; }
99

10+
// NumPy 2.0 Migration
11+
#include <numpy/numpyconfig.h>
12+
13+
#if NPY_ABI_VERSION >= 0x02000000
14+
// Define helper for anything that can't
15+
// be handled by the below helper macros
16+
#define CSP_NUMPY_2
17+
18+
#else
19+
20+
// Numpy 2.0 helpers
21+
#define PyDataType_ELSIZE( descr ) ( ( descr ) -> elsize )
22+
#define PyDataType_C_METADATA( descr ) ( ( descr ) -> c_metadata )
23+
24+
#endif
25+
1026
namespace csp::python
1127
{
1228

cpp/csp/python/NumpyConversions.cpp

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44

55
#include <csp/core/Time.h>
6+
#include <csp/python/Common.h>
67
#include <csp/python/NumpyConversions.h>
78

89
#include <locale>
@@ -59,7 +60,7 @@ int64_t scalingFromNumpyDtUnit( NPY_DATETIMEUNIT base )
5960

6061
NPY_DATETIMEUNIT datetimeUnitFromDescr( PyArray_Descr* descr )
6162
{
62-
PyArray_DatetimeDTypeMetaData* dtypeMeta = (PyArray_DatetimeDTypeMetaData*)(descr -> c_metadata);
63+
PyArray_DatetimeDTypeMetaData* dtypeMeta = (PyArray_DatetimeDTypeMetaData*)( PyDataType_C_METADATA( descr ) );
6364
PyArray_DatetimeMetaData* dtMeta = &(dtypeMeta -> meta);
6465
return dtMeta -> base;
6566
}
@@ -68,7 +69,7 @@ static std::wstring_convert<std::codecvt_utf8<char32_t>, char32_t> wstr_converte
6869

6970
void stringFromNumpyStr( void* data, std::string& out, char numpy_type, int elem_size_bytes )
7071
{
71-
// strings from numpy arrays are fixed width and zero filled.
72+
// strings from numpy arrays are fixed width and zero filled.
7273
// if the last char is 0, can treat as null terminated, else use full width
7374

7475
if( numpy_type == NPY_UNICODELTR)
@@ -87,7 +88,11 @@ void stringFromNumpyStr( void* data, std::string& out, char numpy_type, int elem
8788
out = wstr_converter.to_bytes( wstr );
8889
}
8990
}
91+
#ifdef CSP_NUMPY_2
92+
else if( numpy_type == NPY_STRINGLTR )
93+
#else
9094
else if( numpy_type == NPY_STRINGLTR || numpy_type == NPY_STRINGLTR2 )
95+
#endif
9196
{
9297
const char * const raw_value = (const char *) data;
9398

@@ -144,7 +149,9 @@ void validateNumpyTypeVsCspType( const CspTypePtr & type, char numpy_type_char )
144149
// everything works as object
145150
break;
146151
case NPY_STRINGLTR:
152+
#ifndef CSP_NUMPY_2
147153
case NPY_STRINGLTR2:
154+
#endif
148155
case NPY_UNICODELTR:
149156
case NPY_CHARLTR:
150157
if( cspType != csp::CspType::Type::STRING )

cpp/csp/python/NumpyConversions.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,7 @@ inline PyObject * createNumpyArray( ValueType valueType, const csp::TimeSeriesPr
204204
T lastValue;
205205
if( ts -> valid() )
206206
lastValue = ts -> lastValueTyped<T>();
207-
207+
208208
DateTime lastTime = ( ts -> valid() ? ts -> lastTime() : DateTime() );
209209
switch( valueType )
210210
{
@@ -219,7 +219,7 @@ inline PyObject * createNumpyArray( ValueType valueType, const csp::TimeSeriesPr
219219
case ValueType::TIMESTAMP_VALUE_TUPLE:
220220
{
221221
PyObject * tuple = PyTuple_New( 2 );
222-
PyTuple_SET_ITEM( tuple, 0, adjustStartAndEndTime( as_nparray( ts, ts -> timeline(), lastTime, startIndex,
222+
PyTuple_SET_ITEM( tuple, 0, adjustStartAndEndTime( as_nparray( ts, ts -> timeline(), lastTime, startIndex,
223223
endIndex, extrapolateEnd ), startPolicy, endPolicy, startDt, endDt ) );
224224
PyTuple_SET_ITEM( tuple, 1, as_nparray( ts, ts -> dataline<T>(), lastValue, startIndex, endIndex, extrapolateEnd ) );
225225
return tuple;

cpp/csp/python/NumpyInputAdapter.h

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -29,18 +29,18 @@ class NumpyCurveAccessor
2929
m_descr = nullptr;
3030
}
3131

32-
NumpyCurveAccessor( PyArrayObject * arr )
32+
NumpyCurveAccessor( PyArrayObject * arr )
3333
{
3434
m_nd = PyArray_NDIM( arr );
3535
if( m_nd < 2 )
3636
CSP_THROW( csp::TypeError, "NumpyCurveAccessor is inefficient for a 1-D Numpy array: use PyArray_GETPTR1 to access indexed values" );
37-
37+
3838
// Preprocess strides and dimensions
3939
npy_intp* strides = PyArray_STRIDES( arr );
4040
npy_intp* dims = PyArray_DIMS( arr );
4141
m_outerStride = strides[0];
4242
m_outerDim = dims[0];
43-
m_innerStrides = strides + 1;
43+
m_innerStrides = strides + 1;
4444
m_innerDims = dims + 1;
4545

4646
m_arr = arr;
@@ -58,7 +58,7 @@ class NumpyCurveAccessor
5858
{
5959
if( index >= m_outerDim )
6060
CSP_THROW( csp::TypeError, "Requested data index out of range in NumpyCurveAccessor" );
61-
61+
6262
// Create a view to the (n-1) dimensional array with (n-1) potentially unnatural strides
6363
/*
6464
A note on reference counting for the subarray: NewFromDescr will *steal* a reference to the type descr,
@@ -87,7 +87,7 @@ class NumpyCurveAccessor
8787
private:
8888
char* m_data;
8989
int m_nd;
90-
90+
9191
npy_intp m_outerStride;
9292
npy_intp m_outerDim;
9393
npy_intp* m_innerStrides;
@@ -103,7 +103,7 @@ class NumpyInputAdapter : public PullInputAdapter<T>
103103
using PyArrayObjectPtr = PyPtr<PyArrayObject>;
104104

105105
public:
106-
NumpyInputAdapter( Engine * engine, CspTypePtr & type, PyArrayObject * datetimes,
106+
NumpyInputAdapter( Engine * engine, CspTypePtr & type, PyArrayObject * datetimes,
107107
PyArrayObject * values ) : PullInputAdapter<T>( engine, type, PushMode::LAST_VALUE ),
108108
m_datetimes( PyArrayObjectPtr::incref( datetimes ) ),
109109
m_values( PyArrayObjectPtr::incref( values ) ),
@@ -113,7 +113,7 @@ class NumpyInputAdapter : public PullInputAdapter<T>
113113
PyArray_Descr* vals_descr = PyArray_DESCR(m_values.ptr());
114114

115115
m_size = static_cast<int>(PyArray_SIZE( datetimes ));
116-
m_elem_size = vals_descr -> elsize;
116+
m_elem_size = PyDataType_ELSIZE(vals_descr);
117117
m_val_type = vals_descr -> type;
118118

119119
char out_type = m_val_type;
@@ -123,7 +123,7 @@ class NumpyInputAdapter : public PullInputAdapter<T>
123123
m_valueAccessor = std::make_unique<NumpyCurveAccessor>( m_values.ptr() );
124124
}
125125
validateNumpyTypeVsCspType( type, out_type );
126-
126+
127127

128128
auto dt_type = dts_descr -> type;
129129
if( dt_type != NPY_DATETIMELTR && dt_type != NPY_OBJECTLTR )
@@ -166,7 +166,7 @@ class NumpyInputAdapter : public PullInputAdapter<T>
166166

167167
++m_index;
168168
}
169-
169+
170170
PullInputAdapter<T>::start( start, end );
171171
}
172172

cpp/csp/python/adapters/parquetadapterimpl.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -303,7 +303,7 @@ class NumpyUnicodeArrayWriter : public TypedDialectGenericListWriterInterface<st
303303
PyObject_Repr( ( PyObject * ) PyArray_DESCR( arrayObject ) ) ) );
304304
}
305305

306-
auto elementSize = PyArray_DESCR( arrayObject ) -> elsize;
306+
auto elementSize = PyDataType_ELSIZE( PyArray_DESCR( arrayObject ) );
307307
auto ndim = PyArray_NDIM( arrayObject );
308308

309309
CSP_TRUE_OR_THROW_RUNTIME( ndim == 1, "While writing to parquet expected numpy array with 1 dimension" << " got " << ndim );
@@ -451,7 +451,7 @@ class NumpyUnicodeReaderImpl final : public TypedDialectGenericListReaderInterfa
451451
{
452452
auto arrayObject = reinterpret_cast<PyArrayObject *>(csp::python::toPythonBorrowed( list ));
453453
std::wstring_convert<std::codecvt_utf8<char32_t>,char32_t> converter;
454-
auto elementSize = PyArray_DESCR( arrayObject ) -> elsize;
454+
auto elementSize = PyDataType_ELSIZE( PyArray_DESCR( arrayObject ) );
455455
auto wideValue = converter.from_bytes( value );
456456
auto nElementsToCopy = std::min( int(elementSize / sizeof(char32_t)), int( wideValue.size() + 1 ) );
457457
std::copy_n( wideValue.c_str(), nElementsToCopy, reinterpret_cast<char32_t*>(PyArray_GETPTR1( arrayObject, index )) );

0 commit comments

Comments
 (0)