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