You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

94 lines
2.6 KiB

4 years ago
  1. # -*- coding: utf-8 -*-
  2. class Walker(object):
  3. def __init__(self):
  4. """Walk from one node to another."""
  5. super(Walker, self).__init__()
  6. def walk(self, start, end):
  7. """
  8. Walk from `start` node to `end` node.
  9. Returns:
  10. (upwards, common, downwards): `upwards` is a list of nodes to go upward to.
  11. `common` top node. `downwards` is a list of nodes to go downward to.
  12. Raises:
  13. WalkError: on no common root node.
  14. >>> from anytree import Node, RenderTree, AsciiStyle
  15. >>> f = Node("f")
  16. >>> b = Node("b", parent=f)
  17. >>> a = Node("a", parent=b)
  18. >>> d = Node("d", parent=b)
  19. >>> c = Node("c", parent=d)
  20. >>> e = Node("e", parent=d)
  21. >>> g = Node("g", parent=f)
  22. >>> i = Node("i", parent=g)
  23. >>> h = Node("h", parent=i)
  24. >>> print(RenderTree(f, style=AsciiStyle()))
  25. Node('/f')
  26. |-- Node('/f/b')
  27. | |-- Node('/f/b/a')
  28. | +-- Node('/f/b/d')
  29. | |-- Node('/f/b/d/c')
  30. | +-- Node('/f/b/d/e')
  31. +-- Node('/f/g')
  32. +-- Node('/f/g/i')
  33. +-- Node('/f/g/i/h')
  34. Create a walker:
  35. >>> w = Walker()
  36. This class is made for walking:
  37. >>> w.walk(f, f)
  38. ((), Node('/f'), ())
  39. >>> w.walk(f, b)
  40. ((), Node('/f'), (Node('/f/b'),))
  41. >>> w.walk(b, f)
  42. ((Node('/f/b'),), Node('/f'), ())
  43. >>> w.walk(h, e)
  44. ((Node('/f/g/i/h'), Node('/f/g/i'), Node('/f/g')), Node('/f'), (Node('/f/b'), Node('/f/b/d'), Node('/f/b/d/e')))
  45. >>> w.walk(d, e)
  46. ((), Node('/f/b/d'), (Node('/f/b/d/e'),))
  47. For a proper walking the nodes need to be part of the same tree:
  48. >>> w.walk(Node("a"), Node("b"))
  49. Traceback (most recent call last):
  50. ...
  51. anytree.walker.WalkError: Node('/a') and Node('/b') are not part of the same tree.
  52. """
  53. s = start.path
  54. e = end.path
  55. if start.root != end.root:
  56. msg = "%r and %r are not part of the same tree." % (start, end)
  57. raise WalkError(msg)
  58. # common
  59. c = Walker.__calc_common(s, e)
  60. assert c[0] is start.root
  61. len_c = len(c)
  62. # up
  63. if start is c[-1]:
  64. up = tuple()
  65. else:
  66. up = tuple(reversed(s[len_c:]))
  67. # down
  68. if end is c[-1]:
  69. down = tuple()
  70. else:
  71. down = e[len_c:]
  72. return up, c[-1], down
  73. @staticmethod
  74. def __calc_common(s, e):
  75. return tuple([si for si, ei in zip(s, e) if si is ei])
  76. class WalkError(RuntimeError):
  77. """Walk Error."""