Skip to content

Commit e135059

Browse files
Various tweaks to boost Python test coverage.
1 parent 668de1d commit e135059

File tree

7 files changed

+143
-80
lines changed

7 files changed

+143
-80
lines changed

python/CHANGELOG.rst

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,18 @@
1+
--------------------
2+
[0.1.0] - 2019-XX-XX
3+
--------------------
4+
5+
Initial release after separation from msprime 0.6.2.
6+
7+
**Breaking changes**
8+
- Removal of the previously deprecated ``sort_tables``, ``simplify_tables``
9+
and ``load_tables`` functions. All code should change to using corresponding
10+
TableCollection methods.
11+
12+
**New features**:
13+
14+
**Bug fixes**:
15+
116
----------------------
217
[0.1.0a1] - 2019-01-19
318
----------------------

python/requirements/development.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,4 @@ svgwrite
1616
pyparsing
1717
pysam
1818
PyVCF
19+
python_jsonschema_objects

python/tests/test_file_format.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -379,6 +379,12 @@ def test_no_version_number(self):
379379
root.close()
380380
self.assertRaises(ValueError, tskit.load_legacy, self.temp_file)
381381

382+
def test_unknown_legacy_version(self):
383+
root = h5py.File(self.temp_file, "w")
384+
root.attrs['format_version'] = (1024, 0) # Arbitrary unknown version
385+
root.close()
386+
self.assertRaises(ValueError, tskit.load_legacy, self.temp_file)
387+
382388

383389
class TestDumpFormat(TestFileFormat):
384390
"""

python/tests/test_highlevel.py

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1484,6 +1484,25 @@ def test_tables_sequence_length_round_trip(self):
14841484
new_ts = tables.tree_sequence()
14851485
self.assertEqual(new_ts.sequence_length, sequence_length)
14861486

1487+
def test_migrations(self):
1488+
ts = msprime.simulate(
1489+
population_configurations=[
1490+
msprime.PopulationConfiguration(10),
1491+
msprime.PopulationConfiguration(10)],
1492+
migration_matrix=[[0, 1], [1, 0]],
1493+
random_seed=2,
1494+
record_migrations=True)
1495+
self.assertGreater(ts.num_migrations, 0)
1496+
migrations = list(ts.migrations())
1497+
self.assertEqual(len(migrations), ts.num_migrations)
1498+
for migration in migrations:
1499+
self.assertIn(migration.source, [0, 1])
1500+
self.assertIn(migration.dest, [0, 1])
1501+
self.assertGreater(migration.time, 0)
1502+
self.assertEqual(migration.left, 0)
1503+
self.assertEqual(migration.right, 1)
1504+
self.assertTrue(0 <= migration.node < ts.num_nodes)
1505+
14871506

14881507
class TestFileUuid(HighLevelTestCase):
14891508
"""
@@ -2045,3 +2064,95 @@ def test_nonbinary(self):
20452064
self.assertTrue(found)
20462065
for _ in range(self.num_random_permutations):
20472066
self.verify_random_permutation(ts)
2067+
2068+
2069+
class SimpleContainersMixin(object):
2070+
"""
2071+
Tests for the SimpleContainer classes.
2072+
"""
2073+
def test_equality(self):
2074+
c1, c2 = self.get_instances(2)
2075+
self.assertTrue(c1 == c1)
2076+
self.assertFalse(c1 == c2)
2077+
self.assertFalse(c1 != c1)
2078+
self.assertTrue(c1 != c2)
2079+
c3, = self.get_instances(1)
2080+
self.assertTrue(c1 == c3)
2081+
self.assertFalse(c1 != c3)
2082+
2083+
def test_repr(self):
2084+
c, = self.get_instances(1)
2085+
self.assertGreater(len(repr(c)), 0)
2086+
2087+
2088+
class TestIndividualContainer(unittest.TestCase, SimpleContainersMixin):
2089+
def get_instances(self, n):
2090+
return [
2091+
tskit.Individual(id_=j, flags=j, location=[j], nodes=[j], metadata=b"x" * j)
2092+
for j in range(n)]
2093+
2094+
2095+
class TestNodeContainer(unittest.TestCase, SimpleContainersMixin):
2096+
def get_instances(self, n):
2097+
return [
2098+
tskit.Node(
2099+
id_=j, flags=j, time=j, population=j, individual=j, metadata=b"x" * j)
2100+
for j in range(n)]
2101+
2102+
2103+
class TestEdgeContainer(unittest.TestCase, SimpleContainersMixin):
2104+
def get_instances(self, n):
2105+
return [
2106+
tskit.Edge(left=j, right=j, parent=j, child=j) for j in range(n)]
2107+
2108+
2109+
class TestSiteContainer(unittest.TestCase, SimpleContainersMixin):
2110+
def get_instances(self, n):
2111+
return [
2112+
tskit.Site(
2113+
id_=j, position=j, ancestral_state="A" * j,
2114+
mutations=TestMutationContainer().get_instances(j),
2115+
metadata=b"x" * j)
2116+
for j in range(n)]
2117+
2118+
2119+
class TestMutationContainer(unittest.TestCase, SimpleContainersMixin):
2120+
def get_instances(self, n):
2121+
return [
2122+
tskit.Mutation(
2123+
id_=j, site=j, node=j, derived_state="A" * j, parent=j,
2124+
metadata=b"x" * j)
2125+
for j in range(n)]
2126+
2127+
2128+
class TestMigrationContainer(unittest.TestCase, SimpleContainersMixin):
2129+
def get_instances(self, n):
2130+
return [
2131+
tskit.Migration(left=j, right=j, node=j, source=j, dest=j, time=j)
2132+
for j in range(n)]
2133+
2134+
2135+
class TestPopulationContainer(unittest.TestCase, SimpleContainersMixin):
2136+
def get_instances(self, n):
2137+
return [tskit.Population(id_=j, metadata="x" * j) for j in range(n)]
2138+
2139+
2140+
class TestProvenanceContainer(unittest.TestCase, SimpleContainersMixin):
2141+
def get_instances(self, n):
2142+
return [
2143+
tskit.Provenance(id_=j, timestamp="x" * j, record="y" * j) for j in range(n)]
2144+
2145+
2146+
class TestEdgesetContainer(unittest.TestCase, SimpleContainersMixin):
2147+
def get_instances(self, n):
2148+
return [
2149+
tskit.Edgeset(left=j, right=j, parent=j, children=j) for j in range(n)]
2150+
2151+
2152+
class TestVariantContainer(unittest.TestCase, SimpleContainersMixin):
2153+
def get_instances(self, n):
2154+
return [
2155+
tskit.Variant(
2156+
site=TestSiteContainer().get_instances(1)[0],
2157+
alleles=["A" * j, "T"], genotypes=np.zeros(j, dtype=np.int8))
2158+
for j in range(n)]

