Skip to content

Commit 2e15f49

Browse files
committed
Support Python 3.13
Signed-off-by: Tim Paine <3105306+timkpaine@users.noreply.github.com>
1 parent d35c2c3 commit 2e15f49

7 files changed

Lines changed: 76 additions & 21 deletions

File tree

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ inputs:
1111
- 'cp310'
1212
- 'cp311'
1313
- 'cp312'
14+
- 'cp313'
1415
default: 'cp39'
1516

1617
runs:

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ inputs:
1010
- '3.10'
1111
- '3.11'
1212
- '3.12'
13+
- '3.13'
1314
default: '3.9'
1415

1516
runs:
@@ -33,4 +34,4 @@ runs:
3334

3435
- name: Install cibuildwheel and twine
3536
shell: bash
36-
run: pip install cibuildwheel==2.16.5 twine
37+
run: pip install cibuildwheel==2.23.0 twine

.github/workflows/build.yml

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ jobs:
119119
env:
120120
FULL_RUN: ${{ github.event.inputs.ci-full }}
121121
if: ${{ github.event_name == 'workflow_dispatch' }}
122-
122+
123123
- name: Display and Setup Build Args (Schedule)
124124
id: setupschedule
125125
run: |
@@ -202,11 +202,13 @@ jobs:
202202
- "3.10"
203203
- "3.11"
204204
- "3.12"
205+
- "3.13"
205206
cibuildwheel:
206207
- "cp39"
207208
- "cp310"
208209
- "cp311"
209210
- "cp312"
211+
- "cp313"
210212
is-full-run:
211213
- ${{ needs.initialize.outputs.FULL_RUN == 'true' }}
212214
exclude:
@@ -221,24 +223,40 @@ jobs:
221223
cibuildwheel: "cp311"
222224
- python-version: "3.9"
223225
cibuildwheel: "cp312"
226+
- python-version: "3.9"
227+
cibuildwheel: "cp313"
224228
- python-version: "3.10"
225229
cibuildwheel: "cp39"
226230
- python-version: "3.10"
227231
cibuildwheel: "cp311"
228232
- python-version: "3.10"
229233
cibuildwheel: "cp312"
234+
- python-version: "3.10"
235+
cibuildwheel: "cp313"
230236
- python-version: "3.11"
231237
cibuildwheel: "cp39"
232238
- python-version: "3.11"
233239
cibuildwheel: "cp310"
234240
- python-version: "3.11"
235241
cibuildwheel: "cp312"
242+
- python-version: "3.11"
243+
cibuildwheel: "cp313"
236244
- python-version: "3.12"
237245
cibuildwheel: "cp39"
238246
- python-version: "3.12"
239247
cibuildwheel: "cp310"
240248
- python-version: "3.12"
241249
cibuildwheel: "cp311"
250+
- python-version: "3.12"
251+
cibuildwheel: "cp313"
252+
- python-version: "3.13"
253+
cibuildwheel: "cp39"
254+
- python-version: "3.13"
255+
cibuildwheel: "cp310"
256+
- python-version: "3.13"
257+
cibuildwheel: "cp311"
258+
- python-version: "3.13"
259+
cibuildwheel: "cp312"
242260

243261

244262
##############################################
@@ -261,6 +279,10 @@ jobs:
261279
os: windows-2022
262280
python-version: "3.11"
263281

282+
- is-full-run: false
283+
os: windows-2022
284+
python-version: "3.13"
285+
264286
# avoid unnecessary use of mac resources
265287
- is-full-run: false
266288
os: macos-14
@@ -270,6 +292,14 @@ jobs:
270292
os: macos-14
271293
python-version: "3.10"
272294

295+
- is-full-run: false
296+
os: macos-14
297+
python-version: "3.11"
298+
299+
- is-full-run: false
300+
os: macos-14
301+
python-version: "3.13"
302+
273303
runs-on: ${{ matrix.os }}
274304

