Skip to content

Commit b74b303

Browse files
authored
test: roundtrip bincode metadata into Python (#750)
1 parent c05e250 commit b74b303

File tree

10 files changed

+400
-0
lines changed

10 files changed

+400
-0
lines changed

.github/workflows/python.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,14 @@ jobs:
3838
- name: run JSON metadata example
3939
run: |
4040
cargo run --example json_metadata --features derive
41+
- name: run bincode metadata example
42+
run: |
43+
cargo run --example bincode_metadata --features derive
4144
- name: setup Python and run tests
4245
run: |
4346
uv venv -p ${{ matrix.python }}
4447
source .venv/bin/activate
4548
uv pip install -r python/requirements_locked_3_13.txt
49+
uv pip install python/tskit_glue
4650
python -m pytest python
4751

Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,3 +60,7 @@ name = "tree_traversals"
6060
[[example]]
6161
name = "json_metadata"
6262
required-features = ["derive"]
63+
64+
[[example]]
65+
name = "bincode_metadata"
66+
required-features = ["derive"]

examples/bincode_metadata.rs

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
#[derive(serde::Serialize, serde::Deserialize, tskit::metadata::MutationMetadata)]
2+
#[serializer("bincode")]
3+
struct MutationMetadata {
4+
effect_size: f64,
5+
dominance: f64,
6+
}
7+
8+
#[derive(serde::Serialize, serde::Deserialize, tskit::metadata::IndividualMetadata)]
9+
#[serializer("bincode")]
10+
struct IndividualMetadata {
11+
name: String,
12+
phenotypes: Vec<i32>,
13+
}
14+
15+
fn main() {
16+
let ts = make_treeseq().unwrap();
17+
ts.dump("with_bincode_metadata.trees", 0).unwrap();
18+
}
19+
20+
fn make_tables() -> anyhow::Result<tskit::TableCollection> {
21+
let mut tables = tskit::TableCollection::new(100.0)?;
22+
let pop0 = tables.add_population()?;
23+
let ind0 = tables.add_individual_with_metadata(
24+
0,
25+
None,
26+
None,
27+
&IndividualMetadata {
28+
name: "Jerome".to_string(),
29+
phenotypes: vec![0, 1, 2, 0],
30+
},
31+
)?;
32+
let node0 = tables.add_node(tskit::NodeFlags::new_sample(), 0.0, pop0, ind0)?;
33+
let site0 = tables.add_site(50.0, Some("A".as_bytes()))?;
34+
let _ = tables.add_mutation_with_metadata(
35+
site0,
36+
node0,
37+
tskit::MutationId::NULL,
38+
1.0,
39+
Some("G".as_bytes()),
40+
&MutationMetadata {
41+
effect_size: -1e-3,
42+
dominance: 0.1,
43+
},
44+
)?;
45+
tables.build_index()?;
46+
Ok(tables)
47+
}
48+
49+
fn make_treeseq() -> anyhow::Result<tskit::TreeSequence> {
50+
Ok(make_tables()?.tree_sequence(0)?)
51+
}

python/requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
tskit>=0.6.3
22
pytest
3+
maturin

python/requirements_locked_3_13.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ jsonschema==4.23.0
1010
# via tskit
1111
jsonschema-specifications==2025.4.1
1212
# via jsonschema
13+
maturin==1.8.6
14+
# via -r requirements.txt
1315
numpy==2.2.5
1416
# via tskit
1517
packaging==25.0

python/test_bincode_metadata.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import tskit
2+
import tskit_glue
3+
import numpy as np
4+
5+
6+
def setup_ts_without_schema():
7+
ts = tskit.TreeSequence.load("with_bincode_metadata.trees")
8+
return ts
9+
10+
11+
def test_individual_metadata():
12+
# NOTE: the assertions here rely on knowing
13+
# what examples/json_metadata.rs put into the
14+
# metadata!
15+
ts = setup_ts_without_schema()
16+
md = tskit_glue.decode_bincode_individual_metadata(ts.individual(0).metadata)
17+
assert md.name() == "Jerome"
18+
assert md.phenotypes() == [0, 1, 2, 0]
19+
20+
21+
def test_mutation_metadata():
22+
# NOTE: the assertions here rely on knowing
23+
# what examples/json_metadata.rs put into the
24+
# metadata!
25+
ts = setup_ts_without_schema()
26+
md = tskit_glue.decode_bincode_mutation_metadata(ts.mutation(0).metadata)
27+
assert np.isclose(md.effect_size(), -1e-3)
28+
assert np.isclose(md.dominance(), 0.1)

python/tskit_glue/Cargo.lock

Lines changed: 202 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

python/tskit_glue/Cargo.toml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
[package]
2+
name = "tskit_glue"
3+
version = "0.1.0"
4+
edition = "2021"
5+
6+
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
7+
[lib]
8+
name = "tskit_glue"
9+
crate-type = ["cdylib"]
10+
11+
[dependencies]
12+
pyo3 = "0.24.0"
13+
serde = {version = "1.0.203", features = ["derive"]}
14+
bincode = {version = "1.3.1"}

python/tskit_glue/pyproject.toml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
[build-system]
2+
requires = ["maturin>=1.8,<2.0"]
3+
build-backend = "maturin"
4+
5+
[project]
6+
name = "tskit_glue"
7+
requires-python = ">=3.8"
8+
classifiers = [
9+
"Programming Language :: Rust",
10+
"Programming Language :: Python :: Implementation :: CPython",
11+
"Programming Language :: Python :: Implementation :: PyPy",
12+
]
13+
dynamic = ["version"]
14+
[tool.maturin]
15+
features = ["pyo3/extension-module"]

0 commit comments

Comments
 (0)