Skip to content

Commit ab6c58f

Browse files
committed
Overhaul VariantHeader and VariantFile error reporting
Use OSError_from_errno() throughout for I/O errors, and check for errors from bcf_hdr_write(), bcf_hrec_add_key(), and bcf_hrec_set_val(). These are all marked as warn_unused_result. Update libchtslib.pxd as bcf_hrec_add_key(), bcf_hrec_set_val(), and hrec_add_idx() changed from returning void to int back in HTSlib 1.10.
1 parent bc19635 commit ab6c58f

File tree

2 files changed

+33
-29
lines changed

2 files changed

+33
-29
lines changed

pysam/libcbcf.pyx

Lines changed: 30 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ cdef tuple METADATA_LENGTHS = ('FIXED', 'VARIABLE', 'A', 'G', 'R')
143143
########################################################################
144144

145145
from pysam.libcutils cimport force_bytes, force_str, charptr_to_str, charptr_to_str_w_len
146-
from pysam.libcutils cimport encode_filename, from_string_and_size, decode_bytes
146+
from pysam.libcutils cimport OSError_from_errno, encode_filename, from_string_and_size, decode_bytes
147147

148148

149149
########################################################################
@@ -2158,10 +2158,12 @@ cdef class VariantHeader(object):
21582158
quoted = not isinstance(value, unquoted_str) and key not in ("ID", "Number", "Type")
21592159

21602160
key = force_bytes(key)
2161-
bcf_hrec_add_key(hrec, key, <int>len(key))
2161+
if bcf_hrec_add_key(hrec, key, <int>len(key)) < 0:
2162+
raise MemoryError("Could not allocate VCF header record")
21622163

21632164
value = force_bytes(str(value))
2164-
bcf_hrec_set_val(hrec, hrec.nkeys-1, value, <int>len(value), quoted)
2165+
if bcf_hrec_set_val(hrec, hrec.nkeys-1, value, <int>len(value), quoted) < 0:
2166+
raise MemoryError("Could not allocate VCF header record")
21652167
except:
21662168
bcf_hrec_destroy(hrec)
21672169
raise
@@ -4116,42 +4118,42 @@ cdef class VariantFile(HTSFile):
41164118
if not self.htsfile or not self.header:
41174119
return
41184120

4121+
cdef int ret
4122+
41194123
# Write header if no records were written
41204124
if self.htsfile.is_write and not self.header_written:
41214125
with nogil:
4122-
bcf_hdr_write(self.htsfile, self.header.ptr)
4126+
ret = bcf_hdr_write(self.htsfile, self.header.ptr)
4127+
if ret < 0 and errno != EPIPE:
4128+
raise OSError_from_errno("Can't write headers", self.filename)
41234129

4124-
cdef int ret = hts_close(self.htsfile)
4130+
ret = hts_close(self.htsfile)
41254131
self.htsfile = NULL
41264132
self.header = self.index = None
41274133

4128-
if ret < 0:
4129-
global errno
4130-
if errno == EPIPE:
4131-
errno = 0
4132-
else:
4133-
raise IOError(errno, force_str(strerror(errno)))
4134+
if ret < 0 and errno != EPIPE:
4135+
raise OSError_from_errno("Closing failed", self.filename)
41344136

41354137
def close(self):
41364138
"""closes the :class:`pysam.VariantFile`."""
41374139
if not self.htsfile:
41384140
return
41394141

4142+
cdef int ret
4143+
41404144
# Write header if no records were written
41414145
if self.htsfile.is_write and not self.header_written:
41424146
with nogil:
4143-
bcf_hdr_write(self.htsfile, self.header.ptr)
4147+
ret = bcf_hdr_write(self.htsfile, self.header.ptr)
4148+
if ret < 0 and errno != EPIPE:
4149+
raise OSError_from_errno("Can't write headers", self.filename)
41444150

4145-
cdef int ret = hts_close(self.htsfile)
4151+
ret = hts_close(self.htsfile)
41464152
self.htsfile = NULL
41474153
self.header = self.index = None
41484154

