funcnodes-basic 0.2.1__tar.gz → 0.2.3__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.
- funcnodes_basic-0.2.3/MANIFEST.in +14 -0
- {funcnodes_basic-0.2.1 → funcnodes_basic-0.2.3}/PKG-INFO +11 -11
- funcnodes_basic-0.2.3/pyproject.toml +45 -0
- funcnodes_basic-0.2.3/setup.cfg +4 -0
- {funcnodes_basic-0.2.1 → funcnodes_basic-0.2.3/src}/funcnodes_basic/__init__.py +5 -2
- funcnodes_basic-0.2.3/src/funcnodes_basic/dataclass.py +64 -0
- funcnodes_basic-0.2.3/src/funcnodes_basic/input.py +84 -0
- funcnodes_basic-0.2.3/src/funcnodes_basic.egg-info/PKG-INFO +51 -0
- funcnodes_basic-0.2.3/src/funcnodes_basic.egg-info/SOURCES.txt +27 -0
- funcnodes_basic-0.2.3/src/funcnodes_basic.egg-info/dependency_links.txt +1 -0
- funcnodes_basic-0.2.3/src/funcnodes_basic.egg-info/entry_points.txt +3 -0
- funcnodes_basic-0.2.3/src/funcnodes_basic.egg-info/requires.txt +2 -0
- funcnodes_basic-0.2.3/src/funcnodes_basic.egg-info/top_level.txt +1 -0
- funcnodes_basic-0.2.3/tests/test_all_nodes_pytest.py +6 -0
- funcnodes_basic-0.2.3/tests/test_dataclass.py +141 -0
- funcnodes_basic-0.2.3/tests/test_dict.py +101 -0
- funcnodes_basic-0.2.3/tests/test_import.py +8 -0
- funcnodes_basic-0.2.3/tests/test_inputs.py +123 -0
- funcnodes_basic-0.2.3/tests/test_lists.py +239 -0
- funcnodes_basic-0.2.3/tests/test_logic.py +102 -0
- funcnodes_basic-0.2.3/tests/test_math.py +681 -0
- funcnodes_basic-0.2.3/tests/test_strings.py +316 -0
- funcnodes_basic-0.2.1/pyproject.toml +0 -39
- {funcnodes_basic-0.2.1 → funcnodes_basic-0.2.3}/LICENSE +0 -0
- {funcnodes_basic-0.2.1 → funcnodes_basic-0.2.3}/README.md +0 -0
- {funcnodes_basic-0.2.1 → funcnodes_basic-0.2.3/src}/funcnodes_basic/dicts.py +0 -0
- {funcnodes_basic-0.2.1 → funcnodes_basic-0.2.3/src}/funcnodes_basic/lists.py +0 -0
- {funcnodes_basic-0.2.1 → funcnodes_basic-0.2.3/src}/funcnodes_basic/logic.py +0 -0
- {funcnodes_basic-0.2.1 → funcnodes_basic-0.2.3/src}/funcnodes_basic/math_nodes.py +0 -0
- {funcnodes_basic-0.2.1 → funcnodes_basic-0.2.3/src}/funcnodes_basic/strings.py +0 -0
@@ -0,0 +1,14 @@
|
|
1
|
+
graft src
|
2
|
+
graft tests
|
3
|
+
|
4
|
+
global-exclude __pycache__/*
|
5
|
+
prune __pycache__
|
6
|
+
prune */__pycache__
|
7
|
+
recursive-exclude */__pycache__ *
|
8
|
+
|
9
|
+
global-exclude .venv/*
|
10
|
+
global-exclude .venv/**/*
|
11
|
+
prune .venv
|
12
|
+
prune */.venv
|
13
|
+
recursive-exclude .venv *
|
14
|
+
recursive-exclude */.venv *
|
@@ -1,19 +1,20 @@
|
|
1
|
-
Metadata-Version: 2.
|
1
|
+
Metadata-Version: 2.4
|
2
2
|
Name: funcnodes-basic
|
3
|
-
Version: 0.2.
|
3
|
+
Version: 0.2.3
|
4
4
|
Summary: Basic functionalities for funcnodes
|
5
|
+
Author-email: Julian Kimmig <julian.kimmig@linkdlab.de>
|
5
6
|
License: AGPL-3.0
|
6
|
-
|
7
|
-
Author-email: julian.kimmig@linkdlab.de>
|
8
|
-
Requires-Python: >=3.11
|
9
|
-
Classifier: License :: OSI Approved :: GNU Affero General Public License v3 or later (AGPLv3+)
|
10
|
-
Requires-Dist: funcnodes
|
11
|
-
Requires-Dist: funcnodes-core (>=0.3.9)
|
12
|
-
Project-URL: Homepage, https://github.com/Linkdlab/funcnodes_basic
|
13
|
-
Project-URL: download, https://pypi.org/project/funcnodes-basic/#files
|
7
|
+
Project-URL: homepage, https://github.com/Linkdlab/funcnodes_basic
|
14
8
|
Project-URL: source, https://github.com/Linkdlab/funcnodes_basic
|
15
9
|
Project-URL: tracker, https://github.com/Linkdlab/funcnodes_basic/issues
|
10
|
+
Project-URL: download, https://pypi.org/project/funcnodes-basic/#files
|
11
|
+
Classifier: License :: OSI Approved :: GNU Affero General Public License v3 or later (AGPLv3+)
|
12
|
+
Requires-Python: >=3.11
|
16
13
|
Description-Content-Type: text/markdown
|
14
|
+
License-File: LICENSE
|
15
|
+
Requires-Dist: funcnodes-core>=0.3.9
|
16
|
+
Requires-Dist: funcnodes
|
17
|
+
Dynamic: license-file
|
17
18
|
|
18
19
|
# FuncNodes Basic
|
19
20
|
|
@@ -48,4 +49,3 @@ This project is licensed under the MIT License.
|
|
48
49
|
## Contact
|
49
50
|
|
50
51
|
For any questions or issues, please open an issue on the GitHub repository.
|
51
|
-
|
@@ -0,0 +1,45 @@
|
|
1
|
+
[project]
|
2
|
+
name = "funcnodes-basic"
|
3
|
+
version = "0.2.3"
|
4
|
+
description = "Basic functionalities for funcnodes"
|
5
|
+
readme = "README.md"
|
6
|
+
classifiers = [ "License :: OSI Approved :: GNU Affero General Public License v3 or later (AGPLv3+)",]
|
7
|
+
requires-python = ">=3.11"
|
8
|
+
dependencies = [
|
9
|
+
"funcnodes-core>=0.3.9",
|
10
|
+
"funcnodes",
|
11
|
+
]
|
12
|
+
authors = [{name = "Julian Kimmig", email = "julian.kimmig@linkdlab.de"}]
|
13
|
+
|
14
|
+
|
15
|
+
[dependency-groups]
|
16
|
+
dev = [
|
17
|
+
"funcnodes-module>=0.2.9",
|
18
|
+
"pre-commit>=4.1.0",
|
19
|
+
"pytest>=8.3.4",
|
20
|
+
"pytest-funcnodes>=0.1.0",
|
21
|
+
]
|
22
|
+
|
23
|
+
[build-system]
|
24
|
+
requires = [ "setuptools>=42",]
|
25
|
+
build-backend = "setuptools.build_meta"
|
26
|
+
|
27
|
+
[project.license]
|
28
|
+
text = "AGPL-3.0"
|
29
|
+
|
30
|
+
[project.urls]
|
31
|
+
homepage = "https://github.com/Linkdlab/funcnodes_basic"
|
32
|
+
source = "https://github.com/Linkdlab/funcnodes_basic"
|
33
|
+
tracker = "https://github.com/Linkdlab/funcnodes_basic/issues"
|
34
|
+
download = "https://pypi.org/project/funcnodes-basic/#files"
|
35
|
+
|
36
|
+
[project.entry-points."funcnodes.module"]
|
37
|
+
module = "funcnodes_basic"
|
38
|
+
shelf = "funcnodes_basic:NODE_SHELF"
|
39
|
+
|
40
|
+
[tool.setuptools.package-dir]
|
41
|
+
"" = "src"
|
42
|
+
|
43
|
+
|
44
|
+
[tool.setuptools.packages.find]
|
45
|
+
where = [ "src",]
|
@@ -4,15 +4,18 @@ from .math_nodes import NODE_SHELF as math_shelf
|
|
4
4
|
from .lists import NODE_SHELF as lists_shelf
|
5
5
|
from .strings import NODE_SHELF as strings_shelf
|
6
6
|
from .dicts import NODE_SHELF as dicts_shelf
|
7
|
+
from .input import NODE_SHELF as input_shelf
|
8
|
+
from .dataclass import NODE_SHELF as dataclass_shelf
|
7
9
|
|
8
|
-
|
9
|
-
__version__ = "0.2.1"
|
10
|
+
__version__ = "0.2.3"
|
10
11
|
|
11
12
|
NODE_SHELF = Shelf(
|
12
13
|
nodes=[],
|
13
14
|
subshelves=[
|
15
|
+
input_shelf,
|
14
16
|
lists_shelf,
|
15
17
|
dicts_shelf,
|
18
|
+
dataclass_shelf,
|
16
19
|
strings_shelf,
|
17
20
|
math_shelf,
|
18
21
|
logic_shelf,
|
@@ -0,0 +1,64 @@
|
|
1
|
+
import dataclasses
|
2
|
+
import funcnodes_core as fn
|
3
|
+
from typing import Any, Dict
|
4
|
+
|
5
|
+
|
6
|
+
@fn.NodeDecorator(
|
7
|
+
id="dataclass.to_dict",
|
8
|
+
)
|
9
|
+
def dataclass_to_dict(instance: Any) -> Dict[str, Any]:
|
10
|
+
"""
|
11
|
+
Convert a dataclass instance to a dictionary.
|
12
|
+
|
13
|
+
Args:
|
14
|
+
instance (object): The dataclass instance to convert.
|
15
|
+
|
16
|
+
Returns:
|
17
|
+
dict: The dictionary representation of the dataclass instance.
|
18
|
+
"""
|
19
|
+
if not dataclasses.is_dataclass(instance):
|
20
|
+
raise TypeError(f"Expected a dataclass instance, got {type(instance)}")
|
21
|
+
|
22
|
+
return dataclasses.asdict(instance)
|
23
|
+
|
24
|
+
|
25
|
+
@fn.NodeDecorator(
|
26
|
+
id="dataclass.get_field",
|
27
|
+
default_io_options={
|
28
|
+
"instance": {
|
29
|
+
"on": {
|
30
|
+
"after_set_value": fn.decorator.update_other_io_value_options(
|
31
|
+
"field_name",
|
32
|
+
lambda result: {
|
33
|
+
"options": [field.name for field in dataclasses.fields(result)]
|
34
|
+
if dataclasses.is_dataclass(result)
|
35
|
+
else None,
|
36
|
+
},
|
37
|
+
)
|
38
|
+
}
|
39
|
+
}
|
40
|
+
},
|
41
|
+
)
|
42
|
+
def dataclass_get_field(instance: Any, field_name: str) -> Any:
|
43
|
+
"""
|
44
|
+
Get a field value from a dataclass instance.
|
45
|
+
"""
|
46
|
+
if not dataclasses.is_dataclass(instance):
|
47
|
+
raise TypeError(f"Expected a dataclass instance, got {type(instance)}")
|
48
|
+
|
49
|
+
if not hasattr(instance, field_name):
|
50
|
+
raise AttributeError(
|
51
|
+
f"{instance.__class__.__name__} has no field '{field_name}'"
|
52
|
+
)
|
53
|
+
|
54
|
+
return getattr(instance, field_name)
|
55
|
+
|
56
|
+
|
57
|
+
NODE_SHELF = fn.Shelf(
|
58
|
+
nodes=[
|
59
|
+
dataclass_to_dict,
|
60
|
+
dataclass_get_field,
|
61
|
+
],
|
62
|
+
name="dataclass",
|
63
|
+
description="Nodes for working with dataclasses",
|
64
|
+
)
|
@@ -0,0 +1,84 @@
|
|
1
|
+
from typing import Union
|
2
|
+
import funcnodes_core as fn
|
3
|
+
|
4
|
+
|
5
|
+
@fn.NodeDecorator(
|
6
|
+
node_id="input.any",
|
7
|
+
node_name="Input",
|
8
|
+
description="Any input",
|
9
|
+
outputs=[
|
10
|
+
{"name": "out"},
|
11
|
+
],
|
12
|
+
)
|
13
|
+
def any_input(input: Union[str, float, int, bool]) -> str:
|
14
|
+
return input
|
15
|
+
|
16
|
+
|
17
|
+
@fn.NodeDecorator(
|
18
|
+
node_id="input.str",
|
19
|
+
node_name="String Input",
|
20
|
+
description="Input a string",
|
21
|
+
outputs=[
|
22
|
+
{"name": "string"},
|
23
|
+
],
|
24
|
+
)
|
25
|
+
def str_input(input: str) -> str:
|
26
|
+
return str(input)
|
27
|
+
|
28
|
+
|
29
|
+
@fn.NodeDecorator(
|
30
|
+
node_id="input.int",
|
31
|
+
node_name="Integer Input",
|
32
|
+
description="Input an integer",
|
33
|
+
outputs=[
|
34
|
+
{"name": "integer"},
|
35
|
+
],
|
36
|
+
)
|
37
|
+
def int_input(input: int) -> int:
|
38
|
+
return int(input)
|
39
|
+
|
40
|
+
|
41
|
+
@fn.NodeDecorator(
|
42
|
+
node_id="input.float",
|
43
|
+
node_name="Float Input",
|
44
|
+
description="Input a float",
|
45
|
+
outputs=[
|
46
|
+
{"name": "float"},
|
47
|
+
],
|
48
|
+
)
|
49
|
+
def float_input(input: float) -> float:
|
50
|
+
return float(input)
|
51
|
+
|
52
|
+
|
53
|
+
@fn.NodeDecorator(
|
54
|
+
node_id="input.bool",
|
55
|
+
node_name="Boolean Input",
|
56
|
+
description="Input a boolean",
|
57
|
+
outputs=[
|
58
|
+
{"name": "boolean"},
|
59
|
+
],
|
60
|
+
)
|
61
|
+
def bool_input(input: bool) -> bool:
|
62
|
+
if isinstance(input, str):
|
63
|
+
if input.lower() in ("true", "1", "yes"):
|
64
|
+
input = True
|
65
|
+
elif input.lower() in ("false", "0", "no"):
|
66
|
+
input = False
|
67
|
+
elif isinstance(input, (int, float)):
|
68
|
+
input = bool(input)
|
69
|
+
else:
|
70
|
+
input = bool(input)
|
71
|
+
return bool(input)
|
72
|
+
|
73
|
+
|
74
|
+
NODE_SHELF = fn.Shelf(
|
75
|
+
nodes=[
|
76
|
+
any_input,
|
77
|
+
str_input,
|
78
|
+
int_input,
|
79
|
+
float_input,
|
80
|
+
bool_input,
|
81
|
+
],
|
82
|
+
name="Input",
|
83
|
+
description="Simple input nodes",
|
84
|
+
)
|
@@ -0,0 +1,51 @@
|
|
1
|
+
Metadata-Version: 2.4
|
2
|
+
Name: funcnodes-basic
|
3
|
+
Version: 0.2.3
|
4
|
+
Summary: Basic functionalities for funcnodes
|
5
|
+
Author-email: Julian Kimmig <julian.kimmig@linkdlab.de>
|
6
|
+
License: AGPL-3.0
|
7
|
+
Project-URL: homepage, https://github.com/Linkdlab/funcnodes_basic
|
8
|
+
Project-URL: source, https://github.com/Linkdlab/funcnodes_basic
|
9
|
+
Project-URL: tracker, https://github.com/Linkdlab/funcnodes_basic/issues
|
10
|
+
Project-URL: download, https://pypi.org/project/funcnodes-basic/#files
|
11
|
+
Classifier: License :: OSI Approved :: GNU Affero General Public License v3 or later (AGPLv3+)
|
12
|
+
Requires-Python: >=3.11
|
13
|
+
Description-Content-Type: text/markdown
|
14
|
+
License-File: LICENSE
|
15
|
+
Requires-Dist: funcnodes-core>=0.3.9
|
16
|
+
Requires-Dist: funcnodes
|
17
|
+
Dynamic: license-file
|
18
|
+
|
19
|
+
# FuncNodes Basic
|
20
|
+
|
21
|
+
## Overview
|
22
|
+
|
23
|
+
`funcnodes-basic` is a collection of essential nodes designed to handle fundamental operations in [Funcnodes](https://github.com/linkdlab/funcnodes), such as list manipulation, dictionary operations, logic control, mathematical functions, and string processing. It provides a foundation of commonly needed functionalities in computational graphs built with the FuncNodes framework. Each module within this package contains a set of nodes, grouped by their operation type, allowing seamless integration into broader systems.
|
24
|
+
|
25
|
+
## Installation
|
26
|
+
|
27
|
+
Install the package with:
|
28
|
+
|
29
|
+
```bash
|
30
|
+
pip install funcnodes-basic
|
31
|
+
```
|
32
|
+
|
33
|
+
## Getting Started
|
34
|
+
|
35
|
+
Please refere to the [Funcnodes](https://github.com/linkdlab/funcnodes) package
|
36
|
+
|
37
|
+
## Example Usage
|
38
|
+
|
39
|
+
You can integrate the provided nodes in your computational graphs, connecting inputs and outputs between nodes to build complex workflows that utilize dictionaries, lists, logic, math, and string operations.
|
40
|
+
|
41
|
+
## Contribution
|
42
|
+
|
43
|
+
Feel free to contribute to this project by submitting pull requests. You can help by adding new nodes, fixing bugs, or enhancing documentation.
|
44
|
+
|
45
|
+
## License
|
46
|
+
|
47
|
+
This project is licensed under the MIT License.
|
48
|
+
|
49
|
+
## Contact
|
50
|
+
|
51
|
+
For any questions or issues, please open an issue on the GitHub repository.
|
@@ -0,0 +1,27 @@
|
|
1
|
+
LICENSE
|
2
|
+
MANIFEST.in
|
3
|
+
README.md
|
4
|
+
pyproject.toml
|
5
|
+
src/funcnodes_basic/__init__.py
|
6
|
+
src/funcnodes_basic/dataclass.py
|
7
|
+
src/funcnodes_basic/dicts.py
|
8
|
+
src/funcnodes_basic/input.py
|
9
|
+
src/funcnodes_basic/lists.py
|
10
|
+
src/funcnodes_basic/logic.py
|
11
|
+
src/funcnodes_basic/math_nodes.py
|
12
|
+
src/funcnodes_basic/strings.py
|
13
|
+
src/funcnodes_basic.egg-info/PKG-INFO
|
14
|
+
src/funcnodes_basic.egg-info/SOURCES.txt
|
15
|
+
src/funcnodes_basic.egg-info/dependency_links.txt
|
16
|
+
src/funcnodes_basic.egg-info/entry_points.txt
|
17
|
+
src/funcnodes_basic.egg-info/requires.txt
|
18
|
+
src/funcnodes_basic.egg-info/top_level.txt
|
19
|
+
tests/test_all_nodes_pytest.py
|
20
|
+
tests/test_dataclass.py
|
21
|
+
tests/test_dict.py
|
22
|
+
tests/test_import.py
|
23
|
+
tests/test_inputs.py
|
24
|
+
tests/test_lists.py
|
25
|
+
tests/test_logic.py
|
26
|
+
tests/test_math.py
|
27
|
+
tests/test_strings.py
|
@@ -0,0 +1 @@
|
|
1
|
+
|
@@ -0,0 +1 @@
|
|
1
|
+
funcnodes_basic
|
@@ -0,0 +1,141 @@
|
|
1
|
+
from funcnodes_basic import dataclass as dc_nodes
|
2
|
+
import pytest_funcnodes
|
3
|
+
import pytest
|
4
|
+
import funcnodes_core as fn
|
5
|
+
from dataclasses import dataclass
|
6
|
+
|
7
|
+
|
8
|
+
@dataclass
|
9
|
+
class SimpleDataClass:
|
10
|
+
name: str
|
11
|
+
value: int
|
12
|
+
is_active: bool = True
|
13
|
+
|
14
|
+
|
15
|
+
@dataclass
|
16
|
+
class NestedDataClass:
|
17
|
+
id: int
|
18
|
+
data: SimpleDataClass
|
19
|
+
|
20
|
+
|
21
|
+
@pytest_funcnodes.nodetest(dc_nodes.dataclass_to_dict)
|
22
|
+
async def test_dataclass_to_dict():
|
23
|
+
node = dc_nodes.dataclass_to_dict()
|
24
|
+
|
25
|
+
# Test with a simple dataclass
|
26
|
+
instance1 = SimpleDataClass(name="Test1", value=100)
|
27
|
+
node.inputs["instance"].value = instance1
|
28
|
+
await node
|
29
|
+
assert node.outputs["out"].value == {
|
30
|
+
"name": "Test1",
|
31
|
+
"value": 100,
|
32
|
+
"is_active": True,
|
33
|
+
}
|
34
|
+
|
35
|
+
# Test with a nested dataclass
|
36
|
+
instance2 = NestedDataClass(
|
37
|
+
id=1, data=SimpleDataClass(name="Nested", value=200, is_active=False)
|
38
|
+
)
|
39
|
+
node.inputs["instance"].value = instance2
|
40
|
+
await node
|
41
|
+
assert node.outputs["out"].value == {
|
42
|
+
"id": 1,
|
43
|
+
"data": {"name": "Nested", "value": 200, "is_active": False},
|
44
|
+
}
|
45
|
+
|
46
|
+
# Test with non-dataclass input
|
47
|
+
node.inputs["instance"].value = {"not": "a dataclass"}
|
48
|
+
with pytest.raises(fn.NodeTriggerError) as excinfo:
|
49
|
+
await node
|
50
|
+
assert "Expected a dataclass instance" in str(excinfo.value)
|
51
|
+
|
52
|
+
node.inputs["instance"].value = 123
|
53
|
+
with pytest.raises(fn.NodeTriggerError) as excinfo:
|
54
|
+
await node
|
55
|
+
assert "Expected a dataclass instance" in str(excinfo.value)
|
56
|
+
|
57
|
+
|
58
|
+
@pytest_funcnodes.nodetest(dc_nodes.dataclass_get_field)
|
59
|
+
async def test_dataclass_get_field():
|
60
|
+
node = dc_nodes.dataclass_get_field()
|
61
|
+
|
62
|
+
instance_simple = SimpleDataClass(name="TestSimple", value=123)
|
63
|
+
instance_nested = NestedDataClass(
|
64
|
+
id=1, data=SimpleDataClass(name="Nested", value=200, is_active=False)
|
65
|
+
)
|
66
|
+
|
67
|
+
# Test setting instance updates field_name options
|
68
|
+
node.inputs["instance"].value = instance_simple
|
69
|
+
await node # Initial trigger to process instance and update options
|
70
|
+
assert node.inputs["field_name"].value_options["options"] == [
|
71
|
+
"name",
|
72
|
+
"value",
|
73
|
+
"is_active",
|
74
|
+
]
|
75
|
+
|
76
|
+
# Test getting a valid field 'name'
|
77
|
+
node.inputs["field_name"].value = "name"
|
78
|
+
await node
|
79
|
+
assert node.outputs["out"].value == "TestSimple"
|
80
|
+
|
81
|
+
# Test getting a valid field 'value'
|
82
|
+
node.inputs["field_name"].value = "value"
|
83
|
+
await node
|
84
|
+
assert node.outputs["out"].value == 123
|
85
|
+
|
86
|
+
# Test getting a valid field 'is_active'
|
87
|
+
node.inputs["field_name"].value = "is_active"
|
88
|
+
await node
|
89
|
+
assert node.outputs["out"].value is True
|
90
|
+
|
91
|
+
# Test with nested dataclass, first update options
|
92
|
+
node.inputs["instance"].value = instance_nested
|
93
|
+
|
94
|
+
assert node.inputs["field_name"].value_options["options"] == ["id", "data"]
|
95
|
+
|
96
|
+
# Test getting 'id' from nested
|
97
|
+
node.inputs["field_name"].value = "id"
|
98
|
+
await node
|
99
|
+
assert node.outputs["out"].value == 1
|
100
|
+
|
101
|
+
# Test getting 'data' (which is another dataclass)
|
102
|
+
node.inputs["field_name"].value = "data"
|
103
|
+
await node
|
104
|
+
assert node.outputs["out"].value == SimpleDataClass(
|
105
|
+
name="Nested", value=200, is_active=False
|
106
|
+
)
|
107
|
+
|
108
|
+
# Test getting non-existent field
|
109
|
+
node.inputs["instance"].value = instance_simple
|
110
|
+
node.inputs["field_name"].value = "non_existent_field"
|
111
|
+
with pytest.raises(fn.NodeTriggerError) as excinfo:
|
112
|
+
await node
|
113
|
+
assert "has no field 'non_existent_field'" in str(excinfo.value)
|
114
|
+
|
115
|
+
# Test with non-dataclass input
|
116
|
+
node.inputs["instance"].value = {"not": "a dataclass"}
|
117
|
+
node.inputs["field_name"].value = "some_field" # field_name options will be None
|
118
|
+
assert node.inputs["field_name"].value_options["options"] is None
|
119
|
+
|
120
|
+
with pytest.raises(fn.NodeTriggerError) as excinfo:
|
121
|
+
await node # trigger the func with invalid instance
|
122
|
+
assert "Expected a dataclass instance" in str(excinfo.value)
|
123
|
+
|
124
|
+
# Test dynamic update of options when instance changes
|
125
|
+
node.inputs["instance"].value = instance_nested
|
126
|
+
assert node.inputs["field_name"].value_options["options"] == ["id", "data"]
|
127
|
+
node.inputs["field_name"].value = "id"
|
128
|
+
await node
|
129
|
+
assert node.outputs["out"].value == 1
|
130
|
+
|
131
|
+
node.inputs["instance"].value = instance_simple
|
132
|
+
assert node.inputs["field_name"].value_options["options"] == [
|
133
|
+
"name",
|
134
|
+
"value",
|
135
|
+
"is_active",
|
136
|
+
]
|
137
|
+
node.inputs[
|
138
|
+
"field_name"
|
139
|
+
].value = "name" # previous 'id' is no longer valid for options, but value remains
|
140
|
+
await node
|
141
|
+
assert node.outputs["out"].value == "TestSimple"
|
@@ -0,0 +1,101 @@
|
|
1
|
+
from funcnodes_basic import dicts
|
2
|
+
import pytest_funcnodes
|
3
|
+
# DictGetNode,
|
4
|
+
# dict_keys,
|
5
|
+
# dict_values,
|
6
|
+
# dict_items,
|
7
|
+
# dict_from_items,
|
8
|
+
# dict_from_keys_values,
|
9
|
+
# dict_to_list,
|
10
|
+
|
11
|
+
|
12
|
+
@pytest_funcnodes.nodetest(dicts.DictGetNode)
|
13
|
+
async def test_dict_get():
|
14
|
+
testdict = {"a": 1, "b": 2, "c": 3}
|
15
|
+
|
16
|
+
node = dicts.DictGetNode()
|
17
|
+
|
18
|
+
node.inputs["dictionary"].value = testdict
|
19
|
+
await node
|
20
|
+
|
21
|
+
assert node.inputs["key"].value_options["options"] == {"a": "0", "b": "1", "c": "2"}
|
22
|
+
|
23
|
+
node.inputs["key"].value = "0"
|
24
|
+
await node
|
25
|
+
|
26
|
+
assert node.outputs["value"].value == 1
|
27
|
+
|
28
|
+
|
29
|
+
@pytest_funcnodes.nodetest(dicts.dict_keys)
|
30
|
+
async def test_dict_keys():
|
31
|
+
testdict = {"a": 1, "b": 2, "c": 3}
|
32
|
+
|
33
|
+
node = dicts.dict_keys()
|
34
|
+
|
35
|
+
node.inputs["dictionary"].value = testdict
|
36
|
+
await node
|
37
|
+
|
38
|
+
assert node.outputs["out"].value == ["a", "b", "c"]
|
39
|
+
|
40
|
+
|
41
|
+
@pytest_funcnodes.nodetest(dicts.dict_values)
|
42
|
+
async def test_dict_values():
|
43
|
+
testdict = {"a": 1, "b": 2, "c": 3}
|
44
|
+
|
45
|
+
node = dicts.dict_values()
|
46
|
+
|
47
|
+
node.inputs["dictionary"].value = testdict
|
48
|
+
await node
|
49
|
+
|
50
|
+
assert node.outputs["out"].value == [1, 2, 3]
|
51
|
+
|
52
|
+
|
53
|
+
@pytest_funcnodes.nodetest(dicts.dict_items)
|
54
|
+
async def test_dict_items():
|
55
|
+
testdict = {"a": 1, "b": 2, "c": 3}
|
56
|
+
|
57
|
+
node = dicts.dict_items()
|
58
|
+
|
59
|
+
node.inputs["dictionary"].value = testdict
|
60
|
+
await node
|
61
|
+
|
62
|
+
assert node.outputs["out"].value == [("a", 1), ("b", 2), ("c", 3)]
|
63
|
+
|
64
|
+
|
65
|
+
@pytest_funcnodes.nodetest(dicts.dict_from_items)
|
66
|
+
async def test_dict_from_items():
|
67
|
+
testitems = [("a", 1), ("b", 2), ("c", 3)]
|
68
|
+
|
69
|
+
node = dicts.dict_from_items()
|
70
|
+
|
71
|
+
node.inputs["items"].value = testitems
|
72
|
+
await node
|
73
|
+
|
74
|
+
assert node.outputs["out"].value == {"a": 1, "b": 2, "c": 3}
|
75
|
+
|
76
|
+
|
77
|
+
@pytest_funcnodes.nodetest(dicts.dict_from_keys_values)
|
78
|
+
async def test_dict_from_keys_values():
|
79
|
+
testkeys = ["a", "b", "c"]
|
80
|
+
testvalues = [1, 2, 3]
|
81
|
+
|
82
|
+
node = dicts.dict_from_keys_values()
|
83
|
+
|
84
|
+
node.inputs["keys"].value = testkeys
|
85
|
+
node.inputs["values"].value = testvalues
|
86
|
+
await node
|
87
|
+
|
88
|
+
assert node.outputs["out"].value == {"a": 1, "b": 2, "c": 3}
|
89
|
+
|
90
|
+
|
91
|
+
@pytest_funcnodes.nodetest(dicts.dict_to_list)
|
92
|
+
async def test_dict_to_list():
|
93
|
+
testdict = {"a": 1, "b": 2, "c": 3}
|
94
|
+
|
95
|
+
node = dicts.dict_to_list()
|
96
|
+
|
97
|
+
node.inputs["dictionary"].value = testdict
|
98
|
+
await node
|
99
|
+
|
100
|
+
assert node.outputs["keys"].value == ["a", "b", "c"]
|
101
|
+
assert node.outputs["values"].value == [1, 2, 3]
|