__all__ = ['BioPaxObject', 'Controller', 'Entity', 'Pathway', 'Gene',
'Unresolved', 'Observable', 'Named', 'XReferrable']
from typing import List, Optional, TYPE_CHECKING
from ..xml_util import *
if TYPE_CHECKING:
from .util import Xref
[docs]class Unresolved:
"""A placeholder class used while deserializing BioPAX models."""
def __init__(self, obj_id):
self.obj_id = obj_id
[docs]class BioPaxObject:
"""Generic BioPAX Object. It is the parent class of all more specific
BioPAX classes."""
list_types = ['comment']
xml_types = {}
def __init__(self, uid, comment=None, **kwargs):
# Pass on for cooperative inheritance
super().__init__(**kwargs)
self.uid = uid
self.comment = comment if comment else []
@classmethod
def from_xml(cls, element):
uid = get_id_or_about(element)
kwargs = {'uid': uid}
for key in cls.list_types:
kwargs[key] = []
for child in element.getchildren():
key = get_attr_tag(child)
# In some OWL formats, the child is directly defined
# under this tag, in that case we directly deserialize it.
if child.getchildren():
gchild = child.getchildren()[0]
obj_cls = globals()[get_tag(gchild)]
val_to_add = obj_cls.from_xml(gchild)
# Otherwise, we check if the element is a simple type that we
# can just get as a text value
elif (get_datatype(child.attrib) is None
and not get_resource(child.attrib)) \
or is_datatype(child.attrib, 'xsd', 'string') \
or is_datatype(child.attrib, 'xsd', 'int') \
or is_datatype(child.attrib, 'xsd', 'float'):
val_to_add = child.text
# If neither of the above is the case, then we assume that the
# element is a reference that is defined in another block
# somewhere so we treat is as Unresolved until later.
else:
res = get_resource(child.attrib)
val_to_add = Unresolved(res)
if key in cls.list_types:
kwargs[key].append(val_to_add)
else:
kwargs[key] = val_to_add
return cls(**kwargs)
def to_xml(self):
id_type = 'about' if is_url(self.uid) else 'ID'
element = makers['bp'](self.__class__.__name__,
**{nselem('rdf', id_type): self.uid})
for attr in [a for a in dir(self)
if not a.startswith('_')
and a not in {'list_types', 'xml_types',
'to_xml', 'from_xml', 'uid'}]:
val = getattr(self, attr)
if val is None:
continue
# We have to implement special handling for names to make sure
# we don't serialize display/standard names here
if attr == 'name' and isinstance(self, Named):
val = self.get_plain_names()
if isinstance(val, list):
for v in val:
child_elem = self._simple_to_xml(attr, v)
if child_elem is not None:
element.append(child_elem)
else:
child_elem = self._simple_to_xml(attr, val)
if child_elem is not None:
element.append(child_elem)
return element
def _simple_to_xml(self, attr, val):
if isinstance(val, BioPaxObject):
child_elem = makers['bp'](
snake_to_camel(attr),
**{nselem('rdf', 'resource'):
('#%s' % val.uid) if not is_url(val.uid) else val.uid}
)
return child_elem
elif isinstance(val, str):
xml_type = self.xml_types.get(attr, 'string')
child_elem = makers['bp'](
snake_to_camel(attr),
val,
**{nselem('rdf', 'datatype'): nssuffix('xsd', xml_type)}
)
return child_elem
return None
[docs]class XReferrable:
"""A mixin class to add xrefs to a BioPaxObject.
Attributes
----------
xref : List[Xref]
"""
list_types = ['xref']
def __init__(self, xref: Optional[List["Xref"]] = None, **kwargs):
# Pass on for cooperative inheritance
super().__init__(**kwargs)
self.xref = xref if xref else []
[docs]class Named(XReferrable):
"""A mixin class to add names to a BioPaxObject.
Attributes
----------
display_name : str
standard_name : str
name : str
"""
list_types = XReferrable.list_types + ['name']
def __init__(self, display_name=None, standard_name=None, name=None,
**kwargs):
super().__init__(**kwargs)
self.display_name = display_name
self.standard_name = standard_name
self._name = name if name else []
@property
def name(self):
"""All names associated with the object including the standard and
display name, if available."""
std_name = [self.standard_name] if self.standard_name else []
disp_name = [self.display_name] if self.display_name else []
return std_name + disp_name + self._name
def get_plain_names(self):
return self._name
[docs]class Observable:
"""A mixin class to add evidence to a BioPaxObject.
Attributes
----------
evidence : List[Evidence]
"""
list_types = ['evidence']
def __init__(self, evidence=None, **kwargs):
# Pass on for cooperative inheritance
super().__init__(**kwargs)
self.evidence = evidence if evidence else []
[docs]class Entity(BioPaxObject, Observable, Named):
"""BioPAX Entity.
Attributes
----------
availability : str
data_source : List[Provenance]
"""
list_types = BioPaxObject.list_types + Observable.list_types + \
Named.list_types + ['data_source']
def __init__(self,
availability=None,
data_source=None,
**kwargs):
super().__init__(**kwargs)
self.availability = availability
self.data_source = data_source if data_source else []
self._participant_of = set()
@property
def participant_of(self):
return self._participant_of
[docs]class Gene(Entity):
"""BioPAX Gene
Attributes
----------
organism: BioSource
"""
def __init__(self, organism, **kwargs):
super().__init__(**kwargs)
self.organism = organism
[docs]class Controller:
"""BioPAX Controller."""
def __init__(self, **kwargs):
super().__init__(**kwargs)
self._controller_of = set()
@property
def controller_of(self):
return self._controller_of
[docs]class Pathway(Entity, Controller):
"""BioPAX Pathway."""
list_types = Entity.list_types + ['pathway_component', 'pathway_order']
def __init__(self,
pathway_component=None,
pathway_order=None,
organism=None,
**kwargs):
super().__init__(**kwargs)
self.pathway_component = pathway_component if pathway_component else []
self.pathway_order = pathway_order if pathway_order else []
self.organism = organism
# These are necessary to have the objects in the global
# scope, required for some modes of deserialization
from .interaction import *
from .physical_entity import *
from .util import *