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