hgraph 0.0.1__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.
- hgraph/__about__.py +1 -0
- hgraph/__init__.py +6 -0
- hgraph/_builder/__init__.py +7 -0
- hgraph/_builder/_builder.py +29 -0
- hgraph/_builder/_graph_builder.py +82 -0
- hgraph/_builder/_input_builder.py +21 -0
- hgraph/_builder/_node_builder.py +38 -0
- hgraph/_builder/_output_builder.py +21 -0
- hgraph/_builder/_scalar_builder.py +19 -0
- hgraph/_builder/_ts_builder.py +210 -0
- hgraph/_impl/__init__.py +4 -0
- hgraph/_impl/_builder/__init__.py +5 -0
- hgraph/_impl/_builder/_graph_builder.py +56 -0
- hgraph/_impl/_builder/_map_builder.py +44 -0
- hgraph/_impl/_builder/_node_builder.py +87 -0
- hgraph/_impl/_builder/_switch_builder.py +46 -0
- hgraph/_impl/_builder/_ts_builder.py +296 -0
- hgraph/_impl/_impl_configuration.py +9 -0
- hgraph/_impl/_runtime/__init__.py +6 -0
- hgraph/_impl/_runtime/_common.py +34 -0
- hgraph/_impl/_runtime/_evaluation_clock.py +140 -0
- hgraph/_impl/_runtime/_evaluation_engine.py +122 -0
- hgraph/_impl/_runtime/_graph.py +134 -0
- hgraph/_impl/_runtime/_graph_executor.py +54 -0
- hgraph/_impl/_runtime/_map_node.py +152 -0
- hgraph/_impl/_runtime/_node.py +334 -0
- hgraph/_impl/_runtime/_switch_node.py +116 -0
- hgraph/_impl/_types/__init__.py +9 -0
- hgraph/_impl/_types/_input.py +169 -0
- hgraph/_impl/_types/_output.py +92 -0
- hgraph/_impl/_types/_ref.py +207 -0
- hgraph/_impl/_types/_scalar_value.py +49 -0
- hgraph/_impl/_types/_signal.py +17 -0
- hgraph/_impl/_types/_ts.py +73 -0
- hgraph/_impl/_types/_tsb.py +175 -0
- hgraph/_impl/_types/_tsd.py +260 -0
- hgraph/_impl/_types/_tsl.py +168 -0
- hgraph/_impl/_types/_tss.py +252 -0
- hgraph/_impl/graph_construction.md +40 -0
- hgraph/_runtime/__init__.py +11 -0
- hgraph/_runtime/_constants.py +10 -0
- hgraph/_runtime/_evaluation_clock.py +169 -0
- hgraph/_runtime/_evaluation_engine.py +339 -0
- hgraph/_runtime/_global_state.py +83 -0
- hgraph/_runtime/_graph.py +86 -0
- hgraph/_runtime/_graph_executor.py +90 -0
- hgraph/_runtime/_graph_runner.py +51 -0
- hgraph/_runtime/_lifecycle.py +149 -0
- hgraph/_runtime/_map.py +450 -0
- hgraph/_runtime/_node.py +227 -0
- hgraph/_runtime/_switch.py +134 -0
- hgraph/_types/__init__.py +20 -0
- hgraph/_types/_ref_meta_data.py +89 -0
- hgraph/_types/_ref_type.py +54 -0
- hgraph/_types/_scalar_type_meta_data.py +643 -0
- hgraph/_types/_scalar_types.py +94 -0
- hgraph/_types/_scalar_value.py +56 -0
- hgraph/_types/_schema_type.py +128 -0
- hgraph/_types/_time_series_meta_data.py +58 -0
- hgraph/_types/_time_series_types.py +406 -0
- hgraph/_types/_ts_meta_data.py +83 -0
- hgraph/_types/_ts_signal_meta_data.py +46 -0
- hgraph/_types/_ts_type.py +38 -0
- hgraph/_types/_ts_type_var_meta_data.py +62 -0
- hgraph/_types/_tsb_meta_data.py +154 -0
- hgraph/_types/_tsb_type.py +290 -0
- hgraph/_types/_tsd_meta_data.py +98 -0
- hgraph/_types/_tsd_type.py +202 -0
- hgraph/_types/_tsl_meta_data.py +118 -0
- hgraph/_types/_tsl_type.py +172 -0
- hgraph/_types/_tss_meta_data.py +77 -0
- hgraph/_types/_tss_type.py +106 -0
- hgraph/_types/_type_meta_data.py +95 -0
- hgraph/_types/_typing_utils.py +24 -0
- hgraph/_wiring/__init__.py +10 -0
- hgraph/_wiring/_decorators.py +245 -0
- hgraph/_wiring/_graph_builder.py +89 -0
- hgraph/_wiring/_map_wiring_node.py +64 -0
- hgraph/_wiring/_source_code_details.py +14 -0
- hgraph/_wiring/_stub_wiring_node.py +95 -0
- hgraph/_wiring/_switch_wiring_node.py +45 -0
- hgraph/_wiring/_wiring.py +743 -0
- hgraph/_wiring/_wiring_context.py +61 -0
- hgraph/_wiring/_wiring_errors.py +152 -0
- hgraph/_wiring/_wiring_node_signature.py +215 -0
- hgraph/_wiring/_wiring_utils.py +101 -0
- hgraph/nodes/__init__.py +14 -0
- hgraph/nodes/_const.py +22 -0
- hgraph/nodes/_drop_dups.py +13 -0
- hgraph/nodes/_format.py +16 -0
- hgraph/nodes/_graph.py +9 -0
- hgraph/nodes/_math.py +35 -0
- hgraph/nodes/_operators.py +42 -0
- hgraph/nodes/_pass_through.py +10 -0
- hgraph/nodes/_print.py +19 -0
- hgraph/nodes/_record.py +30 -0
- hgraph/nodes/_replay.py +53 -0
- hgraph/nodes/_set_operators.py +35 -0
- hgraph/nodes/_stream_operators.py +30 -0
- hgraph/nodes/_tsd_operators.py +40 -0
- hgraph/nodes/_tsl_operators.py +20 -0
- hgraph/nodes/_write.py +32 -0
- hgraph/test/__init__.py +1 -0
- hgraph/test/_node_unit_tester.py +93 -0
- hgraph/test/testing.md +19 -0
- hgraph-0.0.1.dist-info/METADATA +42 -0
- hgraph-0.0.1.dist-info/RECORD +109 -0
- hgraph-0.0.1.dist-info/WHEEL +4 -0
- hgraph-0.0.1.dist-info/licenses/LICENSE +21 -0
hgraph/__about__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.0.1"
|
hgraph/__init__.py
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
from hgraph._builder._builder import *
|
|
2
|
+
from hgraph._builder._graph_builder import *
|
|
3
|
+
from hgraph._builder._input_builder import *
|
|
4
|
+
from hgraph._builder._node_builder import *
|
|
5
|
+
from hgraph._builder._output_builder import *
|
|
6
|
+
from hgraph._builder._scalar_builder import *
|
|
7
|
+
from hgraph._builder._ts_builder import *
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
2
|
+
from typing import TypeVar, Generic
|
|
3
|
+
|
|
4
|
+
ITEM = TypeVar('ITEM')
|
|
5
|
+
|
|
6
|
+
__all__ = ("Builder",)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class Builder(ABC, Generic[ITEM]):
|
|
10
|
+
"""
|
|
11
|
+
The builder is responsible for constructing and initialising the item type it is responsible for.
|
|
12
|
+
It is also responsible for destroying and cleaning up the resources associated to the item.
|
|
13
|
+
These can be thought of as life-cycle methods.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
@abstractmethod
|
|
17
|
+
def make_instance(self, **kwargs) -> ITEM:
|
|
18
|
+
"""
|
|
19
|
+
Create a new instance of the item.
|
|
20
|
+
And additional attributes required for construction are passed in as kwargs.
|
|
21
|
+
Actual instance of the builder will fix these args for all instances of builder for the type.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
@abstractmethod
|
|
25
|
+
def release_instance(self, item: ITEM):
|
|
26
|
+
"""
|
|
27
|
+
Release the item and it's resources.
|
|
28
|
+
"""
|
|
29
|
+
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import typing
|
|
2
|
+
from abc import abstractmethod
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
|
|
5
|
+
from hgraph._builder._builder import Builder
|
|
6
|
+
from hgraph._runtime._graph import Graph
|
|
7
|
+
|
|
8
|
+
if typing.TYPE_CHECKING:
|
|
9
|
+
from hgraph._runtime._node import Node
|
|
10
|
+
from hgraph._builder._node_builder import NodeBuilder
|
|
11
|
+
from hgraph._types._scalar_types import SCALAR
|
|
12
|
+
|
|
13
|
+
__all__ = ("Edge", "GraphBuilder", "GraphBuilderFactory")
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@dataclass(frozen=True)
|
|
17
|
+
class Edge:
|
|
18
|
+
src_node: int
|
|
19
|
+
output_path: tuple["SCALAR", ...]
|
|
20
|
+
dst_node: int
|
|
21
|
+
input_path: tuple["SCALAR", ...]
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@dataclass(frozen=True)
|
|
25
|
+
class GraphBuilder(Builder["Graph"]):
|
|
26
|
+
node_builders: tuple["NodeBuilder", ...]
|
|
27
|
+
edges: tuple[Edge, ...]
|
|
28
|
+
|
|
29
|
+
@abstractmethod
|
|
30
|
+
def make_instance(self, graph_id: tuple[int, ...], parent_node: "Node" = None) -> "Graph":
|
|
31
|
+
"""
|
|
32
|
+
Construct an instance of a graph. The id provided is the id for the graph instance to be constructed.
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
@abstractmethod
|
|
36
|
+
def release_instance(self, item: "Graph"):
|
|
37
|
+
"""
|
|
38
|
+
Release resources constructed during the build process, plus the graph.
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class GraphBuilderFactory:
|
|
43
|
+
|
|
44
|
+
_graph_builder_class: typing.Optional[typing.Type[GraphBuilder]] = None
|
|
45
|
+
|
|
46
|
+
@staticmethod
|
|
47
|
+
def default():
|
|
48
|
+
from hgraph._impl._builder._graph_builder import PythonGraphBuilder
|
|
49
|
+
return PythonGraphBuilder
|
|
50
|
+
|
|
51
|
+
@staticmethod
|
|
52
|
+
def is_declared() -> bool:
|
|
53
|
+
return GraphBuilderFactory._graph_builder_class is not None
|
|
54
|
+
|
|
55
|
+
@staticmethod
|
|
56
|
+
def declared() -> typing.Type[GraphBuilder]:
|
|
57
|
+
if GraphBuilderFactory._graph_builder_class is None:
|
|
58
|
+
raise RuntimeError("No graph builder type has been declared")
|
|
59
|
+
return GraphBuilderFactory._graph_builder_class
|
|
60
|
+
|
|
61
|
+
@staticmethod
|
|
62
|
+
def declare(cls: typing.Type[GraphBuilder]):
|
|
63
|
+
if GraphBuilderFactory._graph_builder_class is not None:
|
|
64
|
+
raise RuntimeError("A graph builder type has already been declared")
|
|
65
|
+
GraphBuilderFactory._graph_builder_class = cls
|
|
66
|
+
|
|
67
|
+
@staticmethod
|
|
68
|
+
def un_declare():
|
|
69
|
+
GraphBuilderFactory._graph_builder_class = None
|
|
70
|
+
|
|
71
|
+
@staticmethod
|
|
72
|
+
def make(node_builders: tuple["NodeBuilder", ...], edges: tuple[Edge, ...]) -> GraphBuilder:
|
|
73
|
+
"""
|
|
74
|
+
Make a graph builder instance. If no graph builder class is declared, the default builder will be used.
|
|
75
|
+
:param node_builders: The node builders to use
|
|
76
|
+
:param edges: The edges to use for binding the node outputs to inputs.
|
|
77
|
+
:return: The GraphBuilder instance.
|
|
78
|
+
"""
|
|
79
|
+
if GraphBuilderFactory.is_declared():
|
|
80
|
+
return GraphBuilderFactory.declared()(node_builders=node_builders, edges=edges)
|
|
81
|
+
else:
|
|
82
|
+
return GraphBuilderFactory.default()(node_builders=node_builders, edges=edges)
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import typing
|
|
2
|
+
|
|
3
|
+
from _pytest.nodes import Node
|
|
4
|
+
|
|
5
|
+
from hgraph._builder._builder import Builder
|
|
6
|
+
|
|
7
|
+
if typing.TYPE_CHECKING:
|
|
8
|
+
from hgraph._types._time_series_types import TimeSeriesInput
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
__all__ = ('InputBuilder',)
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class InputBuilder(Builder["TimeSeriesInput"]):
|
|
15
|
+
|
|
16
|
+
def make_instance(self, owning_node: Node = None, owning_input: "TimeSeriesInput" = None) -> "TimeSeriesInput":
|
|
17
|
+
"""One of owning_node or owning_input must be defined."""
|
|
18
|
+
pass
|
|
19
|
+
|
|
20
|
+
def release_instance(self, item: "TimeSeriesInput"):
|
|
21
|
+
pass
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
from abc import abstractmethod
|
|
2
|
+
from dataclasses import dataclass
|
|
3
|
+
from typing import Optional, Any, Mapping, List, Tuple, TypeVar
|
|
4
|
+
|
|
5
|
+
from hgraph._builder._builder import Builder
|
|
6
|
+
from hgraph._builder._input_builder import InputBuilder
|
|
7
|
+
from hgraph._builder._output_builder import OutputBuilder
|
|
8
|
+
from hgraph._runtime._node import NodeSignature, Node
|
|
9
|
+
|
|
10
|
+
__all__ = ("NodeBuilder",)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
NODE = TypeVar("NODE", bound=Node)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@dataclass(frozen=True)
|
|
17
|
+
class NodeBuilder(Builder[NODE]):
|
|
18
|
+
node_ndx: int
|
|
19
|
+
signature: NodeSignature
|
|
20
|
+
scalars: Mapping[str, Any]
|
|
21
|
+
input_builder: Optional[InputBuilder] = None
|
|
22
|
+
output_builder: Optional[OutputBuilder] = None
|
|
23
|
+
|
|
24
|
+
@abstractmethod
|
|
25
|
+
def make_instance(self, owning_graph_id: tuple[int, ...]) -> NODE:
|
|
26
|
+
"""
|
|
27
|
+
Construct an instance of a node. The id provided is the id for the node instance to be constructed.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
@abstractmethod
|
|
31
|
+
def release_instance(self, item: NODE):
|
|
32
|
+
"""
|
|
33
|
+
Release any resources constructed during the build process, plus the node.
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
# TODO: Need to ensure that each type of NodeBuilder is described in the abstract and a factory is provided
|
|
38
|
+
# to provide instances of the builder to allow us to support multiple engines.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from typing import TYPE_CHECKING
|
|
2
|
+
|
|
3
|
+
from hgraph._builder._builder import Builder
|
|
4
|
+
|
|
5
|
+
if TYPE_CHECKING:
|
|
6
|
+
from hgraph._runtime._node import Node
|
|
7
|
+
from hgraph._types._time_series_types import TimeSeriesOutput
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
__all__ = ('OutputBuilder',)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class OutputBuilder(Builder["TimeSeriesOutput"]):
|
|
14
|
+
|
|
15
|
+
def make_instance(self, owning_node: "Node" = None, owning_output: "TimeSeriesOutput" = None) -> "TimeSeriesOutput":
|
|
16
|
+
"""One of owning_node or owning_output must be defined."""
|
|
17
|
+
pass
|
|
18
|
+
|
|
19
|
+
def release_instance(self, item: "TimeSeriesOutput"):
|
|
20
|
+
pass
|
|
21
|
+
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from typing import TYPE_CHECKING
|
|
2
|
+
|
|
3
|
+
from hgraph._builder._builder import Builder
|
|
4
|
+
|
|
5
|
+
if TYPE_CHECKING:
|
|
6
|
+
from hgraph._types._scalar_value import ScalarValue
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
__all__ = ('ScalarValueBuilder',)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class ScalarValueBuilder(Builder["ScalarValue"]):
|
|
13
|
+
|
|
14
|
+
def make_instance(self) -> "ScalarValue":
|
|
15
|
+
"""A scalar value is a basic type"""
|
|
16
|
+
pass
|
|
17
|
+
|
|
18
|
+
def release_instance(self, item: "ScalarValue"):
|
|
19
|
+
pass
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
2
|
+
from dataclasses import dataclass
|
|
3
|
+
from typing import Optional, TYPE_CHECKING
|
|
4
|
+
|
|
5
|
+
from _pytest.nodes import Node
|
|
6
|
+
|
|
7
|
+
from hgraph._builder._input_builder import InputBuilder
|
|
8
|
+
from hgraph._builder._output_builder import OutputBuilder
|
|
9
|
+
|
|
10
|
+
if TYPE_CHECKING:
|
|
11
|
+
from hgraph._types._scalar_type_meta_data import HgScalarTypeMetaData
|
|
12
|
+
from hgraph._types._time_series_meta_data import HgTimeSeriesTypeMetaData
|
|
13
|
+
from hgraph._types._time_series_types import TimeSeriesInput, TimeSeriesOutput
|
|
14
|
+
from hgraph._types._tsb_type import TimeSeriesSchema
|
|
15
|
+
|
|
16
|
+
__all__ = ("TSOutputBuilder", "TSInputBuilder", "TimeSeriesBuilderFactory", "TSSInputBuilder", "TSLOutputBuilder",
|
|
17
|
+
"TSLInputBuilder", "TSBOutputBuilder", "TSBInputBuilder", "TSSOutputBuilder", "TSSInputBuilder",
|
|
18
|
+
"TSSignalInputBuilder", "TSDOutputBuilder", "TSDInputBuilder")
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@dataclass(frozen=True)
|
|
22
|
+
class TSOutputBuilder(OutputBuilder):
|
|
23
|
+
|
|
24
|
+
value_tp: "HgScalarTypeMetaData"
|
|
25
|
+
|
|
26
|
+
def make_instance(self, owning_node=None, owning_output=None) -> "TimeSeriesOutput":
|
|
27
|
+
pass
|
|
28
|
+
|
|
29
|
+
def release_instance(self, item: "TimeSeriesOutput"):
|
|
30
|
+
pass
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@dataclass(frozen=True)
|
|
34
|
+
class TSInputBuilder(InputBuilder):
|
|
35
|
+
|
|
36
|
+
value_tp: "HgScalarTypeMetaData"
|
|
37
|
+
|
|
38
|
+
def make_instance(self, owning_node: Node = None, owning_input: "TimeSeriesInput" = None) -> "TimeSeriesInput":
|
|
39
|
+
...
|
|
40
|
+
|
|
41
|
+
def release_instance(self, item: "TimeSeriesInput"):
|
|
42
|
+
...
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class TSSignalInputBuilder(InputBuilder):
|
|
46
|
+
|
|
47
|
+
def make_instance(self, owning_node: Node = None, owning_input: "TimeSeriesInput" = None) -> "TimeSeriesInput":
|
|
48
|
+
...
|
|
49
|
+
|
|
50
|
+
def release_instance(self, item: "TimeSeriesInput"):
|
|
51
|
+
...
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
@dataclass(frozen=True)
|
|
55
|
+
class TSBInputBuilder(InputBuilder):
|
|
56
|
+
|
|
57
|
+
schema: "TimeSeriesSchema"
|
|
58
|
+
|
|
59
|
+
def make_instance(self, owning_node: Node = None, owning_input: "TimeSeriesInput" = None) -> "TimeSeriesInput":
|
|
60
|
+
...
|
|
61
|
+
|
|
62
|
+
def release_instance(self, item: "TimeSeriesInput"):
|
|
63
|
+
...
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
@dataclass(frozen=True)
|
|
67
|
+
class TSBOutputBuilder(OutputBuilder):
|
|
68
|
+
|
|
69
|
+
schema: "TimeSeriesSchema"
|
|
70
|
+
|
|
71
|
+
def make_instance(self, owning_node: Node = None, owning_output: "TimeSeriesOutput" = None) -> "TimeSeriesOutput":
|
|
72
|
+
...
|
|
73
|
+
|
|
74
|
+
def release_instance(self, item: "TimeSeriesOutput"):
|
|
75
|
+
...
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
@dataclass(frozen=True)
|
|
79
|
+
class TSLInputBuilder(InputBuilder):
|
|
80
|
+
|
|
81
|
+
value_tp: "HgTimeSeriesTypeMetaData"
|
|
82
|
+
size_tp: "HgScalarTypeMetaData"
|
|
83
|
+
|
|
84
|
+
def make_instance(self, owning_node: Node = None, owning_input: "TimeSeriesInput" = None) -> "TimeSeriesInput":
|
|
85
|
+
...
|
|
86
|
+
|
|
87
|
+
def release_instance(self, item: "TimeSeriesInput"):
|
|
88
|
+
...
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
@dataclass(frozen=True)
|
|
92
|
+
class TSLOutputBuilder(OutputBuilder):
|
|
93
|
+
|
|
94
|
+
value_tp: "HgTimeSeriesTypeMetaData"
|
|
95
|
+
size_tp: "HgScalarTypeMetaData"
|
|
96
|
+
|
|
97
|
+
def make_instance(self, owning_node: Node = None, owning_output: "TimeSeriesOutput" = None) -> "TimeSeriesOutput":
|
|
98
|
+
...
|
|
99
|
+
|
|
100
|
+
def release_instance(self, item: "TimeSeriesOutput"):
|
|
101
|
+
...
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
class TSDInputBuilder(InputBuilder):
|
|
105
|
+
|
|
106
|
+
key_tp: "HgScalarTypeMetaData"
|
|
107
|
+
value_tp: "HgTimeSeriesTypeMetaData"
|
|
108
|
+
|
|
109
|
+
def make_instance(self, owning_node: Node = None, owning_input: "TimeSeriesInput" = None) -> "TimeSeriesInput":
|
|
110
|
+
...
|
|
111
|
+
|
|
112
|
+
def release_instance(self, item: "TimeSeriesInput"):
|
|
113
|
+
...
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
@dataclass(frozen=True)
|
|
117
|
+
class TSDOutputBuilder(OutputBuilder):
|
|
118
|
+
|
|
119
|
+
key_tp: "HgScalarTypeMetaData"
|
|
120
|
+
value_tp: "HgTimeSeriesTypeMetaData"
|
|
121
|
+
|
|
122
|
+
def make_instance(self, owning_node: Node = None, owning_output: "TimeSeriesOutput" = None) -> "TimeSeriesOutput":
|
|
123
|
+
...
|
|
124
|
+
|
|
125
|
+
def release_instance(self, item: "TimeSeriesOutput"):
|
|
126
|
+
...
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
@dataclass(frozen=True)
|
|
130
|
+
class TSSOutputBuilder(OutputBuilder):
|
|
131
|
+
|
|
132
|
+
value_tp: "HgScalarTypeMetaData"
|
|
133
|
+
|
|
134
|
+
def make_instance(self, owning_node: Node = None, owning_output: "TimeSeriesOutput" = None) -> "TimeSeriesOutput":
|
|
135
|
+
...
|
|
136
|
+
|
|
137
|
+
def release_instance(self, item: "TimeSeriesOutput"):
|
|
138
|
+
...
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
@dataclass(frozen=True)
|
|
142
|
+
class TSSInputBuilder(InputBuilder):
|
|
143
|
+
|
|
144
|
+
value_tp: "HgScalarTypeMetaData"
|
|
145
|
+
|
|
146
|
+
def make_instance(self, owning_node: Node = None, owning_input: "TimeSeriesInput" = None) -> "TimeSeriesInput":
|
|
147
|
+
...
|
|
148
|
+
|
|
149
|
+
def release_instance(self, item: "TimeSeriesInput"):
|
|
150
|
+
...
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
class REFInputBuilder(InputBuilder):
|
|
154
|
+
value_tp: "HgTimeSeriesTypeMetaData"
|
|
155
|
+
|
|
156
|
+
def make_instance(self, owning_node: Node = None, owning_input: "TimeSeriesInput" = None) -> "TimeSeriesInput":
|
|
157
|
+
...
|
|
158
|
+
|
|
159
|
+
def release_instance(self, item: "TimeSeriesInput"):
|
|
160
|
+
...
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
@dataclass(frozen=True)
|
|
164
|
+
class REFOutputBuilder(OutputBuilder):
|
|
165
|
+
value_tp: "HgTimeSeriesTypeMetaData"
|
|
166
|
+
|
|
167
|
+
def make_instance(self, owning_node: Node = None, owning_output: "TimeSeriesOutput" = None) -> "TimeSeriesOutput":
|
|
168
|
+
...
|
|
169
|
+
|
|
170
|
+
def release_instance(self, item: "TimeSeriesOutput"):
|
|
171
|
+
...
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
class TimeSeriesBuilderFactory:
|
|
175
|
+
|
|
176
|
+
_instance: Optional["TimeSeriesBuilderFactory"] = None
|
|
177
|
+
|
|
178
|
+
@staticmethod
|
|
179
|
+
def declare_default_factory():
|
|
180
|
+
from hgraph._impl._builder._ts_builder import PythonTimeSeriesBuilderFactory
|
|
181
|
+
TimeSeriesBuilderFactory.declare(PythonTimeSeriesBuilderFactory())
|
|
182
|
+
|
|
183
|
+
@staticmethod
|
|
184
|
+
def has_instance() -> bool:
|
|
185
|
+
return TimeSeriesBuilderFactory._instance is not None
|
|
186
|
+
|
|
187
|
+
@staticmethod
|
|
188
|
+
def instance() -> "TimeSeriesBuilderFactory":
|
|
189
|
+
if TimeSeriesBuilderFactory._instance is None:
|
|
190
|
+
raise RuntimeError("No time-series builder factory has been declared")
|
|
191
|
+
return TimeSeriesBuilderFactory._instance
|
|
192
|
+
|
|
193
|
+
@staticmethod
|
|
194
|
+
def declare(factory: "TimeSeriesBuilderFactory"):
|
|
195
|
+
if TimeSeriesBuilderFactory._instance is not None:
|
|
196
|
+
raise RuntimeError("A time-series builder factory has already been declared")
|
|
197
|
+
TimeSeriesBuilderFactory._instance = factory
|
|
198
|
+
|
|
199
|
+
@staticmethod
|
|
200
|
+
def un_declare():
|
|
201
|
+
TimeSeriesBuilderFactory._instance = None
|
|
202
|
+
|
|
203
|
+
@abstractmethod
|
|
204
|
+
def make_input_builder(self, value_tp: "HgTimeSeriesTypeMetaData") -> TSInputBuilder:
|
|
205
|
+
"""Return an instance of an input builder for the given type"""
|
|
206
|
+
|
|
207
|
+
@abstractmethod
|
|
208
|
+
def make_output_builder(self, value_tp: "HgTimeSeriesTypeMetaData") -> TSOutputBuilder:
|
|
209
|
+
"""Return an instance of an output builder for the given type"""
|
|
210
|
+
|
hgraph/_impl/__init__.py
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
|
|
3
|
+
from hgraph._builder._graph_builder import GraphBuilder
|
|
4
|
+
from hgraph._impl._runtime._graph import PythonGraph
|
|
5
|
+
from hgraph._runtime._graph import Graph
|
|
6
|
+
from hgraph._runtime._node import Node
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
__all__ = ("PythonGraphBuilder",)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@dataclass(frozen=True)
|
|
13
|
+
class PythonGraphBuilder(GraphBuilder):
|
|
14
|
+
"""
|
|
15
|
+
Builds a graph (set of nodes with edges)
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
@staticmethod
|
|
19
|
+
def _extract_output(node: Node, path: [int]):
|
|
20
|
+
if not path:
|
|
21
|
+
raise RuntimeError("No path to find an output for")
|
|
22
|
+
output = node.output
|
|
23
|
+
for item in path:
|
|
24
|
+
output = output[item]
|
|
25
|
+
return output
|
|
26
|
+
|
|
27
|
+
@staticmethod
|
|
28
|
+
def _extract_input(node: Node, path: [int]):
|
|
29
|
+
if not path:
|
|
30
|
+
raise RuntimeError("No path to find an input for")
|
|
31
|
+
input = node.input
|
|
32
|
+
for item in path:
|
|
33
|
+
input = input[item]
|
|
34
|
+
return input
|
|
35
|
+
|
|
36
|
+
def make_instance(self, graph_id: tuple[int, ...], parent_node: Node = None) -> Graph:
|
|
37
|
+
nodes = [nb.make_instance(graph_id) for nb in self.node_builders]
|
|
38
|
+
for edge in self.edges:
|
|
39
|
+
src_node: Node = nodes[edge.src_node]
|
|
40
|
+
dst_node: Node = nodes[edge.dst_node]
|
|
41
|
+
# TODO: Should we normalise outputs to always be an UnnamedBundleOutput? For now if the path is tuple() assume
|
|
42
|
+
# the output is the node output
|
|
43
|
+
output = src_node.output if edge.output_path == tuple() else self._extract_output(src_node, edge.output_path)
|
|
44
|
+
input_ = self._extract_input(dst_node, edge.input_path)
|
|
45
|
+
input_.bind_output(output)
|
|
46
|
+
# The nodes are initialised within the context of the graph
|
|
47
|
+
return PythonGraph(graph_id=graph_id, nodes=tuple(nodes), parent_node=parent_node)
|
|
48
|
+
|
|
49
|
+
def release_instance(self, item: Graph):
|
|
50
|
+
for node, node_builder in zip(item.nodes, self.node_builders):
|
|
51
|
+
node_builder.release_instance(node)
|
|
52
|
+
item.dispose()
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from typing import TYPE_CHECKING, Mapping, cast
|
|
3
|
+
|
|
4
|
+
from hgraph._builder._node_builder import NodeBuilder
|
|
5
|
+
from hgraph._impl._runtime._map_node import PythonMapNodeImpl
|
|
6
|
+
from hgraph._types._time_series_types import TimeSeriesOutput
|
|
7
|
+
from hgraph._types._tsb_type import TimeSeriesBundleInput
|
|
8
|
+
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
from hgraph._builder._graph_builder import GraphBuilder
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@dataclass(frozen=True)
|
|
14
|
+
class PythonMapNodeBuilder(NodeBuilder):
|
|
15
|
+
nested_graph: "GraphBuilder" = None # This is the generator function
|
|
16
|
+
input_node_ids: Mapping[str, int] = None # The nodes representing the stub inputs in the nested graph.
|
|
17
|
+
output_node_id: int = None # The node representing the stub output in the nested graph.
|
|
18
|
+
multiplexed_args: frozenset[str] =None # The inputs that need to be de-multiplexed.
|
|
19
|
+
|
|
20
|
+
def make_instance(self, owning_graph_id: tuple[int, ...]) -> PythonMapNodeImpl:
|
|
21
|
+
node = PythonMapNodeImpl(
|
|
22
|
+
node_ndx=self.node_ndx,
|
|
23
|
+
owning_graph_id=owning_graph_id,
|
|
24
|
+
signature=self.signature,
|
|
25
|
+
scalars=self.scalars,
|
|
26
|
+
nested_graph_builder=self.nested_graph,
|
|
27
|
+
input_node_ids=self.input_node_ids,
|
|
28
|
+
output_node_id=self.output_node_id,
|
|
29
|
+
multiplexed_args=self.multiplexed_args
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
if self.input_builder:
|
|
33
|
+
ts_input: TimeSeriesBundleInput = cast(TimeSeriesBundleInput,
|
|
34
|
+
self.input_builder.make_instance(owning_node=node))
|
|
35
|
+
node.input = ts_input
|
|
36
|
+
|
|
37
|
+
if self.output_builder:
|
|
38
|
+
ts_output: TimeSeriesOutput = self.output_builder.make_instance(owning_node=node)
|
|
39
|
+
node.output = ts_output
|
|
40
|
+
|
|
41
|
+
return node
|
|
42
|
+
|
|
43
|
+
def release_instance(self, item: PythonMapNodeImpl):
|
|
44
|
+
pass
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from typing import Callable
|
|
3
|
+
|
|
4
|
+
from hgraph._builder._node_builder import NodeBuilder
|
|
5
|
+
from hgraph._impl._runtime._node import NodeImpl, GeneratorNodeImpl, PythonPushQueueNodeImpl
|
|
6
|
+
from hgraph._types._time_series_types import TimeSeriesOutput
|
|
7
|
+
from hgraph._types._tsb_type import TimeSeriesBundleInput
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
__all__ = ("PythonNodeBuilder", "PythonGeneratorNodeBuilder", "PythonPushQueueNodeBuilder")
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@dataclass(frozen=True)
|
|
14
|
+
class PythonNodeBuilder(NodeBuilder):
|
|
15
|
+
eval_fn: Callable = None # The eval fn must be supplied.
|
|
16
|
+
start_fn: Callable = None
|
|
17
|
+
stop_fn: Callable = None
|
|
18
|
+
|
|
19
|
+
def make_instance(self, owning_graph_id: tuple[int, ...]) -> NodeImpl:
|
|
20
|
+
node = NodeImpl(
|
|
21
|
+
node_ndx=self.node_ndx,
|
|
22
|
+
owning_graph_id=owning_graph_id,
|
|
23
|
+
signature=self.signature,
|
|
24
|
+
scalars=self.scalars,
|
|
25
|
+
eval_fn=self.eval_fn,
|
|
26
|
+
start_fn=self.start_fn,
|
|
27
|
+
stop_fn=self.stop_fn
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
if self.input_builder:
|
|
31
|
+
ts_input: TimeSeriesBundleInput = self.input_builder.make_instance(owning_node=node)
|
|
32
|
+
node.input = ts_input
|
|
33
|
+
|
|
34
|
+
if self.output_builder:
|
|
35
|
+
ts_output: TimeSeriesOutput = self.output_builder.make_instance(owning_node=node)
|
|
36
|
+
node.output = ts_output
|
|
37
|
+
|
|
38
|
+
return node
|
|
39
|
+
|
|
40
|
+
def release_instance(self, item: NodeImpl):
|
|
41
|
+
pass
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@dataclass(frozen=True)
|
|
45
|
+
class PythonGeneratorNodeBuilder(NodeBuilder):
|
|
46
|
+
eval_fn: Callable = None # This is the generator function
|
|
47
|
+
|
|
48
|
+
def make_instance(self, owning_graph_id: tuple[int, ...]) -> GeneratorNodeImpl:
|
|
49
|
+
node = GeneratorNodeImpl(
|
|
50
|
+
node_ndx=self.node_ndx,
|
|
51
|
+
owning_graph_id=owning_graph_id,
|
|
52
|
+
signature=self.signature,
|
|
53
|
+
scalars=self.scalars,
|
|
54
|
+
eval_fn=self.eval_fn
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
if self.output_builder:
|
|
58
|
+
ts_output: TimeSeriesOutput = self.output_builder.make_instance(owning_node=node)
|
|
59
|
+
node.output = ts_output
|
|
60
|
+
|
|
61
|
+
return node
|
|
62
|
+
|
|
63
|
+
def release_instance(self, item: GeneratorNodeImpl):
|
|
64
|
+
pass
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
@dataclass(frozen=True)
|
|
68
|
+
class PythonPushQueueNodeBuilder(NodeBuilder):
|
|
69
|
+
eval_fn: Callable = None # This is the generator function
|
|
70
|
+
|
|
71
|
+
def make_instance(self, owning_graph_id: tuple[int, ...]) -> PythonPushQueueNodeImpl:
|
|
72
|
+
node = PythonPushQueueNodeImpl(
|
|
73
|
+
node_ndx=self.node_ndx,
|
|
74
|
+
owning_graph_id=owning_graph_id,
|
|
75
|
+
signature=self.signature,
|
|
76
|
+
scalars=self.scalars,
|
|
77
|
+
eval_fn=self.eval_fn
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
if self.output_builder:
|
|
81
|
+
ts_output: TimeSeriesOutput = self.output_builder.make_instance(owning_node=node)
|
|
82
|
+
node.output = ts_output
|
|
83
|
+
|
|
84
|
+
return node
|
|
85
|
+
|
|
86
|
+
def release_instance(self, item: PythonPushQueueNodeImpl):
|
|
87
|
+
pass
|