Skip to content

Commit

Permalink
v 0.7.0
Browse files Browse the repository at this point in the history
- 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)
  • Loading branch information
krish-adi committed Aug 30, 2022
1 parent 46b0d0a commit 91efb8c
Show file tree
Hide file tree
Showing 10 changed files with 130 additions and 49 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

Release dates are in YYYY-MM-DD

## [0.7.0] - 2022-08-30

- 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)

## [0.6.1] - 2022-07-25

- Fix base_blocks_list passed to the compute engine.
Expand Down
9 changes: 4 additions & 5 deletions Developer.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,12 @@ Following are the notes for working on the development of the barfi.

- In terminal 1
```shell
$ cd st_barfi/frontend
$ npm run serve
make serve
```

- In terminal 2
```shell
$ streamlit run st_barfi/__init__.py
make run
```

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

```shell
$ . venv/bin/activate # activate the venv you created earlier
$ streamlit run st_barfi/__init__.py # run the root test
$ streamlit run barfi/__init__.py # run the root test
```

## Node environment

Install and initialize the component's frontend:

```shell
$ cd st_barfi/frontend
$ cd barfi/frontend
$ npm install # Install npm dependencies
$ npm run serve # Start the dev server
```
Expand Down
75 changes: 48 additions & 27 deletions barfi/block_builder.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import types
from typing import Callable
from typing import Callable, Any
from .option_builder import build_option


Expand All @@ -18,12 +18,13 @@ def __init__(self, name: str = 'Block') -> None:
self._inputs = {}
self._outputs = {}
self._options = {}
self._state = {'info': None}
self._interface_names = []

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

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