python/tskit/drawing.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
try:
1212
import svgwrite
1313
_svgwrite_imported = True
14-
except ImportError:
14+
except ImportError: # pragma: no cover
1515
_svgwrite_imported = False
1616

1717
IS_PY2 = sys.version_info[0] < 3

python/tskit/exceptions.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
VersionTooOldError.__doc__ = """
2525
The version of the file is too old and cannot be read by the library.
2626
"""
27-
except AttributeError:
27+
except AttributeError: # pragma: no cover
2828
# Python2 throws attribute error. Ignore.
2929
pass
3030

python/tskit/trees.py

Lines changed: 8 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
import functools
1515
try:
1616
import concurrent.futures
17-
except ImportError:
17+
except ImportError: # pragma: no cover
1818
# We're on Python2; any attempts to use futures are dealt with below.
1919
pass
2020

@@ -339,6 +339,12 @@ def __init__(self, site, alleles, genotypes):
339339
self.position = site.position
340340
self.index = site.id
341341

342+
def __eq__(self, other):
343+
return (
344+
self.site == other.site and
345+
self.alleles == other.alleles and
346+
np.array_equal(self.genotypes, other.genotypes))
347+
342348

343349
class Edgeset(SimpleContainer):
344350
def __init__(self, left, right, parent, children):
@@ -1170,82 +1176,6 @@ def load(path):
11701176
formats.raise_hdf5_format_error(path, e)
11711177

11721178

