funcnodes 0.2__tar.gz → 0.2.1__tar.gz

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 (53) hide show
  1. funcnodes-0.2.1/PKG-INFO +360 -0
  2. funcnodes-0.2.1/README.md +340 -0
  3. {funcnodes-0.2 → funcnodes-0.2.1}/funcnodes/__init__.py +4 -1
  4. {funcnodes-0.2 → funcnodes-0.2.1}/funcnodes/__main__.py +245 -143
  5. {funcnodes-0.2 → funcnodes-0.2.1}/funcnodes/_logging.py +37 -0
  6. {funcnodes-0.2 → funcnodes-0.2.1}/funcnodes/basic_nodes/files.py +164 -139
  7. {funcnodes-0.2 → funcnodes-0.2.1}/funcnodes/basic_nodes/logic.py +11 -6
  8. {funcnodes-0.2 → funcnodes-0.2.1}/funcnodes/config.py +140 -100
  9. {funcnodes-0.2 → funcnodes-0.2.1}/funcnodes/eventmanager.py +47 -8
  10. funcnodes-0.2.1/funcnodes/exceptions.py +5 -0
  11. {funcnodes-0.2 → funcnodes-0.2.1}/funcnodes/io.py +3 -0
  12. {funcnodes-0.2 → funcnodes-0.2.1}/funcnodes/lib/libfinder.py +246 -180
  13. {funcnodes-0.2 → funcnodes-0.2.1}/funcnodes/node.py +28 -6
  14. {funcnodes-0.2 → funcnodes-0.2.1}/funcnodes/nodemaker.py +314 -66
  15. {funcnodes-0.2 → funcnodes-0.2.1}/funcnodes/nodespace.py +110 -1
  16. {funcnodes-0.2 → funcnodes-0.2.1}/funcnodes/utils/serialization.py +63 -68
  17. {funcnodes-0.2 → funcnodes-0.2.1}/funcnodes/worker/__init__.py +2 -1
  18. {funcnodes-0.2 → funcnodes-0.2.1}/funcnodes/worker/external_worker.py +26 -4
  19. funcnodes-0.2.1/funcnodes/worker/remote_worker.py +114 -0
  20. funcnodes-0.2.1/funcnodes/worker/websocket.py +278 -0
  21. {funcnodes-0.2 → funcnodes-0.2.1}/funcnodes/worker/worker.py +4 -92
  22. {funcnodes-0.2 → funcnodes-0.2.1}/funcnodes/worker/worker_manager.py +933 -657
  23. {funcnodes-0.2 → funcnodes-0.2.1}/pyproject.toml +2 -2
  24. funcnodes-0.2/PKG-INFO +0 -22
  25. funcnodes-0.2/README.md +0 -2
  26. funcnodes-0.2/funcnodes/exceptions.py +0 -2
  27. funcnodes-0.2/funcnodes/worker/websocket.py +0 -152
  28. {funcnodes-0.2 → funcnodes-0.2.1}/LICENSE +0 -0
  29. {funcnodes-0.2 → funcnodes-0.2.1}/funcnodes/basic_nodes/__init__.py +0 -0
  30. {funcnodes-0.2 → funcnodes-0.2.1}/funcnodes/basic_nodes/math.py +0 -0
  31. {funcnodes-0.2 → funcnodes-0.2.1}/funcnodes/frontends/__init__.py +0 -0
  32. {funcnodes-0.2 → funcnodes-0.2.1}/funcnodes/frontends/funcnodes_react/__init__.py +0 -0
  33. {funcnodes-0.2 → funcnodes-0.2.1}/funcnodes/frontends/funcnodes_react/asset-manifest.json +0 -0
  34. {funcnodes-0.2 → funcnodes-0.2.1}/funcnodes/frontends/funcnodes_react/css/style.css +0 -0
  35. {funcnodes-0.2 → funcnodes-0.2.1}/funcnodes/frontends/funcnodes_react/favicon.ico +0 -0
  36. {funcnodes-0.2 → funcnodes-0.2.1}/funcnodes/frontends/funcnodes_react/index.html +0 -0
  37. {funcnodes-0.2 → funcnodes-0.2.1}/funcnodes/frontends/funcnodes_react/js/424.js +0 -0
  38. {funcnodes-0.2 → funcnodes-0.2.1}/funcnodes/frontends/funcnodes_react/js/424.js.LICENSE.txt +0 -0
  39. {funcnodes-0.2 → funcnodes-0.2.1}/funcnodes/frontends/funcnodes_react/js/main.js +0 -0
  40. {funcnodes-0.2 → funcnodes-0.2.1}/funcnodes/frontends/funcnodes_react/js/main.js.LICENSE.txt +0 -0
  41. {funcnodes-0.2 → funcnodes-0.2.1}/funcnodes/frontends/funcnodes_react/logo192.png +0 -0
  42. {funcnodes-0.2 → funcnodes-0.2.1}/funcnodes/frontends/funcnodes_react/logo512.png +0 -0
  43. {funcnodes-0.2 → funcnodes-0.2.1}/funcnodes/frontends/funcnodes_react/manifest.json +0 -0
  44. {funcnodes-0.2 → funcnodes-0.2.1}/funcnodes/frontends/funcnodes_react/robots.txt +0 -0
  45. {funcnodes-0.2 → funcnodes-0.2.1}/funcnodes/frontends/funcnodes_react/run.py +0 -0
  46. {funcnodes-0.2 → funcnodes-0.2.1}/funcnodes/lib/__init__.py +0 -0
  47. {funcnodes-0.2 → funcnodes-0.2.1}/funcnodes/lib/lib.py +0 -0
  48. {funcnodes-0.2 → funcnodes-0.2.1}/funcnodes/lib/libparser.py +0 -0
  49. {funcnodes-0.2 → funcnodes-0.2.1}/funcnodes/triggerstack.py +0 -0
  50. {funcnodes-0.2 → funcnodes-0.2.1}/funcnodes/utils/__init__.py +0 -0
  51. {funcnodes-0.2 → funcnodes-0.2.1}/funcnodes/utils/data.py +0 -0
  52. {funcnodes-0.2 → funcnodes-0.2.1}/funcnodes/utils/nodeutils.py +0 -0
  53. {funcnodes-0.2 → funcnodes-0.2.1}/funcnodes/worker/loop.py +0 -0
