Skip to content

Commit 91efb8c

Browse files
committed
v 0.7.0
- Add Block state to store data private to the block. Using block.get_state('key'), block.set_state('key'). - Block state also stores execution status and errors. Using block.get_state('info'), block.set_state('info'). Note: 'info' is a reserved key. - When a block fails on its compute function, its descendants are skipped and the rest of the blocks are computed. - Add delete schema function. (@zabrewer)
1 parent 46b0d0a commit 91efb8c

File tree

10 files changed

+130
-49
lines changed

10 files changed

+130
-49
lines changed

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
66

77
Release dates are in YYYY-MM-DD
88

9+
## [0.7.0] - 2022-08-30
10+
11+
- Add Block state to store data private to the block. Using block.get_state('key'), block.set_state('key').
12+
- Block state also stores execution status and errors. Using block.get_state('info'), block.set_state('info'). Note: 'info' is a reserved key.
13+
- When a block fails on its compute function, its descendants are skipped and the rest of the blocks are computed.
14+
- Add delete schema function. (@zabrewer)
15+
916
## [0.6.1] - 2022-07-25
1017

1118
- Fix base_blocks_list passed to the compute engine.

Developer.md

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,12 @@ Following are the notes for working on the development of the barfi.
66

77
- In terminal 1
88
```shell
9-
$ cd st_barfi/frontend
10-
$ npm run serve
9+
make serve
1110
```
1211

1312
- In terminal 2
1413
```shell
15-
$ streamlit run st_barfi/__init__.py
14+
make run
1615
```
1716

1817
## Requirements
@@ -34,15 +33,15 @@ Run the components's Streamlit app:
3433

3534
```shell
3635
$ . venv/bin/activate # activate the venv you created earlier
37-
$ streamlit run st_barfi/__init__.py # run the root test
36+
$ streamlit run barfi/__init__.py # run the root test
3837
```
3938

4039
## Node environment
4140

4241
Install and initialize the component's frontend:
4342

4443
```shell
45-
$ cd st_barfi/frontend
44+
$ cd barfi/frontend
4645
$ npm install # Install npm dependencies
4746
$ npm run serve # Start the dev server
4847
```

barfi/block_builder.py

Lines changed: 48 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import types
2-
from typing import Callable
2+
from typing import Callable, Any
33
from .option_builder import build_option
44

55

@@ -18,12 +18,13 @@ def __init__(self, name: str = 'Block') -> None:
1818
self._inputs = {}
1919
self._outputs = {}
2020
self._options = {}
21+
self._state = {'info': None}
2122
self._interface_names = []
2223

2324
def __repr__(self) -> str:
2425
return f'<barfi.Block of type `{self._type}` at {hex(id(self))}>'
2526

26-
def __str__(self) -> str:
27+
def __str__(self) -> str:
2728
inputs_name = [input['name'] for input in self._inputs]
2829
outputs_name = [output['name'] for output in self._outputs]
2930
options_name = [option['name'] for option in self._options]
@@ -33,7 +34,7 @@ def __str__(self) -> str:
3334
line_4 = f'Options: {options_name!r} '
3435
return line_1 + line_2 + line_3 + line_4
3536

