nodes.py 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. """
  2. provides node functionality for the eventable odml types
  3. Document, Section, Property and Value
  4. additionally implements change notifications up to the corresponding section
  5. """
  6. import odml.property
  7. import odml.tools.event as event
  8. def identity_index(obj, val):
  9. """
  10. same as obj.index(val) but will only return a position
  11. if the object val is found in the list
  12. i.e.
  13. >>> identity_index(["1"], "1")
  14. will raise an ValueError because the two strings
  15. are different objects with the same content
  16. >>> a = "1"
  17. >>> identity_index([a], a)
  18. 0
  19. The difference to obj.index comes apparent here as well:
  20. >>> a="1", b="1"
  21. >>> identity_index([a,b], b)
  22. 1
  23. >>> [a,b].index(b)
  24. 0
  25. """
  26. for i, v in enumerate(obj):
  27. if v is val: return i
  28. import pdb; pdb.set_trace()
  29. raise ValueError("%s does not contain the item %s" % (repr(obj), repr(val)))
  30. class RootNode(object):
  31. @property
  32. def children(self):
  33. return self._sections
  34. def from_path(self, path):
  35. child = self.children[path[0]]
  36. if len(path) == 1:
  37. return child
  38. return child.from_path(path[1:])
  39. def to_path(self, parent=None):
  40. return ()
  41. def path_to(self, child):
  42. """return the path from this node to its direct child *child*"""
  43. return (identity_index(self._sections, child),)
  44. class ParentedNode(RootNode):
  45. def to_path(self, parent=None):
  46. if self.parent is parent:
  47. return self.parent.path_to(self)
  48. return self.parent.to_path(parent) + self.parent.path_to(self)
  49. def successor(self):
  50. return self.parent.children[self.position + 1]
  51. def next(self):
  52. """
  53. returns the next section following in this section's parent's list of sections
  54. returns None if there is no further element available
  55. i.e.:
  56. parent
  57. sec-a (<- self)
  58. sec-b
  59. will return sec-b
  60. """
  61. try:
  62. return self.successor()
  63. except IndexError:
  64. return None
  65. @property
  66. def position(self):
  67. return self.parent.path_to(self)[-1]
  68. class SectionNode(ParentedNode):
  69. """
  70. SectionNodes are special as they wrap two types of sub-nodes:
  71. * SubSections (path = (0, idx))
  72. * Properties (path = (1, idx))
  73. """
  74. def from_path(self, path):
  75. assert len(path) > 1
  76. if path[0] == 0: # sections
  77. return super(SectionNode, self).from_path(path[1:])
  78. # else: properties
  79. child = self._props[path[1]]
  80. if len(path) == 2:
  81. return child
  82. return child.from_path(path[2:])
  83. def path_to(self, child):
  84. if isinstance(child, odml.property.Property):
  85. return (1, identity_index(self._props, child))
  86. return (0, identity_index(self._sections, child))
  87. class PropertyNode(ParentedNode):
  88. @property
  89. def children(self):
  90. return self.values
  91. def successor(self):
  92. return self.parent._props[self.position + 1]
  93. def path_to(self, child):
  94. return (identity_index(self.values, child),)
  95. class ValueNode(ParentedNode):
  96. def path_from(self, path):
  97. raise TypeError("Value objects have no children")
  98. def path_to(self, child):
  99. raise TypeError("Value objects have no children")
  100. #TODO? provide this externally?
  101. name = "nodes"
  102. provides = event.provides + ["nodes"]
  103. class Document(event.Document, RootNode): pass
  104. class Value(event.Value, ValueNode): pass
  105. class Property(event.Property, PropertyNode): pass
  106. class Section(event.Section, SectionNode): pass
  107. import sys, odml
  108. odml.addImplementation(sys.modules[__name__])