Skip to content

Commit 1d9e58b

Browse files
authored
Merge pull request #36 from bcdev/forman-x-contrib_docs
More documentation
2 parents 220bd3d + bc9462d commit 1d9e58b

File tree

13 files changed

+229
-71
lines changed

13 files changed

+229
-71
lines changed

chartlets.py/chartlets/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from .callback import Callback
2+
from .channel import Channel
23
from .channel import Input
34
from .channel import Output
45
from .channel import State

chartlets.py/chartlets/channel.py

Lines changed: 59 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,25 @@ def no_trigger(self):
4242

4343

4444
class Input(Channel):
45-
"""An input value read from component state.
46-
A component state change may trigger callback invocation.
45+
"""Describes the source of a parameter value for the user-provided
46+
layout and callback functions.
47+
`Input` instances are used as arguments passed to the
48+
`layout` and `callback` decorators.
49+
50+
An `Input` describes from which property in which state a parameter
51+
value is read. According state changes trigger callback invocation.
52+
53+
Args:
54+
id:
55+
Value of a component's "id" property.
56+
Used only if `source` is `"component"`.
57+
property:
58+
Name of the property of a component or state.
59+
To address properties in nested objects or arrays
60+
use a dot (`.`) to separate property names and array
61+
indexes.
62+
source: One of `"component"` (the default), `"container"`,
63+
or `"app"`.
4764
"""
4865

4966
# noinspection PyShadowingBuiltins
@@ -58,8 +75,26 @@ def __init__(
5875

5976

6077
class State(Input):
61-
"""An input value read from component state.
62-
Does not trigger callback invocation.
78+
"""Describes the source of a parameter value for the user-provided
79+
layout and callback functions.
80+
`State` instances are used as arguments passed to the
81+
`layout` and `callback` decorators.
82+
83+
Just like an `Input`, a `State` describes from which property in which state
84+
a parameter value is read, but according state changes
85+
will **not*Ü* trigger callback invocation.
86+
87+
Args:
88+
id:
89+
Value of a component's "id" property.
90+
Used only if `source` is `"component"`.
91+
property:
92+
Name of the property of a component or state.
93+
To address properties in nested objects or arrays
94+
use a dot (`.`) to separate property names and array
95+
indexes.
96+
source: One of `"component"` (the default), `"container"`,
97+
or `"app"`.
6398
"""
6499

