File: testseqio.py

package info (click to toggle)
python-cutadapt 1.12-2~bpo8%2B1
  • links: PTS, VCS
  • area: main
  • in suites: jessie-backports
  • size: 2,112 kB
  • sloc: python: 4,297; makefile: 166
file content (352 lines) | stat: -rw-r--r-- 9,812 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
# coding: utf-8
from __future__ import print_function, division, absolute_import

import sys
import os
import shutil
from textwrap import dedent
from nose.tools import raises
from tempfile import mkdtemp
from cutadapt.seqio import (Sequence, ColorspaceSequence, FormatError,
	FastaReader, FastqReader, FastaQualReader, InterleavedSequenceReader,
	FastaWriter, FastqWriter, InterleavedSequenceWriter, open as openseq,
	sequence_names_match)
from cutadapt.compat import StringIO


# files tests/data/simple.fast{q,a}
simple_fastq = [
	Sequence("first_sequence", "SEQUENCE1", ":6;;8<=:<"),
	Sequence("second_sequence", "SEQUENCE2", "83<??:(61")
	]

simple_fasta = [ Sequence(x.name, x.sequence, None) for x in simple_fastq ]


class TestSequence:
	@raises(FormatError)
	def test_too_many_qualities(self):
		Sequence(name="name", sequence="ACGT", qualities="#####")

	@raises(FormatError)
	def test_too_many_qualities_colorspace(self):
		ColorspaceSequence(name="name", sequence="T0123", qualities="#####")

	@raises(FormatError)
	def test_invalid_primer(self):
		ColorspaceSequence(name="name", sequence="K0123", qualities="####")


class TestFastaReader:
	def test(self):
		with FastaReader("tests/data/simple.fasta") as f:
			reads = list(f)
		assert reads == simple_fasta

		fasta = StringIO(">first_sequence\nSEQUENCE1\n>second_sequence\nSEQUENCE2\n")
		reads = list(FastaReader(fasta))
		assert reads == simple_fasta

	def test_with_comments(self):
		fasta = StringIO(dedent(
			"""
			# a comment
			# another one
			>first_sequence
			SEQUENCE1
			>second_sequence
			SEQUENCE2
			"""))
		reads = list(FastaReader(fasta))
		assert reads == simple_fasta

	@raises(FormatError)
	def test_wrong_format(self):
		fasta = StringIO(dedent(
			"""
			# a comment
			# another one
			unexpected
			>first_sequence
			SEQUENCE1
			>second_sequence
			SEQUENCE2
			"""))
		reads = list(FastaReader(fasta))

	def test_fastareader_keeplinebreaks(self):
		with FastaReader("tests/data/simple.fasta", keep_linebreaks=True) as f:
			reads = list(f)
		assert reads[0] == simple_fasta[0]
		assert reads[1].sequence == 'SEQUEN\nCE2'

	def test_context_manager(self):
		filename = "tests/data/simple.fasta"
		with open(filename) as f:
			assert not f.closed
			reads = list(openseq(f))
			assert not f.closed
		assert f.closed

		with FastaReader(filename) as sr:
			tmp_sr = sr
			assert not sr._file.closed
			reads = list(sr)
			assert not sr._file.closed
		assert tmp_sr._file is None
		# Open it a second time
		with FastaReader(filename) as sr:
			pass


class TestFastqReader:
	def test_fastqreader(self):
		with FastqReader("tests/data/simple.fastq") as f:
			reads = list(f)
		assert reads == simple_fastq

	def test_fastqreader_dos(self):
		with FastqReader("tests/data/dos.fastq") as f:
			dos_reads = list(f)
		with FastqReader("tests/data/small.fastq") as f:
			unix_reads = list(f)
		assert dos_reads == unix_reads

	@raises(FormatError)
	def test_fastq_wrongformat(self):
		with FastqReader("tests/data/withplus.fastq") as f:
			reads = list(f)

	@raises(FormatError)
	def test_fastq_incomplete(self):
		fastq = StringIO("@name\nACGT+\n")
		with FastqReader(fastq) as fq:
			list(fq)

	def test_context_manager(self):
		filename = "tests/data/simple.fastq"
		with open(filename) as f:
			assert not f.closed
			reads = list(openseq(f))
			assert not f.closed
		assert f.closed

		with FastqReader(filename) as sr:
			tmp_sr = sr
			assert not sr._file.closed
			reads = list(sr)
			assert not sr._file.closed
		assert tmp_sr._file is None


