Skip to content

Commit d1bc5e0

Browse files
Merge pull request #42 from jeromekelleher/bump-python-coverage
Bump python coverage
2 parents e699164 + 31c34d2 commit d1bc5e0

File tree

11 files changed

+296
-96
lines changed

11 files changed

+296
-96
lines changed

c/tsk_genotypes.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ tsk_hapgen_get_haplotype(tsk_hapgen_t *self, tsk_id_t sample_index, char **haplo
180180
{
181181
int ret = 0;
182182

183-
if (sample_index >= (tsk_id_t) self->num_samples) {
183+
if (sample_index < 0 || sample_index >= (tsk_id_t) self->num_samples) {
184184
ret = TSK_ERR_OUT_OF_BOUNDS;
185185
goto out;
186186
}

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/_tskitmodule.c

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -195,9 +195,6 @@ handle_library_error(int err)
195195
case TSK_ERR_FILE_FORMAT:
196196
PyErr_SetString(TskitFileFormatError, tsk_strerror(err));
197197
break;
198-
case TSK_ERR_OUT_OF_BOUNDS:
199-
PyErr_SetString(PyExc_IndexError, tsk_strerror(err));
200-
break;
201198
default:
202199
PyErr_SetString(TskitLibraryError, tsk_strerror(err));
203200
}
@@ -6167,6 +6164,7 @@ TreeSequence_get_pairwise_diversity(TreeSequence *self, PyObject *args, PyObject
61676164
&PyList_Type, &py_samples)) {
61686165
goto out;
61696166
}
6167+
/* TODO Change this to reading numpy array */
61706168
if (parse_sample_ids(py_samples, self->tree_sequence, &num_samples, &samples) != 0) {
61716169
goto out;
61726170
}
@@ -7893,16 +7891,16 @@ HaplotypeGenerator_get_haplotype(HaplotypeGenerator *self, PyObject *args)
78937891
int err;
78947892
PyObject *ret = NULL;
78957893
char *haplotype;
7896-
unsigned int sample_id;
7894+
int sample_id;
78977895

78987896
if (HaplotypeGenerator_check_state(self) != 0) {
78997897
goto out;
79007898
}
7901-
if (!PyArg_ParseTuple(args, "I", &sample_id)) {
7899+
if (!PyArg_ParseTuple(args, "i", &sample_id)) {
79027900
goto out;
79037901
}
79047902
err = tsk_hapgen_get_haplotype(self->haplotype_generator,
7905-
(uint32_t) sample_id, &haplotype);
7903+
(tsk_id_t) sample_id, &haplotype);
79067904
if (err != 0) {
79077905
handle_library_error(err);
79087906
goto out;
@@ -8203,6 +8201,9 @@ LdCalculator_get_r2(LdCalculator *self, PyObject *args)
82038201
return ret;
82048202
}
82058203

8204+
/* TODO this implementation is brittle and cumbersome. Replace with something that
8205+
* returns a numpy array directly. Passing in the memory is a premature optimisation.
8206+
*/
82068207
static PyObject *
82078208
LdCalculator_get_r2_array(LdCalculator *self, PyObject *args, PyObject *kwds)
82088209
{

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/tests/test_lowlevel.py

Lines changed: 143 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ def verify_tree_dict(self, n, pi):
8888
self.assertGreaterEqual(num_children[j], 2)
8989

9090
def get_example_tree_sequence(self):
91-
ts = msprime.simulate(10, recombination_rate=0.1, random_seed=1)
91+
ts = msprime.simulate(10, recombination_rate=0.1, mutation_rate=1, random_seed=1)
9292
return ts.ll_tree_sequence
9393

9494
def get_example_tree_sequences(self):
@@ -288,8 +288,8 @@ def test_get_samples(self):
288288
def test_pairwise_diversity(self):
289289
for ts in self.get_example_tree_sequences():
290290
for bad_type in ["", None, {}]:
291-
self.assertRaises(
292-
TypeError, ts.get_pairwise_diversity, bad_type)
291+
self.assertRaises(TypeError, ts.get_pairwise_diversity, bad_type)
292+
self.assertRaises(TypeError, ts.get_pairwise_diversity, [0, bad_type])
293293
self.assertRaises(
294294
ValueError, ts.get_pairwise_diversity, [])
295295
self.assertRaises(
@@ -419,6 +419,121 @@ def test_iterator(self):
419419
self.verify_iterator(_tskit.TreeIterator(tree))
420420

421421

422+
class TestVcfConverter(LowLevelTestCase):
423+
"""
424+
Tests for the VcfConverter class.
425+
"""
426+
def test_uninitialised_tree_sequence(self):
427+
ts = _tskit.TreeSequence()
428+
self.assertRaises(ValueError, _tskit.VcfConverter, ts)
429+
430+
def test_constructor(self):
431+
self.assertRaises(TypeError, _tskit.VcfConverter)
432+
self.assertRaises(TypeError, _tskit.VcfConverter, None)
433+
ts = self.get_example_tree_sequence()
434+
self.assertRaises(TypeError, _tskit.VcfConverter, ts, ploidy="xyt")
435+
self.assertRaises(ValueError, _tskit.VcfConverter, ts, ploidy=0)
436+
self.assertRaises(TypeError, _tskit.VcfConverter, ts, contig_id=None)
437+
self.assertRaises(ValueError, _tskit.VcfConverter, ts, contig_id="")
438+
439+
def test_iterator(self):
440+
ts = self.get_example_tree_sequence()
441+
self.verify_iterator(_tskit.VcfConverter(ts))
442+
443+
444+
class TestVariantGenerator(LowLevelTestCase):
445+
"""
446+
Tests for the VariantGenerator class.
447+
"""
448+
def test_uninitialised_tree_sequence(self):
449+
ts = _tskit.TreeSequence()
450+
self.assertRaises(ValueError, _tskit.VariantGenerator, ts)
451+
452+
def test_constructor(self):
453+
self.assertRaises(TypeError, _tskit.VariantGenerator)
454+
self.assertRaises(TypeError, _tskit.VariantGenerator, None)
455+
ts = self.get_example_tree_sequence()
456+
self.assertRaises(ValueError, _tskit.VariantGenerator, ts, samples={})
457+
self.assertRaises(
458+
_tskit.LibraryError, _tskit.VariantGenerator, ts, samples=[-1, 2])
459+
460+
def test_iterator(self):
461+
ts = self.get_example_tree_sequence()
462+
self.verify_iterator(_tskit.VariantGenerator(ts))
463+
464+
465+
class TestHaplotypeGenerator(LowLevelTestCase):
466+
"""
467+
Tests for the HaplotypeGenerator class.
468+
"""
469+
def test_uninitialised_tree_sequence(self):
470+
ts = _tskit.TreeSequence()
471+
self.assertRaises(ValueError, _tskit.HaplotypeGenerator, ts)
472+
473+
def test_constructor(self):
474+
self.assertRaises(TypeError, _tskit.HaplotypeGenerator)
475+
self.assertRaises(TypeError, _tskit.HaplotypeGenerator, None)
476+
477+
def test_bad_id(self):
478+
ts = self.get_example_tree_sequence()
479+
hg = _tskit.HaplotypeGenerator(ts)
480+
n = ts.get_num_samples()
481+
for bad_id in [-1, n, n + 1]:
482+
with self.assertRaises(_tskit.LibraryError):
483+
hg.get_haplotype(bad_id)
484+
485+
486+
class TestLdCalculator(LowLevelTestCase):
487+
"""
488+
Tests for the LdCalculator class.
489+
"""
490+
def test_uninitialised_tree_sequence(self):
491+
ts = _tskit.TreeSequence()
492+
self.assertRaises(ValueError, _tskit.LdCalculator, ts)
493+
494+
def test_constructor(self):
495+
self.assertRaises(TypeError, _tskit.LdCalculator)
496+
self.assertRaises(TypeError, _tskit.LdCalculator, None)
497+
498+
def test_get_r2(self):
499+
ts = self.get_example_tree_sequence()
500+
calc = _tskit.LdCalculator(ts)
501+
n = ts.get_num_sites()
502+
for bad_id in [-1, n, n + 1]:
503+
with self.assertRaises(_tskit.LibraryError):
504+
calc.get_r2(0, bad_id)
505+
with self.assertRaises(_tskit.LibraryError):
506+
calc.get_r2(bad_id, 0)
507+
508+
def test_get_r2_array(self):
509+
ts = self.get_example_tree_sequence()
510+
calc = _tskit.LdCalculator(ts)
511+
512+
self.assertRaises(TypeError, calc.get_r2_array)
513+
self.assertRaises(TypeError, calc.get_r2_array, None)
514+
# Doesn't support buffer protocol, so raises typeerror
515+
self.assertRaises(TypeError, calc.get_r2_array, None, 0)
516+
517+
n = ts.get_num_sites()
518+
self.assertGreater(n, 2)
519+
with self.assertRaises(BufferError):
520+
calc.get_r2_array(bytes(100), 0)
521+
522+
buff = bytearray(1024)
523+
with self.assertRaises(ValueError):
524+
calc.get_r2_array(buff, 0, max_distance=-1)
525+
with self.assertRaises(ValueError):
526+
calc.get_r2_array(buff, 0, direction=1000)
527+
# TODO this API is poor, we should explicitly catch these negative
528+
# size errors.
529+
for bad_max_mutations in [-2, -3, -2**32]:
530+
with self.assertRaises(BufferError):
531+
calc.get_r2_array(buff, 0, max_mutations=bad_max_mutations)
532+
for bad_start_pos in [-1, n, n + 1]:
533+
with self.assertRaises(_tskit.LibraryError):
534+
calc.get_r2_array(buff, bad_start_pos)
535+
536+
422537
class TestTree(LowLevelTestCase):
423538
"""
424539
Tests on the low-level sparse tree interface.
@@ -450,6 +565,31 @@ def test_flags(self):
450565
self.assertRaises(ValueError, st.get_right_sample, 0)
451566
self.assertRaises(ValueError, st.get_next_sample, 0)
452567

568+
def test_site_errors(self):
569+
ts = self.get_example_tree_sequence()
570+
for bad_index in [-1, ts.get_num_sites(), ts.get_num_sites() + 1]:
571+
self.assertRaises(IndexError, ts.get_site, bad_index)
572+
573+
def test_mutation_errors(self):
574+
ts = self.get_example_tree_sequence()
575+
for bad_index in [-1, ts.get_num_mutations(), ts.get_num_mutations() + 1]:
576+
self.assertRaises(IndexError, ts.get_mutation, bad_index)
577+
578+
def test_individual_errors(self):
579+
ts = self.get_example_tree_sequence()
580+
for bad_index in [-1, ts.get_num_individuals(), ts.get_num_individuals() + 1]:
581+
self.assertRaises(IndexError, ts.get_individual, bad_index)
582+
583+
def test_population_errors(self):
584+
ts = self.get_example_tree_sequence()
585+
for bad_index in [-1, ts.get_num_populations(), ts.get_num_populations() + 1]:
586+
self.assertRaises(IndexError, ts.get_population, bad_index)
587+
588+
def test_provenance_errors(self):
589+
ts = self.get_example_tree_sequence()
590+
for bad_index in [-1, ts.get_num_provenances(), ts.get_num_provenances() + 1]:
591+
self.assertRaises(IndexError, ts.get_provenance, bad_index)
592+
453593
def test_sites(self):
454594
for ts in self.get_example_tree_sequences():
455595
st = _tskit.Tree(ts)

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

0 commit comments

Comments
 (0)