65100
# noinspection PyShadowingBuiltins
@@ -73,7 +108,26 @@ def __init__(
73108

74109

75110
class Output(Channel):
76-
"""Callback output."""
111+
"""Describes the target of a value returned from a user-provided
112+
callback function.
113+
`Output` instances are used as arguments passed to the
114+
`callback` decorators.
115+
116+
An `Output` describes which property in which state should be
117+
updated from the returned callback value.
118+
119+
Args:
120+
id:
121+
Value of a component's "id" property.
122+
Used only if `source` is `"component"`.
123+
property:
124+
Name of the property of a component or state.
125+
To address properties in nested objects or arrays
126+
use a dot (`.`) to separate property names and array
127+
indexes.
128+
target: One of `"component"` (the default), `"container"`,
129+
or `"app"`.
130+
"""
77131

78132
# noinspection PyShadowingBuiltins
79133
def __init__(

chartlets.py/chartlets/component.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ class Component(ABC):
88
"""Base class for components.
99
Provides the common attributes that apply to all components.
1010
"""
11+
1112
# Common HTML properties
1213
id: str | None = None
1314
"""HTML `id` property. Required for referring to this component."""

chartlets.py/chartlets/contribution.py

Lines changed: 52 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ class Contribution(ABC):
1818
name: A name that should be unique within an extension.
1919
initial_state: contribution specific attribute values.
2020
"""
21+
2122
# noinspection PyShadowingBuiltins
2223
def __init__(self, name: str, **initial_state):
2324
self.name = name
@@ -27,8 +28,13 @@ def __init__(self, name: str, **initial_state):
2728
self.callbacks: list[Callback] = []
2829

2930
def to_dict(self) -> dict[str, Any]:
30-
"""Convert this contribution into a
31-
JSON serializable dictionary.
31+
"""Convert this contribution into a JSON serializable dictionary.
32+
33+
May be overridden by subclasses to allow for specific
34+
JSON serialization.
35+
36+
Returns:
37+
A JSON serializable dictionary.
3238
"""
3339
d = dict(name=self.name)
3440
if self.initial_state is not None:
@@ -41,10 +47,13 @@ def to_dict(self) -> dict[str, Any]:
4147
d.update(callbacks=[cb.to_dict() for cb in self.callbacks])
4248
return d
4349

44-
def layout(self, *args) -> Callable:
45-
"""A decorator for a user-provided function that
50+
def layout(self, *args) -> Callable[[Callable], Callable]:
51+
"""Provides a decorator for a user-provided function that
4652
returns the initial user interface layout.
4753
54+
The layout decorator should only be used once for
55+
given contribution instance.
56+
4857
The decorated function must return an instance of
4958
a `chartlets.Component`, usually a `chartlets.components.Box`
5059
that arranges other components in some layout.
@@ -56,16 +65,18 @@ def layout(self, *args) -> Callable:
5665
called `ctx`.
5766
5867
Other parameters of the decorated function are user-defined
59-
and must have a corresponding `chartlets.Input` argument
60-
in the `layout` decorator in the same order.
68+
and must have a corresponding `chartlets.Input` or
69+
`chartlets.State` arguments in the `layout` decorator in the
70+
same order.
6171
6272
Args:
63-
args: Instances of the `chartlets.Input` class that
73+
args:
74+
`chartlets.Input` or `chartlets.State` objects that
6475
define the source of the value for the corresponding
6576
parameter of the decorated function. Optional.
6677
6778
Returns:
68-
The decorated, user-provided function.
79+
The decorator.
6980
"""
7081

7182
def decorator(function: Callable) -> Callable:
@@ -77,7 +88,39 @@ def decorator(function: Callable) -> Callable:
7788
return decorator
7889

7990
def callback(self, *args: Channel) -> Callable[[Callable], Callable]:
80-
"""Decorator."""
91+
"""Provide a decorator for a user-provided callback function.
92+
93+
Callback functions are event handlers that react
94+
to events fired by the host application state or by events
95+
fired by related components provided by this contribution's
96+
layout.
97+
98+
The first parameter of the decorated function must be a
99+
positional argument. It provides an application-specific
100+
context that is used to allow for access server-side
101+
configuration and resources. The parameter should be
102+
called `ctx`.
103+
104+
Other parameters of the decorated function are user-defined
105+
and must have a corresponding `chartlets.Input` argument
106+
in the `layout` decorator in the same order.
107+
108+
The return value of the decorated function is used to change
109+
the component or the application state as described by its
110+
`Output` argument passed to the decorator. If more than out
111+
output is specified, the function is supposed to return a tuple
112+
of values with the same number of items in the order given
113+
by the `Output` arguments passed to the decorator.
114+
115+
Args:
116+
args:
117+
`chartlets.Input`, `chartlets.State`, and `Output` objects that
118+
define sources and targets for the parameters passed to the
119+
callback function and the returned from the callback function.
120+
121+
Returns:
122+
The decorated, user-provided function.
123+
"""
81124

82125
def decorator(function: Callable) -> Callable:
83126
self.callbacks.append(
@@ -88,6 +131,3 @@ def decorator(function: Callable) -> Callable:
88131
return function
89132

90133
return decorator
91-
92-
def __str__(self):
93-
return self.name

chartlets.py/chartlets/controllers/callback.py

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,23 @@
55

66

77
# POST /chartlets/callback
8-
def get_callback_results(ext_ctx: ExtensionContext | None, data: dict[str, Any]):
9-
"""Generate the response for `POST /chartlets/callback`.
8+
def get_callback_results(
9+
ext_ctx: ExtensionContext | None, data: dict[str, Any]
10+
) -> Response:
11+
"""Generate the response for the endpoint `POST /chartlets/callback`.
1012
1113
Args:
12-
ext_ctx: Extension context.
14+
ext_ctx: Extension context. If `None`,
15+
the function returns a 404 error response.
1316
data: A dictionary deserialized from a request JSON body
14-
that may contain a key `callbackRequests` of type `list`.
17+
that should contain a key `callbackRequests` of type `list`.
1518
Returns:
16-
A JSON-serializable list.
19+
A `Response` object.
20+
On success, the response is a list of state-change requests
21+
grouped by contributions.
1722
"""
1823
if ext_ctx is None:
19-
return Response.failed(
20-
404, f"no contributions configured"
21-
)
24+
return Response.failed(404, f"no contributions configured")
2225

2326
# TODO: validate data
2427
callback_requests: list[dict] = data.get("callbackRequests") or []

chartlets.py/chartlets/controllers/contributions.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,18 @@
33

44

55
def get_contributions(ext_ctx: ExtensionContext | None) -> Response:
6-
"""Generate the response for `GET /chartlets/contributions`."""
6+
"""Generate the response for the endpoint `GET /chartlets/contributions`.
7+
8+
Args:
9+
ext_ctx: Extension context. If `None`,
10+
the function returns a 404 error response.
11+
Returns:
12+
A `Response` object.
13+
On success, the response is a dictionary that represents
14+
a JSON-serialized component tree.
15+
"""
716
if ext_ctx is None:
8-
return Response.failed(
9-
404, f"no contributions configured"
10-
)
17+
return Response.failed(404, f"no contributions configured")
1118

1219
extensions = ext_ctx.extensions
1320
contributions = ext_ctx.contributions

chartlets.py/chartlets/controllers/layout.py

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,22 +10,23 @@ def get_layout(
1010
contrib_index: int,
1111
data: dict[str, Any],
1212
) -> Response:
13-
"""Generate the response for
13+
"""Generate the response for the endpoint
1414
`POST /chartlets/layout/{contrib_point_name}/{contrib_index}`.
1515
1616
Args:
17-
ext_ctx: Extension context.
17+
ext_ctx: Extension context. If `None`,
18+
the function returns a 404 error response.
1819
contrib_point_name: Contribution point name.
1920
contrib_index: Contribution index.
2021
data: A dictionary deserialized from a request JSON body
2122
that may contain a key `inputValues` of type `list`.
2223
Returns:
23-
A JSON-serializable dictionary.
24+
A `Response` object.
25+
On success, the response is a dictionary that represents
26+
a JSON-serialized component tree.
2427
"""
2528
if ext_ctx is None:
26-
return Response.failed(
27-
404, f"no contributions configured"
28-
)
29+
return Response.failed(404, f"no contributions configured")
2930

3031
# TODO: validate data
3132
input_values = data.get("inputValues") or []

docs/api/components.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# Components API
2+
3+
The Chartlets component API is used by both application contributors
4+
and providers:
5+
6+
- Application contributors use the concrete component classes to create a
7+
contribution's user interface and to output updated components or parts of
8+
it from their callback implementations.
9+
- Application providers use the abstract base classes `Component` and
10+
`Container` to implement new specific components.
11+
12+
13+
## Specific Components
14+
15+
::: chartlets.components.Box
16+
17+
::: chartlets.components.Button
18+
19+
::: chartlets.components.Checkbox
20+
21+
::: chartlets.components.Dropdown
22+
23+
::: chartlets.components.Plot
24+
25+
::: chartlets.components.Typography
26+
27+
## Base classes
28+
29+
::: chartlets.Component
30+
31+
::: chartlets.Container

docs/api/contribution.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Contribution API
2+
3+
The Chartlets contribution API is used by both application providers
4+
and contributors:
5+
6+
- Application providers implement specific contributions for their application
7+
using `Contribution` as a base class.
8+
- Application contributors use the instantiate and parameterize the specific
9+
contributions and implement the functions to create the contributions'
10+
user interface (layout) and their event handlers (callbacks).
11+
12+
13+
::: chartlets.Contribution
14+
15+
::: chartlets.Input
16+
17+
::: chartlets.State
18+
19+
::: chartlets.Output
20+
21+
::: chartlets.Channel

docs/api/controllers.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# Controllers API
2+
3+
The Chartlets controllers API is used by application providers only.
4+
As an application contributor you do not need to care about it.
5+
6+
A controller implements the logic of a specific Chartlets web API endpoint.
7+
It is used to efficiently implement the Chartlets web API using any
8+
web framework such as FastAPI, Flask or Tornado.
9+
10+
Controllers are imported from the `chartlets.components` module, for example:
11+
12+
```python
13+
from chartlets.controllers import get_layout
14+
```
15+
16+
17+
::: chartlets.controllers.get_contributions
18+
19+
::: chartlets.controllers.get_layout
20+
21+
::: chartlets.controllers.get_callback_results
22+
23+
::: chartlets.ExtensionContext
24+
25+
::: chartlets.Response

0 commit comments

Comments
 (0)