@@ -0,0 +1,360 @@
1
+ Metadata-Version: 2.1
2
+ Name: funcnodes
3
+ Version: 0.2.1
4
+ Summary: funcnodes
5
+ Author: Julian Kimmig
6
+ Author-email: julian.kimmig@linkdlab.de
7
+ Requires-Python: >=3.9,<4.0
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: Programming Language :: Python :: 3.9
10
+ Classifier: Programming Language :: Python :: 3.10
11
+ Classifier: Programming Language :: Python :: 3.11
12
+ Classifier: Programming Language :: Python :: 3.12
13
+ Requires-Dist: exposedfunctionality (>=0.3.6,<0.4.0)
14
+ Requires-Dist: poetry-plugin-export (>=1.7.1,<2.0.0)
15
+ Requires-Dist: python-dotenv (>=1.0.1,<2.0.0)
16
+ Requires-Dist: virtualenv (>=20.25.1,<21.0.0)
17
+ Requires-Dist: websockets (>=12.0,<13.0)
18
+ Description-Content-Type: text/markdown
19
+
20
+ # Funcnodes Project README
21
+
22
+ ## Project Overview
23
+
24
+ Funcnodes is a flexible and modular framework designed for building and managing computational graphs, particularly suited for tasks involving data processing, machine learning, and scientific computations but also capable of hardware controll and arbitarty other functions. The core of Funcnodes revolves around "nodes," which are individual units of computation that can be connected to form complex workflows. Each node performs specific operations, and users can dynamically create and manage these nodes through a web interface or programmatically via an API. The project includes a variety of built-in nodes for common operations and supports custom nodes for specialized tasks.
25
+
26
+ ## Table of Contents
27
+
28
+ 1. [Installation Instructions](#installation-instructions)
29
+ 2. [Usage Guide](#usage-guide)
30
+ 3. [Configuration](#configuration)
31
+ 4. [Testing](#testing)
32
+ 5. [Architecture](#architecture)
33
+ 6. [Contributing Guidelines](#contributing-guidelines)
34
+ 7. [Licensing Information](#licensing-information)
35
+ 8. [Credits and Acknowledgements](#credits-and-acknowledgements)
36
+ 9. [Additional Sections](#additional-sections)
37
+
38
+ ## Installation Instructions
39
+
40
+ ### Prerequisites
41
+
42
+ - Python 3.8 or higher
43
+ - pip (Python package manager)
44
+
45
+ ### Steps
46
+
47
+ 1. **Clone the repository:**
48
+
49
+ ```bash
50
+ pip install funcnodes
51
+ ```
52
+
53
+ 2. **Configure environment variables (optional):**
54
+ Create a `.env` file in the root directory and specify configuration options as needed (see [Configuration](#configuration)).
55
+
56
+ ## Underlying logic
57
+
58
+ Funcnodes basically wraps arbitary functions in Nodes. The inputs of a Node(usually) mirrors the function arguments and the output(s) is the return of the function. Whenever an input recieves data the respective nodes is triggered, meaning its function is tried to be called.
59
+ This happens if:
60
+
61
+ - the node is not currently running, in which case a new trigger process is qeued.
62
+ - if all necessary inputs are set, inputs can can have default values, in which case they act like kwargs and are optional.
63
+
64
+ This also means under "default" conditions, if a Node with two inputs recieves values on both inputs "simultanously" (which can never really happen, but it can be close), the Node will (try) to run twice. Once it the first input value is set and a second time when the seconds value is set.
65
+ Imagin this funcions as a Node
66
+
67
+ ```python
68
+ def add(a,b=1):
69
+ return a + b
70
+ ```
71
+
72
+ setting a (as 2) and directly b (as 3) would result in
73
+
74
+ ```python
75
+ add(2)
76
+ add(2,3)
77
+ ```
78
+
79
+ The function or the respective Node is called two times. While this can be prevented and is intendet by design it is also important to keep in mind.
80
+
81
+ If a Nodes finishes triggering, it updates its outputs with newly generated values. if the outputs are connected to other inputs these inputs are then updated as well and the respective Notes are due to be triggered.
82
+
83
+ The behaviour of the individual inputs and outputs can be manipulated on a Nodeclass level and on a Node instance level (see the respective section).
84
+
85
+ ## Usage Guide
86
+
87
+ ### Use funcnodes programmatically
88
+
89
+ While not the intendet usage it is important (or at least usefull) to understand how funcnodes workes on a programatically level. Esprecially for testing the implmenetation of new nodes can be tested on a programatically level.
90
+ Since funcnodes makes usage of async functions all node specific
91
+
92
+ #### Nodes
93
+
94
+ Node instances can be created from the respective class (see [Generating New Nodes in Funcnodes](#generating-new-nodes-in-funcnodes)).
95
+
96
+ ```python
97
+ from funcnodes.basic_nodes.math import add_node
98
+
99
+ async def main():
100
+ add_node = Adadd_nodedNode() # generate Node instance that does a, b: a+b
101
+
102
+ add_node.inputs["a"].value = 1 # sets the input of a to 1
103
+
104
+ # since add_node requires both inputs to be set, nothing happens
105
+ # node is not ready to trigger:
106
+ print("ready2trigger",add_node_ins.ready_to_trigger())
107
+ # an is also not currently in trigger:
108
+ print("in_trigger",add_node_ins.in_trigger)
109
+
110
+ add_node_ins.inputs["b"].value = 3
111
+
112
+ # node is not ready to trigger:
113
+ print("ready2trigger",add_node_ins.ready_to_trigger())
114
+ # since it is currently in trigger:
115
+ print("in_trigger",add_node_ins.in_trigger)
116
+
117
+ # since it is still triggering the output is not set:
118
+ print("out", add_node_ins.outputs["out"].value)
119
+
120
+ # wait fot the actuall trigger process
121
+ await add_node_ins
122
+
123
+ # now the output is as expected
124
+ print("out", 5)
125
+
126
+ ... # call asyinc main function.
127
+
128
+ >>> ready2trigger, False
129
+ >>> in_trigger, False
130
+ >>> ready2trigger, False
131
+ >>> in_trigger, True
132
+ >>> out <NoValue>
133
+ >>> out 5
134
+ ```
135
+
136
+ #### Connections (Edges)
137
+
138
+ Connections can be created via inputs and outputs (direction invariant):
139
+
140
+ ```python
141
+ add_node1 = add_node()
142
+ add_node2 = add_node()
143
+
144
+ add_node1.outputs["out"].connect(add_node2.inputs["a"])
145
+
146
+ # or short via:
147
+ add_node1.o["out"].c(add_node2.i["a"])
148
+
149
+ # or shorter:
150
+ add_node1.o["out"] > add_node2.i["a"]
151
+ ```
152
+
153
+ by default inputs can have only 1 connection. If a connection already exists between an output and an input, nothing happens.
154
+ if input is already occupied, an error will be raised by default, or the existing connection will be removed if connect is called with the replace=True argument.
155
+
156
+ ### Web Interface
157
+
158
+ Funcnodes was initially developed for internal use, but we decided to make it publicly available.
159
+ While this package mainly serves as the backend logic it also comes with a initial browser based user interface, which allowes the vizual interaction with the system.
160
+
161
+ 1. **Starting the server:**
162
+
163
+ ```bash
164
+ funcnodes runserver
165
+ ```
166
+
167
+ 2. **Accessing the web interface:**
168
+ Open `http://localhost:8000` in a web browser to access the web interface where you can visually manage and connect nodes.
169
+
170
+ When calling the runserver comand, a simple server is run up, which provides the basic web interface.
171
+
172
+ #### WorkerManager
173
+
174
+ The interaction beween the interface and the backend happens via websockers. The first instance that accepts websokcet connections is the WorkerManager, which runs in a seperate window or in the background (depending on our system). It is automatically started when the server starts and it is not already running.
175
+ It can be seperatly run via:
176
+
177
+ ```bash
178
+ funcnodes startworkermanager
179
+ ```
180
+
181
+ The workermanager starts, stops and creates worker (the instances that are running the Node functionalities)
182
+
183
+ #### Worker
184
+
185
+ Each Worker runs in a seperate process in the python environment it was created in.
186
+ New worker can be created via the web interface or via:
187
+ ```bash
188
+ funcnodes worker new --name=<otional non unique name> --uuid=<otional unique id>
189
+ ```
190
+ Where the uuid is created automatically if not provided and the name falls back to the uuid if not provided.
191
+
192
+ To run a worker process, simpy activate it via the webinterface or in the cli via:
193
+ ```bash
194
+ funcnodes worker start --name=<otional non unique name> --uuid=<otional unique id>
195
+ ```
196
+ either name or uuid have to be provided. If uuid is provided it will start the respective worker. Otherwise it will start the first worker with the matching name.
197
+
198
+
199
+ ### Using the web interface
200
+
201
+ One important feature is that each
202
+
203
+ ## Generating New Nodes in Funcnodes
204
+
205
+ In Funcnodes, nodes are the fundamental units of computation that you can configure and connect to form complex data processing workflows. You can create custom nodes by either defining new classes or using decorators. This guide will walk you through both methods, helping you extend the functionality of Funcnodes with custom operations.
206
+
207
+ ### Method 1: Defining Node Classes
208
+
209
+ To create a new node, you can subclass the `Node` class from Funcnodes and define the computation that the node should perform. Here's a step-by-step guide:
210
+
211
+ 1. **Import the Base Class:**
212
+ Import the `Node` class along with the `NodeInput` and `NodeOutput` classes which are used to define the inputs and outputs of your node.
213
+
214
+ ```python
215
+ from funcnodes import Node, NodeInput, NodeOutput
216
+ ```
217
+
218
+ 2. **Define the Node Class:**
219
+ Create a new class that inherits from `Node`. Define properties and methods as required.
220
+
221
+ ```python
222
+ class MyCustomNode(Node):
223
+ node_id = "my_custom_node"
224
+ node_name = "My Custom Node"
225
+
226
+ # Define inputs
227
+ input1 = NodeInput(id="input1", type="float")
228
+ input2 = NodeInput(id="input2", type="float")
229
+
230
+ # Define outputs
231
+ output = NodeOutput(id="output", type="float")
232
+
233
+ # Define the computation performed by the node
234
+ async def func(self, input1: float, input2: float) -> None:
235
+ result = input1 + input2 # Example operation
236
+ self.outputs['output'].value = result
237
+ ```
238
+
239
+ 3. **Initiate and use the Nodes:**
240
+ Once the class is defined, you can create an instance of this node in your node space and connect it as needed.
241
+
242
+ ```python
243
+ from funcnodes import NodeSpace
244
+
245
+ ns = NodeSpace()
246
+ custom_node = MyCustomNode()
247
+
248
+ # Add the node to the node space
249
+ ns.add_node_instance(custom_node)
250
+ ```
251
+
252
+ ### Method 2: Using Decorators
253
+
254
+ Funcnodes provides a decorator `@NodeDecorator` that simplifies the creation of custom nodes by automatically handling boilerplate code.
255
+
256
+ 1. **Import the Decorator:**
257
+ Import the `NodeDecorator` from funcnodes.
258
+
259
+ ```python
260
+ from funcnodes.nodemaker import NodeDecorator
261
+ ```
262
+
263
+ 2. **Define the Function:**
264
+ Write a regular Python function that performs the computation you want. This function should accept inputs as arguments and return the output.
265
+
266
+ ```python
267
+ @NodeDecorator("addition_node")
268
+ def addition(a: float, b: float) -> float:
269
+ return a + b
270
+ ```
271
+
272
+ The `@NodeDecorator` takes the node ID as an argument and automatically creates a node class based on the signature of the function.
273
+
274
+ 3. **Use the Decorated Node:**
275
+ After defining the function with the decorator, it can be instantiated and used just like any other node class.
276
+
277
+ ```python
278
+ addition_node = addition() # Instantiate the node
279
+ ns.add_node_instance(addition_node) # Add to the node space
280
+ ```
281
+
282
+ ### Conclusion
283
+
284
+ Both methods allow for flexibility and integration into the Funcnodes ecosystem, enabling you to create custom nodes that fit your specific data processing needs. Whether you choose to define a full class or use the decorator approach depends on your preference and the complexity of the node's functionality.
285
+
286
+ 3. **Adding nodes programmatically:**
287
+
288
+ ```python
289
+ from funcnodes import NodeSpace, get_nodeclass
290
+
291
+ # Create a new node space
292
+ ns = NodeSpace()
293
+
294
+ # Get a node class and instantiate it
295
+ AddNode = get_nodeclass('add_node')
296
+ add_node = AddNode()
297
+
298
+ # Add the node to the node space
299
+ ns.add_node_instance(add_node)
300
+ ```
301
+
302
+ 4. **Connecting nodes:**
303
+ ```python
304
+ # Assuming add_node and another node (output_node) are already instantiated
305
+ add_node.outputs['sum'].connect(output_node.inputs['input'])
306
+ ```
307
+
308
+ ### Advanced Usage
309
+
310
+ - **Creating custom nodes:**
311
+ You can create custom nodes by extending the `Node` class. Refer to the [Contributing Guidelines](#contributing-guidelines) for more details on setting up a development environment for creating custom nodes.
312
+
313
+ ## Configuration
314
+
315
+ Environment variables and configuration options can be set in a `.env` file or directly in the system environment. Key configuration options include:
316
+
317
+ - `FUNCNODES_CONFIG_DIR`: Path to the configuration directory (defaults to `~/.funcnodes`).
318
+ - `FUNCNODES_PORT`: Port for the web server (defaults to `8000`).
319
+
320
+ ## Testing
321
+
322
+ To run tests:
323
+
324
+ ```bash
325
+ pytest tests/
326
+ ```
327
+
328
+ Ensure you have `pytest` installed, or install it using `pip install pytest`.
329
+
330
+ ## Architecture
331
+
332
+ Funcnodes uses a modular architecture with the following key components:
333
+
334
+ - **Node:** Basic unit of computation.
335
+ - **NodeSpace:** Container managing a collection of nodes.
336
+ - **Worker:** Handles the execution of node spaces.
337
+ - **Frontend:** Web interface for managing nodes and node spaces interactively.
338
+
339
+ ## Contributing Guidelines
340
+
341
+ Contributions are welcome! Please refer to `CONTRIBUTING.md` for detailed instructions on setting up your development environment, coding standards, and the pull request process.
342
+
343
+ ## Licensing Information
344
+
345
+ This project is licensed under the MIT License - see the `LICENSE` file for details.
346
+
347
+ ## Credits and Acknowledgements
348
+
349
+ - **[Your Name]**: Project lead and main developer.
350
+ - **[Contributor Names]**: Additional contributors who helped with various features.
351
+ - **Open-source libraries:** List any third-party libraries or tools used.
352
+
353
+ ## Additional Sections
354
+
355
+ - **Deployment Guide:** For instructions on deploying Funcnodes in a production environment.
356
+ - **FAQ:** Common questions and troubleshooting tips related to Funcnodes.
357
+ - **Troubleshooting:** Specific troubleshooting steps for common issues.
358
+
359
+ For more detailed documentation, visit the [official Funcnodes documentation](#).
360
+
@@ -0,0 +1,340 @@
1
+ # Funcnodes Project README
2
+
3
+ ## Project Overview
4
+
5
+ Funcnodes is a flexible and modular framework designed for building and managing computational graphs, particularly suited for tasks involving data processing, machine learning, and scientific computations but also capable of hardware controll and arbitarty other functions. The core of Funcnodes revolves around "nodes," which are individual units of computation that can be connected to form complex workflows. Each node performs specific operations, and users can dynamically create and manage these nodes through a web interface or programmatically via an API. The project includes a variety of built-in nodes for common operations and supports custom nodes for specialized tasks.
6
+
7
+ ## Table of Contents
8
+
9
+ 1. [Installation Instructions](#installation-instructions)
10
+ 2. [Usage Guide](#usage-guide)
11
+ 3. [Configuration](#configuration)
12
+ 4. [Testing](#testing)
13
+ 5. [Architecture](#architecture)
14
+ 6. [Contributing Guidelines](#contributing-guidelines)
15
+ 7. [Licensing Information](#licensing-information)
16
+ 8. [Credits and Acknowledgements](#credits-and-acknowledgements)
17
+ 9. [Additional Sections](#additional-sections)
18
+
19
+ ## Installation Instructions
20
+
21
+ ### Prerequisites
22
+
23
+ - Python 3.8 or higher
24
+ - pip (Python package manager)
25
+
26
+ ### Steps
27
+
28
+ 1. **Clone the repository:**
29
+
30
+ ```bash
31
+ pip install funcnodes
32
+ ```
33
+
34
+ 2. **Configure environment variables (optional):**
35
+ Create a `.env` file in the root directory and specify configuration options as needed (see [Configuration](#configuration)).
36
+
37
+ ## Underlying logic
38
+
39
+ Funcnodes basically wraps arbitary functions in Nodes. The inputs of a Node(usually) mirrors the function arguments and the output(s) is the return of the function. Whenever an input recieves data the respective nodes is triggered, meaning its function is tried to be called.
40
+ This happens if:
41
+
42
+ - the node is not currently running, in which case a new trigger process is qeued.
43
+ - if all necessary inputs are set, inputs can can have default values, in which case they act like kwargs and are optional.
44
+
45
+ This also means under "default" conditions, if a Node with two inputs recieves values on both inputs "simultanously" (which can never really happen, but it can be close), the Node will (try) to run twice. Once it the first input value is set and a second time when the seconds value is set.
46
+ Imagin this funcions as a Node
47
+
48
+ ```python
49
+ def add(a,b=1):
50
+ return a + b
51
+ ```
52
+
53
+ setting a (as 2) and directly b (as 3) would result in
54
+
55
+ ```python
56
+ add(2)
57
+ add(2,3)
58
+ ```
59
+
60
+ The function or the respective Node is called two times. While this can be prevented and is intendet by design it is also important to keep in mind.
61
+
62
+ If a Nodes finishes triggering, it updates its outputs with newly generated values. if the outputs are connected to other inputs these inputs are then updated as well and the respective Notes are due to be triggered.
63
+
64
+ The behaviour of the individual inputs and outputs can be manipulated on a Nodeclass level and on a Node instance level (see the respective section).
65
+
66
+ ## Usage Guide
67
+
68
+ ### Use funcnodes programmatically
69
+
70
+ While not the intendet usage it is important (or at least usefull) to understand how funcnodes workes on a programatically level. Esprecially for testing the implmenetation of new nodes can be tested on a programatically level.
71
+ Since funcnodes makes usage of async functions all node specific
72
+
73
+ #### Nodes
74
+
75
+ Node instances can be created from the respective class (see [Generating New Nodes in Funcnodes](#generating-new-nodes-in-funcnodes)).
76
+
77
+ ```python
78
+ from funcnodes.basic_nodes.math import add_node
79
+
80
+ async def main():
81
+ add_node = Adadd_nodedNode() # generate Node instance that does a, b: a+b
82
+
83
+ add_node.inputs["a"].value = 1 # sets the input of a to 1
84
+
85
+ # since add_node requires both inputs to be set, nothing happens
86
+ # node is not ready to trigger:
87
+ print("ready2trigger",add_node_ins.ready_to_trigger())
88
+ # an is also not currently in trigger:
89
+ print("in_trigger",add_node_ins.in_trigger)
90
+
91
+ add_node_ins.inputs["b"].value = 3
92
+
93
+ # node is not ready to trigger:
94
+ print("ready2trigger",add_node_ins.ready_to_trigger())
95
+ # since it is currently in trigger:
96
+ print("in_trigger",add_node_ins.in_trigger)
97
+
98
+ # since it is still triggering the output is not set:
99
+ print("out", add_node_ins.outputs["out"].value)
100
+
101
+ # wait fot the actuall trigger process
102
+ await add_node_ins
103
+
104
+ # now the output is as expected
105
+ print("out", 5)
106
+
107
+ ... # call asyinc main function.
108
+
109
+ >>> ready2trigger, False
110
+ >>> in_trigger, False
111
+ >>> ready2trigger, False
112
+ >>> in_trigger, True
113
+ >>> out <NoValue>
114
+ >>> out 5
115
+ ```
116
+
117
+ #### Connections (Edges)
118
+
119
+ Connections can be created via inputs and outputs (direction invariant):
120
+
121
+ ```python
122
+ add_node1 = add_node()
123
+ add_node2 = add_node()
124
+
125
+ add_node1.outputs["out"].connect(add_node2.inputs["a"])
126
+
127
+ # or short via:
128
+ add_node1.o["out"].c(add_node2.i["a"])
129
+
130
+ # or shorter:
131
+ add_node1.o["out"] > add_node2.i["a"]
132
+ ```
133
+
134
+ by default inputs can have only 1 connection. If a connection already exists between an output and an input, nothing happens.
135
+ if input is already occupied, an error will be raised by default, or the existing connection will be removed if connect is called with the replace=True argument.
136
+
137
+ ### Web Interface
138
+
139
+ Funcnodes was initially developed for internal use, but we decided to make it publicly available.
140
+ While this package mainly serves as the backend logic it also comes with a initial browser based user interface, which allowes the vizual interaction with the system.
141
+
142
+ 1. **Starting the server:**
143
+
144
+ ```bash
145
+ funcnodes runserver
146
+ ```
147
+
148
+ 2. **Accessing the web interface:**
149
+ Open `http://localhost:8000` in a web browser to access the web interface where you can visually manage and connect nodes.
150
+
151
+ When calling the runserver comand, a simple server is run up, which provides the basic web interface.
152
+
153
+ #### WorkerManager
154
+
155
+ The interaction beween the interface and the backend happens via websockers. The first instance that accepts websokcet connections is the WorkerManager, which runs in a seperate window or in the background (depending on our system). It is automatically started when the server starts and it is not already running.
156
+ It can be seperatly run via:
157
+
158
+ ```bash
159
+ funcnodes startworkermanager
160
+ ```
161
+
162
+ The workermanager starts, stops and creates worker (the instances that are running the Node functionalities)
163
+
164
+ #### Worker
165
+
166
+ Each Worker runs in a seperate process in the python environment it was created in.
167
+ New worker can be created via the web interface or via:
168
+ ```bash
169
+ funcnodes worker new --name=<otional non unique name> --uuid=<otional unique id>
170
+ ```
171
+ Where the uuid is created automatically if not provided and the name falls back to the uuid if not provided.
172
+
173
+ To run a worker process, simpy activate it via the webinterface or in the cli via:
174
+ ```bash
175
+ funcnodes worker start --name=<otional non unique name> --uuid=<otional unique id>
176
+ ```
177
+ either name or uuid have to be provided. If uuid is provided it will start the respective worker. Otherwise it will start the first worker with the matching name.
178
+
179
+
180
+ ### Using the web interface
181
+
182
+ One important feature is that each
183
+
184
+ ## Generating New Nodes in Funcnodes
185
+
186
+ In Funcnodes, nodes are the fundamental units of computation that you can configure and connect to form complex data processing workflows. You can create custom nodes by either defining new classes or using decorators. This guide will walk you through both methods, helping you extend the functionality of Funcnodes with custom operations.
187
+
188
+ ### Method 1: Defining Node Classes
189
+
190
+ To create a new node, you can subclass the `Node` class from Funcnodes and define the computation that the node should perform. Here's a step-by-step guide:
191
+
192
+ 1. **Import the Base Class:**
193
+ Import the `Node` class along with the `NodeInput` and `NodeOutput` classes which are used to define the inputs and outputs of your node.
194
+
195
+ ```python
196
+ from funcnodes import Node, NodeInput, NodeOutput
197
+ ```
198
+
199
+ 2. **Define the Node Class:**
200
+ Create a new class that inherits from `Node`. Define properties and methods as required.
201
+
202
+ ```python
203
+ class MyCustomNode(Node):
204
+ node_id = "my_custom_node"
205
+ node_name = "My Custom Node"
206
+
207
+ # Define inputs
208
+ input1 = NodeInput(id="input1", type="float")
209
+ input2 = NodeInput(id="input2", type="float")
210
+
211
+ # Define outputs
212
+ output = NodeOutput(id="output", type="float")
213
+
214
+ # Define the computation performed by the node
215
+ async def func(self, input1: float, input2: float) -> None:
216
+ result = input1 + input2 # Example operation
217
+ self.outputs['output'].value = result
218
+ ```
219
+
220
+ 3. **Initiate and use the Nodes:**
221
+ Once the class is defined, you can create an instance of this node in your node space and connect it as needed.
222
+
223
+ ```python
224
+ from funcnodes import NodeSpace
225
+
226
+ ns = NodeSpace()
227
+ custom_node = MyCustomNode()
228
+
229
+ # Add the node to the node space
230
+ ns.add_node_instance(custom_node)
231
+ ```
232
+
233
+ ### Method 2: Using Decorators
234
+
235
+ Funcnodes provides a decorator `@NodeDecorator` that simplifies the creation of custom nodes by automatically handling boilerplate code.
236
+
237
+ 1. **Import the Decorator:**
238
+ Import the `NodeDecorator` from funcnodes.
239
+
240
+ ```python
241
+ from funcnodes.nodemaker import NodeDecorator
242
+ ```
243
+
244
+ 2. **Define the Function:**
245
+ Write a regular Python function that performs the computation you want. This function should accept inputs as arguments and return the output.
246
+
247
+ ```python
248
+ @NodeDecorator("addition_node")
249
+ def addition(a: float, b: float) -> float:
250
+ return a + b
251
+ ```
252
+
253
+ The `@NodeDecorator` takes the node ID as an argument and automatically creates a node class based on the signature of the function.
254
+
255
+ 3. **Use the Decorated Node:**
256
+ After defining the function with the decorator, it can be instantiated and used just like any other node class.
257
+
258
+ ```python
259
+ addition_node = addition() # Instantiate the node
260
+ ns.add_node_instance(addition_node) # Add to the node space
261
+ ```
262
+
263
+ ### Conclusion
264
+
265
+ Both methods allow for flexibility and integration into the Funcnodes ecosystem, enabling you to create custom nodes that fit your specific data processing needs. Whether you choose to define a full class or use the decorator approach depends on your preference and the complexity of the node's functionality.
266
+
267
+ 3. **Adding nodes programmatically:**
268
+
269
+ ```python
270
+ from funcnodes import NodeSpace, get_nodeclass
271
+
272
+ # Create a new node space
273
+ ns = NodeSpace()
274
+
275
+ # Get a node class and instantiate it
276
+ AddNode = get_nodeclass('add_node')
277
+ add_node = AddNode()
278
+
279
+ # Add the node to the node space
280
+ ns.add_node_instance(add_node)
281
+ ```
282
+
283
+ 4. **Connecting nodes:**
284
+ ```python
285
+ # Assuming add_node and another node (output_node) are already instantiated
286
+ add_node.outputs['sum'].connect(output_node.inputs['input'])
287
+ ```
288
+
289
+ ### Advanced Usage
290
+
291
+ - **Creating custom nodes:**
292
+ You can create custom nodes by extending the `Node` class. Refer to the [Contributing Guidelines](#contributing-guidelines) for more details on setting up a development environment for creating custom nodes.
293
+
294
+ ## Configuration
295
+
296
+ Environment variables and configuration options can be set in a `.env` file or directly in the system environment. Key configuration options include:
297
+
298
+ - `FUNCNODES_CONFIG_DIR`: Path to the configuration directory (defaults to `~/.funcnodes`).
299
+ - `FUNCNODES_PORT`: Port for the web server (defaults to `8000`).
300
+
301
+ ## Testing
302
+
303
+ To run tests:
304
+
305
+ ```bash
306
+ pytest tests/
307
+ ```
308
+
309
+ Ensure you have `pytest` installed, or install it using `pip install pytest`.
310
+
311
+ ## Architecture
312
+
313
+ Funcnodes uses a modular architecture with the following key components:
314
+
315
+ - **Node:** Basic unit of computation.
316
+ - **NodeSpace:** Container managing a collection of nodes.
317
+ - **Worker:** Handles the execution of node spaces.
318
+ - **Frontend:** Web interface for managing nodes and node spaces interactively.
319
+
320
+ ## Contributing Guidelines
321
+
322
+ Contributions are welcome! Please refer to `CONTRIBUTING.md` for detailed instructions on setting up your development environment, coding standards, and the pull request process.
323
+
324
+ ## Licensing Information
325
+
326
+ This project is licensed under the MIT License - see the `LICENSE` file for details.
327
+
328
+ ## Credits and Acknowledgements
329
+
330
+ - **[Your Name]**: Project lead and main developer.
331
+ - **[Contributor Names]**: Additional contributors who helped with various features.
332
+ - **Open-source libraries:** List any third-party libraries or tools used.
333
+
334
+ ## Additional Sections
335
+
336
+ - **Deployment Guide:** For instructions on deploying Funcnodes in a production environment.
337
+ - **FAQ:** Common questions and troubleshooting tips related to Funcnodes.
338
+ - **Troubleshooting:** Specific troubleshooting steps for common issues.
339
+
340
+ For more detailed documentation, visit the [official Funcnodes documentation](#).