Package Bio :: Package Phylo :: Module PhyloXMLIO
[hide private]
[frames] | no frames]

Source Code for Module Bio.Phylo.PhyloXMLIO

  1  # Copyright (C) 2009 by Eric Talevich (eric.talevich@gmail.com) 
  2  # This code is part of the Biopython distribution and governed by its 
  3  # license. Please see the LICENSE file that should have been included 
  4  # as part of this package. 
  5   
  6  """PhyloXML reader/parser, writer, and associated functions. 
  7   
  8  Instantiates tree elements from a parsed PhyloXML file, and constructs an XML 
  9  file from a `Bio.Phylo.PhyloXML` object. 
 10   
 11  About capitalization: 
 12   - phyloXML means the file format specification 
 13   - PhyloXML means the Biopython module `Bio.Phylo.PhyloXML` and its classes 
 14   - Phyloxml means the top-level class used by `PhyloXMLIO.read` (but not 
 15     `Bio.Phylo.read`!), containing a list of Phylogenies (objects derived from 
 16     `BaseTree.Tree`) 
 17   
 18  """ 
 19   
 20  import sys 
 21   
 22  from Bio._py3k import basestring 
 23  from Bio._py3k import unicode 
 24   
 25  from Bio.Phylo import PhyloXML as PX 
 26   
 27  # For speed try to use cElementTree rather than ElementTree 
 28  try: 
 29      if (3, 0) <= sys.version_info[:2] <= (3, 1): 
 30          # Workaround for bug in python 3.0 and 3.1, 
 31          # see http://bugs.python.org/issue9257 
 32          from xml.etree import ElementTree as ElementTree 
 33      else: 
 34          from xml.etree import cElementTree as ElementTree 
 35  except ImportError: 
 36      from xml.etree import ElementTree as ElementTree 
 37   
 38   
 39  # Recognize the phyloXML namespace when parsing 
 40  # See http://effbot.org/zone/element-namespaces.htm 
 41  NAMESPACES = { 
 42      'phy': 'http://www.phyloxml.org', 
 43  } 
 44   
 45  try: 
 46      register_namespace = ElementTree.register_namespace 
 47  except AttributeError: 
 48      if not hasattr(ElementTree, '_namespace_map'): 
 49          # cElementTree needs the pure-Python xml.etree.ElementTree 
 50          from xml.etree import ElementTree as ET_py 
 51          ElementTree._namespace_map = ET_py._namespace_map 
 52   
