Schema declarations on the Application state allows Framework to handle complex serialisation
scenarios, while also allowing your IDE and toolchains to provide autocomplete and type checking.
Schema declaration
import writer as wf
class AppSchema(wf.WriterState):
counter: int
initial_state = wf.init_state({
"counter": 0
}, schema=AppSchema)
def increment(state: AppSchema):
state.counter += 1
Accessing an attribute by its key is always possible.
def increment(state: AppSchema):
state["counter"] += 1
Attributes missing from the schema remain accessible by their key.
initial_state = wf.init_state({
"counter": 0,
"message": None
}, schema=AppSchema)
def increment(state: AppSchema):
state['message'] = "Hello pigeon"
Schema composition
Schema composition allows you to model a complex Application state.
class MyappSchema(wf.State):
title: str
class AppSchema(wf.WriterState):
my_app: MyappSchema
counter: int
initial_state = wf.init_state({
"counter": 0,
"my_app": {
"title": "Nested value"
}
}, schema=AppSchema)
Calculated properties
Calculated properties are updated automatically when a dependency changes.
They can be used to calculate values derived from application state.
class MyAppState(wf.State):
counter: List[int]
class MyState(wf.WriterState):
counter: List[int]
@wf.property(['counter', 'app.counter'])
def total_counter(self):
return sum(self.counter) + sum(self.app.counter)
initial_state = wf.init_state({
"counter": 0,
"my_app": {
"counter": 0
}
}, schema=MyState)
Multi-level dictionary
Some components like Vega Lite Chart require specifying a graph in the form of a multi-level dictionary.
A schema allows you to specify that an attribute which contains a dictionary
must be treated as a dictionary, rather than as a group of state.
class AppSchema(wf.WriterState):
vegas_graph: dict
def handle_vega_graph(state: AppSchema):
graph = state.vega_graph
graph["data"]["values"][0]["b"] += 1000
state.vega_graph = graph
initial_state = wf.init_state({
"vegas_graph": {
"data": {
"values": [
{"a": "C", "b": 2}, {"a": "C", "b": 7}, {"a": "C", "b": 4},
{"a": "D", "b": 1}, {"a": "D", "b": 2}, {"a": "D", "b": 6},
{"a": "E", "b": 8}, {"a": "E", "b": 4}, {"a": "E", "b": 7}
]
},
"mark": "bar",
"encoding": {
"x": {"field": "a", "type": "nominal"},
"y": {"aggregate": "average", "field": "b", "type": "quantitative"}
}
},
}, schema=AppSchema)
Type checking
A schema allows you to check the integrity of your back-end using the type system.
The code below will raise an error with mypy.
$ mypy apps/myapp/main.py
apps/myapp/main.py:7: error: "AppSchema" has no attribute "countr"; maybe "counter"? [attr-defined]
Here is the code, can you spot the error ?
import writer as wf
class AppSchema(wf.WriterState):
counter: int
def increment(state: AppSchema):
state.countr += 1
initial_state = wf.init_state({
"counter": 26,
}, schema=AppSchema)