Skip to content

Commit d5facd3

Browse files
committed
NumPy 2.0 build support
Signed-off-by: Tim Paine <[email protected]>
1 parent fac1334 commit d5facd3

File tree

8 files changed

+126
-50
lines changed

8 files changed

+126
-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: 81 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -658,7 +658,70 @@ jobs:
658658
####################################################
659659
# Test Dependencies/Regressions #
660660
####################################################
661-
test_dependencies:
661+
test_buildtime_dependencies:
662+
needs:
663+
- initialize
664+
strategy:
665+
matrix:
666+
os:
667+
- ubuntu-20.04
668+
python-version:
669+
- 3.9
670+
packages:
671+
- '"numpy>=2" "pandas>=2.2" "pyarrow>=16.1"'
672+
673+
runs-on: ${{ matrix.os }}
674+
675+
steps:
676+
- name: Checkout
677+
uses: actions/checkout@v4
678+
with:
679+
submodules: recursive
680+
fetch-depth: 0
681+
682+
- name: Set up Python ${{ matrix.python-version }}
683+
uses: ./.github/actions/setup-python
684+
with:
685+
version: '${{ matrix.python-version }}'
686+
687+
- name: Set up Caches
688+
uses: ./.github/actions/setup-caches
689+
690+
- name: Install python dependencies
691+
run: make requirements
692+
693+
- name: Install test dependencies
694+
shell: bash
695+
run: sudo apt-get install graphviz
696+
697+
# If we're checking a build-time dependency, install
698+
# the dependency, and then try to build
699+
- name: Install packages - ${{ matrix.packages }} (build time dependency check)
700+
run: python -m pip install -U ${{ matrix.packages }}
701+
702+
- name: Python Wheel Steps - ${{ matrix.packages }} (build time dependency check)
703+
run: make dist-py-cibw
704+
env:
705+
CIBW_BUILD: cp39-manylinux*
706+
CIBW_BUILD_FRONTEND: "pip; args: --no-build-isolation"
707+
CIBW_BUILD_VERBOSITY: 3
708+
CIBW_BEFORE_BUILD: make requirements && pip install ${{ matrix.packages }}
709+
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 }}"
710+
711+
- name: Move Wheel
712+
run: mv dist/*.whl .
713+
714+
- name: Install wheel (build time dependency check)
715+
run: python -m pip install -U *manylinux*.whl --target .
716+
717+
- name: Install packages - ${{ matrix.packages }} (build time dependency check)
718+
run: python -m pip install -U ${{ matrix.packages }}
719+
720+
# Run tests to check dependencies
721+
- name: Python Test Steps (build time dependency check)
722+
run: make test
723+
724+
test_runtime_dependencies:
662725
needs:
663726
- initialize
664727
- build
@@ -669,10 +732,10 @@ jobs:
669732
- ubuntu-20.04
670733
python-version:
671734
- 3.9
672-
package:
673-
- "sqlalchemy>=2"
674-
- "sqlalchemy<2"
675-
- "numpy==1.19.5"
735+
packages:
736+
- '"sqlalchemy>=2"'
737+
- '"sqlalchemy<2"'
738+
- '"numpy==1.19.5"'
676739

677740
runs-on: ${{ matrix.os }}
678741

@@ -681,6 +744,7 @@ jobs:
681744
uses: actions/checkout@v4
682745
with:
683746
submodules: recursive
747+
fetch-depth: 0
684748

685749
- name: Set up Python ${{ matrix.python-version }}
686750
uses: ./.github/actions/setup-python
@@ -694,26 +758,28 @@ jobs:
694758
shell: bash
695759
run: sudo apt-get install graphviz
696760

697-
- name: Download wheel
761+
- name: Download wheel (run time dependency check)
698762
uses: actions/download-artifact@v4
699763
with:
700764
name: csp-dist-${{ runner.os }}-${{ runner.arch }}-${{ matrix.python-version }}
701765

702-
- name: Install wheel
766+
- name: Install wheel (run time dependency check)
703767
run: |
704768
python -m pip install -U *manylinux*.whl
705769
python -m pip install -U --no-deps *manylinux*.whl --target .
706770
707-
- name: Install package - ${{ matrix.package }}
708-
run: python -m pip install -U "${{ matrix.package }}"
771+
- name: Install packages - ${{ matrix.packages }} (run time dependency check)
772+
run: python -m pip install -U ${{ matrix.packages }}
709773

710-
- name: Python Test Steps
774+
# Run tests to check dependencies
775+
- name: Python Test Steps (run time dependency check)
711776
run: make test TEST_ARGS="-k TestDBReader"
712-
if: ${{ contains( 'sqlalchemy', matrix.package )}}
777+
if: ${{ contains( matrix.packages, 'sqlalchemy' )}}
713778

714-
- name: Python Test Steps
779+
# For e.g. numpy dep changes, run all tests
780+
- name: Python Test Steps (run time dependency check)
715781
run: make test
716-
if: ${{ contains( 'numpy', matrix.package )}}
782+
if: ${{ contains( matrix.packages, 'numpy' )}}
717783

718784
###########################################################################################################
719785
#.........................................................................................................#
@@ -762,7 +828,8 @@ jobs:
762828
- build
763829
- test
764830
- test_sdist
765-
- test_dependencies
831+
- test_buildtime_dependencies
832+
- test_runtime_dependencies
766833

767834
if: startsWith(github.ref, 'refs/tags/v')
768835
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)