diff --git a/python/CHANGELOG.rst b/python/CHANGELOG.rst index 9fc257c9d9..ce051f4e18 100644 --- a/python/CHANGELOG.rst +++ b/python/CHANGELOG.rst @@ -16,6 +16,9 @@ and a window breakpoint falls within an internal missing interval. (:user:`nspope`, :pr:`3176`, :issue:`3175`) +- Prevent iterating over a ``TopologyCounter`` + (:user:`benjeffery` , :pr:`3202`, :issue:`1462`) + -------------------- [0.6.4] - 2025-05-21 -------------------- diff --git a/python/tests/test_combinatorics.py b/python/tests/test_combinatorics.py index 97c85af8e8..8073b845df 100644 --- a/python/tests/test_combinatorics.py +++ b/python/tests/test_combinatorics.py @@ -635,6 +635,11 @@ def verify_topologies(self, ts, sample_sets=None, expected=None): assert actual_topologies == expected[i][sample_set_indexes] assert actual_topologies == actual_inc_topologies + def test_no_iterate(self): + with pytest.raises(TypeError, match="not iterable"): + for _ in tskit.Tree.generate_star(3).count_topologies(): + pass + def subsample_topologies(self, ts, sample_sets, sample_set_indexes): subsample_sets = [sample_sets[i] for i in sample_set_indexes] topologies = collections.Counter() diff --git a/python/tskit/combinatorics.py b/python/tskit/combinatorics.py index 880ec73675..058f6e633e 100644 --- a/python/tskit/combinatorics.py +++ b/python/tskit/combinatorics.py @@ -541,6 +541,12 @@ def __setitem__(self, sample_set_indexes, counter): k = TopologyCounter._to_key(sample_set_indexes) self.topologies[k] = counter + def __iter__(self): + raise TypeError( + "TopologyCounter object is not iterable, " + "iterate over '.topologies' instead" + ) + @staticmethod def _to_key(sample_set_indexes): if not isinstance(sample_set_indexes, collections.abc.Iterable):