Skip to content

Commit

Permalink
Tests branch (#14)
Browse files Browse the repository at this point in the history
* fixed tests, also fixed some warnings by upgrading from deprecated pydantic methods

* moved pydantic models for code parser into request.py

* added simple tests to code runner

* adding test execute code file

---------

Co-authored-by: Red-Giuliano <[email protected]>
  • Loading branch information
Red-Giuliano and Red-Giuliano authored Sep 18, 2023
1 parent 7b96e56 commit 73819ac
Show file tree
Hide file tree
Showing 9 changed files with 147 additions and 62 deletions.
6 changes: 3 additions & 3 deletions zt_backend/models/components/slider.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from pydantic import Field, validator
from pydantic import Field, field_validator
from zt_backend.models.components.zt_component import ZTComponent
from zt_backend.models.validations import validate_color, validate_min_less_than_max
from zt_backend.models.state import component_values
Expand All @@ -18,11 +18,11 @@ class Slider(ZTComponent):
size: str = Field('large', description="Size of the slider.")
rounded: bool = Field(True, description="Determines if the slider has rounded edges.")

@validator('color', allow_reuse=True, pre=True)
@field_validator('color')
def validate_color(cls, color):
return validate_color(color)

@validator('value', always=True)
@field_validator('value')
def get_value_from_global_state(cls, value, values):
id = values.get('id') # Get the id if it exists in the field values
try:
Expand Down
4 changes: 2 additions & 2 deletions zt_backend/models/components/zt_component.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
from pydantic import BaseModel, Field, validator
from pydantic import BaseModel, Field, field_validator
from zt_backend.models.state import created_components, context_globals

class ZTComponent(BaseModel):
id: str = Field(description="Unique id for a component")
variable_name: str = Field('fake', description="Optional variable name associated with a component")

@validator('id', always=True)
@field_validator('id',mode='before')
def validate_unique_component_id(cls, id):
if context_globals['exec_mode'] and id in created_components:
raise Exception("Component ID is not unique")
Expand Down
4 changes: 2 additions & 2 deletions zt_backend/models/notebook.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from pydantic import BaseModel, Field, SerializeAsAny, root_validator
from pydantic import BaseModel, Field, SerializeAsAny, model_validator
from typing import OrderedDict, List, Dict, Any
from zt_backend.models.components.zt_component import ZTComponent
from zt_backend.models.components.slider import Slider
Expand All @@ -20,7 +20,7 @@ class CodeCell(BaseModel):
components: List[SerializeAsAny[ZTComponent]]
cellType: str = Field('code', enum=['code', 'markdown'])

@root_validator(pre=True)
@model_validator(mode='before')
def deserialize_components(cls, values):
components = values.get('components', [])
values['components'] = [deserialize_component(comp) for comp in components]
Expand Down
16 changes: 15 additions & 1 deletion zt_backend/models/request.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from pydantic import BaseModel, root_validator
from pydantic import BaseModel
from typing import List, Dict, Union
from zt_backend.models.components.slider import Slider
from zt_backend.models.components.zt_component import ZTComponent
Expand All @@ -17,10 +17,24 @@ class CodeRequest(BaseModel):
id: str
code: str


class Request(BaseModel):
cells: List[CodeRequest]
components: Dict[str, Union[str, bool, int]]


class Cell(BaseModel):
code: str
defined_names: List[str]
loaded_names: List[str]
child_cells: List[int] = []
parent_cells: List[int] = []
previous_child_cells: List[int] = []

class CodeDict(BaseModel):
cells: Dict[str, Cell]


# @root_validator(pre=True)
# def deserialize_components(cls, values):
# components = values.get('components', [])
Expand Down
3 changes: 2 additions & 1 deletion zt_backend/models/state.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
component_values = {}
created_components=[]
context_globals={"exec_mode": False}
context_globals={"exec_mode": False}
cell_outputs_dict = {}
16 changes: 3 additions & 13 deletions zt_backend/runner/code_cell_parser.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,9 @@
from typing import List, Dict, Union, Tuple, Any
from pydantic import BaseModel
import astroid
from zt_backend.models.request import Request

# Define Pydantic model for Cell
class Cell(BaseModel):
code: str
defined_names: List[str]
loaded_names: List[str]
child_cells: List[int] = []
parent_cells: List[int] = []
previous_child_cells: List[int] = []

class CodeDict(BaseModel):
cells: Dict[str, Cell]
from zt_backend.models.request import Request,Cell,CodeDict



def get_imports(module) -> List[str]:
import_froms = [node.names[0][1] or node.names[0][0] for node in module.nodes_of_class(astroid.ImportFrom)]
Expand Down
3 changes: 1 addition & 2 deletions zt_backend/runner/execute_code.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from contextlib import redirect_stdout
from zt_backend.models import request, response
from zt_backend.models.components.zt_component import ZTComponent
from zt_backend.models.state import component_values, created_components, context_globals
from zt_backend.models.state import component_values, created_components, context_globals,cell_outputs_dict
from zt_backend.runner.code_cell_parser import parse_cells,build_dependency_graph,find_downstream_cells, CodeDict,Cell


Expand Down Expand Up @@ -59,7 +59,6 @@ def execute_request(request: request.Request):
cell_outputs = []
component_values.update(request.components)
component_globals={'global_state': component_values}
cell_outputs_dict = {}
added_components = []
dependency_graph = build_dependency_graph(parse_cells(request))
downstream_cells = find_downstream_cells(dependency_graph,request.cells[0].id)
Expand Down
117 changes: 79 additions & 38 deletions zt_backend/tests/test_code_cell_parser.py
Original file line number Diff line number Diff line change
@@ -1,80 +1,121 @@
from zt_backend.runner.code_cell_parser import parse_cells, build_dependency_graph
from zt_backend.models.request import Request,CodeRequest,Cell,CodeDict
import pytest


# Test 1: No dependencies between cells
def test_no_dependencies():
cells = ["a = 1", "b = 2", "c = 3"]
cells = Request(cells=[CodeRequest(id="0",code="a = 1"),
CodeRequest(id="1",code="b = 2"),
CodeRequest(id="2",code="c = 3")],\
components={"":""})
cell_dict = build_dependency_graph(parse_cells(cells))
assert cell_dict[0]['defined_names'] == ['a'] and cell_dict[0]['loaded_names'] == [], "Test 1 Failed"
assert cell_dict[1]['defined_names'] == ['b'] and cell_dict[1]['loaded_names'] == [], "Test 1 Failed"
assert cell_dict[2]['defined_names'] == ['c'] and cell_dict[2]['loaded_names'] == [], "Test 1 Failed"
assert cell_dict.cells['0'].defined_names == ['a'] and cell_dict.cells['0'].loaded_names == [], "Test 1 Failed"
assert cell_dict.cells['1'].defined_names == ['b'] and cell_dict.cells['1'].loaded_names == [], "Test 1 Failed"
assert cell_dict.cells['2'].defined_names == ['c'] and cell_dict.cells['2'].loaded_names == [], "Test 1 Failed"

# Test 2: Simple dependencies
def test_simple_dependencies():
cells = ["a = 1", "b = a + 2", "c = b + 3"]
cells = Request(cells=[CodeRequest(id="0", code="a = 1"),
CodeRequest(id="1", code="b = a + 2"),
CodeRequest(id="2", code="c = b + 3")],
components={})
cell_dict = build_dependency_graph(parse_cells(cells))
assert cell_dict[0]['defined_names'] == ['a'] and cell_dict[0]['loaded_names'] == [], "Test 2 Failed"
assert cell_dict[1]['defined_names'] == ['b'] and cell_dict[1]['loaded_names'] == ['a'], "Test 2 Failed"
assert cell_dict[2]['defined_names'] == ['c'] and cell_dict[2]['loaded_names'] == ['b'], "Test 2 Failed"

assert cell_dict.cells['0'].defined_names == ['a'] and cell_dict.cells['0'].loaded_names == [], "Test 2 Failed"
assert cell_dict.cells['1'].defined_names == ['b'] and cell_dict.cells['1'].loaded_names == ['a'], "Test 2 Failed"
assert cell_dict.cells['2'].defined_names == ['c'] and cell_dict.cells['2'].loaded_names == ['b'], "Test 2 Failed"

# Test 3: Complex dependencies
def test_complex_dependencies():
cells = ["a = 1", "b = a + 2", "c = b + a"]
cells = Request(cells=[CodeRequest(id="0", code="a = 1"),
CodeRequest(id="1", code="b = a + 2"),
CodeRequest(id="2", code="c = b + a")],
components={})
cell_dict = build_dependency_graph(parse_cells(cells))
assert cell_dict[0]['defined_names'] == ['a'] and cell_dict[0]['loaded_names'] == [], "Test 3 Failed"
assert cell_dict[1]['defined_names'] == ['b'] and cell_dict[1]['loaded_names'] == ['a'], "Test 3 Failed"
assert cell_dict[2]['defined_names'] == ['c'] and set(cell_dict[2]['loaded_names']) == set(['b', 'a']), "Test 3 Failed"

assert cell_dict.cells['0'].defined_names == ['a'] and cell_dict.cells['0'].loaded_names == [], "Test 3 Failed"
assert cell_dict.cells['1'].defined_names == ['b'] and cell_dict.cells['1'].loaded_names == ['a'], "Test 3 Failed"
assert cell_dict.cells['2'].defined_names == ['c'] and set(cell_dict.cells['2'].loaded_names) == set(['b', 'a']), "Test 3 Failed"

# Test 4: Overriding dependencies
def test_overriding_dependencies():
cells = ["a = 1", "b = a + 2", "a = 3", "c = a + b"]
cells = Request(cells=[CodeRequest(id="0", code="a = 1"),
CodeRequest(id="1", code="b = a + 2"),
CodeRequest(id="2", code="a = 3"),
CodeRequest(id="3", code="c = a + b")],
components={})
cell_dict = build_dependency_graph(parse_cells(cells))
assert cell_dict[0]['defined_names'] == ['a'] and cell_dict[0]['loaded_names'] == [], "Test 4 Failed"
assert cell_dict[1]['defined_names'] == ['b'] and cell_dict[1]['loaded_names'] == ['a'], "Test 4 Failed"
assert cell_dict[2]['defined_names'] == ['a'] and cell_dict[2]['loaded_names'] == [], "Test 4 Failed"
assert cell_dict[3]['defined_names'] == ['c'] and set(cell_dict[3]['loaded_names']) == set(['a', 'b']), "Test 4 Failed"

assert cell_dict.cells['0'].defined_names == ['a'] and cell_dict.cells['0'].loaded_names == [], "Test 4 Failed"
assert cell_dict.cells['1'].defined_names == ['b'] and cell_dict.cells['1'].loaded_names == ['a'], "Test 4 Failed"
assert cell_dict.cells['2'].defined_names == ['a'] and cell_dict.cells['2'].loaded_names == [], "Test 4 Failed"
assert cell_dict.cells['3'].defined_names == ['c'] and set(cell_dict.cells['3'].loaded_names) == set(['a', 'b']), "Test 4 Failed"

# Test 5: Function definitions and calls
def test_function_definitions_and_calls():
cells = ["def f(x): return x + 2", "a = f(2)", "b = a + 3"]
cells = Request(cells=[CodeRequest(id="0", code="def f(x): return x + 2"),
CodeRequest(id="1", code="a = f(2)"),
CodeRequest(id="2", code="b = a + 3")],
components={})
cell_dict = build_dependency_graph(parse_cells(cells))
assert cell_dict[0]['defined_names'] == ['f'] and cell_dict[0]['loaded_names'] == [], "Test 5 Failed"
assert cell_dict[1]['defined_names'] == ['a'] and cell_dict[1]['loaded_names'] == ['f'], "Test 5 Failed"

assert cell_dict.cells['0'].defined_names == ['f'] and cell_dict.cells['0'].loaded_names == [], "Test 5 Failed"
assert cell_dict.cells['1'].defined_names == ['a'] and cell_dict.cells['1'].loaded_names == ['f'], "Test 5 Failed"
assert cell_dict.cells['2'].defined_names == ['b'] and cell_dict.cells['2'].loaded_names == ['a'], "Test 5 Failed"

# Test 6: Dependencies with function calls and redefinitions
def test_dependencies_with_function_calls_and_redefinitions():
cells = ["def f(x): return x + 2", "a = f(2)", "b = a + 3", "def g(x): return x * 2", "c = g(b)"]
cells = Request(cells=[CodeRequest(id="0", code="def f(x): return x + 2"),
CodeRequest(id="1", code="a = f(2)"),
CodeRequest(id="2", code="b = a + 3"),
CodeRequest(id="3", code="def g(x): return x * 2"),
CodeRequest(id="4", code="c = g(b)")],
components={})
cell_dict = build_dependency_graph(parse_cells(cells))
assert cell_dict[0]['defined_names'] == ['f'] and cell_dict[0]['loaded_names'] == [], "Test 6 Failed"
assert cell_dict[1]['defined_names'] == ['a'] and cell_dict[1]['loaded_names'] == ['f'], "Test 6 Failed"
assert cell_dict[2]['defined_names'] == ['b'] and cell_dict[2]['loaded_names'] == ['a'], "Test 6 Failed"
assert cell_dict[3]['defined_names'] == ['g'] and cell_dict[3]['loaded_names'] == [], "Test 6 Failed"
assert cell_dict[4]['defined_names'] == ['c'] and set(cell_dict[4]['loaded_names']) == set(['g', 'b']), "Test 6 Failed"

assert cell_dict.cells['0'].defined_names == ['f'] and cell_dict.cells['0'].loaded_names == [], "Test 6 Failed"
assert cell_dict.cells['1'].defined_names == ['a'] and cell_dict.cells['1'].loaded_names == ['f'], "Test 6 Failed"
assert cell_dict.cells['2'].defined_names == ['b'] and cell_dict.cells['2'].loaded_names == ['a'], "Test 6 Failed"
assert cell_dict.cells['3'].defined_names == ['g'] and cell_dict.cells['3'].loaded_names == [], "Test 6 Failed"
assert cell_dict.cells['4'].defined_names == ['c'] and set(cell_dict.cells['4'].loaded_names) == set(['g', 'b']), "Test 6 Failed"

# Test 7: Importing a module in one cell and using it in another
def test_importing_module():
cells = ["import math", "a = math.sqrt(4)"]
cells = Request(cells=[CodeRequest(id="0", code="import math"),
CodeRequest(id="1", code="a = math.sqrt(4)")],
components={})
cell_dict = build_dependency_graph(parse_cells(cells))
assert cell_dict[0]['defined_names'] == ['math'] and cell_dict[0]['loaded_names'] == [], "Test 7 Failed"
assert cell_dict[1]['defined_names'] == ['a'] and cell_dict[1]['loaded_names'] == ['math'], "Test 7 Failed"

assert cell_dict.cells['0'].defined_names == ['math'] and cell_dict.cells['0'].loaded_names == [], "Test 7 Failed"
assert cell_dict.cells['1'].defined_names == ['a'] and cell_dict.cells['1'].loaded_names == ['math'], "Test 7 Failed"

# Test 8: Defining multiple variables in one cell
def test_multiple_variables_in_one_cell():
cells = ["a = 1; b = 2; c = 3", "d = a + b + c"]
cells = Request(cells=[CodeRequest(id="0", code="a = 1; b = 2; c = 3"),
CodeRequest(id="1", code="d = a + b + c")],
components={})
cell_dict = build_dependency_graph(parse_cells(cells))
assert set(cell_dict[0]['defined_names']) == set(['a', 'b', 'c']) and cell_dict[0]['loaded_names'] == [], "Test 8 Failed"
assert cell_dict[1]['defined_names'] == ['d'] and set(cell_dict[1]['loaded_names']) == set(['a', 'b', 'c']), "Test 8 Failed"

assert set(cell_dict.cells['0'].defined_names) == set(['a', 'b', 'c']) and cell_dict.cells['0'].loaded_names == [], "Test 8 Failed"
assert cell_dict.cells['1'].defined_names == ['d'] and set(cell_dict.cells['1'].loaded_names) == set(['a', 'b', 'c']), "Test 8 Failed"

# Test 9: Multiline cells
def test_multiline_cells():
cells = ["a = 1\nb = 2\nc = 3", "d = a + b\ne = c * d"]
cells = Request(cells=[CodeRequest(id="0", code="a = 1\nb = 2\nc = 3"),
CodeRequest(id="1", code="d = a + b\ne = c * d")],
components={})
cell_dict = build_dependency_graph(parse_cells(cells))
assert set(cell_dict[0]['defined_names']) == set(['a', 'b', 'c']) and cell_dict[0]['loaded_names'] == [], "Test 9 Failed"
assert set(cell_dict[1]['defined_names']) == set(['d', 'e']) and set(cell_dict[1]['loaded_names']) == set(['a', 'b', 'c']), "Test 9 Failed"

assert set(cell_dict.cells['0'].defined_names) == set(['a', 'b', 'c']) and cell_dict.cells['0'].loaded_names == [], "Test 9 Failed"
assert set(cell_dict.cells['1'].defined_names) == set(['d', 'e']) and set(cell_dict.cells['1'].loaded_names) == set(['a', 'b', 'c']), "Test 9 Failed"

# Test 10: Importing a module with an alias in one cell and using it in another
def test_importing_module_with_alias():
cells = ["import math as m", "a = m.sqrt(4)"]
cells = Request(cells=[CodeRequest(id="0", code="import math as m"),
CodeRequest(id="1", code="a = m.sqrt(4)")],
components={})
cell_dict = build_dependency_graph(parse_cells(cells))
assert cell_dict[0]['defined_names'] == ['m'] and cell_dict[0]['loaded_names'] == [], "Test 10 Failed"
assert cell_dict[1]['defined_names'] == ['a'] and cell_dict[1]['loaded_names'] == ['m'], "Test 10 Failed"

assert cell_dict.cells['0'].defined_names == ['m'] and cell_dict.cells['0'].loaded_names == [], "Test 10 Failed"
assert cell_dict.cells['1'].defined_names == ['a'] and cell_dict.cells['1'].loaded_names == ['m'], "Test 10 Failed"
40 changes: 40 additions & 0 deletions zt_backend/tests/test_execute_code.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
from zt_backend.runner.execute_code import execute_request, cell_outputs_dict
from zt_backend.models.request import Request, CodeRequest
import pytest

# Initialize or clear cell_outputs_dict
def setup_function():
cell_outputs_dict.clear()

# Test for simple variable assignment
def test_execute_request_simple_variable():
setup_function()
req = Request(cells=[CodeRequest(id="0", code="a = 1")], components={})
execute_request(req)
assert 'a' in cell_outputs_dict['0']
assert cell_outputs_dict['0']['a'] == 1

# Test for function definition
def test_execute_request_function_definition():
setup_function()
req = Request(cells=[CodeRequest(id="1", code="def add(x, y): return x + y")], components={})
execute_request(req)
assert 'add' in cell_outputs_dict['1']

# Test for importing modules
def test_execute_request_import_module():
setup_function()
req = Request(cells=[CodeRequest(id="2", code="import math")], components={})
execute_request(req)
assert 'math' in cell_outputs_dict['2']

# Test for multiple cells with dependencies
def test_execute_request_multiple_cells_with_dependencies():
setup_function()
req = Request(cells=[CodeRequest(id="3", code="a = 1"),
CodeRequest(id="4", code="b = a + 1")], components={})
execute_request(req)
assert 'a' in cell_outputs_dict['3']
assert 'b' in cell_outputs_dict['4']
assert cell_outputs_dict['3']['a'] == 1
assert cell_outputs_dict['4']['b'] == 2

0 comments on commit 73819ac

Please sign in to comment.