def add_input(self, name: str = None, value = None) -> None:
def add_input(self, name: str = None, value=None) -> None:
"""
A function defined to add an Input interface to the Block
Expand All @@ -43,18 +44,20 @@ def add_input(self, name: str = None, value = None) -> None:
Interface options:
name (str) : The name of the Input interface.
value (any) : The default value for this input interface.
"""
"""
if name:
if name in self._interface_names: raise ValueError(f'name: {name} already exists as an interface to the Block.')
if name in self._interface_names:
raise ValueError(
f'name: {name} already exists as an interface to the Block.')
self._inputs[name] = {'value': value, 'id': None}
self._interface_names.append(name)
else:
in_nos = len(self._inputs)
name = 'Input ' + str(in_nos + 1)
self._inputs[name] = {'value': value, 'id': None}
self._interface_names.append(name)
self._inputs[name] = {'value': value, 'id': None}
self._interface_names.append(name)

def add_output(self, name: str = None, value = None) -> None:
def add_output(self, name: str = None, value=None) -> None:
"""
A function defined to add an Output interface to the Block
Expand All @@ -66,40 +69,55 @@ def add_output(self, name: str = None, value = None) -> None:
value (any) : The default value for this output interface.
"""
if name:
if name in self._interface_names: raise ValueError(f'name: {name} already exists as an interface to the Block.')
if name in self._interface_names:
raise ValueError(
f'name: {name} already exists as an interface to the Block.')
self._outputs[name] = {'value': value, 'id': None}
self._interface_names.append(name)
self._interface_names.append(name)
else:
out_nos = len(self._outputs)
name = 'Output ' + str(out_nos + 1)
self._outputs[name] = {'value': value, 'id': None}
self._interface_names.append(name)

def get_interface(self, name: str):

if name in self._inputs:
return self._inputs[name]['value']
return self._inputs[name]['value']
elif name in self._outputs:
return self._outputs[name]['value']
return self._outputs[name]['value']
else:
raise ValueError(f'No interface with name: {name} found for Block')
return None
raise ValueError(f'No interface with name: {name} found for Block')

def set_interface(self, name: str, value) -> None:
if name in self._inputs:
self._inputs[name]['value'] = value
self._inputs[name]['value'] = value
elif name in self._outputs:
self._outputs[name]['value'] = value
else:
raise ValueError(f'No interface with name: {name} found for Block')
raise ValueError(f'No interface with name: {name} found for Block')

def _set_interface_id(self, name: str, id: str) -> None:
if name in self._inputs:
self._inputs[name]['id'] = id
elif name in self._outputs:
elif name in self._outputs:
self._outputs[name]['id'] = id
else:
raise ValueError(f'No interface with name: {name} found for Block')
raise ValueError(f'No interface with name: {name} found for Block')

def set_state(self, key: str, value: Any) -> None:
reserved_state_keys = ['info']
if key in reserved_state_keys:
raise ValueError(
f'Key: {key} used for setting state of block is reserved. Use another key.')
else:
self._state[key] = value

def get_state(self, key: str) -> Any:
if key in self._state:
return self._state[key]
else:
raise ValueError(f'Key: {key} does not exist in state.')

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

if name in self._options:
raise ValueError(f'Option with name: {name} aready exists in Block.')
raise ValueError(
f'Option with name: {name} aready exists in Block.')

_option = build_option(name, type, kwargs)

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

def set_option(self, name: str, **kwargs):
# Can only set the 'value' property for now.
def set_option(self, name: str, **kwargs):
# Can only set the 'value' property for now.
if name in self._options:
for arg, value in kwargs.items():
if arg in self._options[name]:
if arg in ['value']:
self._options[name][arg] = value
else:
raise ValueError(f'Cannot set or invalid property: {arg} for Block option.')
raise ValueError(
f'Cannot set or invalid property: {arg} for Block option.')
else:
raise ValueError(f'Property: {arg} is not a valid option property for {name}.')
else:
raise ValueError(
f'Property: {arg} is not a valid option property for {name}.')
else:
raise ValueError(f'Option name: {name} does not exist in Block.')

def get_option(self, name: str):
def get_option(self, name: str):
if name in self._options:
return self._options[name]['value']
else:
else:
raise ValueError(f'Option name: {name} does not exist in Block.')

def _export(self):
Expand Down
45 changes: 33 additions & 12 deletions barfi/compute_engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,10 @@ def _map_block_link(self):
['id']] = _interface[0]
_child_block._set_interface_id(
name=_interface[0], id=_interface[1]['id'])
_block_interfaces[_interface[0]] = _interface[1]
_block_interfaces[_interface[0]] = _interface[1]

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

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

def _execute_compute(self):
if bool(self._editor_state):

skip_node = set()
descendant_set = {}
for node in nx.topological_sort(self._graph):
self._active_blocks[node]['block']._on_compute()
for key, value in self._active_blocks[node]['block']._outputs.items():
if node not in skip_node:
try:
for find_to in self._map_link_interface_id_from_to[value['id']]:
find_to_block = self._map_interface_id_block_id[find_to]
self._active_blocks[find_to_block]['block'].set_interface(
name=self._map_interface_id_name[find_to], value=value['value'])
except:
pass
self._active_blocks[node]['block']._on_compute()
self._active_blocks[node]['block']._state['info'] = {
'status': 'Computed'}

for _, value in self._active_blocks[node]['block']._outputs.items():
try:
for find_to in self._map_link_interface_id_from_to[value['id']]:
find_to_block = self._map_interface_id_block_id[find_to]
self._active_blocks[find_to_block]['block'].set_interface(
name=self._map_interface_id_name[find_to], value=value['value'])
except:
pass

except Exception as e:
self._active_blocks[node]['block']._state['info'] = {
'status': 'Errored', 'exception': e.args}
node_desc = nx.descendants(self._graph, node)
descendant_set[self._active_blocks[node]
['name']] = node_desc
skip_node = skip_node.union(node_desc)

else:
for parent, child_set in descendant_set.items():
if node in child_set:
break
self._active_blocks[node]['block']._state['info'] = {
'status': 'Errored', 'message': 'Parent block errored', 'parent': parent}
17 changes: 17 additions & 0 deletions barfi/manage_schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,20 @@ def load_schema_name(schema_name: str) -> Dict:
else:
raise ValueError(
f'Schema :{schema_name}: not found in the saved schemas')


def delete_schema(schema_name: str):
try:
with open('schemas.barfi', 'rb') as handle_read:
schemas = pickle.load(handle_read)
except FileNotFoundError:
schemas = {}

if schema_name in schemas:
del schemas[schema_name]
else:
raise ValueError(
f'Schema :{schema_name}: not found in the saved schemas')

with open('schemas.barfi', 'wb') as handle_write:
pickle.dump(schemas, handle_write, protocol=pickle.HIGHEST_PROTOCOL)
2 changes: 1 addition & 1 deletion docs/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
author = 'Adithya Krishnan'

# The short X.Y version.
version = '0.6.0'
version = '0.7.0'
# The full version, including alpha/beta/rc tags.
release = 'alpha'

Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

setuptools.setup(
name="barfi",
version="0.6.1",
version="0.7.0",
author="Adithya Krishnan",
author_email="[email protected]",
description="Framework for a graphical programming environment.",
Expand Down
Empty file added tests/__init__.py
Empty file.
20 changes: 18 additions & 2 deletions tests/test_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,29 @@
import streamlit as st
from test_blocks import base_blocks, base_blocks_category



barfi_schema_name = st.selectbox(
'Select a saved schema to load:', barfi_schemas())

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

barfi_result = st_barfi(base_blocks=base_blocks_category, compute_engine=compute_engine,
load_schema=barfi_schema_name)
barfi_result = st_barfi(base_blocks=base_blocks_category, compute_engine=compute_engine, load_schema=barfi_schema_name)

if barfi_result:
st.write(barfi_result)
st.write(barfi_result['Feed-1']['block'].get_interface('Output 1'))
st.write(barfi_result['Feed-2']['block'].get_interface('Output 1'))
st.write(barfi_result['Splitter-1']['block'].get_interface('Input 1'))
st.write(barfi_result['Splitter-1']['block'].get_interface('Output 1'))
st.write(barfi_result['Splitter-1']['block'].get_interface('Output 2'))
st.write(barfi_result['Mixer-1']['block'].get_interface('Input 1'))
st.write(barfi_result['Mixer-1']['block'].get_interface('Input 2'))
st.write(barfi_result['Mixer-1']['block'].get_interface('Output 1'))
st.write(barfi_result['Result-1']['block'].get_interface('Input 1'))

st.write(barfi_result['Feed-1']['block'].get_state('info'))
st.write(barfi_result['Feed-2']['block'].get_state('info'))
st.write(barfi_result['Splitter-1']['block'].get_state('info'))
st.write(barfi_result['Mixer-1']['block'].get_state('info'))
st.write(barfi_result['Result-1']['block'].get_state('info'))
2 changes: 1 addition & 1 deletion tests/test_blocks.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ def splitter_func(self):
mixer.add_input()
mixer.add_input()
mixer.add_output()
def mixer_func(self):
def mixer_func(self):
in_1 = self.get_interface(name='Input 1')
in_2 = self.get_interface(name='Input 2')
value = (in_1 + in_2)
Expand Down

0 comments on commit 91efb8c

Please sign in to comment.