#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
nmrstarlib.nmrstarlib
~~~~~~~~~~~~~~~~~~~~~
This module provides the :class:`~nmrstarlib.nmrstarlib.StarFile` superclass
and :class:`~nmrstarlib.nmrstarlib.NMRStarFile` and :class:`~nmrstarlib.nmrstarlib.CIFFile`
subclasses that store data from a single NMR-STAR file and CIF file in the form of an
:py:class:`~collections.OrderedDict`. Data can be accessed directly from the
:class:`~nmrstarlib.nmrstarlib.StarFile` instance using bracket
accessors.
The NMR-STAR format is a hierarchical dictionary containing data on
NMR experiments. The data is divided into a series of "saveframes"
which each contain a number of key-value pairs and "loops".
Each "saveframe" has a unique name, which is used as the key in
the dictionary, corresponding to another dictionary containing the
information in the "saveframe". Since "loops" in NMR-Star format do
not have unique names, the keys for them inside the "saveframe" dictionary
are simply "loop_0", "loop_1", etc.
The CIF format is another STAR-derived format that is similar to NMR-STAR,
i.e. it contains key-value pairs of data and contains "loops" but does not have
"saveframe" data organization.
"""
from __future__ import print_function, division
from collections import OrderedDict
import sys
import os
import io
import pprint
import json
try:
from .cbmrblex import bmrblex
except ImportError:
from .bmrblex import bmrblex
BMRB_REST = "http://rest.bmrb.wisc.edu/bmrb/NMR-STAR3/"
PDB_REST = "https://files.rcsb.org/view/"
VERBOSE = False
NMRSTAR_VERSION = "3"
NMRSTAR_CONSTANTS = {}
RESONANCE_CLASSES = {}
SPECTRUM_DESCRIPTIONS = {}
[docs]def update_constants(nmrstar2cfg="", nmrstar3cfg="", resonance_classes_cfg="", spectrum_descriptions_cfg=""):
"""Update constant variables.
:return: None
:rtype: :py:obj:`None`
"""
nmrstar_constants = {}
resonance_classes = {}
spectrum_descriptions = {}
this_directory = os.path.dirname(__file__)
nmrstar2_config_filepath = os.path.join(this_directory, "conf/constants_nmrstar2.json")
nmrstar3_config_filepath = os.path.join(this_directory, "conf/constants_nmrstar3.json")
resonance_classes_config_filepath = os.path.join(this_directory, "conf/resonance_classes.json")
spectrum_descriptions_config_filepath = os.path.join(this_directory, "conf/spectrum_descriptions.json")
with open(nmrstar2_config_filepath, "r") as nmrstar2config, open(nmrstar3_config_filepath, "r") as nmrstar3config:
nmrstar_constants["2"] = json.load(nmrstar2config)
nmrstar_constants["3"] = json.load(nmrstar3config)
with open(resonance_classes_config_filepath, "r") as config:
resonance_classes.update(json.load(config))
with open(spectrum_descriptions_config_filepath, "r") as config:
spectrum_descriptions.update(json.load(config))
if nmrstar2cfg:
with open(nmrstar2cfg, "r") as nmrstar2config:
nmrstar_constants["2"].update(json.load(nmrstar2config))
if nmrstar3cfg:
with open(nmrstar2cfg, "r") as nmrstar3config:
nmrstar_constants["3"].update(json.load(nmrstar3config))
if resonance_classes_cfg:
with open(nmrstar2cfg, "r") as config:
resonance_classes.update(json.load(config))
if spectrum_descriptions_cfg:
with open(spectrum_descriptions_cfg, "r") as config:
spectrum_descriptions.update(json.load(config))
NMRSTAR_CONSTANTS.update(nmrstar_constants)
RESONANCE_CLASSES.update(resonance_classes)
SPECTRUM_DESCRIPTIONS.update(spectrum_descriptions)
update_constants()
[docs]class StarFile(OrderedDict):
"""StarFile class that stores the data from a single NMR-STAR or CIF file in the form of an
:py:class:`~collections.OrderedDict`."""
[docs] def __init__(self, *args, **kwds):
"""`StarFile` initializer. Leave `frame_categories` as :py:obj:`None` to
read everything. Otherwise it can be a list of saveframe categories to read, skipping the rest.
:param str source: Source `StarFile` instance was created from - local file or URL address.
"""
super(StarFile, self).__init__(*args, **kwds)
[docs] @staticmethod
def read(filehandle, source):
"""Read data into a :class:`~nmrstarlib.nmrstarlib.StarFile` instance.
:param filehandle: file-like object.
:type filehandle: :py:class:`io.TextIOWrapper`, :py:class:`gzip.GzipFile`,
:py:class:`bz2.BZ2File`, :py:class:`zipfile.ZipFile`
:param str source: String indicating where file is coming from (path, url).
:return: subclass of :class:`~nmrstarlib.nmrstarlib.StarFile`.
:rtype: :class:`~nmrstarlib.nmrstarlib.NMRStarFile` or :class:`~nmrstarlib.nmrstarlib.CIFFile`
"""
input_str = filehandle.read()
nmrstar_str = StarFile._is_nmrstar(input_str)
cif_str = StarFile._is_cif(input_str)
json_str = StarFile._is_json(input_str)
if not input_str:
pass
elif nmrstar_str:
starfile = NMRStarFile(source)
starfile._build_file(nmrstar_str)
filehandle.close()
return starfile
elif cif_str:
starfile = CIFFile(source)
starfile._build_file(cif_str)
filehandle.close()
return starfile
elif json_str:
if u"save_" in json_str:
starfile = NMRStarFile(source)
starfile.update(json.loads(json_str, object_pairs_hook=OrderedDict))
starfile.id = starfile[u"data"]
filehandle.close()
return starfile
elif u"entry.id" in json_str:
starfile = CIFFile(source)
starfile.update(json.loads(json_str, object_pairs_hook=OrderedDict))
starfile.id = starfile[u"data"]
filehandle.close()
return starfile
else:
raise TypeError("Unknown file format")
else:
raise TypeError("Unknown file format")
[docs] def write(self, filehandle, file_format):
"""Write :class:`~nmrstarlib.nmrstarlib.StarFile` data into file.
:param filehandle: file-like object.
:type filehandle: :py:class:`io.TextIOWrapper`
:param str file_format: Format to use to write data: `nmrstar`, `cif`, or `json`.
:return: None
:rtype: :py:obj:`None`
"""
try:
if file_format == "json":
json_str = self._to_json()
filehandle.write(json_str)
elif file_format == "nmrstar" and isinstance(self, NMRStarFile):
nmrstar_str = self._to_star()
filehandle.write(nmrstar_str)
elif file_format == "cif" and isinstance(self, CIFFile):
cif_str = self._to_star()
filehandle.write(cif_str)
else:
raise TypeError("Unknown file format.")
except IOError:
raise IOError('"filehandle" parameter must be writable.')
filehandle.close()
[docs] def writestr(self, file_format):
"""Write :class:`~nmrstarlib.nmrstarlib.StarFile` data into string.
:param str file_format: Format to use to write data: `nmrstar`, `cif`, or `json`.
:return: String representing the :class:`~nmrstarlib.nmrstarlib.StarFile` instance.
:rtype: :py:class:`str`
"""
try:
if file_format == "json":
json_str = self._to_json()
return json_str
elif file_format == "nmrstar" and isinstance(self, NMRStarFile):
nmrstar_str = self._to_star()
return nmrstar_str
elif file_format == "cif" and isinstance(self, CIFFile):
cif_str = self._to_star()
return cif_str
else:
raise TypeError("Unknown file format.")
except IOError:
raise IOError('"filehandle" parameter must be writable.')
[docs] def print_file(self, f=sys.stdout, file_format="", tw=3):
"""Print :class:`~nmrstarlib.nmrstarlib.StarFile` into a file or stdout.
:param io.StringIO f: writable file-like stream.
:param str file_format: Format to use: `nmrstar`, `cif`, or `json`.
:param int tw: Tab width.
:return: None
:rtype: :py:obj:`None`
"""
raise NotImplementedError("Subclass must implement print method.")
[docs] def _to_json(self):
"""Save :class:`~nmrstarlib.nmrstarlib.StarFile` into JSON string.
:return: JSON string.
:rtype: :py:class:`str`
"""
return json.dumps(self, sort_keys=False, indent=4)
[docs] def _to_star(self):
"""Save :class:`~nmrstarlib.nmrstarlib.StarFile` into NMR-STAR or CIF formatted string.
:return: NMR-STAR string.
:rtype: :py:class:`str`
"""
star_str = io.StringIO()
self.print_file(star_str)
return star_str.getvalue()
[docs] @staticmethod
def _is_nmrstar(string):
"""Test if input string is in NMR-STAR format.
:param string: Input string.
:type string: :py:class:`str` or :py:class:`bytes`
:return: Input string if in NMR-STAR format or False otherwise.
:rtype: :py:class:`str` or :py:obj:`False`
"""
if (string[0:5] == u"data_" and u"save_" in string) or (string[0:5] == b"data_" and b"save_" in string):
return string
return False
[docs] @staticmethod
def _is_cif(string):
"""Test if input string is in CIF format.
:param string: Input string.
:type string: :py:class:`str` or :py:class:`bytes`
:return: Input string if in CIF format or False otherwise.
:rtype: :py:class:`str` or :py:obj:`False`
"""
if (string[0:5] == u"data_" and u"_entry.id" in string) or (string[0:5] == b"data_" and b"_entry.id" in string):
return string
return False
[docs] @staticmethod
def _is_json(string):
"""Test if input string is in JSON format.
:param string: Input string.
:type string: :py:class:`str` or :py:class:`bytes`
:return: Input string if in JSON format or False otherwise.
:rtype: :py:class:`str` or :py:obj:`False`
"""
try:
if isinstance(string, bytes):
string = string.decode("utf-8")
json.loads(string)
elif isinstance(string, str):
json.loads(string)
else:
raise TypeError("Expecting <class 'str'> or <class 'bytes'>, but {} was passed".format(type(string)))
return string
except ValueError:
return False
[docs]class NMRStarFile(StarFile):
"""NMRStarFile class that stores the data from a single NMR-STAR file in the form of an
:py:class:`~collections.OrderedDict`."""
[docs] def __init__(self, source="", frame_categories=None, *args, **kwds):
"""`NMRStarFile` initializer. Leave `frame_categories` as :py:obj:`None` to
read everything. Otherwise it can be a list of saveframe categories to read, skipping the rest.
:param str source: Source `StarFile` instance was created from - local file or URL address.
:param list frame_categories: List of saveframe names.
"""
super(NMRStarFile, self).__init__(*args, **kwds)
self.source = source
self._frame_categories = frame_categories
self.id = ""
[docs] def _build_file(self, nmrstar_str):
"""Build :class:`~nmrstarlib.nmrstarlib.NMRStarFile` object.
:param nmrstar_str: NMR-STAR-formatted string.
:type nmrstar_str: :py:class:`str` or :py:class:`bytes`
:return: instance of :class:`~nmrstarlib.nmrstarlib.NMRStarFile`.
:rtype: :class:`~nmrstarlib.nmrstarlib.NMRStarFile`
"""
odict = self
comment_count = 0
lexer = bmrblex(nmrstar_str)
token = next(lexer)
while token != u"":
try:
if token[0:5] == u"save_":
name = token
frame = self._build_saveframe(lexer)
if frame:
odict[name] = frame
elif token[0:5] == u"data_":
self.id = token[5:]
odict[u"data"] = self.id
elif token.lstrip().startswith(u"#"):
odict[u"comment_{}".format(comment_count)] = token
comment_count += 1
else:
print("Error: Invalid token {}".format(token), file=sys.stderr)
print("In _build_starfile try block", file=sys.stderr)
raise InvalidToken("{}".format(token))
except IndexError:
print("Error: Invalid token {}".format(token), file=sys.stderr)
print("In _build_starfile except block", file=sys.stderr)
raise
finally:
token = next(lexer)
return self
[docs] def _build_saveframe(self, lexer):
"""Build NMR-STAR file saveframe.
:param lexer: instance of the lexical analyzer.
:type lexer: :func:`~nmrstarlib.bmrblex.bmrblex`
:return: Saveframe dictionary.
:rtype: :py:class:`collections.OrderedDict`
"""
odict = OrderedDict()
loop_count = 0
token = next(lexer)
while token != u"save_":
try:
if token[0] == u"_":
# This strips off the leading underscore of tagnames for readability
odict[token[1:]] = next(lexer)
# Skip the saveframe if it's not in the list of wanted categories
if self._frame_categories:
if token == "_Saveframe_category" and odict[token[1:]] not in self._frame_categories:
raise SkipSaveFrame()
elif token == u"loop_":
odict[u"loop_{}".format(loop_count)] = self._build_loop(lexer)
loop_count += 1
elif token.lstrip().startswith(u"#"):
continue
else:
print("Error: Invalid token {}".format(token), file=sys.stderr)
print("In _build_saveframe try block", file=sys.stderr)
raise InvalidToken("{}".format(token))
except IndexError:
print("Error: Invalid token {}".format(token), file=sys.stderr)
print("In _build_saveframe except block", file=sys.stderr)
raise
except SkipSaveFrame:
self._skip_saveframe(lexer)
odict = None
finally:
if odict is None:
token = u"save_"
else:
token = next(lexer)
return odict
[docs] def _build_loop(self, lexer):
"""Build saveframe loop.
:param lexer: instance of lexical analyzer.
:type lexer: :func:`~nmrstarlib.bmrblex.bmrblex`
:return: Fields and values of the loop.
:rtype: :py:class:`tuple`
"""
fields = []
values = []
token = next(lexer)
while token[0] == u"_":
fields.append(token[1:])
token = next(lexer)
while token != u"stop_":
values.append(token)
token = next(lexer)
assert float(len(values) / len(fields)).is_integer(), \
"Error in loop construction: number of fields must be equal to number of values."
values = [OrderedDict(zip(fields, values[i:i + len(fields)])) for i in range(0, len(values), len(fields))]
return fields, values
[docs] def _skip_saveframe(self, lexer):
"""Skip entire saveframe - keep emitting tokens until the end of saveframe.
:param lexer: instance of the lexical analyzer class.
:type lexer: :class:`~nmrstarlib.bmrblex.bmrblex`
:return: None
:rtype: :py:obj:`None`
"""
token = u""
while token != u"save_":
token = next(lexer)
[docs] def print_file(self, f=sys.stdout, file_format="nmrstar", tw=3):
"""Print :class:`~nmrstarlib.nmrstarlib.NMRStarFile` into a file or stdout.
:param io.StringIO f: writable file-like stream.
:param str file_format: Format to use: `nmrstar` or `json`.
:param int tw: Tab width.
:return: None
:rtype: :py:obj:`None`
"""
if file_format == "nmrstar":
for saveframe in self.keys():
if saveframe == u"data":
print(u"{}_{}\n".format(saveframe, self[saveframe]), file=f)
elif saveframe.startswith(u"comment"):
print(u"{}".format(self[saveframe]), file=f)
else:
print(u"{}".format(saveframe), file=f)
self.print_saveframe(saveframe, f, file_format, tw)
print(u"\nsave_\n\n", file=f)
elif file_format == "json":
print(self._to_json(), file=f)
[docs] def print_saveframe(self, sf, f=sys.stdout, file_format="nmrstar", tw=3):
"""Print saveframe into a file or stdout.
We need to keep track of how far over everything is tabbed. The "tab width"
variable tw does this for us.
:param str sf: Saveframe name.
:param io.StringIO f: writable file-like stream.
:param str file_format: Format to use: `nmrstar` or `json`.
:param int tw: Tab width.
:return: None
:rtype: :py:obj:`None`
"""
if file_format == "nmrstar":
for sftag in self[sf].keys():
# handle loops
if sftag[:5] == "loop_":
print(u"\n{}loop_".format(tw * u" "), file=f)
self.print_loop(sf, sftag, f, file_format, tw * 2)
print(u"\n{}stop_".format(tw * u" "), file=f)
# handle the NMR-Star "multiline string"
elif self[sf][sftag].endswith(u"\n"):
print(u"{}_{}".format(tw * u" ", sftag), file=f)
print(u";\n{};".format(self[sf][sftag]), file=f)
elif len(self[sf][sftag].split()) > 1:
# need to escape value with quotes (i.e. u"'{}'".format()) if value consists of two or more words
print(u"{}_{}\t {}".format(tw * u" ", sftag, u"'{}'".format(self[sf][sftag])), file=f)
else:
print(u"{}_{}\t {}".format(tw * u" ", sftag, self[sf][sftag]), file=f)
elif file_format == "json":
print(json.dumps(self[sf], sort_keys=False, indent=4), file=f)
[docs] def print_loop(self, sf, sftag, f=sys.stdout, file_format="nmrstar", tw=3):
"""Print loop into a file or stdout.
:param str sf: Saveframe name.
:param str sftag: Saveframe tag, i.e. field name.
:param io.StringIO f: writable file-like stream.
:param str file_format: Format to use: `nmrstar` or `json`.
:param int tw: Tab width.
:return: None
:rtype: :py:obj:`None`
"""
if file_format == "nmrstar":
# First print the fields
for field in self[sf][sftag][0]:
print(u"{}_{}".format(tw * u" ", field), file=f)
print(u"", file=f) # new line between fields and values
# Then print the values
for valuesdict in self[sf][sftag][1]:
# need to escape value with quotes (i.e. u"'{}'".format()) if value consists of two or more words
print(u"{}{}".format(tw * u" ", u" ".join([u"'{}'".format(value) if len(value.split()) > 1 else value for value
in valuesdict.values()])), file=f)
elif file_format == "json":
print(json.dumps(self[sf][sftag], sort_keys=False, indent=4), file=f)
[docs] def chem_shifts_by_residue(self, amino_acids=None, atoms=None, amino_acids_and_atoms=None, nmrstar_version="3"):
"""Organize chemical shifts by amino acid residue.
:param list amino_acids: List of amino acids three-letter codes.
:param list atoms: List of BMRB atom type codes.
:param dict amino_acids_and_atoms: Amino acid and its atoms key-value pairs.
:param str nmrstar_version: Version of NMR-STAR format to use for look up chemical shifts loop.
:return: List of OrderedDict per each chain
:rtype: :py:class:`list` of :py:class:`collections.OrderedDict`
"""
if (amino_acids_and_atoms and amino_acids) or (amino_acids_and_atoms and atoms):
raise ValueError('"amino_acids_and_atoms" parameter cannot be used simultaneously with '
'"amino_acids" and "atoms" parameters, one or another must be provided.')
chemshifts_loop = NMRSTAR_CONSTANTS[nmrstar_version]["chemshifts_loop"]
aminoacid_seq_id = NMRSTAR_CONSTANTS[nmrstar_version]["aminoacid_seq_id"]
aminoacid_code = NMRSTAR_CONSTANTS[nmrstar_version]["aminoacid_code"]
atom_code = NMRSTAR_CONSTANTS[nmrstar_version]["atom_code"]
chemshift_value = NMRSTAR_CONSTANTS[nmrstar_version]["chemshift_value"]
chains = []
for saveframe in self:
if saveframe == u"data" or saveframe.startswith(u"comment"):
continue
else:
for ind in self[saveframe].keys():
if ind.startswith(u"loop_"):
if list(self[saveframe][ind][0]) == chemshifts_loop:
chem_shifts_dict = OrderedDict()
for entry in self[saveframe][ind][1]:
residue_id = entry[aminoacid_seq_id]
chem_shifts_dict.setdefault(residue_id, OrderedDict())
chem_shifts_dict[residue_id][u"AA3Code"] = entry[aminoacid_code]
chem_shifts_dict[residue_id][u"Seq_ID"] = residue_id
chem_shifts_dict[residue_id][entry[atom_code]] = entry[chemshift_value]
chains.append(chem_shifts_dict)
if amino_acids_and_atoms:
for chem_shifts_dict in chains:
for aa_dict in list(chem_shifts_dict.values()):
if aa_dict[u"AA3Code"].upper() not in list(amino_acids_and_atoms.keys()):
chem_shifts_dict.pop(aa_dict[u"Seq_ID"])
else:
for resonance in list(aa_dict.keys()):
if resonance in (u"AA3Code", u"Seq_ID") or resonance.upper() in amino_acids_and_atoms[aa_dict[u"AA3Code"]]:
continue
else:
aa_dict.pop(resonance)
else:
if amino_acids:
for chem_shifts_dict in chains:
for aa_dict in list(chem_shifts_dict.values()):
if aa_dict[u"AA3Code"].upper() not in amino_acids:
chem_shifts_dict.pop(aa_dict[u"Seq_ID"])
if atoms:
for chem_shifts_dict in chains:
for aa_dict in chem_shifts_dict.values():
for resonance in list(aa_dict.keys()):
if resonance in (u"AA3Code", u"Seq_ID") or resonance.upper() in atoms:
continue
else:
aa_dict.pop(resonance)
return chains
[docs]class CIFFile(StarFile):
"""CIFFile class that stores the data from a single CIF file in the form of an
:py:class:`~collections.OrderedDict`."""
[docs] def __init__(self, source="", *args, **kwds):
"""`CIFFile` initializer.
:param str source: Source `CIFFile` instance was created from - local file or URL address.
"""
super(CIFFile, self).__init__(*args, **kwds)
self.source = source
self.id = ""
[docs] def _build_file(self, cif_str):
"""Build :class:`~nmrstarlib.nmrstarlib.CIFFile` object.
:param cif_str: NMR-STAR-formatted string.
:type cif_str: :py:class:`str` or :py:class:`bytes`
:return: instance of :class:`~nmrstarlib.nmrstarlib.CIFFile`.
:rtype: :class:`~nmrstarlib.nmrstarlib.CIFFile`
"""
odict = self
comment_count = 0
loop_count = 0
lexer = bmrblex(cif_str)
token = next(lexer)
while token != u"":
try:
if token[0:5] == u"data_":
self.id = token[5:]
self[u"data"] = self.id
elif token.lstrip().startswith(u"#"):
odict[u"comment_{}".format(comment_count)] = token
comment_count += 1
elif token[0] == u"_":
# This strips off the leading underscore of tagnames for readability
value = next(lexer)
odict[token[1:]] = value
elif token == u"loop_":
odict[u"loop_{}".format(loop_count)] = self._build_loop(lexer)
loop_count += 1
else:
print("Error: Invalid token {}".format(token), file=sys.stderr)
print("In _build_file try block", file=sys.stderr)
raise InvalidToken("{}".format(token))
except IndexError:
print("Error: Invalid token {}".format(token), file=sys.stderr)
print("In _build_file except block", file=sys.stderr)
raise
finally:
token = next(lexer)
return self
[docs] def _build_loop(self, lexer):
"""Build loop.
:param lexer: instance of lexical analyzer.
:type lexer: :func:`~nmrstarlib.bmrblex.bmrblex`
:return: Fields and values of the loop.
:rtype: :py:class:`tuple`
"""
fields = []
values = []
token = next(lexer)
while token[0] == u"_":
fields.append(token[1:])
token = next(lexer)
while not token.startswith(u"#"):
values.append(token)
token = next(lexer)
assert float(len(values)/len(fields)).is_integer(), \
"Error in loop construction: number of fields must be equal to number of values."
values = [OrderedDict(zip(fields, values[i:i + len(fields)])) for i in range(0, len(values), len(fields))]
return fields, values
[docs] def print_file(self, f=sys.stdout, file_format="cif", tw=0):
"""Print :class:`~nmrstarlib.nmrstarlib.CIFFile` into a file or stdout.
:param io.StringIO f: writable file-like stream.
:param str file_format: Format to use: `cif` or `json`.
:param int tw: Tab width.
:return: None
:rtype: :py:obj:`None`
"""
if file_format == "cif":
for key in self.keys():
if key == u"data":
print(u"{}_{}".format(key, self[key]), file=f)
elif key.startswith(u"comment"):
print(u"{}".format(self[key].strip()), file=f)
elif key.startswith(u"loop_"):
print(u"{}loop_".format(tw * u" "), file=f)
self.print_loop(key, f, file_format, tw)
else:
# handle the NMR-Star "multiline string"
if self[key].endswith(u"\n"):
print(u"{}_{}".format(tw * u" ", key), file=f)
print(u";{};".format(self[key]), file=f)
# need to escape value with quotes (i.e. u"'{}'".format()) if value consists of two or more words
elif len(self[key].split()) > 1:
print(u"{}_{}\t {}".format(tw * u" ", key, u"'{}'".format(self[key])), file=f)
else:
print(u"{}_{}\t {}".format(tw * u" ", key, self[key]), file=f)
elif file_format == "json":
print(self._to_json(), file=f)
[docs] def print_loop(self, loop_number, f=sys.stdout, file_format="cif", tw=0):
"""Print loop into a file or stdout.
:param io.StringIO f: writable file-like stream.
:param str file_format: Format to use: `cif` or `json`.
:param int tw: Tab width.
:return: None
:rtype: :py:obj:`None`
"""
if file_format == "cif":
# First print the fields
for field in self[loop_number][0]:
print(u"{}_{}".format(tw * u" ", field), file=f)
# Then print the values
for valuesdict in self[loop_number][1]:
# need to escape value with quotes (i.e. u"'{}'".format()) if value consists of two or more words
print(u"{}{}".format(tw * u" ", u" ".join([u"'{}'".format(value) if len(value.split()) > 1 else value for value
in valuesdict.values()])), file=f)
print(u"{}{}".format(tw * u" ", u"# "), file=f)
elif file_format == "json":
print(json.dumps(self[loop_number], sort_keys=False, indent=4), file=f)
[docs]class InvalidToken(Exception):
[docs] def __init__(self, value):
self.parameter = value
[docs] def __str__(self):
return repr(self.parameter)
[docs]class SkipSaveFrame(Exception):
[docs] def __init__(self):
pass
[docs] def __str__(self):
return "Skipping save frame"
[docs]def list_spectrums():
"""List all available spectrum names that can be used for peak list simulation.
:return: None
:rtype: :py:obj:`None`
"""
for spectrum_name in sorted(SPECTRUM_DESCRIPTIONS.keys()):
print(spectrum_name)
[docs]def list_spectrum_descriptions(*args):
"""List all available spectrum descriptions that can be used for peak list simulation.
:param str args: Spectrum name(s), e.g. list_spectrum_descriptions("HNCO", "HNcoCACB"), leave empty to list everything.
:return: None
:rtype: :py:obj:`None`
"""
if args:
for spectrum_name in args:
pprint.pprint({spectrum_name: SPECTRUM_DESCRIPTIONS.get(spectrum_name, None)}, width=120)
else:
pprint.pprint(SPECTRUM_DESCRIPTIONS, width=120)