Source code for vcfparser.meta_header_parser
import re
import shlex
from collections import OrderedDict
[docs]class MetaDataParser:
"""Parses a meta lines of the vcf files."""
def __init__(self, header_file):
self.header_file = header_file
self.infos_ = []
self.filters_ = []
self.contig = []
self.format_ = []
self.alt_ = []
self.other_lines = []
self.fileformat = None
self.reference = None
self.sample_names = []
self.is_gvcf = False
self.gvcf_blocks = []
self.record_keys = []
# to write of header lines only
self.raw_meta_data = ''
self.format_pattern = re.compile(r'''\#\#FORMAT=<
ID=(?P<id>.+),\s*
Number=(?P<number>-?\d+|\.|[AGR]),\s*
Type=(?P<type>.+),\s*
Description="(?P<desc>.*)"
>''', re.VERBOSE)
self.meta_pattern = re.compile(r'''##(?P<key>.+?)=(?P<val>.+)''')
[docs] @staticmethod
def split_to_dict(string):
string = string.lstrip('<').rstrip('>')
splitter = shlex.shlex(string, posix=True)
splitter.whitespace_split = True
splitter.whitespace = ","
tags_dict = dict(pair.split("=") for pair in splitter)
return tags_dict
@staticmethod
def _parse_gvcf_block(lines):
"""extract the GVCF blocks"""
# e.g: ##GVCFBlock55-56=minGQ=55(inclusive),maxGQ=56(exclusive)
# converts to {'minGQ': '55(inclusive)', 'maxGQ': '56(exclusive)', 'Block': '55-56'}
gvcf_block = re.search("##GVCFBlock(.*?)=", lines, 0).group(1)
# update tags_dict
to_replace = '##GVCFBlock' + gvcf_block + "="
string = lines.rstrip(">").replace(to_replace, "")
tags_dict = dict(pair.split("=") for pair in string.split(','))
tags_dict["Block"] = gvcf_block
return tags_dict
[docs] def parse_lines(self):
"""Parse a vcf metadataline"""
for line in self.header_file:
self.raw_meta_data += line
if line.startswith('##'):
line = line.rstrip()
line_info = line[2:].split("=", 1)
if line_info[0] == "fileformat":
try:
self.fileformat = line_info[1]
except IndexError:
raise SyntaxError("fileformat must have a value")
if line_info[0] == "reference":
try:
self.reference = line_info[1]
except IndexError:
raise SyntaxError("Refrence value is not provided")
elif line_info[0] == "INFO":
self.infos_.append(self.split_to_dict(line_info[1]))
elif line_info[0] == "FILTER":
self.filters_.append(self.split_to_dict(line_info[1]))
elif line_info[0] == "contig":
self.contig.append(self.split_to_dict(line_info[1]))
elif line_info[0] == "FORMAT":
match = self.format_pattern.match(line)
if not match:
raise SyntaxError(
"One of the FORMAT lines is malformed: {0}".format(line)
)
matches = [
match.group("id"),
match.group("number"),
match.group("type"),
match.group("desc"),
]
form_keys = ["ID", "Number", "Type", "Description"]
self.format_.append(dict(list(zip(form_keys, matches))))
elif line_info[0] == "ALT":
self.alt_.append(self.split_to_dict(line_info[1]))
elif line_info[0].startswith('GVCF'):
self.is_gvcf = True
self.gvcf_blocks.append(self._parse_gvcf_block(line))
else:
match = self.meta_pattern.match(line)
if not match:
raise SyntaxError(
"One of the meta data lines is malformed: {0}".format(line)
)
self.other_lines.append({match.group("key"): match.group("val")})
else:
self.record_keys = line.lstrip(r"#").strip('\n').split('\t')
self.sample_names = self.record_keys[9:] if len(self.record_keys) > 9 else None
return self