36-
def add_input(self, name: str = None, value = None) -> None:
37+
def add_input(self, name: str = None, value=None) -> None:
3738
"""
3839
A function defined to add an Input interface to the Block
3940
@@ -43,18 +44,20 @@ def add_input(self, name: str = None, value = None) -> None:
4344
Interface options:
4445
name (str) : The name of the Input interface.
4546
value (any) : The default value for this input interface.
46-
"""
47+
"""
4748
if name:
48-
if name in self._interface_names: raise ValueError(f'name: {name} already exists as an interface to the Block.')
49+
if name in self._interface_names:
50+
raise ValueError(
51+
f'name: {name} already exists as an interface to the Block.')
4952
self._inputs[name] = {'value': value, 'id': None}
5053
self._interface_names.append(name)
5154
else:
5255
in_nos = len(self._inputs)
5356
name = 'Input ' + str(in_nos + 1)
54-
self._inputs[name] = {'value': value, 'id': None}
55-
self._interface_names.append(name)
57+
self._inputs[name] = {'value': value, 'id': None}
58+
self._interface_names.append(name)
5659

57-
def add_output(self, name: str = None, value = None) -> None:
60+
def add_output(self, name: str = None, value=None) -> None:
5861
"""
5962
A function defined to add an Output interface to the Block
6063
@@ -66,40 +69,55 @@ def add_output(self, name: str = None, value = None) -> None:
6669
value (any) : The default value for this output interface.
6770
"""
6871
if name:
69-
if name in self._interface_names: raise ValueError(f'name: {name} already exists as an interface to the Block.')
72+
if name in self._interface_names:
73+
raise ValueError(
74+
f'name: {name} already exists as an interface to the Block.')
7075
self._outputs[name] = {'value': value, 'id': None}
71-
self._interface_names.append(name)
76+
self._interface_names.append(name)
7277
else:
7378
out_nos = len(self._outputs)
7479
name = 'Output ' + str(out_nos + 1)
7580
self._outputs[name] = {'value': value, 'id': None}
7681
self._interface_names.append(name)
7782

7883
def get_interface(self, name: str):
79-
84+
8085
if name in self._inputs:
81-
return self._inputs[name]['value']
86+
return self._inputs[name]['value']
8287
elif name in self._outputs:
83-
return self._outputs[name]['value']
88+
return self._outputs[name]['value']
8489
else:
85-
raise ValueError(f'No interface with name: {name} found for Block')
86-
return None
90+
raise ValueError(f'No interface with name: {name} found for Block')
8791

8892
def set_interface(self, name: str, value) -> None:
8993
if name in self._inputs:
90-
self._inputs[name]['value'] = value
94+
self._inputs[name]['value'] = value
9195
elif name in self._outputs:
9296
self._outputs[name]['value'] = value
9397
else:
94-
raise ValueError(f'No interface with name: {name} found for Block')
98+
raise ValueError(f'No interface with name: {name} found for Block')
9599

96100
def _set_interface_id(self, name: str, id: str) -> None:
97101
if name in self._inputs:
98102
self._inputs[name]['id'] = id
99-
elif name in self._outputs:
103+
elif name in self._outputs:
100104
self._outputs[name]['id'] = id
101105
else:
102-
raise ValueError(f'No interface with name: {name} found for Block')
106+
raise ValueError(f'No interface with name: {name} found for Block')
107+
108+
def set_state(self, key: str, value: Any) -> None:
109+
reserved_state_keys = ['info']
110+
if key in reserved_state_keys:
111+
raise ValueError(
112+
f'Key: {key} used for setting state of block is reserved. Use another key.')
113+
else:
114+
self._state[key] = value
115+
116+
def get_state(self, key: str) -> Any:
117+
if key in self._state:
118+
return self._state[key]
119+
else:
120+
raise ValueError(f'Key: {key} does not exist in state.')
103121

104122
def add_option(self, name: str, type: str, **kwargs) -> None:
105123
"""
@@ -125,30 +143,33 @@ def add_option(self, name: str, type: str, **kwargs) -> None:
125143
'display'], 'Error: Option "type" is not of standard Option interface parameter.'
126144

127145
if name in self._options:
128-
raise ValueError(f'Option with name: {name} aready exists in Block.')
146+
raise ValueError(
147+
f'Option with name: {name} aready exists in Block.')
129148

130149
_option = build_option(name, type, kwargs)
131150

132151
self._options[_option['name']] = _option
133152

134-
def set_option(self, name: str, **kwargs):
135-
# Can only set the 'value' property for now.
153+
def set_option(self, name: str, **kwargs):
154+
# Can only set the 'value' property for now.
136155
if name in self._options:
137156
for arg, value in kwargs.items():
138157
if arg in self._options[name]:
139158
if arg in ['value']:
140159
self._options[name][arg] = value
141160
else:
142-
raise ValueError(f'Cannot set or invalid property: {arg} for Block option.')
161+
raise ValueError(
162+
f'Cannot set or invalid property: {arg} for Block option.')
143163
else:
144-
raise ValueError(f'Property: {arg} is not a valid option property for {name}.')
145-
else:
164+
raise ValueError(
165+
f'Property: {arg} is not a valid option property for {name}.')
166+
else:
146167
raise ValueError(f'Option name: {name} does not exist in Block.')
147168

148-
def get_option(self, name: str):
169+
def get_option(self, name: str):
149170
if name in self._options:
150171
return self._options[name]['value']
151-
else:
172+
else:
152173
raise ValueError(f'Option name: {name} does not exist in Block.')
153174

154175
def _export(self):

barfi/compute_engine.py

Lines changed: 33 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -62,10 +62,10 @@ def _map_block_link(self):
6262
['id']] = _interface[0]
6363
_child_block._set_interface_id(
6464
name=_interface[0], id=_interface[1]['id'])
65-
_block_interfaces[_interface[0]] = _interface[1]
65+
_block_interfaces[_interface[0]] = _interface[1]
6666

67-
for _option in _block['options']:
68-
_child_block.set_option(name=_option[0], value=_option[1])
67+
for _option in _block['options']:
68+
_child_block.set_option(name=_option[0], value=_option[1])
6969