4149-
if ret < 0:
4150-
global errno
4151-
if errno == EPIPE:
4152-
errno = 0
4153-
else:
4154-
raise IOError(errno, force_str(strerror(errno)))
4155+
if ret < 0 and errno != EPIPE:
4156+
raise OSError_from_errno("Closing failed", self.filename)
41554157

41564158
def __iter__(self):
41574159
if not self.is_open:
@@ -4188,7 +4190,7 @@ cdef class VariantFile(HTSFile):
41884190
elif ret == -2:
41894191
raise IOError('truncated file')
41904192
elif errno:
4191-
raise IOError(errno, strerror(errno))
4193+
raise OSError_from_errno("Unable to fetch next record", self.filename)
41924194
else:
41934195
raise IOError('unable to fetch next record')
41944196

@@ -4335,7 +4337,7 @@ cdef class VariantFile(HTSFile):
43354337

43364338
if not self.htsfile:
43374339
if errno:
4338-
raise IOError(errno, 'could not open variant file `{}`: {}'.format(filename, force_str(strerror(errno))))
4340+
raise OSError_from_errno("Could not open variant file", filename)
43394341
else:
43404342
raise ValueError('could not open variant file `{}`'.format(filename))
43414343

@@ -4485,15 +4487,19 @@ cdef class VariantFile(HTSFile):
44854487
raise ValueError('record must not be None')
44864488

44874489
if not self.is_open:
4488-
return ValueError('I/O operation on closed file')
4490+
raise ValueError('I/O operation on closed file')
44894491

44904492
if not self.htsfile.is_write:
44914493
raise ValueError('cannot write to a Variantfile opened for reading')
44924494

4495+
cdef int ret
4496+
44934497
if not self.header_written:
44944498
self.header_written = True
44954499
with nogil:
4496-
bcf_hdr_write(self.htsfile, self.header.ptr)
4500+
ret = bcf_hdr_write(self.htsfile, self.header.ptr)
4501+
if ret < 0:
4502+
raise OSError_from_errno("Can't write headers", self.filename)
44974503

44984504
#if record.header is not self.header:
44994505
# record.translate(self.header)
@@ -4506,13 +4512,11 @@ cdef class VariantFile(HTSFile):
45064512
# Sync END annotation before writing
45074513
bcf_sync_end(record)
45084514

4509-
cdef int ret
4510-
45114515
with nogil:
45124516
ret = bcf_write1(self.htsfile, self.header.ptr, record.ptr)
45134517

45144518
if ret < 0:
4515-
raise IOError(errno, strerror(errno))
4519+
raise OSError_from_errno("Can't write record", self.filename)
45164520

45174521
return ret
45184522

pysam/libchtslib.pxd

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1871,10 +1871,10 @@ cdef extern from "htslib/vcf.h" nogil:
18711871
#
18721872
bcf_hrec_t *bcf_hdr_get_hrec(const bcf_hdr_t *hdr, int type, const char *key, const char *value, const char *str_class)
18731873
bcf_hrec_t *bcf_hrec_dup(bcf_hrec_t *hrec)
1874-
void bcf_hrec_add_key(bcf_hrec_t *hrec, const char *str, int len)
1875-
void bcf_hrec_set_val(bcf_hrec_t *hrec, int i, const char *str, int len, int is_quoted)
1874+
int bcf_hrec_add_key(bcf_hrec_t *hrec, const char *str, size_t len)
1875+
int bcf_hrec_set_val(bcf_hrec_t *hrec, int i, const char *str, size_t len, int is_quoted)
18761876
int bcf_hrec_find_key(bcf_hrec_t *hrec, const char *key)
1877-
void hrec_add_idx(bcf_hrec_t *hrec, int idx)
1877+
int hrec_add_idx(bcf_hrec_t *hrec, int idx)
18781878
void bcf_hrec_destroy(bcf_hrec_t *hrec)
18791879

18801880
#************************************************************************

0 commit comments

Comments
 (0)