Skip to content

Commit b74b98c

Browse files
hyanwongmergify[bot]
authored andcommitted
Add table.replace_with
And output sensible error messages when trying to set tables directly.
1 parent 3c24e2d commit b74b98c

File tree

3 files changed

+81
-0
lines changed

3 files changed

+81
-0
lines changed

python/CHANGELOG.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44

55
**Changes**
66

7+
- Tables in a table collection can be replaced using the replace_with method
8+
(:user:`hyanwong`, :issue:`1489` :pr:`2389`)
9+
710
- SVG drawing routines now return a special string object that is automatically
811
rendered in a Jupyter notebook (:user:`hyanwong`, :pr:`2377`)
912

python/tests/test_tables.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -823,6 +823,11 @@ def test_bad_offsets(self):
823823
t.append_columns(**input_data)
824824
input_data[offset_col.name] = np.copy(original_offset)
825825

826+
def test_replace_with_wrong_class(self):
827+
t = self.table_class()
828+
with pytest.raises(TypeError, match="wrong type"):
829+
t.replace_with(tskit.BaseTable(None, None))
830+
826831

827832
class MetadataTestsMixin:
828833
"""
@@ -3834,6 +3839,12 @@ def test_dump_load_errors(self, ts_fixture):
38343839
with pytest.raises(TypeError):
38353840
func(bad_filename)
38363841

3842+
def test_set_table(self):
3843+
tc = tskit.TableCollection()
3844+
for name, table in tc.table_name_map.items():
3845+
with pytest.raises(AttributeError, match="replace_with"):
3846+
setattr(tc, name, table)
3847+
38373848

38383849
class TestEqualityOptions:
38393850
def test_equals_provenance(self):
@@ -4383,6 +4394,20 @@ def test_set_columns_not_implemented(self):
43834394
with pytest.raises(NotImplementedError):
43844395
t.set_columns()
43854396

4397+
def test_replace_with(self, ts_fixture):
4398+
# Although replace_with is a BaseTable method, it is simpler to test it
4399+
# on the subclasses directly, as some differ e.g. in having metadata schemas
4400+
original_tables = ts_fixture.dump_tables()
4401+
original_tables.nodes.metadata_schema = tskit.MetadataSchema.permissive_json()
4402+
new_tables = ts_fixture.dump_tables()
4403+
new_tables.clear(clear_provenance=True, clear_metadata_schemas=True)
4404+
4405+
# write all the data back in again
4406+
for name, table in new_tables.table_name_map.items():
4407+
new_table = getattr(original_tables, name)
4408+
table.replace_with(new_table)
4409+
new_tables.assert_equals(original_tables)
4410+
43864411

43874412
class TestSubsetTables:
43884413
"""

python/tskit/tables.py

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -589,6 +589,23 @@ def append(self, row):
589589
}
590590
)
591591

592+
def replace_with(self, other):
593+
# Overwrite the contents of this table with a copy of the other table
594+
params = {}
595+
for column in self.column_names:
596+
try:
597+
params[column] = getattr(other, column)
598+
except AttributeError:
599+
raise TypeError(
600+
"Replacement table has wrong type: it lacks a {column} column"
601+
)
602+
try:
603+
# Not all tables have a metadata_schema: if they do, encode it with repr
604+
params["metadata_schema"] = repr(other.metadata_schema)
605+
except AttributeError:
606+
pass
607+
self.set_columns(**params)
608+
592609
def clear(self):
593610
"""
594611
Deletes all rows in this table.
@@ -2869,6 +2886,10 @@ class TableCollection(metadata.MetadataProvider):
28692886
method.
28702887
"""
28712888

2889+
set_err_text = (
2890+
"Cannot set tables in a table collection: use table.replace_with() instead."
2891+
)
2892+
28722893
def __init__(self, sequence_length=0):
28732894
self._ll_tables = _tskit.TableCollection(sequence_length)
28742895
super().__init__(self._ll_tables)
@@ -2880,55 +2901,87 @@ def individuals(self) -> IndividualTable:
28802901
"""
28812902
return IndividualTable(ll_table=self._ll_tables.individuals)
28822903

2904+
@individuals.setter
2905+
def individuals(self, value):
2906+
raise AttributeError(self.set_err_text)
2907+
28832908
@property
28842909
def nodes(self) -> NodeTable:
28852910
"""
28862911
The :ref:`sec_node_table_definition` in this collection.
28872912
"""
28882913
return NodeTable(ll_table=self._ll_tables.nodes)
28892914

2915+
@nodes.setter
2916+
def nodes(self, value):
2917+
raise AttributeError(self.set_err_text)
2918+
28902919
@property
28912920
def edges(self) -> EdgeTable:
28922921
"""
28932922
The :ref:`sec_edge_table_definition` in this collection.
28942923
"""
28952924
return EdgeTable(ll_table=self._ll_tables.edges)
28962925

2926+
@edges.setter
2927+
def edges(self, value):
2928+
raise AttributeError(self.set_err_text)
2929+
28972930
@property
28982931
def migrations(self) -> MigrationTable:
28992932
"""
29002933
The :ref:`sec_migration_table_definition` in this collection
29012934
"""
29022935
return MigrationTable(ll_table=self._ll_tables.migrations)
29032936

2937+
@migrations.setter
2938+
def migrations(self, value):
2939+
raise AttributeError(self.set_err_text)
2940+
29042941
@property
29052942
def sites(self) -> SiteTable:
29062943
"""
29072944
The :ref:`sec_site_table_definition` in this collection.
29082945
"""
29092946
return SiteTable(ll_table=self._ll_tables.sites)
29102947

2948+
@sites.setter
2949+
def sites(self, value):
2950+
raise AttributeError(self.set_err_text)
2951+
29112952
@property
29122953
def mutations(self) -> MutationTable:
29132954
"""
29142955
The :ref:`sec_mutation_table_definition` in this collection.
29152956
"""
29162957
return MutationTable(ll_table=self._ll_tables.mutations)
29172958

2959+
@mutations.setter
2960+
def mutations(self, value):
2961+
raise AttributeError(self.set_err_text)
2962+
29182963
@property
29192964
def populations(self) -> PopulationTable:
29202965
"""
29212966
The :ref:`sec_population_table_definition` in this collection.
29222967
"""
29232968
return PopulationTable(ll_table=self._ll_tables.populations)
29242969

2970+
@populations.setter
2971+
def populations(self, value):
2972+
raise AttributeError(self.set_err_text)
2973+
29252974
@property
29262975
def provenances(self) -> ProvenanceTable:
29272976
"""
29282977
The :ref:`sec_provenance_table_definition` in this collection.
29292978
"""
29302979
return ProvenanceTable(ll_table=self._ll_tables.provenances)
29312980

2981+
@provenances.setter
2982+
def provenances(self, value):
2983+
raise AttributeError(self.set_err_text)
2984+
29322985
@property
29332986
def indexes(self) -> TableCollectionIndexes:
29342987
"""

0 commit comments

Comments
 (0)