7070
# Active blocks build from the base-blocks and editor-state
7171
self._active_blocks[_block['id']] = {
@@ -131,14 +131,35 @@ def _map_block_link(self):
131131

132132
def _execute_compute(self):
133133
if bool(self._editor_state):
134-
134+
skip_node = set()
135+
descendant_set = {}
135136
for node in nx.topological_sort(self._graph):
136-
self._active_blocks[node]['block']._on_compute()
137-
for key, value in self._active_blocks[node]['block']._outputs.items():
137+
if node not in skip_node:
138138
try:
139-
for find_to in self._map_link_interface_id_from_to[value['id']]:
140-
find_to_block = self._map_interface_id_block_id[find_to]
141-
self._active_blocks[find_to_block]['block'].set_interface(
142-
name=self._map_interface_id_name[find_to], value=value['value'])
143-
except:
144-
pass
139+
self._active_blocks[node]['block']._on_compute()
140+
self._active_blocks[node]['block']._state['info'] = {
141+
'status': 'Computed'}
142+
143+
for _, value in self._active_blocks[node]['block']._outputs.items():
144+
try:
145+
for find_to in self._map_link_interface_id_from_to[value['id']]:
146+
find_to_block = self._map_interface_id_block_id[find_to]
147+
self._active_blocks[find_to_block]['block'].set_interface(
148+
name=self._map_interface_id_name[find_to], value=value['value'])
149+
except:
150+
pass
151+
152+
except Exception as e:
153+
self._active_blocks[node]['block']._state['info'] = {
154+
'status': 'Errored', 'exception': e.args}
155+
node_desc = nx.descendants(self._graph, node)
156+
descendant_set[self._active_blocks[node]
157+
['name']] = node_desc
158+
skip_node = skip_node.union(node_desc)
159+
160+
else:
161+
for parent, child_set in descendant_set.items():
162+
if node in child_set:
163+
break
164+
self._active_blocks[node]['block']._state['info'] = {
165+
'status': 'Errored', 'message': 'Parent block errored', 'parent': parent}

barfi/manage_schema.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,3 +37,20 @@ def load_schema_name(schema_name: str) -> Dict:
3737
else:
3838
raise ValueError(
3939
f'Schema :{schema_name}: not found in the saved schemas')
40+
41+
42+
def delete_schema(schema_name: str):
43+
try:
44+
with open('schemas.barfi', 'rb') as handle_read:
45+
schemas = pickle.load(handle_read)
46+
except FileNotFoundError:
47+
schemas = {}
48+
49+
if schema_name in schemas:
50+
del schemas[schema_name]
51+
else:
52+
raise ValueError(
53+
f'Schema :{schema_name}: not found in the saved schemas')
54+
55+
with open('schemas.barfi', 'wb') as handle_write:
56+
pickle.dump(schemas, handle_write, protocol=pickle.HIGHEST_PROTOCOL)

docs/source/conf.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
author = 'Adithya Krishnan'
2323

2424
# The short X.Y version.
25-
version = '0.6.0'
25+
version = '0.7.0'
2626
# The full version, including alpha/beta/rc tags.
2727
release = 'alpha'
2828

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
setuptools.setup(
1111
name="barfi",
12-
version="0.6.1",
12+
version="0.7.0",
1313
author="Adithya Krishnan",
1414
author_email="krishsandeep@gmail.com",
1515
description="Framework for a graphical programming environment.",

tests/__init__.py

Whitespace-only changes.

tests/test_app.py

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,29 @@
55
import streamlit as st
66
from test_blocks import base_blocks, base_blocks_category
77

8+
9+
810
barfi_schema_name = st.selectbox(
911
'Select a saved schema to load:', barfi_schemas())
1012

1113
compute_engine = st.checkbox('Activate barfi compute engine', value=False)
1214

13-
barfi_result = st_barfi(base_blocks=base_blocks_category, compute_engine=compute_engine,
14-
load_schema=barfi_schema_name)
15+
barfi_result = st_barfi(base_blocks=base_blocks_category, compute_engine=compute_engine, load_schema=barfi_schema_name)
1516

1617
if barfi_result:
1718
st.write(barfi_result)
19+
st.write(barfi_result['Feed-1']['block'].get_interface('Output 1'))
20+
st.write(barfi_result['Feed-2']['block'].get_interface('Output 1'))
21+
st.write(barfi_result['Splitter-1']['block'].get_interface('Input 1'))
22+
st.write(barfi_result['Splitter-1']['block'].get_interface('Output 1'))
23+
st.write(barfi_result['Splitter-1']['block'].get_interface('Output 2'))
24+
st.write(barfi_result['Mixer-1']['block'].get_interface('Input 1'))
25+
st.write(barfi_result['Mixer-1']['block'].get_interface('Input 2'))
26+
st.write(barfi_result['Mixer-1']['block'].get_interface('Output 1'))
27+
st.write(barfi_result['Result-1']['block'].get_interface('Input 1'))
28+
29+
st.write(barfi_result['Feed-1']['block'].get_state('info'))
30+
st.write(barfi_result['Feed-2']['block'].get_state('info'))
31+
st.write(barfi_result['Splitter-1']['block'].get_state('info'))
32+
st.write(barfi_result['Mixer-1']['block'].get_state('info'))
33+
st.write(barfi_result['Result-1']['block'].get_state('info'))

tests/test_blocks.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ def splitter_func(self):
2424
mixer.add_input()
2525
mixer.add_input()
2626
mixer.add_output()
27-
def mixer_func(self):
27+
def mixer_func(self):
2828
in_1 = self.get_interface(name='Input 1')
2929
in_2 = self.get_interface(name='Input 2')
3030
value = (in_1 + in_2)

0 commit comments

Comments
 (0)