"""This module is used to provide a container to the PDB file itself and its
records - but not the data contained within them."""
[docs]class PdbRecord:
"""Represents the lines, or 'records' in a PDB file.
Indexing a ``PdbRecord`` will get the equivalent slice of the record text,
only stripped, and converted to ``int`` or ``float`` if possible. Empty
sub-strings will return ``None``.
:param str text: The raw text of the record.
:param PdbFile pdb_file: Optional: a :py:class:`.PdbFile` that the record should be associated with."""
def __init__(self, text, pdb_file=None):
if not isinstance(text, str):
raise TypeError("PdbRecord text must be str, not '%s'" % str(text))
if len(text) == 0:
raise ValueError("PdbRecord cannot be created wiyh empty string")
expanded_text = text[:80] if len(text) >= 80 else text + (" " * (80 - len(text)))
self._text = expanded_text
self._name = expanded_text[:6].strip()
self._content = expanded_text[6:]
if pdb_file is not None and not isinstance(pdb_file, PdbFile):
raise TypeError("pdb_file text must be PdbFile, not '%s'" % str(pdb_file))
self._pdb_file = pdb_file
def __repr__(self):
if self.number():
return "<PdbRecord %i (%s)>" % (self.number(), self._name)
else:
return "<PdbRecord (%s)>" % self._name
def __contains__(self, item):
return item in self._text
def __getitem__(self, key):
chunk = self._text[key].strip()
if not chunk: return None
if chunk.count(".") == 1:
try:
return float(chunk)
except ValueError:
pass
try:
return int(chunk)
except ValueError:
return chunk
[docs] def get_as_string(self, start, end):
"""Indexing a record will automatically convert the value to an integer
or float if it can - using this method instead will force it to return a
string.
:param int start: The start of the subsection.
:param int end: The end of the subsection.
:rtype: ``str``"""
splice = self[start:end]
return str(splice) if splice is not None else None
[docs] def number(self):
"""The record's line number in its associated :py:class:`.PdbFile`. If
there is no file associated, this will return ``None``.
:rtype: ``int``"""
if self._pdb_file:
return self._pdb_file.records().index(self) + 1
else:
return None
[docs] def name(self, name=None):
"""The record's name (the first six characters). If a string value is
supplied, the name will be set to the new value, and the text will also
be updated.
:param str name: (optional) A new name to change to.
:rtype: ``str``"""
if name:
if not isinstance(name, str):
raise TypeError("Record name must be str, not '%s'" % str(name))
if len(name) > 6:
raise ValueError(
"Record name must be <= 6 chars, which '%s' is not" % name
)
self._name = name
self._text = "%-6s%s" % (self._name, self._content)
else:
return self._name
[docs] def content(self, content=None):
"""The record's text exlcuding the first six characters. If a string
value is supplied, the content will be set to the new value, and the
text will also be updated.
:param str content: (optional) A new content to change to.
:rtype: ``str``"""
if content:
if not isinstance(content, str):
raise TypeError(
"Record content must be str, not '%s'" % str(content)
)
if len(content) > 74:
raise ValueError(
"Record content must be <= 74 chars, which '%s' is not" % content
)
self._content = content + (" " * (74 - len(content)))
self._text = "%-6s%s" % (self._name, self._content)
else:
return self._content
[docs] def text(self, text=None):
"""The record's text, extended to 80 characters. If a string
value is supplied, the text will be set to the new value, and the
name and content will also be updated.
:param str text: (optional) A new text to change to.
:rtype: ``str``"""
if text:
if not isinstance(text, str):
raise TypeError(
"Record text must be str, not '%s'" % str(text)
)
if len(text) > 80:
raise ValueError(
"Record text must be <= 80 chars, which '%s' is not" % text
)
self._text = text + (" " * (80 - len(text)))
self._name = self._text[:6].strip()
self._content = self._text[6:]
else:
return self._text
[docs] def pdb_file(self, pdb_file=None):
"""The :py:class:`.PdbFile` that the record is associated with. This
method can update the associated file by passing a :py:class:`.PdbFile`
to it.
:param PdbFile pdb_file: (optional) A new :py:class:`.PdbFile` to set.
:rtype: ``PdbFile``"""
if pdb_file:
if not isinstance(pdb_file, PdbFile):
raise TypeError(
"pdb_file text must be PdbFile, not '%s'" % str(pdb_file)
)
self._pdb_file = pdb_file
else:
return self._pdb_file
[docs]class PdbFile:
"""A PDB File - a representation of the file itself, with no processing of
the data it contains (other than reading record names from the start of each
line).
:param str file_string: The raw text of a PDB file."""
def __init__(self, file_string=""):
self._source = "".join([
char for char in file_string if 32 <= ord(char) <= 126 or char=="\n"
]) if file_string else None
self._records = [
PdbRecord(line, self) for line in self._source.split("\n") if line
] if file_string else []
def __repr__(self):
return "<PdbFile (%i Records)>" % len(self._records)
def __contains__(self, item):
return item in self.records()
def __len__(self):
return len(self.records())
[docs] def source(self):
"""The object from which this PdbFile was created."""
return self._source
[docs] def records(self):
"""A list of :py:class:`PdbRecord` objects.
:returns: list of :py:class:`PdbRecord` objects."""
return self._records[:]
[docs] def get_record_by_name(self, record_name):
"""Gets the first :py:class:`PdbRecord` of a given name.
:param str record_name: record name to search by.
:rtype: :py:class:`PdbRecord` or ``None`` if there is no match."""
if not isinstance(record_name, str):
raise TypeError(
"Can only search for record by str, not '%s'" % str(record_name)
)
for record in self.records():
if record.name() == record_name:
return record
[docs] def get_records_by_name(self, record_name):
"""Gets all :py:class:`PdbRecord` objects of a given name.
:param str record_name: record name to search by.
:returns: ``list`` of :py:class:`PdbRecord` objects."""
if not isinstance(record_name, str):
raise TypeError(
"Can only search for records by str, not '%s'" % str(record_name)
)
return [record for record in self.records() if record.name() == record_name]
[docs] def add_record(self, record):
"""Adds a :py:class:`PdbRecord` to the end of the list of records.
:param PdbRecord record: The :py:class:`PdbRecord` to add."""
if not isinstance(record, PdbRecord):
raise TypeError(
"Can only add PdbRecord objects to PdbFiles, not '%s'" % str(record)
)
self._records.append(record)
record.pdb_file(self)
[docs] def remove_record(self, record):
"""Removes a :py:class:`PdbRecord` from the list of records.
:param PdbRecord record: The :py:class:`PdbRecord` to remove."""
if not isinstance(record, PdbRecord):
raise TypeError(
"Can only remove PdbRecord objects from PdbFiles, not '%s'" % str(record)
)
self._records.remove(record)
record._pdb_file = None
[docs] def convert_to_string(self):
"""Converts the PdbFile to a string, that can be written to file."""
lines = [record.text() for record in self.records()]
return "\n".join(lines)
[docs] def to_pdb_data_file(self):
"""Converts the PdbFile to a :py:class:`.PdbDataFile`."""
from ..converters.pdbfile2pdbdatafile import pdb_data_file_from_pdb_file
return pdb_data_file_from_pdb_file(self)