275305
steps:
@@ -426,6 +456,7 @@ jobs:
426456
- "3.10"
427457
- 3.11
428458
- 3.12
459+
- 3.13
429460
is-full-run:
430461
- ${{ needs.initialize.outputs.FULL_RUN == 'true' }}
431462
exclude:
@@ -449,6 +480,10 @@ jobs:
449480
os: windows-2022
450481
python-version: "3.11"
451482

483+
- is-full-run: false
484+
os: windows-2022
485+
python-version: "3.13"
486+
452487
# avoid unnecessary use of mac resources
453488
- is-full-run: false
454489
os: macos-14
@@ -458,6 +493,14 @@ jobs:
458493
os: macos-14
459494
python-version: "3.10"
460495

496+
- is-full-run: false
497+
os: macos-14
498+
python-version: "3.11"
499+
500+
- is-full-run: false
501+
os: macos-14
502+
python-version: "3.13"
503+
461504
runs-on: ${{ matrix.os }}
462505

463506
steps:
@@ -581,9 +624,9 @@ jobs:
581624

582625
- name: Install python dependencies
583626
run: make requirements
584-
627+
585628
- name: Install requirements
586-
run: sudo make dependencies-debian
629+
run: sudo make dependencies-debian
587630

588631
- uses: actions/download-artifact@v5
589632
with:

conda/dev-environment-unix.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ dependencies:
4040
- pytest-asyncio
4141
- pytest-cov
4242
- pytest-sugar
43-
- python<3.13
43+
- python<3.14
4444
- python-build
4545
- python-graphviz
4646
- python-rapidjson

conda/dev-environment-win.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ dependencies:
4040
- pytest-asyncio
4141
- pytest-cov
4242
- pytest-sugar
43-
- python<3.13
43+
- python<3.14
4444
- python-build
4545
- python-graphviz
4646
- python-rapidjson

cpp/csp/python/Common.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33

44
#include <Python.h>
55

6-
#define IS_PRE_PYTHON_3_11 (PY_MAJOR_VERSION < 3 || (PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION < 11) )
6+
#define IS_LT_PYTHON_3_11 (PY_MAJOR_VERSION < 3 || (PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION < 11) )
7+
#define IS_GE_PYTHON_3_13 (PY_MAJOR_VERSION < 3 || (PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION > 12) )
78

89
#define INIT_PYDATETIME if( !PyDateTimeAPI ) { PyDateTime_IMPORT; }
910

cpp/csp/python/PyNode.cpp

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,10 @@
1515
#include <limits>
1616
#include <frameobject.h>
1717

