diff --git a/cheuph/__init__.py b/cheuph/__init__.py index aa2cd6e..a93c15e 100644 --- a/cheuph/__init__.py +++ b/cheuph/__init__.py @@ -1,6 +1,7 @@ from typing import List from .element import * +from .element_supply import * from .exceptions import * from .markup import * from .tree_display import * @@ -8,6 +9,7 @@ from .tree_list import * __all__: List[str] = [] __all__ += element.__all__ +__all__ += element_supply.__all__ __all__ += exceptions.__all__ __all__ += markup.__all__ __all__ += tree_display.__all__ diff --git a/cheuph/element.py b/cheuph/element.py index acaccdd..ddc0b8e 100644 --- a/cheuph/element.py +++ b/cheuph/element.py @@ -4,7 +4,7 @@ from typing import Hashable, List, Optional from .exceptions import ElementException, TreeException from .markup import AttributedText -__all__ = ["Id", "Element", "ElementSupply", "RenderedElement"] +__all__ = ["Id", "Element", "RenderedElement"] Id = Hashable @@ -47,101 +47,6 @@ class Element(abc.ABC): ) -> RenderedElement: pass -class ElementSupply(abc.ABC): - @abc.abstractmethod - def get(self, element_id: Id) -> Element: - pass - - @abc.abstractmethod - def get_parent_id(self, element_id: Id) -> Optional[Id]: - pass - - def get_parent(self, element_id: Id) -> Optional[Element]: - parent_id = self.get_parent_id(element_id) - - if parent_id is not None: - return self.get(parent_id) - else: - return None - - @abc.abstractmethod - def get_children_ids(self, element_id: Id) -> List[Id]: - pass - - def get_children(self, element_id: Id) -> List[Element]: - children_ids = self.get_children_ids(element_id) - - children: List[Element] = [] - for child_id in children_ids: - children.append(self.get(child_id)) - - return children - - # There is not a clear-cut way to get the previous or next "sibling" of an - # element that is itself a child of the implicit root (None), since - # get_children() doesn't accept None in its element_id argument. - # - # Because of this, the get_previous_id() and get_next_id() functions are - # abstract (until I decide to change the signature of get_children(), that - # is :P). - - @abc.abstractmethod - def get_previous_id(self, element_id: Id) -> Optional[Id]: - pass - - def get_previous(self, element_id: Id) -> Optional[Element]: - previous_id = self.get_previous_id(element_id) - - if previous_id is not None: - return self.get(previous_id) - else: - return None - - @abc.abstractmethod - def get_next_id(self, element_id: Id) -> Optional[Id]: - pass - - def get_next(self, element_id: Id) -> Optional[Element]: - next_id = self.get_next_id(element_id) - - if next_id is not None: - return self.get(next_id) - else: - return None - - def get_furthest_ancestor_id(self, - element_id: Id, - root_id: Optional[Id] = None, - ) -> Id: - current_id = element_id - - while True: - parent_id = self.get_parent_id(current_id) - - if parent_id == root_id: - return current_id - elif parent_id is None: - raise TreeException("Reached implicit root before hitting specified root") - - current_id = parent_id - - def get_furthest_ancestor(self, - element_id: Id, - root_id: Optional[Id] = None, - ) -> Element: - return self.get(self.get_furthest_ancestor_id(element_id, - root_id=root_id)) - - def get_tree(self, tree_id: Id) -> Element: - tree = self.get(tree_id) - - children: List[Element] = [] - for child_id in self.get_children_ids(tree_id): - children.append(self.get_tree(child_id)) - - tree.children = children - return tree - class RenderedElement: def __init__(self, element: Element, diff --git a/cheuph/element_supply.py b/cheuph/element_supply.py new file mode 100644 index 0000000..d93f91f --- /dev/null +++ b/cheuph/element_supply.py @@ -0,0 +1,151 @@ +import abc +from typing import List, Optional + +from .element import Element, Id +from .exceptions import TreeException + +__all__ = ["ElementSupply", "MemoryElementSupply"] + +class ElementSupply(abc.ABC): + """ + An ElementSupply is an interface to query some resource containing + Elements. The elements could for example be kept in memory, in a database + or somewhere else. + + The element ids must be unique, and the elements and their parents must + form one or more trees (i. e. must not contain any cycles). + """ + + @abc.abstractmethod + def get(self, element_id: Id) -> Element: + """ + Get a single element by its id. + """ + + pass + + def get_parent_id(self, element_id: Id) -> Optional[Id]: + """ + Get the id of the parent's element. + + This function is redundant, since you can just use element.parent_id. + """ + + return self.get(element_id).parent_id + + def get_parent(self, element_id: Id) -> Optional[Element]: + """ + Like get_parent_id, but returns the Element instead. + """ + + parent_id = self.get_parent_id(element_id) + + if parent_id is not None: + return self.get(parent_id) + else: + return None + + @abc.abstractmethod + def get_children_ids(self, element_id: Optional[Id]) -> List[Id]: + """ + Get a list of the ids of all the element's children. + """ + + pass + + def get_children(self, element_id: Optional[Id]) -> List[Element]: + """ + Get a list of all children of an element. + + If the id passed is None, return a list of all top-level elements + instead. + """ + + children_ids = self.get_children_ids(element_id) + + children: List[Element] = [] + for child_id in children_ids: + children.append(self.get(child_id)) + + return children + + # There is not a clear-cut way to get the previous or next "sibling" of an + # element that is itself a child of the implicit root (None), since + # get_children() doesn't accept None in its element_id argument. + # + # Because of this, the get_previous_id() and get_next_id() functions are + # abstract (until I decide to change the signature of get_children(), that + # is :P). + + def get_previous_id(self, element_id: Id) -> Optional[Id]: + """ + Get the id of an element's previous sibling (i. e. the sibling just + above it). + + Returns None if there is no previous sibling. + + Depending on the amount of elements in your ElementSupply, the default + implementation might get very slow. + """ + + pass # TODO + + def get_previous(self, element_id: Id) -> Optional[Element]: + """ + Like get_previous_id(), but returns the Element instead. + """ + + previous_id = self.get_previous_id(element_id) + + if previous_id is not None: + return self.get(previous_id) + else: + return None + + def get_next_id(self, element_id: Id) -> Optional[Id]: + """ + Get the id of an element's next sibling (i. e. the sibling just below + it). + + Returns None if there is no next sibling. + + Depending on the amount of elements in your ElementSupply, the default + implementation might get very slow. + """ + + pass # TODO + + def get_next(self, element_id: Id) -> Optional[Element]: + """ + Like get_next_id(), but returns the Element instead. + """ + + next_id = self.get_next_id(element_id) + + if next_id is not None: + return self.get(next_id) + else: + return None + +# def get_furthest_ancestor_id(self, +# element_id: Id, +# root_id: Optional[Id] = None, +# ) -> Id: +# current_id = element_id +# +# while True: +# parent_id = self.get_parent_id(current_id) +# +# if parent_id == root_id: +# return current_id +# elif parent_id is None: +# raise TreeException("Reached implicit root before hitting specified root") +# +# current_id = parent_id +# +# def get_furthest_ancestor(self, +# element_id: Id, +# root_id: Optional[Id] = None, +# ) -> Element: +# return self.get(self.get_furthest_ancestor_id(element_id, +# root_id=root_id)) diff --git a/cheuph/tree_display.py b/cheuph/tree_display.py index e452f4d..5297de1 100644 --- a/cheuph/tree_display.py +++ b/cheuph/tree_display.py @@ -1,7 +1,8 @@ import collections from typing import Any, List, Optional, Set -from .element import Element, ElementSupply, Id, RenderedElement +from .element import Element, Id, RenderedElement +from .element_supply import ElementSupply from .exceptions import TreeException from .markup import AttributedText from .tree_list import TreeList