Skip to content

Commit 009f521

Browse files
committed
Conditionally compile
1 parent b29241b commit 009f521

5 files changed

Lines changed: 44 additions & 17 deletions

File tree

.github/workflows/tests.yml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,15 @@ jobs:
126126
python -m pytest -x --cov=tskit --cov-report=xml --cov-branch -n2 --durations=20 tests
127127
fi
128128
129+
- name: Build and run with numpy 1.x
130+
working-directory: python
131+
run: |
132+
source ~/.profile
133+
conda activate anaconda-client-env
134+
conda install --yes "numpy<2"
135+
python -m pytest -x --cov=tskit --cov-report=xml --cov-branch -n2 tests/test_lowlevel.py
136+
137+
129138
- name: Upload coverage to Codecov
130139
uses: codecov/codecov-action@v5.4.0
131140
with:

python/_tskitmodule.c

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,17 +23,27 @@
2323
* SOFTWARE.
2424
*/
2525

26-
#define PY_SSIZE_T_CLEAN
27-
#define NPY_NO_DEPRECATED_API NPY_2_0_API_VERSION
28-
#define NPY_TARGET_VERSION NPY_2_0_API_VERSION
2926
#define TSK_BUG_ASSERT_MESSAGE \
3027
"Please open an issue on" \
3128
" GitHub, ideally with a reproducible example." \
3229
" (https://github.com/tskit-dev/tskit/issues)"
3330

31+
#define PY_SSIZE_T_CLEAN
3432
#include <Python.h>
35-
#include <structmember.h>
33+
#include <numpy/numpyconfig.h>
34+
35+
#if defined(NPY_2_0_API_VERSION) && NPY_API_VERSION >= NPY_2_0_API_VERSION
36+
#define NPY_NO_DEPRECATED_API NPY_2_0_API_VERSION
37+
#undef NPY_FEATURE_VERSION
38+
#define NPY_FEATURE_VERSION NPY_2_0_API_VERSION
39+
#define HAVE_NUMPY_2 1
40+
#else
41+
#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
42+
#define HAVE_NUMPY_2 0
43+
#endif
3644
#include <numpy/arrayobject.h>
45+
46+
#include <structmember.h>
3747
#include <float.h>
3848

3949
#include "kastore.h"
@@ -10775,6 +10785,7 @@ TreeSequence_make_array(TreeSequence *self, tsk_size_t size, int dtype, void *da
1077510785
return make_owned_array((PyObject *) self, size, dtype, data);
1077610786
}
1077710787

10788+
#if HAVE_NUMPY_2
1077810789
PyObject *
1077910790
TreeSequence_decode_ragged_string_column(
1078010791
TreeSequence *self, tsk_size_t num_rows, const char *data, const tsk_size_t *offset)
@@ -10825,6 +10836,7 @@ TreeSequence_decode_ragged_string_column(
1082510836
Py_XDECREF(array);
1082610837
return ret;
1082710838
}
10839+
#endif
1082810840

1082910841
static PyObject *
1083010842
TreeSequence_get_individuals_flags(TreeSequence *self, void *closure)
@@ -11073,6 +11085,7 @@ TreeSequence_get_sites_position(TreeSequence *self, void *closure)
1107311085
return ret;
1107411086
}
1107511087

11088+
#if HAVE_NUMPY_2
1107611089
static PyObject *
1107711090
TreeSequence_get_sites_ancestral_state(TreeSequence *self, void *closure)
1107811091
{
@@ -11088,6 +11101,7 @@ TreeSequence_get_sites_ancestral_state(TreeSequence *self, void *closure)
1108811101
out:
1108911102
return ret;
1109011103
}
11104+
#endif
1109111105

