code0-hercules 1.3.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (258) hide show
  1. code0_hercules-1.3.0.dist-info/METADATA +79 -0
  2. code0_hercules-1.3.0.dist-info/RECORD +258 -0
  3. code0_hercules-1.3.0.dist-info/WHEEL +5 -0
  4. code0_hercules-1.3.0.dist-info/entry_points.txt +2 -0
  5. code0_hercules-1.3.0.dist-info/top_level.txt +1 -0
  6. hercules/__init__.py +81 -0
  7. hercules/_metadata.py +38 -0
  8. hercules/_tsgen/convert.mjs +96 -0
  9. hercules/_tsgen/package-lock.json +366 -0
  10. hercules/_tsgen/package.json +11 -0
  11. hercules/_tucana/__init__.py +29 -0
  12. hercules/_tucana/helpers.py +73 -0
  13. hercules/action.py +345 -0
  14. hercules/actions/__init__.py +21 -0
  15. hercules/actions/execution.py +121 -0
  16. hercules/actions/flow_execution.py +11 -0
  17. hercules/actions/flow_update.py +18 -0
  18. hercules/actions/module_configurations.py +14 -0
  19. hercules/actions/sub_flow_execution.py +14 -0
  20. hercules/cli.py +137 -0
  21. hercules/decorators/__init__.py +0 -0
  22. hercules/decorators/datatype.py +39 -0
  23. hercules/decorators/event.py +35 -0
  24. hercules/decorators/function.py +59 -0
  25. hercules/decorators/meta.py +82 -0
  26. hercules/decorators/runtime_event.py +15 -0
  27. hercules/decorators/runtime_function.py +18 -0
  28. hercules/definitions/__init__.py +162 -0
  29. hercules/definitions/cron_action/__init__.py +0 -0
  30. hercules/definitions/cron_action/data_types/__init__.py +0 -0
  31. hercules/definitions/cron_action/data_types/cron_day_of_month.py +11 -0
  32. hercules/definitions/cron_action/data_types/cron_day_of_week.py +11 -0
  33. hercules/definitions/cron_action/data_types/cron_hour.py +11 -0
  34. hercules/definitions/cron_action/data_types/cron_minute.py +11 -0
  35. hercules/definitions/cron_action/data_types/cron_month.py +11 -0
  36. hercules/definitions/cron_action/runtime_flow_types/__init__.py +0 -0
  37. hercules/definitions/cron_action/runtime_flow_types/cron.py +19 -0
  38. hercules/definitions/rest_action/__init__.py +0 -0
  39. hercules/definitions/rest_action/data_types/__init__.py +0 -0
  40. hercules/definitions/rest_action/data_types/rest_adapter_input.py +19 -0
  41. hercules/definitions/rest_action/data_types/rest_auth_type.py +19 -0
  42. hercules/definitions/rest_action/data_types/rest_auth_value.py +11 -0
  43. hercules/definitions/rest_action/runtime_flow_types/__init__.py +0 -0
  44. hercules/definitions/rest_action/runtime_flow_types/rest.py +20 -0
  45. hercules/definitions/rest_action/runtime_functions/__init__.py +0 -0
  46. hercules/definitions/rest_action/runtime_functions/rest_control_respond.py +21 -0
  47. hercules/definitions/taurus_boolean/__init__.py +0 -0
  48. hercules/definitions/taurus_boolean/data_types/__init__.py +0 -0
  49. hercules/definitions/taurus_boolean/data_types/boolean.py +11 -0
  50. hercules/definitions/taurus_boolean/runtime_functions/__init__.py +0 -0
  51. hercules/definitions/taurus_boolean/runtime_functions/std_boolean_as_number.py +18 -0
  52. hercules/definitions/taurus_boolean/runtime_functions/std_boolean_as_text.py +18 -0
  53. hercules/definitions/taurus_boolean/runtime_functions/std_boolean_from_number.py +18 -0
  54. hercules/definitions/taurus_boolean/runtime_functions/std_boolean_from_text.py +18 -0
  55. hercules/definitions/taurus_boolean/runtime_functions/std_boolean_is_equal.py +19 -0
  56. hercules/definitions/taurus_boolean/runtime_functions/std_boolean_negate.py +18 -0
  57. hercules/definitions/taurus_color/__init__.py +0 -0
  58. hercules/definitions/taurus_color/data_types/__init__.py +0 -0
  59. hercules/definitions/taurus_color/data_types/color.py +17 -0
  60. hercules/definitions/taurus_color/runtime_functions/__init__.py +0 -0
  61. hercules/definitions/taurus_color/runtime_functions/std_color_as_hex.py +18 -0
  62. hercules/definitions/taurus_color/runtime_functions/std_color_as_hsl.py +18 -0
  63. hercules/definitions/taurus_color/runtime_functions/std_color_as_rgb.py +18 -0
  64. hercules/definitions/taurus_color/runtime_functions/std_color_from_hex.py +18 -0
  65. hercules/definitions/taurus_color/runtime_functions/std_color_from_hsl.py +21 -0
  66. hercules/definitions/taurus_color/runtime_functions/std_color_from_rgb.py +21 -0
  67. hercules/definitions/taurus_control/__init__.py +0 -0
  68. hercules/definitions/taurus_control/data_types/__init__.py +0 -0
  69. hercules/definitions/taurus_control/data_types/comparator.py +21 -0
  70. hercules/definitions/taurus_control/data_types/consumer.py +20 -0
  71. hercules/definitions/taurus_control/data_types/predicate.py +20 -0
  72. hercules/definitions/taurus_control/data_types/runnable.py +13 -0
  73. hercules/definitions/taurus_control/data_types/transform.py +20 -0
  74. hercules/definitions/taurus_control/runtime_functions/__init__.py +0 -0
  75. hercules/definitions/taurus_control/runtime_functions/std_control_if.py +19 -0
  76. hercules/definitions/taurus_control/runtime_functions/std_control_if_else.py +21 -0
  77. hercules/definitions/taurus_control/runtime_functions/std_control_return.py +18 -0
  78. hercules/definitions/taurus_control/runtime_functions/std_control_stop.py +16 -0
  79. hercules/definitions/taurus_control/runtime_functions/std_control_value.py +18 -0
  80. hercules/definitions/taurus_date/__init__.py +0 -0
  81. hercules/definitions/taurus_date/data_types/__init__.py +0 -0
  82. hercules/definitions/taurus_date/data_types/date.py +11 -0
  83. hercules/definitions/taurus_date/data_types/hour.py +11 -0
  84. hercules/definitions/taurus_date/data_types/minute.py +11 -0
  85. hercules/definitions/taurus_date/data_types/month.py +22 -0
  86. hercules/definitions/taurus_date/runtime_functions/__init__.py +0 -0
  87. hercules/definitions/taurus_date/runtime_functions/std_date_format.py +20 -0
  88. hercules/definitions/taurus_date/runtime_functions/std_date_from.py +24 -0
  89. hercules/definitions/taurus_date/runtime_functions/std_date_from_text.py +19 -0
  90. hercules/definitions/taurus_date/runtime_functions/std_date_from_unix.py +18 -0
  91. hercules/definitions/taurus_date/runtime_functions/std_date_now.py +16 -0
  92. hercules/definitions/taurus_file/__init__.py +0 -0
  93. hercules/definitions/taurus_file/data_types/__init__.py +0 -0
  94. hercules/definitions/taurus_file/data_types/file.py +19 -0
  95. hercules/definitions/taurus_file/runtime_functions/__init__.py +0 -0
  96. hercules/definitions/taurus_file/runtime_functions/std_file_size.py +18 -0
  97. hercules/definitions/taurus_http/__init__.py +0 -0
  98. hercules/definitions/taurus_http/data_types/__init__.py +0 -0
  99. hercules/definitions/taurus_http/data_types/http_auth_place.py +12 -0
  100. hercules/definitions/taurus_http/data_types/http_auth_type.py +17 -0
  101. hercules/definitions/taurus_http/data_types/http_auth_value.py +11 -0
  102. hercules/definitions/taurus_http/data_types/http_method.py +16 -0
  103. hercules/definitions/taurus_http/data_types/http_payload.py +11 -0
  104. hercules/definitions/taurus_http/data_types/http_request.py +19 -0
  105. hercules/definitions/taurus_http/data_types/http_response.py +18 -0
  106. hercules/definitions/taurus_http/data_types/http_schema.py +31 -0
  107. hercules/definitions/taurus_http/data_types/http_status_code.py +13 -0
  108. hercules/definitions/taurus_http/data_types/http_url.py +13 -0
  109. hercules/definitions/taurus_http/runtime_functions/__init__.py +0 -0
  110. hercules/definitions/taurus_http/runtime_functions/http_request_send.py +26 -0
  111. hercules/definitions/taurus_list/__init__.py +0 -0
  112. hercules/definitions/taurus_list/data_types/__init__.py +0 -0
  113. hercules/definitions/taurus_list/data_types/list.py +13 -0
  114. hercules/definitions/taurus_list/runtime_functions/__init__.py +0 -0
  115. hercules/definitions/taurus_list/runtime_functions/std_list_at.py +20 -0
  116. hercules/definitions/taurus_list/runtime_functions/std_list_concat.py +20 -0
  117. hercules/definitions/taurus_list/runtime_functions/std_list_filter.py +19 -0
  118. hercules/definitions/taurus_list/runtime_functions/std_list_find.py +20 -0
  119. hercules/definitions/taurus_list/runtime_functions/std_list_find_index.py +19 -0
  120. hercules/definitions/taurus_list/runtime_functions/std_list_find_last.py +20 -0
  121. hercules/definitions/taurus_list/runtime_functions/std_list_first.py +18 -0
  122. hercules/definitions/taurus_list/runtime_functions/std_list_flat.py +18 -0
  123. hercules/definitions/taurus_list/runtime_functions/std_list_for_each.py +19 -0
  124. hercules/definitions/taurus_list/runtime_functions/std_list_index_of.py +19 -0
  125. hercules/definitions/taurus_list/runtime_functions/std_list_is_empty.py +18 -0
  126. hercules/definitions/taurus_list/runtime_functions/std_list_join.py +19 -0
  127. hercules/definitions/taurus_list/runtime_functions/std_list_last.py +18 -0
  128. hercules/definitions/taurus_list/runtime_functions/std_list_map.py +19 -0
  129. hercules/definitions/taurus_list/runtime_functions/std_list_max.py +18 -0
  130. hercules/definitions/taurus_list/runtime_functions/std_list_min.py +18 -0
  131. hercules/definitions/taurus_list/runtime_functions/std_list_pop.py +18 -0
  132. hercules/definitions/taurus_list/runtime_functions/std_list_push.py +19 -0
  133. hercules/definitions/taurus_list/runtime_functions/std_list_remove.py +19 -0
  134. hercules/definitions/taurus_list/runtime_functions/std_list_reverse.py +18 -0
  135. hercules/definitions/taurus_list/runtime_functions/std_list_size.py +18 -0
  136. hercules/definitions/taurus_list/runtime_functions/std_list_sort.py +19 -0
  137. hercules/definitions/taurus_list/runtime_functions/std_list_sort_reverse.py +19 -0
  138. hercules/definitions/taurus_list/runtime_functions/std_list_sum.py +18 -0
  139. hercules/definitions/taurus_list/runtime_functions/std_list_to_unique.py +18 -0
  140. hercules/definitions/taurus_number/__init__.py +0 -0
  141. hercules/definitions/taurus_number/data_types/__init__.py +0 -0
  142. hercules/definitions/taurus_number/data_types/number.py +11 -0
  143. hercules/definitions/taurus_number/runtime_functions/__init__.py +0 -0
  144. hercules/definitions/taurus_number/runtime_functions/std_number_abs.py +18 -0
  145. hercules/definitions/taurus_number/runtime_functions/std_number_add.py +19 -0
  146. hercules/definitions/taurus_number/runtime_functions/std_number_arccos.py +18 -0
  147. hercules/definitions/taurus_number/runtime_functions/std_number_arcsin.py +18 -0
  148. hercules/definitions/taurus_number/runtime_functions/std_number_arctan.py +18 -0
  149. hercules/definitions/taurus_number/runtime_functions/std_number_as_text.py +18 -0
  150. hercules/definitions/taurus_number/runtime_functions/std_number_clamp.py +20 -0
  151. hercules/definitions/taurus_number/runtime_functions/std_number_cos.py +18 -0
  152. hercules/definitions/taurus_number/runtime_functions/std_number_cosh.py +18 -0
  153. hercules/definitions/taurus_number/runtime_functions/std_number_divide.py +20 -0
  154. hercules/definitions/taurus_number/runtime_functions/std_number_euler.py +16 -0
  155. hercules/definitions/taurus_number/runtime_functions/std_number_exponential.py +19 -0
  156. hercules/definitions/taurus_number/runtime_functions/std_number_from_text.py +18 -0
  157. hercules/definitions/taurus_number/runtime_functions/std_number_has_digits.py +18 -0
  158. hercules/definitions/taurus_number/runtime_functions/std_number_is_equal.py +19 -0
  159. hercules/definitions/taurus_number/runtime_functions/std_number_is_greater.py +19 -0
  160. hercules/definitions/taurus_number/runtime_functions/std_number_is_less.py +19 -0
  161. hercules/definitions/taurus_number/runtime_functions/std_number_is_positive.py +18 -0
  162. hercules/definitions/taurus_number/runtime_functions/std_number_is_zero.py +18 -0
  163. hercules/definitions/taurus_number/runtime_functions/std_number_ln.py +18 -0
  164. hercules/definitions/taurus_number/runtime_functions/std_number_log.py +19 -0
  165. hercules/definitions/taurus_number/runtime_functions/std_number_max.py +19 -0
  166. hercules/definitions/taurus_number/runtime_functions/std_number_min.py +19 -0
  167. hercules/definitions/taurus_number/runtime_functions/std_number_modulo.py +19 -0
  168. hercules/definitions/taurus_number/runtime_functions/std_number_multiply.py +19 -0
  169. hercules/definitions/taurus_number/runtime_functions/std_number_negate.py +18 -0
  170. hercules/definitions/taurus_number/runtime_functions/std_number_pi.py +16 -0
  171. hercules/definitions/taurus_number/runtime_functions/std_number_random_number.py +19 -0
  172. hercules/definitions/taurus_number/runtime_functions/std_number_remove_digits.py +18 -0
  173. hercules/definitions/taurus_number/runtime_functions/std_number_root.py +19 -0
  174. hercules/definitions/taurus_number/runtime_functions/std_number_round.py +19 -0
  175. hercules/definitions/taurus_number/runtime_functions/std_number_round_down.py +19 -0
  176. hercules/definitions/taurus_number/runtime_functions/std_number_round_up.py +19 -0
  177. hercules/definitions/taurus_number/runtime_functions/std_number_sin.py +18 -0
  178. hercules/definitions/taurus_number/runtime_functions/std_number_sinh.py +18 -0
  179. hercules/definitions/taurus_number/runtime_functions/std_number_square.py +18 -0
  180. hercules/definitions/taurus_number/runtime_functions/std_number_square_root.py +18 -0
  181. hercules/definitions/taurus_number/runtime_functions/std_number_subtract.py +19 -0
  182. hercules/definitions/taurus_number/runtime_functions/std_number_tan.py +18 -0
  183. hercules/definitions/taurus_object/__init__.py +0 -0
  184. hercules/definitions/taurus_object/data_types/__init__.py +0 -0
  185. hercules/definitions/taurus_object/data_types/object.py +13 -0
  186. hercules/definitions/taurus_object/data_types/type.py +13 -0
  187. hercules/definitions/taurus_object/runtime_functions/__init__.py +0 -0
  188. hercules/definitions/taurus_object/runtime_functions/std_object_contains_key.py +19 -0
  189. hercules/definitions/taurus_object/runtime_functions/std_object_get.py +19 -0
  190. hercules/definitions/taurus_object/runtime_functions/std_object_keys.py +18 -0
  191. hercules/definitions/taurus_object/runtime_functions/std_object_set.py +20 -0
  192. hercules/definitions/taurus_object/runtime_functions/std_object_size.py +18 -0
  193. hercules/definitions/taurus_text/__init__.py +0 -0
  194. hercules/definitions/taurus_text/data_types/__init__.py +0 -0
  195. hercules/definitions/taurus_text/data_types/text.py +11 -0
  196. hercules/definitions/taurus_text/data_types/text_encoding.py +13 -0
  197. hercules/definitions/taurus_text/runtime_functions/__init__.py +0 -0
  198. hercules/definitions/taurus_text/runtime_functions/std_text_append.py +19 -0
  199. hercules/definitions/taurus_text/runtime_functions/std_text_as_bytes.py +18 -0
  200. hercules/definitions/taurus_text/runtime_functions/std_text_at.py +19 -0
  201. hercules/definitions/taurus_text/runtime_functions/std_text_byte_size.py +18 -0
  202. hercules/definitions/taurus_text/runtime_functions/std_text_capitalize.py +18 -0
  203. hercules/definitions/taurus_text/runtime_functions/std_text_chars.py +18 -0
  204. hercules/definitions/taurus_text/runtime_functions/std_text_contains.py +19 -0
  205. hercules/definitions/taurus_text/runtime_functions/std_text_decode.py +19 -0
  206. hercules/definitions/taurus_text/runtime_functions/std_text_encode.py +19 -0
  207. hercules/definitions/taurus_text/runtime_functions/std_text_ends_with.py +19 -0
  208. hercules/definitions/taurus_text/runtime_functions/std_text_from_ascii.py +18 -0
  209. hercules/definitions/taurus_text/runtime_functions/std_text_hex.py +18 -0
  210. hercules/definitions/taurus_text/runtime_functions/std_text_index_of.py +19 -0
  211. hercules/definitions/taurus_text/runtime_functions/std_text_insert.py +20 -0
  212. hercules/definitions/taurus_text/runtime_functions/std_text_is_equal.py +19 -0
  213. hercules/definitions/taurus_text/runtime_functions/std_text_length.py +18 -0
  214. hercules/definitions/taurus_text/runtime_functions/std_text_lowercase.py +18 -0
  215. hercules/definitions/taurus_text/runtime_functions/std_text_octal.py +18 -0
  216. hercules/definitions/taurus_text/runtime_functions/std_text_prepend.py +19 -0
  217. hercules/definitions/taurus_text/runtime_functions/std_text_remove.py +20 -0
  218. hercules/definitions/taurus_text/runtime_functions/std_text_replace.py +20 -0
  219. hercules/definitions/taurus_text/runtime_functions/std_text_replace_first.py +21 -0
  220. hercules/definitions/taurus_text/runtime_functions/std_text_replace_last.py +20 -0
  221. hercules/definitions/taurus_text/runtime_functions/std_text_reverse.py +18 -0
  222. hercules/definitions/taurus_text/runtime_functions/std_text_split.py +19 -0
  223. hercules/definitions/taurus_text/runtime_functions/std_text_starts_with.py +19 -0
  224. hercules/definitions/taurus_text/runtime_functions/std_text_swapcase.py +18 -0
  225. hercules/definitions/taurus_text/runtime_functions/std_text_to_ascii.py +18 -0
  226. hercules/definitions/taurus_text/runtime_functions/std_text_trim.py +18 -0
  227. hercules/definitions/taurus_text/runtime_functions/std_text_uppercase.py +18 -0
  228. hercules/events.py +23 -0
  229. hercules/internal/__init__.py +0 -0
  230. hercules/internal/connection.py +37 -0
  231. hercules/internal/literal.py +113 -0
  232. hercules/internal/module_builder.py +261 -0
  233. hercules/internal/pydantic_codegen.py +82 -0
  234. hercules/internal/schema.py +158 -0
  235. hercules/internal/tsgen.py +93 -0
  236. hercules/manager/__init__.py +0 -0
  237. hercules/manager/base.py +73 -0
  238. hercules/manager/config.py +45 -0
  239. hercules/manager/datatype.py +8 -0
  240. hercules/manager/event.py +8 -0
  241. hercules/manager/flow.py +12 -0
  242. hercules/manager/function.py +8 -0
  243. hercules/manager/runtime_event.py +8 -0
  244. hercules/manager/runtime_function.py +8 -0
  245. hercules/map/__init__.py +0 -0
  246. hercules/map/datatype.py +41 -0
  247. hercules/map/event.py +74 -0
  248. hercules/map/function.py +76 -0
  249. hercules/map/runtime_event.py +43 -0
  250. hercules/map/runtime_function.py +64 -0
  251. hercules/models/__init__.py +0 -0
  252. hercules/models/datatype.py +49 -0
  253. hercules/models/event.py +35 -0
  254. hercules/models/function.py +37 -0
  255. hercules/models/runtime_event.py +29 -0
  256. hercules/models/runtime_function.py +33 -0
  257. hercules/schema.py +38 -0
  258. hercules/types.py +88 -0