class TestFastaQualReader:
	@raises(FormatError)
	def test_mismatching_read_names(self):
		fasta = StringIO(">name\nACG")
		qual = StringIO(">nome\n3 5 7")
		list(FastaQualReader(fasta, qual))

	@raises(FormatError)
	def test_invalid_quality_value(self):
		fasta = StringIO(">name\nACG")
		qual = StringIO(">name\n3 xx 7")
		list(FastaQualReader(fasta, qual))


class TestSeqioOpen:
	def setup(self):
		self._tmpdir = mkdtemp()

	def teardown(self):
		shutil.rmtree(self._tmpdir)

	def test_sequence_reader(self):
		# test the autodetection
		with openseq("tests/data/simple.fastq") as f:
			reads = list(f)
		assert reads == simple_fastq

		with openseq("tests/data/simple.fasta") as f:
			reads = list(f)
		assert reads == simple_fasta

		with open("tests/data/simple.fastq") as f:
			reads = list(openseq(f))
		assert reads == simple_fastq

		# make the name attribute unavailable
		f = StringIO(open("tests/data/simple.fastq").read())
		reads = list(openseq(f))
		assert reads == simple_fastq

		f = StringIO(open("tests/data/simple.fasta").read())
		reads = list(openseq(f))
		assert reads == simple_fasta

	def test_autodetect_fasta_format(self):
		path = os.path.join(self._tmpdir, 'tmp.fasta')
		with openseq(path, mode='w') as f:
			assert isinstance(f, FastaWriter)
			for seq in simple_fastq:
				f.write(seq)
		assert list(openseq(path)) == simple_fasta

	def test_write_qualities_to_fasta(self):
		path = os.path.join(self._tmpdir, 'tmp.fasta')
		with openseq(path, mode='w', qualities=True) as f:
			assert isinstance(f, FastaWriter)
			for seq in simple_fastq:
				f.write(seq)
		assert list(openseq(path)) == simple_fasta

	def test_autodetect_fastq_format(self):
		path = os.path.join(self._tmpdir, 'tmp.fastq')
		with openseq(path, mode='w') as f:
			assert isinstance(f, FastqWriter)
			for seq in simple_fastq:
				f.write(seq)
		assert list(openseq(path)) == simple_fastq

	@raises(ValueError)
	def test_fastq_qualities_missing(self):
		path = os.path.join(self._tmpdir, 'tmp.fastq')
		openseq(path, mode='w', qualities=False)


class TestInterleavedReader:
	def test(self):
		expected = [
			(Sequence('read1/1 some text', 'TTATTTGTCTCCAGC', '##HHHHHHHHHHHHH'),
			Sequence('read1/2 other text', 'GCTGGAGACAAATAA', 'HHHHHHHHHHHHHHH')),
			(Sequence('read3/1', 'CCAACTTGATATTAATAACA', 'HHHHHHHHHHHHHHHHHHHH'),
			Sequence('read3/2', 'TGTTATTAATATCAAGTTGG', '#HHHHHHHHHHHHHHHHHHH'))
		]
		reads = list(InterleavedSequenceReader("tests/cut/interleaved.fastq"))
		for (r1, r2), (e1, e2) in zip(reads, expected):
			print(r1, r2, e1, e2)

		assert reads == expected
		with openseq("tests/cut/interleaved.fastq", interleaved=True) as f:
			reads = list(f)
		assert reads == expected

	@raises(FormatError)
	def test_missing_partner(self):
		s = StringIO('@r1\nACG\n+\nHHH')
		list(InterleavedSequenceReader(s))

	@raises(FormatError)
	def test_incorrectly_paired(self):
		s = StringIO('@r1/1\nACG\n+\nHHH\n@wrong_name\nTTT\n+\nHHH')
		list(InterleavedSequenceReader(s))


