Skip to content

Commit 7ca52c5

Browse files
committed
Renamed InputOutput into Channel with common property "link"
1 parent f1c9b34 commit 7ca52c5

File tree

15 files changed

+252
-206
lines changed

15 files changed

+252
-206
lines changed

dashipy/dashipy/__init__.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from .callback import Callback
2-
from .inputoutput import Input
3-
from .inputoutput import Output
4-
from .inputoutput import State
2+
from .channel import Input
3+
from .channel import Output
4+
from .channel import State
55
from .component import Component
66
from .container import Container
77
from .extension import Contribution

dashipy/dashipy/callback.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import types
33
from typing import Any, Callable
44

5-
from dashipy.inputoutput import (
5+
from dashipy.channel import (
66
Input,
77
Output,
88
State,

dashipy/dashipy/channel.py

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
from abc import ABC
2+
from typing import Any, Literal
3+
4+
from .util.assertions import assert_is_instance_of
5+
from .util.assertions import assert_is_none
6+
from .util.assertions import assert_is_one_of
7+
8+
9+
Link = Literal["component"] | Literal["container"] | Literal["app"]
10+
11+
12+
class Channel(ABC):
13+
"""Base class for `Input`, `State`, and `Output`.
14+
Instances are used as argument passed to
15+
the `layout` and `callback` decorators.
16+
"""
17+
18+
# noinspection PyShadowingBuiltins
19+
def __init__(
20+
self,
21+
link: Link | None = None,
22+
id: str | None = None,
23+
property: str | None = None,
24+
):
25+
self.link = link
26+
self.id = id
27+
self.property = property
28+
29+
def to_dict(self) -> dict[str, Any]:
30+
d = {
31+
k: v
32+
for k, v in self.__dict__.items()
33+
if not k.startswith("_") and v is not None
34+
}
35+
if self.no_trigger:
36+
d |= dict(noTrigger=True)
37+
return d
38+
39+
@property
40+
def no_trigger(self):
41+
return isinstance(self, State)
42+
43+
44+
class Input(Channel):
45+
"""An input value read from component state.
46+
A component state change may trigger callback invocation.
47+
"""
48+
49+
# noinspection PyShadowingBuiltins
50+
def __init__(
51+
self,
52+
id: str | None = None,
53+
property: str | None = None,
54+
source: Link | None = None,
55+
):
56+
link, id, property = _validate_input_params(source, id, property)
57+
super().__init__(link=link, id=id, property=property)
58+
59+
60+
class State(Input):
61+
"""An input value read from component state.
62+
Does not trigger callback invocation.
63+
"""
64+
65+
# noinspection PyShadowingBuiltins
66+
def __init__(
67+
self,
68+
id: str | None = None,
69+
property: str | None = None,
70+
source: Link | None = None,
71+
):
72+
super().__init__(id=id, property=property, source=source)
73+
74+
75+
class Output(Channel):
76+
"""Callback output."""
77+
78+
# noinspection PyShadowingBuiltins
79+
def __init__(
80+
self,
81+
id: str | None = None,
82+
property: str | None = None,
83+
target: Link | None = None,
84+
):
85+
target, id, property = _validate_output_params(target, id, property)
86+
super().__init__(link=target, id=id, property=property)
87+
88+
89+
NoneType = type(None)
90+
91+
92+
# noinspection PyShadowingBuiltins
93+
def _validate_input_params(
94+
source: Link | None, id: str | None, property: str | None
95+
) -> tuple[Link, str | None, str | None]:
96+
return _validate_params("source", source, id, property)
97+
98+
99+
# noinspection PyShadowingBuiltins
100+
def _validate_output_params(
101+
target: Link | None, id: str | None, property: str | None
102+
) -> tuple[Link, str | None, str | None]:
103+
return _validate_params("target", target, id, property)
104+
105+
106+
# noinspection PyShadowingBuiltins
107+
def _validate_params(
108+
link_name: str, link: Link | None, id: str | None, property: str | None
109+
) -> tuple[Link, str | None, str | None]:
110+
assert_is_one_of(link_name, link, ("component", "container", "app", None))
111+
if not link or link == "component":
112+
assert_is_instance_of("id", id, (str, NoneType))
113+
assert_is_instance_of("property", id, (str, NoneType))
114+
link = link or "component"
115+
if property is None and id is not None:
116+
property = "value"
117+
else:
118+
assert_is_none("id", id)
119+
assert_is_instance_of("property", property, str)
120+
# noinspection PyTypeChecker
121+
return link, id, property

dashipy/dashipy/contribution.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
from abc import ABC
33

44
from .callback import Callback
5-
from .inputoutput import InputOutput
5+
from .channel import Channel
66

77

88
class Contribution(ABC):
@@ -37,7 +37,7 @@ def decorator(function: Callable) -> Callable:
3737

3838
return decorator
3939

40-
def callback(self, *args: InputOutput) -> Callable[[Callable], Callable]:
40+
def callback(self, *args: Channel) -> Callable[[Callable], Callable]:
4141
"""Decorator."""
4242

4343
def decorator(function: Callable) -> Callable:

dashipy/dashipy/inputoutput.py

Lines changed: 0 additions & 110 deletions
This file was deleted.

dashipy/dashipy/util/assertions.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@ def assert_is_one_of(name: str, value: Any, value_set: Container):
1010

1111
def assert_is_instance_of(name: str, value: Any, type_set: Type | tuple[Type, ...]):
1212
if not isinstance(value, type_set):
13-
raise ValueError(
13+
raise TypeError(
1414
f"value of {name!r} must be an instance of {type_set!r}, but was {value!r}"
1515
)
16+
17+
18+
def assert_is_none(name: str, value: Any):
19+
if value is not None:
20+
raise TypeError(f"value of {name!r} must be None, but was {value!r}")

dashipy/my_extension/my_panel_3.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from dashipy import Component, Input, Output
1+
from dashipy import Component, Input, State, Output
22
from dashipy.components import Box, Dropdown, Checkbox, Typography
33
from dashipy.demo.contribs import Panel
44
from dashipy.demo.context import Context
@@ -11,7 +11,7 @@
1111

1212

1313
@panel.layout(
14-
Input(property="selectedDatasetId", source="app"),
14+
Input(source="app", property="selectedDatasetId"),
1515
)
1616
def render_panel(
1717
ctx: Context,
@@ -53,21 +53,25 @@ def render_panel(
5353

5454
# noinspection PyUnusedLocal
5555
@panel.callback(
56-
Input("selectedDatasetId", source="app"),
56+
Input(source="app", property="selectedDatasetId"),
5757
Input("opaque"),
5858
Input("color"),
59+
State("info_text", "text"),
5960
Output("info_text", "text"),
6061
)
6162
def update_info_text(
6263
ctx: Context,
6364
dataset_id: str = "",
6465
opaque: bool = False,
6566
color: int = 0,
67+
info_text: str = ""
6668
) -> str:
6769
opaque = opaque or False
6870
color = color if color is not None else 0
6971
return (
7072
f"The dataset is {dataset_id},"
7173
f" the color is {COLORS[color][0]} and"
72-
f" it {'is' if opaque else 'is not'} opaque"
74+
f" it {'is' if opaque else 'is not'} opaque."
75+
f" The length of the last info text"
76+
f" was {len(info_text or "")}."
7377
)
Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -61,26 +61,22 @@ def test_to_dict_with_no_outputs(self):
6161
},
6262
"inputs": [
6363
{
64-
"class": "Input",
65-
"source": "component",
64+
"link": "component",
6665
"id": "a",
6766
"property": "value",
6867
},
6968
{
70-
"class": "Input",
71-
"source": "component",
69+
"link": "component",
7270
"id": "b",
7371
"property": "value",
7472
},
7573
{
76-
"class": "Input",
77-
"source": "component",
74+
"link": "component",
7875
"id": "c",
7976
"property": "value",
8077
},
8178
{
82-
"class": "Input",
83-
"source": "component",
79+
"link": "component",
8480
"id": "d",
8581
"property": "value",
8682
},
@@ -115,22 +111,19 @@ def test_to_dict_with_two_outputs(self):
115111
},
116112
"inputs": [
117113
{
118-
"class": "Input",
119-
"source": "component",
114+
"link": "component",
120115
"id": "n",
121116
"property": "value",
122117
}
123118
],
124119
"outputs": [
125120
{
126-
"class": "Output",
127-
"target": "component",
121+
"link": "component",
128122
"id": "select",
129123
"property": "options",
130124
},
131125
{
132-
"class": "Output",
133-
"target": "component",
126+
"link": "component",
134127
"id": "select",
135128
"property": "value",
136129
},

0 commit comments

Comments
 (0)