base.py 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292
  1. from abc import ABC, abstractmethod
  2. from typing import Any, List, Tuple, Optional, Union
  3. import uuid
  4. primitive_types = (int, float, str, bool)
  5. type_values = ("Integer", "Float", "String", "Boolean", "Type")
  6. Node = str
  7. Edge = str
  8. Element = Union[Node, Edge]
  9. class State(ABC):
  10. """
  11. Abstract base class for MvS CRUD interface defined in:
  12. http://msdl.cs.mcgill.ca/people/yentl/files/thesis.pdf
  13. This code is based on:
  14. https://msdl.uantwerpen.be/git/yentl/modelverse/src/master/state/modelverse_state
  15. """
  16. @staticmethod
  17. def new_id() -> str:
  18. """
  19. Generates a new UUID
  20. """
  21. return str(uuid.uuid4())
  22. @staticmethod
  23. def is_valid_datavalue(value: Any) -> bool:
  24. """
  25. Checks whether value type is supported.
  26. Args:
  27. value: value whose type needs to be checked
  28. Returns:
  29. True if value type is supported, False otherwise.
  30. """
  31. if isinstance(value, dict) and value.get("type", None) in type_values:
  32. return True
  33. if not isinstance(value, primitive_types):
  34. return False
  35. elif isinstance(value, int) and not (-2**63 <= value <= 2**63 - 1):
  36. return False
  37. return True
  38. def purge(self):
  39. """
  40. Implements a garbage collection routine for implementations that don't have automatic garbage collection.
  41. """
  42. pass
  43. # =========================================================================
  44. # CREATE
  45. # =========================================================================
  46. @abstractmethod
  47. def create_node(self) -> Node:
  48. """
  49. Creates node.
  50. Returns:
  51. The created node.
  52. """
  53. pass
  54. @abstractmethod
  55. def create_edge(self, source: Element, target: Element) -> Optional[Edge]:
  56. """
  57. Creates edge. Source and target elements should already exist.
  58. Args:
  59. source: source element of edge
  60. target: target element of edge
  61. Returns:
  62. The created edge, None if source or target element doesn't exist.
  63. """
  64. pass
  65. @abstractmethod
  66. def create_nodevalue(self, value: Any) -> Optional[Node]:
  67. """
  68. Creates node containing value.
  69. Args:
  70. value: value to assign to new node
  71. Returns:
  72. The created node, None if type of value is not supported.
  73. """
  74. pass
  75. @abstractmethod
  76. def create_dict(self, source: Element, value: Any, target: Element) -> None:
  77. """
  78. Creates named edge between two graph elements.
  79. Args:
  80. source: source element of edge
  81. value: edge label
  82. target: target element of edge
  83. Returns:
  84. Nothing.
  85. """
  86. pass
  87. # =========================================================================
  88. # READ
  89. # =========================================================================
  90. @abstractmethod
  91. def read_root(self) -> Node:
  92. """
  93. Reads state's root node.
  94. Returns:
  95. The state's root node.
  96. """
  97. pass
  98. @abstractmethod
  99. def read_value(self, node: Node) -> Optional[Any]:
  100. """
  101. Reads value of given node.
  102. Args:
  103. node: node whose value to read
  104. Returns:
  105. I node exists, value stored in node, else None.
  106. """
  107. pass
  108. @abstractmethod
  109. def read_outgoing(self, elem: Element) -> Optional[List[Edge]]:
  110. """
  111. Retrieves edges whose source is given element.
  112. Args:
  113. elem: source element of edges to retrieve
  114. Returns:
  115. If elem exists, list of edges whose source is elem, else None.
  116. """
  117. pass
  118. @abstractmethod
  119. def read_incoming(self, elem: Element) -> Optional[List[Edge]]:
  120. """
  121. Retrieves edges whose target is given element.
  122. Args:
  123. elem: target element of edges to retrieve
  124. Returns:
  125. If elem exists, list of edges whose target is elem, else None.
  126. """
  127. pass
  128. @abstractmethod
  129. def read_edge(self, edge: Edge) -> Tuple[Optional[Node], Optional[Node]]:
  130. """
  131. Reads source and target of given edge.
  132. Args:
  133. edge: edge whose source and target to read
  134. Returns:
  135. If edge exists, tuple containing source (first) and target (second) node, else (None, None)
  136. """
  137. pass
  138. @abstractmethod
  139. def read_dict(self, elem: Element, value: Any) -> Optional[Element]:
  140. """
  141. Reads element connected to given element through edge with label = value.
  142. Args:
  143. elem: source element
  144. value: edge label
  145. Returns:
  146. If elem doesn't exist or no edge is found with given label, None, else target element of edge with label = value originating from source.
  147. """
  148. pass
  149. @abstractmethod
  150. def read_dict_keys(self, elem: Element) -> Optional[List[Any]]:
  151. """
  152. Reads labels of outgoing edges starting in given node.
  153. Args:
  154. elem: source element
  155. Returns:
  156. If elem exists, list of (unique) edge labels, else None.
  157. """
  158. pass
  159. @abstractmethod
  160. def read_dict_edge(self, elem: Element, value: Any) -> Optional[Edge]:
  161. """
  162. Reads edge between two elements connected through edge with label = value.
  163. Args:
  164. elem: source element
  165. value: edge label
  166. Returns:
  167. If elem doesn't exist or no edge is found with given label, None, else edge with label = value originating from source.
  168. """
  169. pass
  170. @abstractmethod
  171. def read_dict_node(self, elem: Element, value_node: Node) -> Optional[Element]:
  172. """
  173. Reads element connected to given element through edge with label node = value_node.
  174. Args:
  175. elem: source element
  176. value_node: edge label node
  177. Returns:
  178. If elem exists, target element of edge with label stored in value_node originating from elem, else None.
  179. """
  180. pass
  181. @abstractmethod
  182. def read_dict_node_edge(self, elem: Element, value_node: Node) -> Optional[Edge]:
  183. """
  184. Reads edge connecting two elements through edge with label node = value_node.
  185. Args:
  186. elem: source element
  187. value_node: edge label node
  188. Returns:
  189. If elem exists, edge with label node = value_node, originating from source, else None.
  190. """
  191. pass
  192. @abstractmethod
  193. def read_reverse_dict(self, elem: Element, value: Any) -> Optional[List[Element]]:
  194. """
  195. Retrieves a list of all elements that have an outgoing edge, having label = value, towards the passed element.
  196. Args:
  197. elem: target element
  198. value: edge label
  199. Returns:
  200. If elem exists, list of elements with an outgoing edge with label = value towards elem, else None.
  201. """
  202. pass
  203. # =========================================================================
  204. # UPDATE
  205. # =========================================================================
  206. """
  207. Updates are done by performing subsequent CREATE and DELETE operations:
  208. http://msdl.cs.mcgill.ca/people/yentl/files/thesis.pdf
  209. """
  210. # =========================================================================
  211. # DELETE
  212. # =========================================================================
  213. @abstractmethod
  214. def delete_node(self, node: Node) -> None:
  215. """
  216. Deletes given node from state graph.
  217. Args:
  218. node: node to be deleted
  219. Returns:
  220. None
  221. """
  222. pass
  223. @abstractmethod
  224. def delete_edge(self, edge: Edge) -> None:
  225. """
  226. Deletes given edge from state graph.
  227. Args:
  228. edge: edge to be deleted
  229. Returns:
  230. None
  231. """
  232. pass