53 - def register_namespace(prefix, uri):
54 ElementTree._namespace_map[uri] = prefix
55 56 for prefix, uri in NAMESPACES.items(): 57 register_namespace(prefix, uri) 58 59 # Tell ElementTree how to write to text handles 60 DEFAULT_ENCODING = ("unicode" if sys.version_info[0] >= 3 else "utf-8") 61 62
63 -class PhyloXMLError(Exception):
64 """Exception raised when PhyloXML object construction cannot continue. 65 66 XML syntax errors will be found and raised by the underlying ElementTree 67 module; this exception is for valid XML that breaks the phyloXML 68 specification. 69 """ 70 71 pass
72 73 74 # --------------------------------------------------------- 75 # Public API 76
77 -def read(file):
78 """Parse a phyloXML file or stream and build a tree of Biopython objects. 79 80 The children of the root node are phylogenies and possibly other arbitrary 81 (non-phyloXML) objects. 82 83 :returns: a single `Bio.Phylo.PhyloXML.Phyloxml` object. 84 85 """ 86 return Parser(file).read()
87 88
89 -def parse(file):
90 """Iterate over the phylogenetic trees in a phyloXML file. 91 92 This ignores any additional data stored at the top level, but may be more 93 memory-efficient than the `read` function. 94 95 :returns: a generator of `Bio.Phylo.PhyloXML.Phylogeny` objects. 96 97 """ 98 return Parser(file).parse()
99 100
101 -def write(obj, file, encoding=DEFAULT_ENCODING, indent=True):
102 """Write a phyloXML file. 103 104 :Parameters: 105 obj 106 an instance of `Phyloxml`, `Phylogeny` or `BaseTree.Tree`, or an 107 iterable of either of the latter two. The object will be converted 108 to a Phyloxml object before serialization. 109 file 110 either an open handle or a file name. 111 112 """ 113 def fix_single(tree): 114 if isinstance(tree, PX.Phylogeny): 115 return tree 116 if isinstance(tree, PX.Clade): 117 return tree.to_phylogeny() 118 if isinstance(tree, PX.BaseTree.Tree): 119 return PX.Phylogeny.from_tree(tree) 120 if isinstance(tree, PX.BaseTree.Clade): 121 return PX.Phylogeny.from_tree(PX.BaseTree.Tree(root=tree)) 122 else: 123 raise ValueError("iterable must contain Tree or Clade types")
124 125 if isinstance(obj, PX.Phyloxml): 126 pass 127 elif isinstance(obj, (PX.BaseTree.Tree, PX.BaseTree.Clade)): 128 obj = fix_single(obj).to_phyloxml() 129 elif hasattr(obj, '__iter__'): 130 obj = PX.Phyloxml({}, phylogenies=(fix_single(t) for t in obj)) 131 else: 132 raise ValueError("First argument must be a Phyloxml, Phylogeny, " 133 "Tree, or iterable of Trees or Phylogenies.") 134 return Writer(obj).write(file, encoding=encoding, indent=indent) 135 136 137 # --------------------------------------------------------- 138 # Functions I wish ElementTree had 139
140 -def _local(tag):
141 """Extract the local tag from a namespaced tag name.""" 142 if tag[0] == '{': 143 return tag[tag.index('}') + 1:] 144 return tag
145 146
147 -def _split_namespace(tag):
148 """Split a tag into namespace and local tag strings.""" 149 try: 150 return tag[1:].split('}', 1) 151 except ValueError: 152 return ('', tag)
153 154
155 -def _ns(tag, namespace=NAMESPACES['phy']):
156 """Format an XML tag with the given namespace.""" 157 return '{%s}%s' % (namespace, tag)
158 159
160 -def _get_child_as(parent, tag, construct):
161 """Find a child node by tag, and pass it through a constructor. 162 163 Returns None if no matching child is found. 164 """ 165 child = parent.find(_ns(tag)) 166 if child is not None: 167 return construct(child)
168 169
170 -def _get_child_text(parent, tag, construct=unicode):
171 """Find a child node by tag; pass its text through a constructor. 172 173 Returns None if no matching child is found. 174 """ 175 child = parent.find(_ns(tag)) 176 if child is not None and child.text: 177 return construct(child.text)
178 179
180 -def _get_children_as(parent, tag, construct):
181 """Find child nodes by tag; pass each through a constructor. 182 183 Returns an empty list if no matching child is found. 184 """ 185 return [construct(child) for child in 186 parent.findall(_ns(tag))]
187 188
189 -def _get_children_text(parent, tag, construct=unicode):
190 """Find child nodes by tag; pass each node's text through a constructor. 191 192 Returns an empty list if no matching child is found. 193 """ 194 return [construct(child.text) for child in 195 parent.findall(_ns(tag)) 196 if child.text]
197 198
199 -def _indent(elem, level=0):
200 """Add line breaks and indentation to ElementTree in-place. 201 202 Sources: 203 - http://effbot.org/zone/element-lib.htm#prettyprint 204 - http://infix.se/2007/02/06/gentlemen-indent-your-xml 205 206 """ 207 i = "\n" + level * " " 208 if len(elem): 209 if not elem.text or not elem.text.strip(): 210 elem.text = i + " " 211 for e in elem: 212 _indent(e, level + 1) 213 if not e.tail or not e.tail.strip(): 214 e.tail = i + " " 215 if not e.tail or not e.tail.strip(): 216 e.tail = i 217 else: 218 if level and (not elem.tail or not elem.tail.strip()): 219 elem.tail = i
220 221 # --------------------------------------------------------- 222 # INPUT 223 # --------------------------------------------------------- 224 225
226 -def _str2bool(text):
227 if text == 'true' or text == '1': 228 return True 229 if text == 'false' or text == '0': 230 return False 231 raise ValueError('String could not be converted to boolean: ' + text)
232 233
234 -def _dict_str2bool(dct, keys):
235 out = dct.copy() 236 for key in keys: 237 if key in out: 238 out[key] = _str2bool(out[key]) 239 return out
240 241
242 -def _int(text):
243 if text is not None: 244 try: 245 return int(text) 246 except Exception: 247 return None
248 249
250 -def _float(text):
251 if text is not None: 252 try: 253 return float(text) 254 except Exception: 255 return None
256 257
258 -def _collapse_wspace(text):
259 """Replace all spans of whitespace with a single space character. 260 261 Also remove leading and trailing whitespace. See "Collapse Whitespace 262 Policy" in the phyloXML spec glossary: 263 http://phyloxml.org/documentation/version_100/phyloxml.xsd.html#Glossary 264 """ 265 if text is not None: 266 return ' '.join(text.split())
267 268 269 # NB: Not currently used
270 -def _replace_wspace(text):
271 """Replace tab, LF and CR characters with spaces, but don't collapse. 272 273 See "Replace Whitespace Policy" in the phyloXML spec glossary: 274 http://phyloxml.org/documentation/version_100/phyloxml.xsd.html#Glossary 275 """ 276 for char in ('\t', '\n', '\r'): 277 if char in text: 278 text = text.replace(char, ' ') 279 return text
280 281
282 -class Parser(object):
283 """Methods for parsing all phyloXML nodes from an XML stream. 284 285 To minimize memory use, the tree of ElementTree parsing events is cleared 286 after completing each phylogeny, clade, and top-level 'other' element. 287 Elements below the clade level are kept in memory until parsing of the 288 current clade is finished -- this shouldn't be a problem because clade is 289 the only recursive element, and non-clade nodes below this level are of 290 bounded size. 291 """ 292
293 - def __init__(self, file):
294 # Get an iterable context for XML parsing events 295 context = iter(ElementTree.iterparse(file, events=('start', 'end'))) 296 event, root = next(context) 297 self.root = root 298 self.context = context
299
300 - def read(self):
301 """Parse the phyloXML file and create a single Phyloxml object.""" 302 phyloxml = PX.Phyloxml(dict((_local(key), val) 303 for key, val in self.root.items())) 304 other_depth = 0 305 for event, elem in self.context: 306 namespace, localtag = _split_namespace(elem.tag) 307 if event == 'start': 308 if namespace != NAMESPACES['phy']: 309 other_depth += 1 310 continue 311 if localtag == 'phylogeny': 312 phylogeny = self._parse_phylogeny(elem) 313 phyloxml.phylogenies.append(phylogeny) 314 if event == 'end' and namespace != NAMESPACES['phy']: 315 # Deal with items not specified by phyloXML 316 other_depth -= 1 317 if other_depth == 0: 318 # We're directly under the root node -- evaluate 319 otr = self.other(elem, namespace, localtag) 320 phyloxml.other.append(otr) 321 self.root.clear() 322 return phyloxml
323
324 - def parse(self):
325 """Parse the phyloXML file incrementally and return each phylogeny.""" 326 phytag = _ns('phylogeny') 327 for event, elem in self.context: 328 if event == 'start' and elem.tag == phytag: 329 yield self._parse_phylogeny(elem)
330 331 # Special parsing cases -- incremental, using self.context 332
333 - def _parse_phylogeny(self, parent):
334 """Parse a single phylogeny within the phyloXML tree. 335 336 Recursively builds a phylogenetic tree with help from parse_clade, then 337 clears the XML event history for the phylogeny element and returns 338 control to the top-level parsing function. 339 """ 340 phylogeny = PX.Phylogeny(**_dict_str2bool(parent.attrib, 341 ['rooted', 'rerootable'])) 342 list_types = { 343 # XML tag, plural attribute 344 'confidence': 'confidences', 345 'property': 'properties', 346 'clade_relation': 'clade_relations', 347 'sequence_relation': 'sequence_relations', 348 } 349 for event, elem in self.context: 350 namespace, tag = _split_namespace(elem.tag) 351 if event == 'start' and tag == 'clade': 352 assert phylogeny.root is None, \ 353 "Phylogeny object should only have 1 clade" 354 phylogeny.root = self._parse_clade(elem) 355 continue 356 if event == 'end': 357 if tag == 'phylogeny': 358 parent.clear() 359 break 360 # Handle the other non-recursive children 361 if tag in list_types: 362 getattr(phylogeny, list_types[tag]).append( 363 getattr(self, tag)(elem)) 364 # Complex types 365 elif tag in ('date', 'id'): 366 setattr(phylogeny, tag, getattr(self, tag)(elem)) 367 # Simple types 368 elif tag in ('name', 'description'): 369 setattr(phylogeny, tag, _collapse_wspace(elem.text)) 370 # Unknown tags 371 elif namespace != NAMESPACES['phy']: 372 phylogeny.other.append(self.other(elem, namespace, tag)) 373 parent.clear() 374 else: 375 # NB: This shouldn't happen in valid files 376 raise PhyloXMLError('Misidentified tag: ' + tag) 377 return phylogeny
378 379 _clade_complex_types = ['color', 'events', 'binary_characters', 'date'] 380 _clade_list_types = { 381 'confidence': 'confidences', 382 'distribution': 'distributions', 383 'reference': 'references', 384 'property': 'properties', 385 } 386 _clade_tracked_tags = set(_clade_complex_types).union(_clade_list_types.keys()).union( 387 ['branch_length', 'name', 'node_id', 'width']) 388
389 - def _parse_clade(self, parent):
390 """Parse a Clade node and its children, recursively.""" 391 clade = PX.Clade(**parent.attrib) 392 if clade.branch_length is not None: 393 clade.branch_length = float(clade.branch_length) 394 # NB: Only evaluate nodes at the current level 395 tag_stack = [] 396 for event, elem in self.context: 397 namespace, tag = _split_namespace(elem.tag) 398 if event == 'start': 399 if tag == 'clade': 400 clade.clades.append(self._parse_clade(elem)) 401 continue 402 if tag == 'taxonomy': 403 clade.taxonomies.append(self._parse_taxonomy(elem)) 404 continue 405 if tag == 'sequence': 406 clade.sequences.append(self._parse_sequence(elem)) 407 continue 408 if tag in self._clade_tracked_tags: 409 tag_stack.append(tag) 410 if event == 'end': 411 if tag == 'clade': 412 elem.clear() 413 break 414 if tag != tag_stack[-1]: 415 continue 416 tag_stack.pop() 417 # Handle the other non-recursive children 418 if tag in self._clade_list_types: 419 getattr(clade, self._clade_list_types[tag]).append( 420 getattr(self, tag)(elem)) 421 elif tag in self._clade_complex_types: 422 setattr(clade, tag, getattr(self, tag)(elem)) 423 elif tag == 'branch_length': 424 # NB: possible collision with the attribute 425 if clade.branch_length is not None: 426 raise PhyloXMLError( 427 'Attribute branch_length was already set ' 428 'for this Clade.') 429 clade.branch_length = _float(elem.text) 430 elif tag == 'width': 431 clade.width = _float(elem.text) 432 elif tag == 'name': 433 clade.name = _collapse_wspace(elem.text) 434 elif tag == 'node_id': 435 clade.node_id = PX.Id(elem.text.strip(), 436 elem.attrib.get('provider')) 437 elif namespace != NAMESPACES['phy']: 438 clade.other.append(self.other(elem, namespace, tag)) 439 elem.clear() 440 else: 441 raise PhyloXMLError('Misidentified tag: ' + tag) 442 return clade
443
444 - def _parse_sequence(self, parent):
445 sequence = PX.Sequence(**parent.attrib) 446 for event, elem in self.context: 447 namespace, tag = _split_namespace(elem.tag) 448 if event == 'end': 449 if tag == 'sequence': 450 parent.clear() 451 break 452 if tag in ('accession', 'mol_seq', 'uri', 453 'domain_architecture'): 454 setattr(sequence, tag, getattr(self, tag)(elem)) 455 elif tag == 'annotation': 456 sequence.annotations.append(self.annotation(elem)) 457 elif tag == 'name': 458 sequence.name = _collapse_wspace(elem.text) 459 elif tag in ('symbol', 'location'): 460 setattr(sequence, tag, elem.text) 461 elif namespace != NAMESPACES['phy']: 462 sequence.other.append(self.other(elem, namespace, tag)) 463 parent.clear() 464 return sequence
465
466 - def _parse_taxonomy(self, parent):
467 taxonomy = PX.Taxonomy(**parent.attrib) 468 for event, elem in self.context: 469 namespace, tag = _split_namespace(elem.tag) 470 if event == 'end': 471 if tag == 'taxonomy': 472 parent.clear() 473 break 474 if tag in ('id', 'uri'): 475 setattr(taxonomy, tag, getattr(self, tag)(elem)) 476 elif tag == 'common_name': 477 taxonomy.common_names.append(_collapse_wspace(elem.text)) 478 elif tag == 'synonym': 479 taxonomy.synonyms.append(elem.text) 480 elif tag in ('code', 'scientific_name', 'authority', 'rank'): 481 # ENH: check_str on rank 482 setattr(taxonomy, tag, elem.text) 483 elif namespace != NAMESPACES['phy']: 484 taxonomy.other.append(self.other(elem, namespace, tag)) 485 parent.clear() 486 return taxonomy
487
488 - def other(self, elem, namespace, localtag):
489 return PX.Other(localtag, namespace, elem.attrib, 490 value=elem.text and elem.text.strip() or None, 491 children=[self.other(child, *_split_namespace(child.tag)) 492 for child in elem])
493 494 # Complex types 495
496 - def accession(self, elem):
497 return PX.Accession(elem.text.strip(), elem.get('source'))
498
499 - def annotation(self, elem):
500 return PX.Annotation( 501 desc=_collapse_wspace(_get_child_text(elem, 'desc')), 502 confidence=_get_child_as(elem, 'confidence', self.confidence), 503 properties=_get_children_as(elem, 'property', self.property), 504 uri=_get_child_as(elem, 'uri', self.uri), 505 **elem.attrib)
506
507 - def binary_characters(self, elem):
508 def bc_getter(elem): 509 return _get_children_text(elem, 'bc')
510 return PX.BinaryCharacters( 511 type=elem.get('type'), 512 gained_count=_int(elem.get('gained_count')), 513 lost_count=_int(elem.get('lost_count')), 514 present_count=_int(elem.get('present_count')), 515 absent_count=_int(elem.get('absent_count')), 516 # Flatten BinaryCharacterList sub-nodes into lists of strings 517 gained=_get_child_as(elem, 'gained', bc_getter), 518 lost=_get_child_as(elem, 'lost', bc_getter), 519 present=_get_child_as(elem, 'present', bc_getter), 520 absent=_get_child_as(elem, 'absent', bc_getter))
521
522 - def clade_relation(self, elem):
523 return PX.CladeRelation( 524 elem.get('type'), elem.get('id_ref_0'), elem.get('id_ref_1'), 525 distance=elem.get('distance'), 526 confidence=_get_child_as(elem, 'confidence', self.confidence))
527
528 - def color(self, elem):
529 red, green, blue = (_get_child_text(elem, color, int) for color in 530 ('red', 'green', 'blue')) 531 return PX.BranchColor(red, green, blue)
532
533 - def confidence(self, elem):
534 return PX.Confidence( 535 _float(elem.text), 536 elem.get('type'))
537
538 - def date(self, elem):
539 return PX.Date( 540 unit=elem.get('unit'), 541 desc=_collapse_wspace(_get_child_text(elem, 'desc')), 542 value=_get_child_text(elem, 'value', float), 543 minimum=_get_child_text(elem, 'minimum', float), 544 maximum=_get_child_text(elem, 'maximum', float), 545 )
546
547 - def distribution(self, elem):
548 return PX.Distribution( 549 desc=_collapse_wspace(_get_child_text(elem, 'desc')), 550 points=_get_children_as(elem, 'point', self.point), 551 polygons=_get_children_as(elem, 'polygon', self.polygon))
552
553 - def domain(self, elem):
554 return PX.ProteinDomain(elem.text.strip(), 555 int(elem.get('from')) - 1, 556 int(elem.get('to')), 557 confidence=_float(elem.get('confidence')), 558 id=elem.get('id'))
559
560 - def domain_architecture(self, elem):
561 return PX.DomainArchitecture( 562 length=int(elem.get('length')), 563 domains=_get_children_as(elem, 'domain', self.domain))
564
565 - def events(self, elem):
566 return PX.Events( 567 type=_get_child_text(elem, 'type'), 568 duplications=_get_child_text(elem, 'duplications', int), 569 speciations=_get_child_text(elem, 'speciations', int), 570 losses=_get_child_text(elem, 'losses', int), 571 confidence=_get_child_as(elem, 'confidence', self.confidence))
572
573 - def id(self, elem):
574 provider = elem.get('provider') or elem.get('type') 575 return PX.Id(elem.text.strip(), provider)
576
577 - def mol_seq(self, elem):
578 is_aligned = elem.get('is_aligned') 579 if is_aligned is not None: 580 is_aligned = _str2bool(is_aligned) 581 return PX.MolSeq(elem.text.strip(), is_aligned=is_aligned)
582
583 - def point(self, elem):
584 return PX.Point( 585 elem.get('geodetic_datum'), 586 _get_child_text(elem, 'lat', float), 587 _get_child_text(elem, 'long', float), 588 alt=_get_child_text(elem, 'alt', float), 589 alt_unit=elem.get('alt_unit'))
590
591 - def polygon(self, elem):
592 return PX.Polygon( 593 points=_get_children_as(elem, 'point', self.point))
594
595 - def property(self, elem):
596 return PX.Property(elem.text.strip(), 597 elem.get('ref'), 598 elem.get('applies_to'), 599 elem.get('datatype'), 600 unit=elem.get('unit'), 601 id_ref=elem.get('id_ref'))
602
603 - def reference(self, elem):
604 return PX.Reference( 605 doi=elem.get('doi'), 606 desc=_get_child_text(elem, 'desc'))
607
608 - def sequence_relation(self, elem):
609 return PX.SequenceRelation( 610 elem.get('type'), elem.get('id_ref_0'), elem.get('id_ref_1'), 611 distance=_float(elem.get('distance')), 612 confidence=_get_child_as(elem, 'confidence', self.confidence))
613
614 - def uri(self, elem):
615 return PX.Uri(elem.text.strip(), 616 desc=_collapse_wspace(elem.get('desc')), 617 type=elem.get('type'))
618 619 620 # --------------------------------------------------------- 621 # OUTPUT 622 # --------------------------------------------------------- 623
624 -def _serialize(value):
625 """Convert a Python primitive to a phyloXML-compatible Unicode string.""" 626 if isinstance(value, float): 627 return unicode(value).upper() 628 elif isinstance(value, bool): 629 return unicode(value).lower() 630 return unicode(value)
631 632
633 -def _clean_attrib(obj, attrs):
634 """Create a dictionary from an object's specified, non-None attributes.""" 635 out = {} 636 for key in attrs: 637 val = getattr(obj, key) 638 if val is not None: 639 out[key] = _serialize(val) 640 return out
641 642
643 -def _handle_complex(tag, attribs, subnodes, has_text=False):
644 def wrapped(self, obj): 645 elem = ElementTree.Element(tag, _clean_attrib(obj, attribs)) 646 for subn in subnodes: 647 if isinstance(subn, basestring): 648 # singular object: method and attribute names are the same 649 if getattr(obj, subn) is not None: 650 elem.append(getattr(self, subn)(getattr(obj, subn))) 651 else: 652 # list: singular method, pluralized attribute name 653 method, plural = subn 654 for item in getattr(obj, plural): 655 elem.append(getattr(self, method)(item)) 656 if has_text: 657 elem.text = _serialize(obj.value) 658 return elem
659 wrapped.__doc__ = "Serialize a %s and its subnodes, in order." % tag 660 return wrapped 661 662
663 -def _handle_simple(tag):
664 def wrapped(self, obj): 665 elem = ElementTree.Element(tag) 666 elem.text = _serialize(obj) 667 return elem
668 wrapped.__doc__ = "Serialize a simple %s node." % tag 669 return wrapped 670 671
672 -class Writer(object):
673 """Methods for serializing a PhyloXML object to XML.""" 674
675 - def __init__(self, phyloxml):
676 """Build an ElementTree from a PhyloXML object.""" 677 assert isinstance(phyloxml, PX.Phyloxml), "Not a Phyloxml object" 678 self._tree = ElementTree.ElementTree(self.phyloxml(phyloxml))
679
680 - def write(self, file, encoding=DEFAULT_ENCODING, indent=True):
681 if indent: 682 _indent(self._tree.getroot()) 683 self._tree.write(file, encoding) 684 return len(self._tree.getroot())
685 686 # Convert classes to ETree elements 687
688 - def phyloxml(self, obj):
689 elem = ElementTree.Element('phyloxml', obj.attributes) # Namespaces 690 for tree in obj.phylogenies: 691 elem.append(self.phylogeny(tree)) 692 for otr in obj.other: 693 elem.append(self.other(otr)) 694 return elem
695
696 - def other(self, obj):
697 elem = ElementTree.Element(_ns(obj.tag, obj.namespace), obj.attributes) 698 elem.text = obj.value 699 for child in obj.children: 700 elem.append(self.other(child)) 701 return elem
702 703 phylogeny = _handle_complex('phylogeny', 704 ('rooted', 'rerootable', 705 'branch_length_unit', 'type'), 706 ('name', 707 'id', 708 'description', 709 'date', 710 ('confidence', 'confidences'), 711 'clade', 712 ('clade_relation', 'clade_relations'), 713 ('sequence_relation', 714 'sequence_relations'), 715 ('property', 'properties'), 716 ('other', 'other'), 717 )) 718 719 clade = _handle_complex('clade', ('id_source',), 720 ('name', 721 'branch_length', 722 ('confidence', 'confidences'), 723 'width', 724 'color', 725 'node_id', 726 ('taxonomy', 'taxonomies'), 727 ('sequence', 'sequences'), 728 'events', 729 'binary_characters', 730 ('distribution', 'distributions'), 731 'date', 732 ('reference', 'references'), 733 ('property', 'properties'), 734 ('clade', 'clades'), 735 ('other', 'other'), 736 )) 737 738 accession = _handle_complex('accession', ('source',), 739 (), has_text=True) 740 741 annotation = _handle_complex('annotation', 742 ('ref', 'source', 'evidence', 'type'), 743 ('desc', 744 'confidence', 745 ('property', 'properties'), 746 'uri', 747 )) 748
749 - def binary_characters(self, obj):
750 """Serialize a binary_characters node and its subnodes.""" 751 elem = ElementTree.Element('binary_characters', 752 _clean_attrib(obj, 753 ('type', 'gained_count', 'lost_count', 754 'present_count', 'absent_count'))) 755 for subn in ('gained', 'lost', 'present', 'absent'): 756 subelem = ElementTree.Element(subn) 757 for token in getattr(obj, subn): 758 subelem.append(self.bc(token)) 759 elem.append(subelem) 760 return elem
761 762 clade_relation = _handle_complex('clade_relation', 763 ('id_ref_0', 'id_ref_1', 764 'distance', 'type'), 765 ('confidence',)) 766 767 color = _handle_complex('color', (), ('red', 'green', 'blue')) 768 769 confidence = _handle_complex('confidence', ('type',), 770 (), has_text=True) 771 772 date = _handle_complex('date', ('unit',), 773 ('desc', 'value', 'minimum', 'maximum')) 774 775 distribution = _handle_complex('distribution', (), 776 ('desc', 777 ('point', 'points'), 778 ('polygon', 'polygons'), 779 )) 780
781 - def domain(self, obj):
782 """Serialize a domain node.""" 783 elem = ElementTree.Element('domain', 784 {'from': str(obj.start + 1), 'to': str(obj.end)}) 785 if obj.confidence is not None: 786 elem.set('confidence', _serialize(obj.confidence)) 787 if obj.id is not None: 788 elem.set('id', obj.id) 789 elem.text = _serialize(obj.value) 790 return elem
791 792 domain_architecture = _handle_complex('domain_architecture', 793 ('length',), 794 (('domain', 'domains'),)) 795 796 events = _handle_complex('events', (), 797 ('type', 798 'duplications', 799 'speciations', 800 'losses', 801 'confidence', 802 )) 803 804 id = _handle_complex('id', ('provider',), (), has_text=True) 805 806 mol_seq = _handle_complex('mol_seq', ('is_aligned',), 807 (), has_text=True) 808 809 node_id = _handle_complex('node_id', ('provider',), (), has_text=True) 810 811 point = _handle_complex('point', ('geodetic_datum', 'alt_unit'), 812 ('lat', 'long', 'alt')) 813 814 polygon = _handle_complex('polygon', (), (('point', 'points'),)) 815 816 property = _handle_complex('property', 817 ('ref', 'unit', 'datatype', 818 'applies_to', 'id_ref'), 819 (), has_text=True) 820 821 reference = _handle_complex('reference', ('doi',), ('desc',)) 822 823 sequence = _handle_complex('sequence', 824 ('type', 'id_ref', 'id_source'), 825 ('symbol', 826 'accession', 827 'name', 828 'location', 829 'mol_seq', 830 'uri', 831 ('annotation', 'annotations'), 832 'domain_architecture', 833 ('other', 'other'), 834 )) 835 836 sequence_relation = _handle_complex('sequence_relation', 837 ('id_ref_0', 'id_ref_1', 838 'distance', 'type'), 839 ('confidence',)) 840 841 taxonomy = _handle_complex('taxonomy', 842 ('id_source',), 843 ('id', 844 'code', 845 'scientific_name', 846 'authority', 847 ('common_name', 'common_names'), 848 ('synonym', 'synonyms'), 849 'rank', 850 'uri', 851 ('other', 'other'), 852 )) 853 854 uri = _handle_complex('uri', ('desc', 'type'), (), has_text=True) 855 856 # Primitive types 857 858 # Floating point 859 alt = _handle_simple('alt') 860 branch_length = _handle_simple('branch_length') 861 lat = _handle_simple('lat') 862 long = _handle_simple('long') 863 maximum = _handle_simple('maximum') 864 minimum = _handle_simple('minimum') 865 value = _handle_simple('value') 866 width = _handle_simple('width') 867 868 # Integers 869 blue = _handle_simple('blue') 870 duplications = _handle_simple('duplications') 871 green = _handle_simple('green') 872 losses = _handle_simple('losses') 873 red = _handle_simple('red') 874 speciations = _handle_simple('speciations') 875 876 # Strings 877 bc = _handle_simple('bc') 878 code = _handle_simple('code') 879 common_name = _handle_simple('common_name') 880 desc = _handle_simple('desc') 881 description = _handle_simple('description') 882 location = _handle_simple('location') 883 name = _handle_simple('name') 884 rank = _handle_simple('rank') 885 scientific_name = _handle_simple('scientific_name') 886 symbol = _handle_simple('symbol') 887 synonym = _handle_simple('synonym') 888 type = _handle_simple('type')
889