18-
#if !IS_PRE_PYTHON_3_11
18+
#if !IS_LT_PYTHON_3_11
19+
#if IS_GE_PYTHON_3_13
20+
# define Py_BUILD_CORE 1
21+
#endif
1922
#include <internal/pycore_code.h>
2023
#include <internal/pycore_frame.h>
2124
#endif
@@ -48,7 +51,7 @@ PyNode::~PyNode()
4851
void PyNode::init( PyObjectPtr inputs, PyObjectPtr outputs )
4952
{
5053
PyGenObject * pygen = ( PyGenObject * ) m_gen.ptr();
51-
54+
5255
//call gen first yield to setup locals
5356
call_gen();
5457

@@ -61,7 +64,7 @@ void PyNode::init( PyObjectPtr inputs, PyObjectPtr outputs )
6164
m_localVars = ( PyObject *** ) calloc( numInputs(), sizeof( PyObject ** ) );
6265

6366
//printf( "Starting %s slots: %ld rank: %d\n", name(), slots, rank() );
64-
#if IS_PRE_PYTHON_3_11
67+
#if IS_LT_PYTHON_3_11
6568
PyCodeObject * code = ( PyCodeObject * ) pygen -> gi_code;
6669
Py_ssize_t numCells = PyTuple_GET_SIZE( code -> co_cellvars );
6770
size_t cell2argIdx = 0;
@@ -85,7 +88,13 @@ void PyNode::init( PyObjectPtr inputs, PyObjectPtr outputs )
8588
//PY311+ changes
8689
#else
8790
_PyInterpreterFrame * frame = ( _PyInterpreterFrame * ) pygen -> gi_iframe;
88-
PyCodeObject * code = frame -> f_code;
91+
PyCodeObject * code;
92+
93+
#if IS_GE_PYTHON_3_13
94+
code = (PyCodeObject *)PyUnstable_InterpreterFrame_GetCode(frame);
95+
#else
96+
code = frame -> f_code;
97+
#endif
8998
int localPlusIndex = 0;
9099
for( int stackloc = code -> co_argcount; stackloc < code -> co_nlocalsplus; ++stackloc, ++localPlusIndex )
91100
{
@@ -102,7 +111,7 @@ void PyNode::init( PyObjectPtr inputs, PyObjectPtr outputs )
102111
var = &( ( ( PyCellObject * ) *var ) -> ob_ref );
103112
else if( kind == CO_FAST_CELL )
104113
continue;
105-
#endif
114+
#endif
106115
//null var indicates a stack slot for a local state variable ( state hasnt initialized yet )
107116
//we can skip those
108117
if( !*var )
@@ -119,7 +128,7 @@ void PyNode::init( PyObjectPtr inputs, PyObjectPtr outputs )
119128
CSP_ASSERT( !isInputBasket( index ) );
120129

121130
m_localVars[ index ] = var;
122-
//These vars will be "deleted" from the python stack after start
131+
//These vars will be "deleted" from the python stack after start
123132
continue;
124133
}
125134

@@ -215,7 +224,7 @@ void PyNode::stop()
215224
if( this -> rootEngine() -> interrupted() && PyErr_CheckSignals() == -1 )
216225
{
217226
// When an interrupt occurs a KeyboardInterrupt exception is raised in Python, which we need to clear
218-
// before calling "close" on the generator. Else, the close method will fail due to the unhandled
227+
// before calling "close" on the generator. Else, the close method will fail due to the unhandled
219228
// exception, and we lose the state of the generator before the "finally" block that calls stop() is executed.
220229
PyErr_Clear();
221230
}
@@ -230,15 +239,15 @@ PyNode * PyNode::create( PyEngine * pyengine, PyObject * inputs, PyObject * outp
230239
//parse inputs/outputs, create outputs time series, etc
231240
Py_ssize_t numInputs = PyTuple_GET_SIZE( inputs );
232241
Py_ssize_t numOutputs = PyTuple_GET_SIZE( outputs );
233-
242+
234243
if( size_t( numInputs ) >= InputId::maxId() )
235244
CSP_THROW( ValueError, "number of inputs exceeds limit of " << InputId::maxBasketElements() );
236245

237246
if( size_t( numOutputs ) > OutputId::maxId() )
238247
CSP_THROW( ValueError, "number of outputs exceeds limit of " << OutputId::maxBasketElements() );
239248

240-
return pyengine -> engine() -> createOwnedObject<PyNode>( PyObjectPtr::incref( gen ),
241-
PyObjectPtr::incref( inputs ),
249+
return pyengine -> engine() -> createOwnedObject<PyNode>( PyObjectPtr::incref( gen ),
250+
PyObjectPtr::incref( inputs ),
242251
PyObjectPtr::incref( outputs ),
243252
NodeDef( numInputs, numOutputs ) );
244253
}
@@ -317,10 +326,10 @@ static PyObject * PyNode_create( PyObject * module, PyObject * args )
317326
PyObject * outputs;
318327
PyObject * gen;
319328

320-
if( !PyArg_ParseTuple( args, "O!O!O!O!",
321-
&PyEngine::PyType, &engine,
322-
&PyTuple_Type, &inputs,
323-
&PyTuple_Type, &outputs,
329+
if( !PyArg_ParseTuple( args, "O!O!O!O!",
330+
&PyEngine::PyType, &engine,
331+
&PyTuple_Type, &inputs,
332+
&PyTuple_Type, &outputs,
324333
&PyGen_Type, &gen ) )
325334
CSP_THROW( PythonPassthrough, "" );
326335

0 commit comments

Comments
 (0)