hercules/action.py ADDED
@@ -0,0 +1,345 @@
1
+ """The :class:`Action` — the SDK entry point (port of ``src/action.ts``)."""
2
+ from __future__ import annotations
3
+
4
+ import asyncio
5
+ import inspect
6
+ import os
7
+ import uuid
8
+ from dataclasses import replace
9
+ from typing import Callable, Dict, List, Optional
10
+
11
+ from hercules._metadata import get_metadata
12
+ from tucana.generated.aquila import action_pb2
13
+ from hercules._tucana.helpers import PlainValue, construct_value, to_allowed_value
14
+ from hercules.actions import actions as _packet_handlers
15
+ from hercules.events import CodeZeroEvent
16
+ from hercules.internal.connection import create_connection
17
+ from hercules.internal.module_builder import ModuleBuildData, build_module
18
+ from hercules.manager.config import ConfigManager
19
+ from hercules.manager.datatype import DataTypeManager
20
+ from hercules.manager.event import EventManager
21
+ from hercules.manager.flow import FlowManager
22
+ from hercules.manager.function import FunctionManager
23
+ from hercules.manager.runtime_event import RuntimeEventManager
24
+ from hercules.manager.runtime_function import RuntimeFunctionManager
25
+ from hercules.map.datatype import data_type_map
26
+ from hercules.map.event import event_map
27
+ from hercules.map.function import function_map
28
+ from hercules.map.runtime_event import runtime_event_map
29
+ from hercules.map.runtime_function import runtime_function_map
30
+ from hercules.models.event import EventModel
31
+ from hercules.models.function import FunctionProps
32
+ from hercules.types import ConfigurationDefinition, RuntimeError, Translation
33
+
34
+ # Registry of actions constructed while exporting, so the CLI can find them.
35
+ _REGISTERED_ACTIONS: List["Action"] = []
36
+
37
+
38
+ def is_export_mode() -> bool:
39
+ return os.environ.get("HERCULES_EXPORT") == "1"
40
+
41
+
42
+ def registered_actions() -> List["Action"]:
43
+ return list(_REGISTERED_ACTIONS)
44
+
45
+
46
+ class EventEmitter:
47
+ """A minimal Node-style event emitter."""
48
+
49
+ def __init__(self) -> None:
50
+ self._listeners: Dict[str, List[Callable]] = {}
51
+
52
+ def on(self, event, listener: Callable) -> "EventEmitter":
53
+ self._listeners.setdefault(str(event), []).append(listener)
54
+ return self
55
+
56
+ def once(self, event, listener: Callable) -> "EventEmitter":
57
+ def wrapper(*args):
58
+ self.off(event, wrapper)
59
+ return listener(*args)
60
+
61
+ return self.on(event, wrapper)
62
+
63
+ def off(self, event, listener: Callable) -> "EventEmitter":
64
+ listeners = self._listeners.get(str(event))
65
+ if listeners and listener in listeners:
66
+ listeners.remove(listener)
67
+ return self
68
+
69
+ def emit(self, event, *args) -> bool:
70
+ listeners = list(self._listeners.get(str(event), []))
71
+ for listener in listeners:
72
+ result = listener(*args)
73
+ if inspect.isawaitable(result):
74
+ asyncio.ensure_future(result)
75
+ return bool(listeners)
76
+
77
+
78
+ class Action(EventEmitter):
79
+ def __init__(
80
+ self,
81
+ identifier: str,
82
+ version: str,
83
+ aquila_url: Optional[str],
84
+ author: str,
85
+ icon: str,
86
+ documentation: str,
87
+ name: List[Translation],
88
+ configuration_definitions: Optional[List[ConfigurationDefinition]] = None,
89
+ ) -> None:
90
+ super().__init__()
91
+ self._identifier = identifier
92
+ self._version = version
93
+ self._aquila_url = aquila_url
94
+ self._author = author
95
+ self._icon = icon
96
+ self._documentation = documentation
97
+ self._name = name
98
+ self._configuration_definitions = [
99
+ c if isinstance(c, ConfigurationDefinition) else ConfigurationDefinition(**c)
100
+ for c in (configuration_definitions or [])
101
+ ]
102
+
103
+ self._channel = None
104
+ self._stream = None
105
+ self._actions = {a.packet_type: a.handle for a in _packet_handlers}
106
+ # Pending sub flow / flow execution requests awaiting a response, keyed by
107
+ # a per-invocation identifier: sub flow executions use the correlation
108
+ # identifier generated for each invocation (echoed back on the response),
109
+ # flow executions use their execution identifier. Both are unique per call,
110
+ # so each key maps to a single pending request.
111
+ self._pending_executions: Dict[str, asyncio.Future] = {}
112
+
113
+ self.configs = ConfigManager()
114
+ self.flows = FlowManager()
115
+ self.functions = FunctionManager()
116
+ self.runtime_functions = RuntimeFunctionManager()
117
+ self.data_types = DataTypeManager()
118
+ self.events = EventManager()
119
+ self.runtime_events = RuntimeEventManager()
120
+
121
+ if is_export_mode():
122
+ _REGISTERED_ACTIONS.append(self)
123
+
124
+ @property
125
+ def identifier(self) -> str:
126
+ return self._identifier
127
+
128
+ @property
129
+ def version(self) -> str:
130
+ return self._version
131
+
132
+ @property
133
+ def stream(self):
134
+ return self._stream
135
+
136
+ # --- registration ---------------------------------------------------------
137
+ def register_function(self, klass: type) -> None:
138
+ definition = function_map(klass)
139
+ self.functions.set(definition.runtime_name, definition)
140
+
141
+ def register_runtime_function(self, klass: type) -> None:
142
+ omit_definition = get_metadata("hercules:omit_function_definition", klass) or False
143
+ definition = runtime_function_map(klass)
144
+ self.runtime_functions.set(definition.runtime_name, definition)
145
+ if not omit_definition:
146
+ self.functions.set(
147
+ definition.runtime_name,
148
+ FunctionProps(
149
+ runtime_definition_name=definition.runtime_name,
150
+ runtime_name=definition.runtime_name,
151
+ signature=definition.signature,
152
+ throws_error=definition.throws_error,
153
+ name=definition.name,
154
+ description=definition.description,
155
+ documentation=definition.documentation,
156
+ deprecation_message=definition.deprecation_message,
157
+ display_message=definition.display_message,
158
+ alias=definition.alias,
159
+ display_icon=definition.display_icon,
160
+ design=definition.design,
161
+ parameters=[
162
+ replace(p, runtime_definition_name=p.runtime_name)
163
+ for p in (definition.parameters or [])
164
+ ],
165
+ ),
166
+ )
167
+
168
+ def register_data_type_class(self, klass: type) -> None:
169
+ definition = data_type_map(klass)
170
+ self.data_types.set(definition.identifier, definition)
171
+
172
+ def register_event_class(self, klass: type) -> None:
173
+ definition = event_map(klass)
174
+ self.events.set(definition.identifier, definition)
175
+
176
+ def register_runtime_event_class(self, klass: type) -> None:
177
+ omit_definition = get_metadata("hercules:omit_event_definition", klass) or False
178
+ definition = runtime_event_map(klass)
179
+ self.runtime_events.set(definition.identifier, definition)
180
+ if not omit_definition:
181
+ self.events.set(
182
+ definition.identifier,
183
+ EventModel(
184
+ identifier=definition.identifier,
185
+ signature=definition.signature,
186
+ settings=definition.settings,
187
+ editable=definition.editable,
188
+ name=definition.name,
189
+ description=definition.description,
190
+ documentation=definition.documentation,
191
+ display_message=definition.display_message,
192
+ alias=definition.alias,
193
+ display_icon=definition.display_icon,
194
+ runtime_identifier=definition.identifier,
195
+ ),
196
+ )
197
+
198
+ # --- runtime dispatch -----------------------------------------------------
199
+ async def send(self, request) -> None:
200
+ if self._stream is None:
201
+ raise RuntimeError("STREAM_NOT_CONNECTED", "Not connected. Call connect() first.")
202
+ await self._stream.write(request)
203
+
204
+ async def fire(self, target, payload: PlainValue = None):
205
+ """Fire the flow(s) bound to an event, or a single flow by id.
206
+
207
+ ``fire(event_class, payload)`` executes every registered flow whose
208
+ ``type`` matches the event class' identifier, resolving the results
209
+ together as a list. ``fire(flow_id, payload)`` executes just that flow and
210
+ returns its result. Flows are tracked from the ActionFlowUpdate messages
211
+ Aquila streams (see :attr:`flows`); each is executed via
212
+ :meth:`execute_flow`.
213
+ """
214
+ if self._stream is None:
215
+ raise Exception("Not connected. Call connect() first.")
216
+
217
+ # Single flow by id.
218
+ if isinstance(target, int) and not isinstance(target, bool):
219
+ return await self.execute_flow(target, payload)
220
+
221
+ # All flows bound to the event's flow type.
222
+ flow_type = get_metadata("hercules:identifier", target)
223
+ if not flow_type:
224
+ raise Exception(f"{target.__name__} is missing an @Identifier decorator.")
225
+ flows = self.flows.filter(lambda flow, _key: flow.type == flow_type)
226
+ return await asyncio.gather(*(self.execute_flow(flow.flow_id, payload) for flow in flows))
227
+
228
+ async def execute_sub_flow(self, sub_flow, *params: PlainValue) -> PlainValue:
229
+ if self._stream is None:
230
+ raise Exception("Not connected. Call connect() first.")
231
+ execution_identifier = sub_flow.execution_identifier
232
+ # Scope this individual invocation with a fresh correlation identifier so
233
+ # its response can be matched even when the same sub flow is executed
234
+ # repeatedly and responses arrive out of order.
235
+ correlation_identifier = str(uuid.uuid4())
236
+ result = self._await_execution_response(correlation_identifier)
237
+ request = action_pb2.ActionTransferRequest(
238
+ sub_flow_execution=action_pb2.ActionSubFlowExecutionRequest(
239
+ execution_identifier=execution_identifier,
240
+ parameters=[
241
+ construct_value(p if p is not None else None) for p in params
242
+ ],
243
+ correlation_identifier=correlation_identifier,
244
+ )
245
+ )
246
+ await self.send(request)
247
+ self.emit(CodeZeroEvent.stream_message_sent, request)
248
+ return await result
249
+
250
+ async def execute_flow(self, flow_id, payload: Optional[PlainValue] = None) -> PlainValue:
251
+ if self._stream is None:
252
+ raise Exception("Not connected. Call connect() first.")
253
+ execution_identifier = str(uuid.uuid4())
254
+ result = self._await_execution_response(execution_identifier)
255
+ request = action_pb2.ActionTransferRequest(
256
+ flow_execution=action_pb2.ActionFlowExecutionRequest(
257
+ execution_identifier=execution_identifier,
258
+ flow_id=str(flow_id),
259
+ payload=construct_value(payload if payload is not None else None),
260
+ )
261
+ )
262
+ await self.send(request)
263
+ self.emit(CodeZeroEvent.stream_message_sent, request)
264
+ return await result
265
+
266
+ def _await_execution_response(self, identifier: str) -> asyncio.Future:
267
+ future: asyncio.Future = asyncio.get_event_loop().create_future()
268
+ self._pending_executions[identifier] = future
269
+ return future
270
+
271
+ def resolve_execution_response(self, identifier: str, response) -> None:
272
+ pending = self._pending_executions.pop(identifier, None)
273
+ if pending is None:
274
+ self.emit(
275
+ CodeZeroEvent.error,
276
+ Exception(
277
+ f"Received execution response for unknown execution identifier: "
278
+ f"{identifier}"
279
+ ),
280
+ )
281
+ return
282
+ which = response.WhichOneof("result")
283
+ if which == "success":
284
+ pending.set_result(to_allowed_value(response.success))
285
+ elif which == "failure":
286
+ pending.set_exception(RuntimeError(response.failure.code, response.failure.message))
287
+ else:
288
+ pending.set_exception(Exception("Received execution response with no result"))
289
+
290
+ async def connect(
291
+ self,
292
+ auth_token: str,
293
+ aquila_url: Optional[str] = None,
294
+ grpc_options: Optional[list] = None,
295
+ ) -> None:
296
+ if is_export_mode():
297
+ return
298
+ url = aquila_url or self._aquila_url
299
+ if not url:
300
+ raise Exception("aquila_url must be provided in the constructor or connect()")
301
+
302
+ try:
303
+ channel, stream = await create_connection(
304
+ self.build_module(), auth_token, url, grpc_options
305
+ )
306
+ self._channel = channel
307
+ self._stream = stream
308
+ except Exception as err: # noqa: BLE001
309
+ self.emit(CodeZeroEvent.error, err)
310
+ raise
311
+
312
+ self.emit(CodeZeroEvent.connected, self)
313
+ await self._process_stream()
314
+
315
+ async def _process_stream(self) -> None:
316
+ async for message in self._stream:
317
+ self.emit(CodeZeroEvent.stream_message_received, message)
318
+ which = message.WhichOneof("data")
319
+ if which is None:
320
+ self.emit(
321
+ CodeZeroEvent.error,
322
+ Exception("Received unknown message type from stream"),
323
+ )
324
+ continue
325
+ handler = self._actions.get(which)
326
+ if handler is not None:
327
+ handler(self, getattr(message, which))
328
+
329
+ def build_module(self):
330
+ return build_module(
331
+ ModuleBuildData(
332
+ identifier=self._identifier,
333
+ version=self._version,
334
+ author=self._author,
335
+ icon=self._icon,
336
+ documentation=self._documentation,
337
+ name=self._name,
338
+ configuration_definitions=self._configuration_definitions,
339
+ data_types=self.data_types.values(),
340
+ events=self.events.values(),
341
+ runtime_events=self.runtime_events.values(),
342
+ functions=self.functions.values(),
343
+ runtime_functions=self.runtime_functions.values(),
344
+ )
345
+ )
@@ -0,0 +1,21 @@
1
+ """Action packet handlers (port of ``src/actions/index.ts``)."""
2
+ from __future__ import annotations
3
+
4
+ from hercules.actions import (
5
+ execution,
6
+ flow_execution,
7
+ flow_update,
8
+ module_configurations,
9
+ sub_flow_execution,
10
+ )
11
+
12
+ # Ordered list of handlers, keyed by their protobuf ``data`` oneof field name.
13
+ actions = [
14
+ module_configurations,
15
+ execution,
16
+ sub_flow_execution,
17
+ flow_execution,
18
+ flow_update,
19
+ ]
20
+
21
+ __all__ = ["actions"]
@@ -0,0 +1,121 @@
1
+ """Execution request handler (port of ``src/actions/Execution.ts``)."""
2
+ from __future__ import annotations
3
+
4
+ import asyncio
5
+ import inspect
6
+ import time
7
+
8
+ from hercules._tucana.helpers import construct_value
9
+ from tucana.generated.aquila import action_pb2
10
+ from tucana.generated.shared import errors_pb2, execution_result_pb2
11
+ from hercules.events import CodeZeroEvent
12
+ from hercules.internal.literal import resolve_node_value
13
+ from hercules.types import ProjectConfiguration, RuntimeError
14
+
15
+ packet_type = "execution"
16
+
17
+
18
+ def _now_micros() -> int:
19
+ return time.time_ns() // 1000
20
+
21
+
22
+ def _build_params(action, execution, func):
23
+ params = []
24
+ parameters = func.parameters or []
25
+ for index, _param in enumerate(parameters):
26
+ field = execution.parameters[index] if index < len(execution.parameters) else None
27
+ params.append(resolve_node_value(action, field))
28
+ return params
29
+
30
+
31
+ def _wants_context(handler, param_count: int) -> bool:
32
+ try:
33
+ sig = inspect.signature(handler)
34
+ except (TypeError, ValueError):
35
+ return False
36
+ positional = [
37
+ p
38
+ for p in sig.parameters.values()
39
+ if p.kind in (p.POSITIONAL_ONLY, p.POSITIONAL_OR_KEYWORD)
40
+ ]
41
+ # A `*args` handler has no fixed positionals and never gets a context.
42
+ return len(positional) == param_count + 1
43
+
44
+
45
+ def handle(action, execution) -> None:
46
+ action.emit(CodeZeroEvent.execution_request_received, execution)
47
+
48
+ func = action.runtime_functions.get(execution.function_identifier)
49
+ if func is None:
50
+ print("Received execution request for unknown function:", execution.function_identifier)
51
+ return
52
+
53
+ params = _build_params(action, execution, func)
54
+
55
+ conf = action.configs.get(execution.project_id) or ProjectConfiguration(
56
+ project_id=0,
57
+ config_values=[],
58
+ find_config=lambda identifier: None,
59
+ )
60
+
61
+ from hercules.types import FunctionContext
62
+
63
+ context = FunctionContext(
64
+ project_id=execution.project_id,
65
+ execution_id=execution.execution_identifier,
66
+ matched_config=conf,
67
+ execute_flow=lambda flow_id, payload=None: action.execute_flow(flow_id, payload),
68
+ )
69
+
70
+ started_at = _now_micros()
71
+ handler = func.handler
72
+ all_params = (
73
+ [context, *params] if _wants_context(handler, len(params)) else params
74
+ )
75
+
76
+ asyncio.ensure_future(_run(action, execution, handler, all_params, started_at))
77
+
78
+
79
+ async def _run(action, execution, handler, all_params, started_at) -> None:
80
+ try:
81
+ value = handler(*all_params)
82
+ if inspect.isawaitable(value):
83
+ value = await value
84
+ finished_at = _now_micros()
85
+ request = action_pb2.ActionTransferRequest(
86
+ result=action_pb2.ActionExecutionResponse(
87
+ execution_identifier=execution.execution_identifier,
88
+ node_result=execution_result_pb2.NodeExecutionResult(
89
+ started_at=started_at,
90
+ finished_at=finished_at,
91
+ parameter_results=[],
92
+ success=construct_value(value if value is not None else None),
93
+ ),
94
+ )
95
+ )
96
+ await action.send(request)
97
+ except Exception as error: # noqa: BLE001
98
+ finished_at = _now_micros()
99
+ is_runtime_error = isinstance(error, RuntimeError)
100
+ proto_error = errors_pb2.Error(
101
+ code=error.code if is_runtime_error else "UNKNOWN_ERROR",
102
+ category="RUNTIME",
103
+ message=(error.description or str(error)) if is_runtime_error else str(error),
104
+ timestamp=finished_at,
105
+ version=action.version,
106
+ )
107
+ request = action_pb2.ActionTransferRequest(
108
+ result=action_pb2.ActionExecutionResponse(
109
+ execution_identifier=execution.execution_identifier,
110
+ node_result=execution_result_pb2.NodeExecutionResult(
111
+ started_at=started_at,
112
+ finished_at=finished_at,
113
+ parameter_results=[],
114
+ error=proto_error,
115
+ ),
116
+ )
117
+ )
118
+ try:
119
+ await action.send(request)
120
+ except Exception as err: # noqa: BLE001
121
+ print("Failed to send execution error:", err)
@@ -0,0 +1,11 @@
1
+ """Flow execution response handler (port of ``src/actions/FlowExecution.ts``)."""
2
+ from __future__ import annotations
3
+
4
+ from hercules.events import CodeZeroEvent
5
+
6
+ packet_type = "flow_execution_response"
7
+
8
+
9
+ def handle(action, response) -> None:
10
+ action.emit(CodeZeroEvent.flow_execution_response_received, response)
11
+ action.resolve_execution_response(response.execution_identifier, response)
@@ -0,0 +1,18 @@
1
+ """Flow update handler (port of ``src/actions/FlowUpdate.ts``)."""
2
+ from __future__ import annotations
3
+
4
+ from hercules.events import CodeZeroEvent
5
+
6
+ packet_type = "flow_update"
7
+
8
+
9
+ def handle(action, update) -> None:
10
+ which = update.WhichOneof("data")
11
+ if which == "updated_flow":
12
+ flow = update.updated_flow
13
+ action.flows.set(flow.flow_id, flow)
14
+ action.emit(CodeZeroEvent.flow_updated, flow)
15
+ elif which == "deleted_flow":
16
+ flow_id = update.deleted_flow
17
+ action.flows.delete(flow_id)
18
+ action.emit(CodeZeroEvent.flow_deleted, flow_id)
@@ -0,0 +1,14 @@
1
+ """Module configurations handler.
2
+
3
+ Port of ``src/actions/ModuleConfigurations.ts``.
4
+ """
5
+ from __future__ import annotations
6
+
7
+ from hercules.events import CodeZeroEvent
8
+
9
+ packet_type = "module_configurations"
10
+
11
+
12
+ def handle(action, data) -> None:
13
+ action.configs.update(data.module_configurations)
14
+ action.emit(CodeZeroEvent.module_updated, data)
@@ -0,0 +1,14 @@
1
+ """Sub flow execution response handler.
2
+
3
+ Port of ``src/actions/SubFlowExecution.ts``.
4
+ """
5
+ from __future__ import annotations
6
+
7
+ from hercules.events import CodeZeroEvent
8
+
9
+ packet_type = "sub_flow_execution_response"
10
+
11
+
12
+ def handle(action, response) -> None:
13
+ action.emit(CodeZeroEvent.sub_flow_execution_response_received, response)
14
+ action.resolve_execution_response(response.correlation_identifier, response)
hercules/cli.py ADDED
@@ -0,0 +1,137 @@
1
+ """``hercules`` command line interface (port of ``src/cli.ts``).
2
+
3
+ Loads an entry file (without connecting to aquila) and prints the Action as a
4
+ JSON document in the shape of the tucana protobuf ``Module`` type.
5
+ """
6
+ from __future__ import annotations
7
+
8
+ import importlib.util
9
+ import os
10
+ import sys
11
+ from pathlib import Path
12
+
13
+ from google.protobuf import json_format
14
+
15
+ from hercules.action import Action, registered_actions
16
+
17
+ USAGE = """Usage: hercules export <entry-file> [options]
18
+
19
+ Loads the given entry file (without connecting to aquila) and prints the
20
+ Action as a JSON document in the shape of the tucana protobuf Module type.
21
+
22
+ Options:
23
+ -o, --out <file> Write the JSON to a file instead of stdout
24
+ --compact Emit compact JSON instead of pretty-printed
25
+ -h, --help Show this help message
26
+
27
+ Examples:
28
+ hercules export src/index.py
29
+ hercules export app.py -o module.json
30
+ """
31
+
32
+
33
+ def _fail(message: str):
34
+ sys.stderr.write(f"{message}\n")
35
+ raise SystemExit(1)
36
+
37
+
38
+ def _parse_export_args(argv):
39
+ entry = None
40
+ out = None
41
+ compact = False
42
+ i = 0
43
+ while i < len(argv):
44
+ arg = argv[i]
45
+ if arg in ("-h", "--help"):
46
+ sys.stdout.write(USAGE)
47
+ raise SystemExit(0)
48
+ elif arg in ("-o", "--out"):
49
+ i += 1
50
+ if i >= len(argv):
51
+ _fail(f"Missing value for {arg}\n\n{USAGE}")
52
+ out = argv[i]
53
+ elif arg == "--compact":
54
+ compact = True
55
+ elif arg.startswith("-"):
56
+ _fail(f"Unknown option: {arg}\n\n{USAGE}")
57
+ elif entry is None:
58
+ entry = arg
59
+ else:
60
+ _fail(f"Unexpected argument: {arg}\n\n{USAGE}")
61
+ i += 1
62
+ if entry is None:
63
+ _fail(f"Missing <entry-file> argument\n\n{USAGE}")
64
+ return entry, out, compact
65
+
66
+
67
+ def _is_action(value) -> bool:
68
+ if isinstance(value, Action):
69
+ return True
70
+ return (
71
+ callable(getattr(value, "build_module", None))
72
+ and isinstance(getattr(value, "identifier", None), str)
73
+ and callable(getattr(value, "register_function", None))
74
+ )
75
+
76
+
77
+ def _find_action(module) -> Action:
78
+ for value in vars(module).values():
79
+ if _is_action(value):
80
+ return value
81
+ registered = registered_actions()
82
+ if registered:
83
+ return registered[-1]
84
+ raise Exception(
85
+ "No Action found. Define your Action instance in the entry file or "
86
+ "construct it during module load."
87
+ )
88
+
89
+
90
+ def _load_entry(entry: str):
91
+ entry_path = Path(os.getcwd()) / entry
92
+ if not entry_path.exists():
93
+ _fail(f"Entry file not found: {entry_path}")
94
+ spec = importlib.util.spec_from_file_location("__hercules_entry__", str(entry_path))
95
+ module = importlib.util.module_from_spec(spec)
96
+ # Make the entry's own directory importable so relative sibling imports work.
97
+ sys.path.insert(0, str(entry_path.parent))
98
+ spec.loader.exec_module(module)
99
+ return module
100
+
101
+
102
+ def _run_export(argv):
103
+ entry, out, compact = _parse_export_args(argv)
104
+ # Must be set before the entry file is loaded: it makes connect() a no-op
105
+ # and registers constructed Action instances for _find_action.
106
+ os.environ["HERCULES_EXPORT"] = "1"
107
+ module = _load_entry(entry)
108
+ action = _find_action(module)
109
+ module_message = action.build_module()
110
+ json_text = json_format.MessageToJson(
111
+ module_message,
112
+ indent=None if compact else 2,
113
+ preserving_proto_field_name=False,
114
+ )
115
+ json_text = (json_text if compact else json_text) + "\n"
116
+ if out:
117
+ out_path = Path(os.getcwd()) / out
118
+ out_path.write_text(json_text)
119
+ sys.stderr.write(f"Module written to {out_path}\n")
120
+ else:
121
+ sys.stdout.write(json_text)
122
+
123
+
124
+ def main(argv=None):
125
+ argv = list(sys.argv[1:] if argv is None else argv)
126
+ if not argv or argv[0] in ("-h", "--help"):
127
+ sys.stdout.write(USAGE)
128
+ raise SystemExit(1 if not argv else 0)
129
+ command, *rest = argv
130
+ if command != "export":
131
+ _fail(f"Unknown command: {command}\n\n{USAGE}")
132
+ _run_export(rest)
133
+ raise SystemExit(0)
134
+
135
+
136
+ if __name__ == "__main__":
137
+ main()
File without changes