Skip to content

Commit 9af734e

Browse files
committed
Conditionally compile
1 parent b29241b commit 9af734e

5 files changed

Lines changed: 43 additions & 20 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: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,16 +24,22 @@
2424
*/
2525

2626
#define PY_SSIZE_T_CLEAN
27+
#include <Python.h>
28+
#include <numpy/numpyconfig.h>
29+
30+
// Simple conditional based on constant existence
31+
#if defined(NPY_2_0_API_VERSION) && NPY_API_VERSION >= NPY_2_0_API_VERSION
2732
#define NPY_NO_DEPRECATED_API NPY_2_0_API_VERSION
28-
#define NPY_TARGET_VERSION NPY_2_0_API_VERSION
29-
#define TSK_BUG_ASSERT_MESSAGE \
30-
"Please open an issue on" \
31-
" GitHub, ideally with a reproducible example." \
32-
" (https://github.com/tskit-dev/tskit/issues)"
33+
#undef NPY_FEATURE_VERSION
34+
#define NPY_FEATURE_VERSION NPY_2_0_API_VERSION
35+
#define HAVE_NUMPY_2 1
36+
#else
37+
#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
38+
#define HAVE_NUMPY_2 0
39+
#endif
40+
#include <numpy/arrayobject.h>
3341

34-
#include <Python.h>
3542
#include <structmember.h>
36-
#include <numpy/arrayobject.h>
3743
#include <float.h>
3844

3945
#include "kastore.h"
@@ -10775,6 +10781,7 @@ TreeSequence_make_array(TreeSequence *self, tsk_size_t size, int dtype, void *da
1077510781
return make_owned_array((PyObject *) self, size, dtype, data);
1077610782
}
1077710783

10784+
#if HAVE_NUMPY_2
1077810785
PyObject *
1077910786
TreeSequence_decode_ragged_string_column(
1078010787
TreeSequence *self, tsk_size_t num_rows, const char *data, const tsk_size_t *offset)
@@ -10825,6 +10832,7 @@ TreeSequence_decode_ragged_string_column(
1082510832
Py_XDECREF(array);
1082610833
return ret;
1082710834
}
10835+
#endif
1082810836

1082910837
static PyObject *
1083010838
TreeSequence_get_individuals_flags(TreeSequence *self, void *closure)
@@ -11073,6 +11081,7 @@ TreeSequence_get_sites_position(TreeSequence *self, void *closure)
1107311081
return ret;
1107411082
}
1107511083

11084+
#if HAVE_NUMPY_2
1107611085
static PyObject *
1107711086
TreeSequence_get_sites_ancestral_state(TreeSequence *self, void *closure)
1107811087
{
@@ -11088,6 +11097,7 @@ TreeSequence_get_sites_ancestral_state(TreeSequence *self, void *closure)
1108811097
out:
1108911098
return ret;
1109011099
}
11100+
#endif
1109111101

1109211102
static PyObject *
1109311103
TreeSequence_get_sites_metadata(TreeSequence *self, void *closure)
@@ -11755,9 +11765,11 @@ static PyGetSetDef TreeSequence_getsetters[] = {
1175511765
{ .name = "sites_position",
1175611766
.get = (getter) TreeSequence_get_sites_position,
1175711767
.doc = "The site position array" },
11768+
#if HAVE_NUMPY_2
1175811769
{ .name = "sites_ancestral_state",
1175911770
.get = (getter) TreeSequence_get_sites_ancestral_state,
1176011771
.doc = "The site ancestral state array" },
11772+
#endif
1176111773
{ .name = "sites_metadata",
1176211774
.get = (getter) TreeSequence_get_sites_metadata,
1176311775
.doc = "The site metadata array" },
@@ -14647,15 +14659,23 @@ PyInit__tskit(void)
1464714659
{
1464814660
PyObject *module;
1464914661

14662+
#if HAVE_NUMPY_2
1465014663
if (PyArray_ImportNumPyAPI() < 0) {
1465114664
return NULL;
1465214665
}
14666+
#else
14667+
import_array();
14668+
#endif
1465314669

1465414670
module = PyModule_Create(&tskitmodule);
1465514671
if (module == NULL) {
1465614672
return NULL;
1465714673
}
1465814674

14675+
if (PyModule_AddIntConstant(module, "HAS_NUMPY_2", HAVE_NUMPY_2)) {
14676+
return NULL;
14677+
}
14678+
1465914679
if (register_lwt_class(module) != 0) {
1466014680
return NULL;
1466114681
}

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)