1109211106
static PyObject *
1109311107
TreeSequence_get_sites_metadata(TreeSequence *self, void *closure)
@@ -11755,9 +11769,11 @@ static PyGetSetDef TreeSequence_getsetters[] = {
1175511769
{ .name = "sites_position",
1175611770
.get = (getter) TreeSequence_get_sites_position,
1175711771
.doc = "The site position array" },
11772+
#if HAVE_NUMPY_2
1175811773
{ .name = "sites_ancestral_state",
1175911774
.get = (getter) TreeSequence_get_sites_ancestral_state,
1176011775
.doc = "The site ancestral state array" },
11776+
#endif
1176111777
{ .name = "sites_metadata",
1176211778
.get = (getter) TreeSequence_get_sites_metadata,
1176311779
.doc = "The site metadata array" },
@@ -14647,15 +14663,23 @@ PyInit__tskit(void)
1464714663
{
1464814664
PyObject *module;
1464914665

14666+
#if HAVE_NUMPY_2
1465014667
if (PyArray_ImportNumPyAPI() < 0) {
1465114668
return NULL;
1465214669
}
14670+
#else
14671+
import_array();
14672+
#endif
1465314673

1465414674
module = PyModule_Create(&tskitmodule);
1465514675
if (module == NULL) {
1465614676
return NULL;
1465714677
}
1465814678

14679+
if (PyModule_AddIntConstant(module, "HAS_NUMPY_2", HAVE_NUMPY_2)) {
14680+
return NULL;
14681+
}
14682+
1465914683
if (register_lwt_class(module) != 0) {
1466014684
return NULL;
1466114685
}

python/requirements/development.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ msprime>=1.0.0
1717
networkx
1818
newick
1919
ninja
20-
numpy>=2
2120
packaging
2221
portion
2322
pre-commit

python/requirements/development.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ dependencies:
2222
- msprime>=1.0.0
2323
- networkx
2424
- ninja
25-
- numpy>2
25+
- numpy
2626
- packaging
2727
- portion
2828
- pre-commit

python/tests/test_lowlevel.py

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1193,7 +1193,6 @@ class TestTreeSequence(LowLevelTestCase, MetadataTestMixin):
11931193
"edges_metadata",
11941194
"edges_metadata_offset",
11951195
"sites_position",
1196-
"sites_ancestral_state",
11971196
"sites_metadata",
11981197
"sites_metadata_offset",
11991198
"mutations_site",
@@ -1916,29 +1915,24 @@ def test_array_read_only(self, name, ts_fixture):
19161915
a[:] = 0
19171916
with pytest.raises(ValueError, match="assignment destination"):
19181917
a[0] = 0
1919-
if name != "sites_ancestral_state":
1920-
with pytest.raises(ValueError, match="cannot set WRITEABLE"):
1921-
a.setflags(write=True)
1918+
with pytest.raises(ValueError, match="cannot set WRITEABLE"):
1919+
a.setflags(write=True)
19221920

19231921
@pytest.mark.parametrize("name", ARRAY_NAMES)
19241922
def test_array_properties(self, name, ts_fixture):
19251923
ts_fixture = ts_fixture.ll_tree_sequence
19261924
a = getattr(ts_fixture, name)
1927-
assert a.base == ts_fixture
19281925
assert not a.flags.writeable
19291926
assert a.flags.aligned
19301927
assert a.flags.c_contiguous
1931-
if name == "sites_ancestral_state":
1932-
assert a.flags.owndata
1933-
else:
1934-
assert not a.flags.owndata
1928+
assert not a.flags.owndata
1929+
assert a.base == ts_fixture
19351930
b = getattr(ts_fixture, name)
19361931
assert a is not b
19371932
assert np.all(a == b)
19381933
# This checks that the underlying pointer to memory is the same in
19391934
# both arrays.
1940-
if name != "sites_ancestral_state":
1941-
assert a.__array_interface__ == b.__array_interface__
1935+
assert a.__array_interface__ == b.__array_interface__
19421936

19431937
@pytest.mark.parametrize("name", ARRAY_NAMES)
19441938
def test_array_lifetime(self, name, ts_fixture):
@@ -1980,6 +1974,7 @@ def test_individuals_nodes(self, ts_fixture):
19801974
a2[:] = 0
19811975
assert a3 is not a2
19821976

1977+
@pytest.mark.skipif(not _tskit.HAS_NUMPY_2, reason="Requires NumPy 2.0+")
19831978
@pytest.mark.parametrize(
19841979
"site_lengths",
19851980
["none", "all-0", "all-1", "all-2", "mixed", "very_long", "unicode"],

0 commit comments

Comments
 (0)