class TestFastaWriter:
	def setup(self):
		self._tmpdir = mkdtemp()
		self.path = os.path.join(self._tmpdir, 'tmp.fasta')

	def teardown(self):
		shutil.rmtree(self._tmpdir)

	def test(self):
		with FastaWriter(self.path) as fw:
			fw.write("name", "CCATA")
			fw.write("name2", "HELLO")
		assert fw._file.closed
		with open(self.path) as t:
			assert t.read() == '>name\nCCATA\n>name2\nHELLO\n'

	def test_linelength(self):
		with FastaWriter(self.path, line_length=3) as fw:
			fw.write("r1", "ACG")
			fw.write("r2", "CCAT")
			fw.write("r3", "TACCAG")
		assert fw._file.closed
		with open(self.path) as t:
			d = t.read()
			assert d == '>r1\nACG\n>r2\nCCA\nT\n>r3\nTAC\nCAG\n'

	def test_write_sequence_object(self):
		with FastaWriter(self.path) as fw:
			fw.write(Sequence("name", "CCATA"))
			fw.write(Sequence("name2", "HELLO"))
		assert fw._file.closed
		with open(self.path) as t:
			assert t.read() == '>name\nCCATA\n>name2\nHELLO\n'

	def test_write_to_file_like_object(self):
		sio = StringIO()
		with FastaWriter(sio) as fw:
			fw.write(Sequence("name", "CCATA"))
			fw.write(Sequence("name2", "HELLO"))
			assert sio.getvalue() == '>name\nCCATA\n>name2\nHELLO\n'
		assert not fw._file.closed

	def test_write_zero_length_sequence(self):
		sio = StringIO()
		with FastaWriter(sio) as fw:
			fw.write(Sequence("name", ""))
			assert sio.getvalue() == '>name\n\n', '{0!r}'.format(sio.getvalue())


class TestFastqWriter:
	def setup(self):
		self._tmpdir = mkdtemp()
		self.path = os.path.join(self._tmpdir, 'tmp.fastq')

	def teardown(self):
		shutil.rmtree(self._tmpdir)

	def test(self):
		with FastqWriter(self.path) as fq:
			fq.writeseq("name", "CCATA", "!#!#!")
			fq.writeseq("name2", "HELLO", "&&&!&&")
		assert fq._file.closed
		with open(self.path) as t:
			assert t.read() == '@name\nCCATA\n+\n!#!#!\n@name2\nHELLO\n+\n&&&!&&\n'

	def test_twoheaders(self):
		with FastqWriter(self.path) as fq:
			fq.write(Sequence("name", "CCATA", "!#!#!", second_header=True))
			fq.write(Sequence("name2", "HELLO", "&&&!&", second_header=True))
		assert fq._file.closed
		with open(self.path) as t:
			assert t.read() == '@name\nCCATA\n+name\n!#!#!\n@name2\nHELLO\n+name2\n&&&!&\n'

	def test_write_to_file_like_object(self):
		sio = StringIO()
		with FastqWriter(sio) as fq:
			fq.writeseq("name", "CCATA", "!#!#!")
			fq.writeseq("name2", "HELLO", "&&&!&&")
		assert sio.getvalue() == '@name\nCCATA\n+\n!#!#!\n@name2\nHELLO\n+\n&&&!&&\n'


class TestInterleavedWriter:
	def test(self):
		reads = [
			(Sequence('A/1 comment', 'TTA', '##H'),
			Sequence('A/2 comment', 'GCT', 'HH#')),
			(Sequence('B/1', 'CC', 'HH'),
			Sequence('B/2', 'TG', '#H'))
		]
		sio = StringIO()
		with InterleavedSequenceWriter(sio) as writer:
			for read1, read2 in reads:
				writer.write(read1, read2)
		assert sio.getvalue() == '@A/1 comment\nTTA\n+\n##H\n@A/2 comment\nGCT\n+\nHH#\n@B/1\nCC\n+\nHH\n@B/2\nTG\n+\n#H\n'


class TestPairedSequenceReader:
	def test_sequence_names_match(self):
		def match(name1, name2):
			seq1 = Sequence(name1, 'ACGT')
			seq2 = Sequence(name2, 'AACC')
			return sequence_names_match(seq1, seq2)

		assert match('abc', 'abc')
		assert match('abc/1', 'abc/2')
		assert match('abc.1', 'abc.2')
		assert match('abc1', 'abc2')
		assert not match('abc', 'xyz')