[med-svn] [Git][med-team/cyvcf2][master] 4 commits: New upstream version 0.20.6
Andreas Tille
gitlab at salsa.debian.org
Fri Sep 18 07:25:17 BST 2020
Andreas Tille pushed to branch master at Debian Med / cyvcf2
Commits:
df085d3c by Andreas Tille at 2020-09-18T08:19:49+02:00
New upstream version 0.20.6
- - - - -
bc1d62ee by Andreas Tille at 2020-09-18T08:19:49+02:00
routine-update: New upstream version
- - - - -
10643253 by Andreas Tille at 2020-09-18T08:19:51+02:00
Update upstream source from tag 'upstream/0.20.6'
Update to upstream version '0.20.6'
with Debian dir fc0c735920642713ef9f9ffa750f1fecd3894b01
- - - - -
821fa640 by Andreas Tille at 2020-09-18T08:22:24+02:00
routine-update: Ready to upload to unstable
- - - - -
8 changed files:
- README.md
- cyvcf2/__init__.py
- cyvcf2/cyvcf2.pyx
- cyvcf2/tests/test_reader.py
- + cyvcf2/tests/test_writer.py
- debian/changelog
- docs/source/index.rst
- + docs/source/writing.rst
Changes:
=====================================
README.md
=====================================
@@ -4,6 +4,8 @@ cyvcf2
Note: cyvcf2 versions < 0.20.0 require htslib < 1.10. cyvcf2 versions >= 0.20.0 require htslib >= 1.10
<!-- ghp-import -p docs/build/html/ -->
+The latest documentation for cyvcf2 can be found here:
+
[![Docs](https://img.shields.io/badge/docs-latest-blue.svg)](http://brentp.github.io/cyvcf2/)
If you use cyvcf2, please cite the [paper](https://academic.oup.com/bioinformatics/article/2971439/cyvcf2)
@@ -86,6 +88,7 @@ autoconf
make
cd ..
+pip install -r requirements.txt
CYTHONIZE=1 pip install -e .
```
=====================================
cyvcf2/__init__.py
=====================================
@@ -2,4 +2,4 @@ from .cyvcf2 import (VCF, Variant, Writer, r_ as r_unphased, par_relatedness,
par_het)
Reader = VCFReader = VCF
-__version__ = "0.20.1"
+__version__ = "0.20.6"
=====================================
cyvcf2/cyvcf2.pyx
=====================================
@@ -1,4 +1,5 @@
#cython: profile=False
+#cython:language_level=3
#cython: embedsignature=True
from __future__ import print_function
@@ -154,27 +155,19 @@ cdef class HTSFile:
cdef hFILE *hf
self.mode = to_bytes(mode)
reading = b"r" in self.mode
- if not reading and b"w" not in self.mode:
+ writing = b"w" in self.mode
+ if not reading and not writing:
raise IOError("No 'r' or 'w' in mode %s" % str(self.mode))
- self.from_path = False
- # for htslib, wbu seems to not work
- if mode == b"wbu":
- mode = to_bytes(b"wb0")
- if isinstance(fname, (basestring, Path)):
- self.from_path = True
+
+ self.from_path = isinstance(fname, (basestring, Path))
+ if self.from_path:
self.fname = to_bytes(str(fname))
if self.fname == b"-":
self.fname = to_bytes(b"/dev/stdin") if reading else to_bytes(b"/dev/stdout")
- if self.fname.endswith(b".gz") and self.mode == b"w":
- self.mode = b"wz"
- elif self.fname.endswith((b".bcf", b".bcf.gz")) and self.mode == b"w":
- self.mode = b"wb"
- self.fname = to_bytes(str(fname))
- self.mode = to_bytes(mode)
+
self.hts = hts_open(self.fname, self.mode)
# from a file descriptor
elif isinstance(fname, int):
- self.mode = to_bytes(mode)
hf = hdopen(int(fname), self.mode)
self.hts = hts_hopen(hf, "<file>", self.mode)
self.fname = None
@@ -182,7 +175,6 @@ cdef class HTSFile:
elif hasattr(fname, "fileno"):
if fname.closed:
raise IOError('I/O operation on closed file')
- self.mode = to_bytes(mode)
hf = hdopen(fname.fileno(), self.mode)
self.hts = hts_hopen(hf, "<file>", self.mode)
# .name can be TextIOWrapper
@@ -304,6 +296,9 @@ cdef class VCF(HTSFile):
ret = bcf_hdr_sync(self.hdr)
if ret != 0:
raise Exception("couldn't add '%s' to header")
+ if line.startswith("##contig"):
+ # need to trigger a refresh of seqnames
+ self._seqnames = []
return ret
def add_info_to_header(self, adict):
@@ -382,6 +377,11 @@ cdef class VCF(HTSFile):
raise Exception("unable to update to header")
def set_index(self, index_path=""):
+ if index_path.endswith(".tbi"):
+ self.idx = tbx_index_load2(to_bytes(self.fname), to_bytes(index_path))
+ if self.idx != NULL:
+ return
+
self.hidx = hts_idx_load2(to_bytes(self.fname), to_bytes(index_path))
if self.hidx == NULL:
self.idx = tbx_index_load2(to_bytes(self.fname), to_bytes(index_path))
@@ -579,11 +579,15 @@ cdef class VCF(HTSFile):
with nogil:
b = bcf_init()
ret = bcf_read(self.hts, self.hdr, b)
- if ret >= 0:
+ if ret >= 0 or b.errcode == BCF_ERR_CTG_UNDEF:
return newVariant(b, self)
else:
bcf_destroy(b)
- raise StopIteration
+ if ret == -1: # end-of-file
+ raise StopIteration
+ else:
+ raise Exception("error parsing variant with `htslib::bcf_read` error-code: %d" % (b.errcode))
+
property samples:
"list of samples pulled from the VCF."
@@ -1026,9 +1030,8 @@ cdef class Genotypes(object):
The last column indicates phased (1 is phased, 0 is unphased).
The other columns indicate the alleles, e.g. [0, 1, 1] is 0|1.
"""
- cdef np.int16_t* to_return = <np.int16_t *>stdlib.malloc(sizeof(np.int16_t)
- * self.n_samples
- * (self.ploidy+1))
+ cdef np.ndarray[np.int16_t, ndim=2] to_return = np.zeros((self.n_samples, self.ploidy + 1),
+ dtype=np.int16)
cdef int ind
cdef int allele
@@ -1036,18 +1039,10 @@ cdef class Genotypes(object):
for ind in range(self.n_samples):
for allele in range(self.ploidy):
- to_return[ind * p + allele] = (self._raw[ind * self.ploidy + allele] >> 1) - 1
- to_return[ind * p + self.ploidy] = (self._raw[ind * self.ploidy + 1] & 1) == 1
+ to_return[ind, allele] = (self._raw[ind * self.ploidy + allele] >> 1) - 1
+ to_return[ind, self.ploidy] = (self._raw[ind * self.ploidy + 1] & 1) == 1
- cdef np.npy_intp shape[2]
- shape[0] = self.n_samples
- shape[1] = self.ploidy + 1
- return np.PyArray_SimpleNewFromData(
- 2,
- shape,
- np.NPY_INT16,
- to_return
- )
+ return to_return
def __getitem__(self, int i):
## return the Allele objects for the i'th sample.
@@ -1434,20 +1429,32 @@ cdef class Variant(object):
cdef np.ndarray[np.float32_t, mode="c"] afloat
cdef np.ndarray[np.int32_t, mode="c"] aint
+ cdef char *bytesp
- cdef int size = data.shape[0]
- if len((<object>data).shape) > 1:
- size *= data.shape[1]
-
+ cdef int size
cdef int ret
if np.issubdtype(data.dtype, np.signedinteger) or np.issubdtype(data.dtype, np.unsignedinteger):
+ size = data.shape[0]
+ if len((<object>data).shape) > 1:
+ size *= data.shape[1]
aint = data.astype(np.int32).reshape((size,))
ret = bcf_update_format_int32(self.vcf.hdr, self.b, to_bytes(name), &aint[0], size)
elif np.issubdtype(data.dtype, np.floating):
+ size = data.shape[0]
+ if len((<object>data).shape) > 1:
+ size *= data.shape[1]
afloat = data.astype(np.float32).reshape((size,))
ret = bcf_update_format_float(self.vcf.hdr, self.b, to_bytes(name), &afloat[0], size)
+ elif np.issubdtype(data.dtype, np.bytes_):
+ if len((<object>data).shape) > 1:
+ raise Exception("Setting string type format fields with number>1 are currently not supported")
+ if not data.flags['C_CONTIGUOUS']:
+ data = np.ascontiguousarray(data)
+ size = data.nbytes
+ bytesp = <char *>data.data
+ ret = bcf_update_format(self.vcf.hdr, self.b, to_bytes(name), bytesp, size, BCF_HT_STR)
else:
- raise Exception("format: currently only float and int numpy arrays are supported. got %s", data.dtype)
+ raise Exception("format: currently only float, int and string (fixed length ASCII np.bytes_) numpy arrays are supported. got %s", data.dtype)
if ret < 0:
raise Exception("error (%d) setting format with: %s" % (ret, data[:100]))
@@ -1845,10 +1852,22 @@ cdef class Variant(object):
return self.INFO.get(b'SVTYPE') is not None
property CHROM:
- "chromosome of the variant."
+ """Chromosome of the variant."""
def __get__(self):
return bcf_hdr_id2name(self.vcf.hdr, self.b.rid).decode()
+ def __set__(self, new_chrom):
+ new_rid = bcf_hdr_id2int(self.vcf.hdr, BCF_DT_CTG, new_chrom.encode())
+ if new_rid < 0:
+ self.vcf.add_to_header("##contig=<ID={}>".format(new_chrom))
+ new_rid = bcf_hdr_id2int(self.vcf.hdr, BCF_DT_CTG, new_chrom.encode())
+ if new_rid < 0:
+ raise ValueError("Unable to add {} to CHROM".format(new_chrom))
+ sys.stderr.write(
+ "[cyvcf2]: added new contig {} to header".format(new_chrom)
+ )
+ self.b.rid = new_rid
+
property var_type:
"type of variant (snp/indel/sv)"
def __get__(self):
@@ -1913,7 +1932,7 @@ cdef class Variant(object):
property FILTER:
"""the value of FILTER from the VCF field.
- a value of PASS in the VCF will give None for this function
+ a value of PASS or '.' in the VCF will give None for this function
"""
def __get__(self):
cdef int i
@@ -2047,7 +2066,8 @@ cdef class INFO(object):
ret = bcf_update_info_flag(self.hdr, self.b, to_bytes(key), b"", int(value))
if ret != 0:
- raise Exception("not able to set flag", key, value, ret)
+ raise Exception("not able to set: %s -> %s (%d)" % (key, value, ret))
+
return
cdef int32_t iint
cdef float ifloat
@@ -2225,6 +2245,22 @@ cdef class Writer(VCF):
path to file
tmpl: VCF
a template to use to create the output header.
+ mode: str
+ | Mode to use for writing the file. If ``None`` (default) is given, the mode is
+ inferred from the filename extension. If stdout (``"-"``) is provided for ``fname``
+ and ``mode`` is left at default, uncompressed VCF will be produced.
+ | Valid values are:
+ | - ``"wbu"``: uncompressed BCF
+ | - ``"wb"``: compressed BCF
+ | - ``"wz"``: compressed VCF
+ | - ``"w"``: uncompressed VCF
+ | Compression level can also be indicated by adding a single integer to one of
+ the compressed modes (e.g. ``"wz4"`` for VCF with compressions level 4).
+
+ Note
+ ----
+ File extensions ``.bcf`` and ``.bcf.gz`` will both return compressed BCF. If you
+ want uncompressed BCF you must explicitly provide the appropriate ``mode``.
Returns
-------
@@ -2236,7 +2272,8 @@ cdef class Writer(VCF):
cdef bint header_written
cdef const bcf_hdr_t *ohdr
- def __init__(Writer self, fname, VCF tmpl, mode="w"):
+ def __init__(Writer self, fname, VCF tmpl, mode=None):
+ mode = self._infer_file_mode(fname, mode)
self._open_htsfile(fname, mode)
bcf_hdr_sync(tmpl.hdr)
self.ohdr = tmpl.hdr
@@ -2244,6 +2281,28 @@ cdef class Writer(VCF):
bcf_hdr_sync(self.hdr)
self.header_written = False
+ @staticmethod
+ def _infer_file_mode(fname, mode=None):
+ if mode is not None:
+ return mode
+
+ from_path = isinstance(fname, (basestring, Path))
+ if not from_path:
+ return "w"
+
+ fname = str(fname)
+ is_compressed = fname.endswith(".gz")
+ fmt_idx = -2 if is_compressed else -1
+ file_fmt = fname.split(".")[fmt_idx]
+ # bcftools output write mode chars - https://github.com/samtools/bcftools/blob/76392b3014de70b7fa5c6b5c9d5bc47361951770/version.c#L64-L70
+ inferred_mode = "w"
+ if file_fmt == "bcf":
+ inferred_mode += "b"
+ if is_compressed and file_fmt == "vcf":
+ inferred_mode += "z"
+
+ return inferred_mode
+
@classmethod
def from_string(Writer cls, fname, header_string, mode="w"):
cdef Writer self = Writer.__new__(Writer)
=====================================
cyvcf2/tests/test_reader.py
=====================================
@@ -67,18 +67,6 @@ def test_format_str():
f = next(vcf).format("RULE")
assert list(f) == ['F2,F3,F4', 'G2,G3,G4']
-"""
-def test_write_format_str():
- vcf = VCF(os.path.join(HERE, "test-format-string.vcf"))
- wtr = Writer("x.vcf", vcf)
- variant = next(vcf)
- variant.set_format("TSTRING", np.array(["asdfxx","35\0"]))
- wtr.write_record(variant)
- wtr.close()
- assert "asdfxx" in str(variant), str(variant)
- assert "35" in str(variant)
- """
-
def test_missing_samples():
samples = ['101976-101976', 'sample_not_in_vcf']
vcf = VCF(VCF_PATH, gts012=True, samples=samples)
@@ -669,6 +657,31 @@ def test_set_format_int3():
assert np.allclose(v.format("P3"), exp)
+def test_set_format_str_bytes_second_longer():
+ vcf = VCF('{}/test-format-string.vcf'.format(HERE))
+ assert vcf.add_format_to_header(dict(ID="STR", Number=1, Type="String", Description="String example")) == 0
+ v = next(vcf)
+
+ v.set_format("STR", np.array([b'foo', b'barbaz']))
+ assert np.all(v.format('STR') == np.array(['foo', 'barbaz']))
+
+def test_set_format_str_bytes_first_longer():
+ vcf = VCF('{}/test-format-string.vcf'.format(HERE))
+ assert vcf.add_format_to_header(dict(ID="STR", Number=1, Type="String", Description="String example")) == 0
+ v = next(vcf)
+
+ v.set_format("STR", np.array([b'foobar', b'baz']))
+ assert np.all(v.format('STR') == np.array(['foobar', 'baz']))
+
+def test_set_format_str_bytes_number3():
+ # Confirm currently not supported
+ vcf = VCF('{}/test-format-string.vcf'.format(HERE))
+ assert vcf.add_format_to_header(dict(ID="STR", Number=3, Type="String", Description="String example")) == 0
+ v = next(vcf)
+
+ contents = np.array([[b'foo', b'barbaz', b'biz'], [b'blub', b'bloop', b'blop']])
+ assert_raises(Exception, v.set_format, "STR", contents)
+
def test_set_gts():
vcf = VCF('{}/test-format-string.vcf'.format(HERE))
v = next(vcf)
@@ -881,6 +894,32 @@ def test_set_pos():
assert v.start == 22
assert v.POS == 23
+def test_set_chrom_when_contig_not_in_header():
+ test_vcf = '{}/test-strict-gt-option-flag.vcf.gz'.format(HERE)
+ new_chrom = "NEW"
+ vcf = VCF(test_vcf, gts012=False)
+ original_seqnames = vcf.seqnames
+ assert new_chrom not in original_seqnames
+ v = next(vcf)
+
+ v.CHROM = new_chrom
+ assert v.CHROM == new_chrom
+ expected_seqnames = sorted(original_seqnames + [new_chrom])
+ assert vcf.seqnames == expected_seqnames
+
+def test_set_chrom_after_contig_is_added_to_header():
+ test_vcf = '{}/test-strict-gt-option-flag.vcf.gz'.format(HERE)
+ new_chrom = "NEW"
+ vcf = VCF(test_vcf, gts012=False)
+ original_seqnames = vcf.seqnames
+ vcf.add_to_header("##contig=<ID={},length=15>".format(new_chrom))
+ expected_seqnames = sorted(original_seqnames + [new_chrom])
+ assert vcf.seqnames == expected_seqnames
+ v = next(vcf)
+
+ v.CHROM = new_chrom
+ assert v.CHROM == new_chrom
+
def test_set_qual():
v = VCF(VCF_PATH)
variant = next(v)
=====================================
cyvcf2/tests/test_writer.py
=====================================
@@ -0,0 +1,109 @@
+from io import StringIO
+
+from ..cyvcf2 import Writer
+
+try:
+ from pathlib import Path
+except ImportError:
+ from pathlib2 import Path # python 2 backport
+
+
+class TestFileModeInference:
+ def test_defaultModeWithVcfFname_returnsUncompressedVcf(self):
+ fname = "test.vcf"
+ mode = None
+
+ actual = Writer._infer_file_mode(fname, mode)
+ expected = "w"
+
+ assert actual == expected
+
+ def test_defaultModeWithBcfFname_returnsCompressedBcf(self):
+ fname = "test.bcf"
+ mode = None
+
+ actual = Writer._infer_file_mode(fname, mode)
+ expected = "wb"
+
+ assert actual == expected
+
+ def test_defaultModeWithBcfFnameAndUncompressedMode_returnsUncompressedBcf(self):
+ fname = "test.bcf"
+ mode = "wbu"
+
+ actual = Writer._infer_file_mode(fname, mode)
+ expected = mode
+
+ assert actual == expected
+
+ def test_defaultModeWithCompressedBcfFname_returnsCompressedBcf(self):
+ fname = "test.bcf.gz"
+ mode = None
+
+ actual = Writer._infer_file_mode(fname, mode)
+ expected = "wb"
+
+ assert actual == expected
+
+ def test_defaultModeWithCompressedVcfFname_returnsCompressedVcf(self):
+ fname = "test.vcf.gz"
+ mode = None
+
+ actual = Writer._infer_file_mode(fname, mode)
+ expected = "wz"
+
+ assert actual == expected
+
+ def test_defaultModeWithStdOut_returnsUncompressedVcf(self):
+ fname = "-"
+ mode = None
+
+ actual = Writer._infer_file_mode(fname, mode)
+ expected = "w"
+
+ assert actual == expected
+
+ def test_defaultModeWithNonVcfName_returnsUncompressedVcf(self):
+ fname = "foo"
+ mode = None
+
+ actual = Writer._infer_file_mode(fname, mode)
+ expected = "w"
+
+ assert actual == expected
+
+ def test_defaultModeWithIntFname_returnsUncompressedVcf(self):
+ fname = 1
+ mode = None
+
+ actual = Writer._infer_file_mode(fname, mode)
+ expected = "w"
+
+ assert actual == expected
+
+ def test_defaultModeWithHandle_returnsUncompressedVcf(self):
+ fname = StringIO()
+ mode = None
+
+ actual = Writer._infer_file_mode(fname, mode)
+ expected = "w"
+
+ assert actual == expected
+
+ def test_defaultModeWithPosixPath_returnsUncompressedVcf(self):
+ fname = Path("test.vcf")
+ mode = None
+
+ actual = Writer._infer_file_mode(fname, mode)
+ expected = "w"
+
+ assert actual == expected
+
+ def test_explicitMode_doesNotInferFromFname(self):
+ fname = "test.vcf.gz"
+ mode = "wb4"
+
+ actual = Writer._infer_file_mode(fname, mode)
+ expected = "wb4"
+
+ assert actual == expected
=====================================
debian/changelog
=====================================
@@ -1,3 +1,10 @@
+cyvcf2 (0.20.6-1) unstable; urgency=medium
+
+ * Team upload.
+ * New upstream version
+
+ -- Andreas Tille <tille at debian.org> Fri, 18 Sep 2020 08:19:58 +0200
+
cyvcf2 (0.20.1-1) unstable; urgency=medium
* Team Upload.
=====================================
docs/source/index.rst
=====================================
@@ -59,10 +59,12 @@ See the :ref:`api` for detailed documentation, but the most common usage is summ
Modifying Existing Records
==========================
+.. this example is also in the writing.rst page
+
`cyvcf2` is optimized for fast reading and extraction from existing files.
However, it also offers some means of modifying existing VCFs. Here, we
-show an example of how to annotate variants with the genes that they overlap.
-
+show an example of how to modify the INFO field at a locus to annotate
+variants with the genes that they overlap.
.. code-block:: python
@@ -88,6 +90,8 @@ show an example of how to annotate variants with the genes that they overlap.
w.close(); vcf.close()
+More info on writing vcfs can be found :doc:`here <writing>`
+
Setting Genotyping Strictness
=============================
@@ -133,13 +137,20 @@ Tests can be run with:
python setup.py test
+Known Limitations
+=================
+* `cyvcf2` currently does not support reading VCFs encoded with UTF-8 with non-ASCII characters in the contents of string-typed FORMAT fields.
+
+For limitations on writing VCFs, see :ref:`here <Limitations with writing>`
+
See Also
========
-Pysam also [has a cython wrapper to htslib](https://github.com/pysam-developers/pysam/blob/master/pysam/cbcf.pyx) and one block of code here is taken directly from that library. But, the optimizations that we want for gemini are very specific so we have chosen to create a separate project.
+Pysam also `has a cython wrapper to htslib <https://github.com/pysam-developers/pysam/blob/master/pysam/cbcf.pyx>`_ and one block of code here is taken directly from that library. But, the optimizations that we want for gemini are very specific so we have chosen to create a separate project.
.. toctree::
- :maxdepth: 2
+ :maxdepth: 1
docstrings
+ writing
=====================================
docs/source/writing.rst
=====================================
@@ -0,0 +1,93 @@
+Modifying Existing VCFs
+=======================
+
+`cyvcf2` is optimized for fast reading and extraction from existing files.
+However, it also offers some means of modifying existing VCFs.
+
+Modifying the INFO field
+------------------------
+
+.. this is the same example as on the index.rst page
+
+Here, we show an example of how to modify the INFO field at a locus to annotate
+variants with the genes that they overlap.
+
+.. code-block:: python
+
+ from cyvcf2 import VCF, Writer
+ vcf = VCF(VCF_PATH)
+ # adjust the header to contain the new field
+ # the keys 'ID', 'Description', 'Type', and 'Number' are required.
+ vcf.add_info_to_header({'ID': 'gene', 'Description': 'overlapping gene',
+ 'Type':'Character', 'Number': '1'})
+
+ # create a new vcf Writer using the input vcf as a template.
+ fname = "out.vcf"
+ w = Writer(fname, vcf)
+
+ for v in vcf:
+ # The get_gene_intersections function is not shown.
+ # This could be any operation to find intersections
+ # or any manipulation required by the user.
+ genes = get_gene_intersections(v)
+ if genes is not None:
+ v.INFO["gene"] = ",".join(genes)
+ w.write_record(v)
+
+ w.close(); vcf.close()
+
+Modifying genotypes and the FORMAT field
+----------------------------------------
+
+Here is an example where we filter some calls from records in a VCF. This demonstrates
+how to add to the FORMAT field and how to modify genotypes at a locus.
+
+.. code-block:: python
+
+ from cyvcf2 import VCF, Writer
+ vcf = VCF(VCF_PATH)
+ # adjust the header to contain the new field
+ # the keys 'ID', 'Description', 'Type', and 'Number' are required.
+ vcf.add_format_to_header({
+ 'ID': 'FILTER_CODE',
+ 'Description': 'Numeric code for filtering reason',
+ 'Type': 'Integer',
+ 'Number': '1'
+ })
+
+ # create a new vcf Writer using the input vcf as a template.
+ fname = "out.vcf"
+ w = Writer(fname, vcf)
+
+ for v in vcf:
+ # The filter_samples function is not shown.
+ # This could be any manipulation required by the user.
+ # Since we specified the FILTER_CODE format field is an Integer
+ # with Number=1, reasons must be an n x 1 numpy array of integers
+ # where n is the number of samples.
+ indicies, reasons = filter_samples(v)
+ if indicies:
+ # add the reasons array to the format dictionary at this locus
+ v.set_format('FILTER_CODE', reasons)
+ for index in indicies:
+ # overwrite the genotypes of each filtered locus to be nocalls
+ # Note: until the reassignment to v.genotypes below, this
+ # leaves the v Variant object in an inconsistent state
+ v.genotypes[index] = [-1]*v.ploidy + [False]
+ # it is necessary to reassign the genotypes field
+ # so that the v Variant object reprocess it and future calls
+ # to functions like v.genotype.array() properly reflect
+ # the changed genotypes
+ v.genotypes = v.genotypes
+
+ w.write_record(v)
+
+ w.close(); vcf.close()
+
+.. _Limitations with writing:
+
+Known Limitations with Writing VCFs
+-----------------------------------
+* `cyvcf2` currently does not support writing VCFs encoded with UTF-8 with non-ASCII characters in the contents of string-typed FORMAT fields.
+* `cyvcf2` currently does not support writing string type format fields with number>1.
+
View it on GitLab: https://salsa.debian.org/med-team/cyvcf2/-/compare/51010340709e9f1595eb9f40eae65ab61774bc15...821fa640498e5928b3d3384339716ae3a74d0c2f
--
View it on GitLab: https://salsa.debian.org/med-team/cyvcf2/-/compare/51010340709e9f1595eb9f40eae65ab61774bc15...821fa640498e5928b3d3384339716ae3a74d0c2f
You're receiving this email because of your account on salsa.debian.org.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/debian-med-commit/attachments/20200918/fb335ba2/attachment-0001.html>
More information about the debian-med-commit
mailing list