1173-
def __load_tables(
1174-
nodes, edges, migrations=None, sites=None, mutations=None,
1175-
provenances=None, individuals=None, populations=None, sequence_length=0):
1176-
"""
1177-
**This method is now deprecated. Please use TableCollection.tree_sequence()
1178-
instead**
1179-
1180-
Loads the tree sequence data from the specified table objects, and
1181-
returns the resulting :class:`.TreeSequence` object. These tables
1182-
must fulfil the properties required for an input tree sequence as
1183-
described in the :ref:`sec_valid_tree_sequence_requirements` section.
1184-
1185-
The ``sequence_length`` parameter determines the
1186-
:attr:`.TreeSequence.sequence_length` of the returned tree sequence. If it
1187-
is 0 or not specified, the value is taken to be the maximum right
1188-
coordinate of the input edges. This parameter is useful in degenerate
1189-
situations (such as when there are zero edges), but can usually be ignored.
1190-
1191-
:param NodeTable nodes: The :ref:`node table <sec_node_table_definition>`
1192-
(required).
1193-
:param EdgeTable edges: The :ref:`edge table <sec_edge_table_definition>`
1194-
(required).
1195-
:param MigrationTable migrations: The :ref:`migration table
1196-
<sec_migration_table_definition>` (optional).
1197-
:param SiteTable sites: The :ref:`site table <sec_site_table_definition>`
1198-
(optional; but if supplied, ``mutations`` must also be specified).
1199-
:param MutationTable mutations: The :ref:`mutation table
1200-
<sec_mutation_table_definition>` (optional; but if supplied, ``sites``
1201-
must also be specified).
1202-
:param ProvenanceTable provenances: The :ref:`provenance table
1203-
<sec_provenance_table_definition>` (optional).
1204-
:param IndividualTable individuals: The :ref:`individual table
1205-
<sec_individual_table_definition>` (optional).
1206-
:param PopulationTable populations: The :ref:`population table
1207-
<sec_population_table_definition>` (optional).
1208-
:param float sequence_length: The sequence length of the returned tree sequence. If
1209-
not supplied or zero this will be inferred from the set of edges.
1210-
:return: A :class:`.TreeSequence` consistent with the specified tables.
1211-
:rtype: TreeSequence
1212-
"""
1213-
if sequence_length is None:
1214-
sequence_length = 0
1215-
if sequence_length == 0 and len(edges) > 0:
1216-
sequence_length = edges.right.max()
1217-
kwargs = {
1218-
"nodes": nodes.ll_table, "edges": edges.ll_table,
1219-
"sequence_length": sequence_length}
1220-
if migrations is not None:
1221-
kwargs["migrations"] = migrations.ll_table
1222-
else:
1223-
kwargs["migrations"] = _tskit.MigrationTable()
1224-
if sites is not None:
1225-
kwargs["sites"] = sites.ll_table
1226-
else:
1227-
kwargs["sites"] = _tskit.SiteTable()
1228-
if mutations is not None:
1229-
kwargs["mutations"] = mutations.ll_table
1230-
else:
1231-
kwargs["mutations"] = _tskit.MutationTable()
1232-
if provenances is not None:
1233-
kwargs["provenances"] = provenances.ll_table
1234-
else:
1235-
kwargs["provenances"] = _tskit.ProvenanceTable()
1236-
if individuals is not None:
1237-
kwargs["individuals"] = individuals.ll_table
1238-
else:
1239-
kwargs["individuals"] = _tskit.IndividualTable()
1240-
if populations is not None:
1241-
kwargs["populations"] = populations.ll_table
1242-
else:
1243-
kwargs["populations"] = _tskit.PopulationTable()
1244-
1245-
ll_tables = _tskit.TableCollection(**kwargs)
1246-
return TreeSequence.load_tables(tables.TableCollection(ll_tables=ll_tables))
1247-
1248-
12491179
def parse_individuals(
12501180
source, strict=True, encoding='utf8', base64_metadata=True, table=None):
12511181
"""
@@ -2460,7 +2390,7 @@ def genealogical_nearest_neighbours(self, focal, reference_sets, num_threads=0):
24602390
return self._ll_tree_sequence.genealogical_nearest_neighbours(
24612391
focal, reference_sets)
24622392
else:
2463-
if IS_PY2:
2393+
if IS_PY2: # pragma: no cover
24642394
raise ValueError("Threads not supported on Python 2.")
24652395
worker = functools.partial(
24662396
self._ll_tree_sequence.genealogical_nearest_neighbours,

0 commit comments

Comments
 (0)