Skip to content

Commit

Permalink
Fix for DOT graphs
Browse files Browse the repository at this point in the history
  • Loading branch information
maddenp committed Feb 27, 2024
1 parent 8172996 commit c78ea7e
Show file tree
Hide file tree
Showing 6 changed files with 76 additions and 52 deletions.
2 changes: 1 addition & 1 deletion recipe/meta.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,5 @@
],
"run": []
},
"version": "0.7.2"
"version": "0.7.3"
}
2 changes: 1 addition & 1 deletion recipe/meta.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package:
name: iotaa
version: 0.7.2
version: 0.7.3
source:
path: ../src
build:
Expand Down
23 changes: 9 additions & 14 deletions src/iotaa/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,6 @@ class _State:
def __init__(self) -> None:
self.dry_run = False
self.initialized = False
self.reset()

def initialize(self) -> None:
"""
Expand Down Expand Up @@ -402,7 +401,7 @@ def __iotaa_external__(*args, **kwargs) -> _AssetT:
if not ready or top:
_graph.update_from_task(taskname, assets)
_report_readiness(ready=ready, taskname=taskname, is_external=True)
return _task_final(taskname, assets)
return _task_final(top, taskname, assets)

return _set_metadata(f, __iotaa_external__)

Expand Down Expand Up @@ -433,7 +432,7 @@ def __iotaa_task__(*args, **kwargs) -> _AssetT:
ready_final = _ready(assets)
if ready_final != ready_initial:
_report_readiness(ready=ready_final, taskname=taskname)
return _task_final(taskname, assets)
return _task_final(top, taskname, assets)

return _set_metadata(f, __iotaa_task__)

Expand All @@ -455,7 +454,7 @@ def __iotaa_tasks__(*args, **kwargs) -> _AssetT:
ready = _ready(assets)
if not ready or top:
_report_readiness(ready=ready, taskname=taskname)
return _task_final(taskname, assets)
return _task_final(top, taskname, assets)

return _set_metadata(f, __iotaa_tasks__)

Expand Down Expand Up @@ -518,7 +517,8 @@ def _i_am_top_task() -> bool:
"""
if _state.initialized:
return False
_reset()
_state.initialize()
_graph.reset()
return True


Expand Down Expand Up @@ -603,14 +603,6 @@ def _report_readiness(
)


def _reset() -> None:
"""
Reset state.
"""
_graph.reset()
_state.reset()


def _set_metadata(f_in: Callable, f_out: Callable) -> Callable:
"""
Set metadata on a decorated function.
Expand All @@ -624,14 +616,17 @@ def _set_metadata(f_in: Callable, f_out: Callable) -> Callable:
return f_out


def _task_final(taskname: str, assets: _AssetT) -> _AssetT:
def _task_final(top: bool, taskname: str, assets: _AssetT) -> _AssetT:
"""
Final steps common to all task types.
:param top: Is this the top task?
:param taskname: The current task's name.
:param assets: A collection of assets, one asset, or None.
:return: The same assets that were provided as input.
"""
if top:
_state.reset()
for a in _listify(assets):
setattr(a, "taskname", taskname)
return assets
Expand Down
9 changes: 3 additions & 6 deletions src/iotaa/demo.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,7 @@ def cup(basedir):
@task
def steeped_tea_with_sugar(basedir):
# Add sugar to the steeped tea. Requires tea to have steeped.
for x in ingredient(basedir, "sugar", "Sugar", steeped_tea):
yield x
yield from ingredient(basedir, "sugar", "Sugar", steeped_tea)


@task
Expand Down Expand Up @@ -72,15 +71,13 @@ def steeped_tea(basedir):
@task
def steeping_tea(basedir):
# Pour boiling water over the tea. Requires teabag in cup.
for x in ingredient(basedir, "water", "Boiling water", teabag):
yield x
yield from ingredient(basedir, "water", "Boiling water", teabag)


@task
def teabag(basedir):
# Place tea bag in the cup. Requires box of teabags.
for x in ingredient(basedir, "teabag", "Teabag", box_of_teabags):
yield x
yield from ingredient(basedir, "teabag", "Teabag", box_of_teabags)


@external
Expand Down
89 changes: 60 additions & 29 deletions src/iotaa/tests/test_iotaa.py
Original file line number Diff line number Diff line change
Expand Up @@ -610,42 +610,27 @@ def test__report_readiness(caplog, vals):
assert logged(f"task: {msg}", caplog)


def test__reset():
with patch.object(iotaa, "_graph", new=iotaa._Graph()):
with patch.object(iotaa, "_state", new=iotaa._State()):
iotaa._graph.assets["foo"] = "bar"
iotaa._graph.edges.add("baz")
iotaa._graph.tasks.add("qux")
iotaa._state.initialize()
assert iotaa._graph.assets
assert iotaa._graph.edges
assert iotaa._graph.tasks
assert iotaa._state.initialized
iotaa._reset()
assert not iotaa._graph.assets
assert not iotaa._graph.edges
assert not iotaa._graph.tasks
assert not iotaa._state.initialized


def test__reset_via_task():
with patch.object(iotaa, "_reset") as _reset:
def test_state_reset_via_task():

@iotaa.external
def noop():
yield "noop"
yield iotaa.asset("noop", lambda: True)
@iotaa.external
def noop():
yield "noop"
yield iotaa.asset("noop", lambda: True)

_reset.assert_not_called()
noop()
_reset.assert_called_once_with()
with patch.object(iotaa._graph, "reset") as reset_graph:
with patch.object(iotaa._state, "reset") as reset_state:
reset_graph.assert_not_called()
reset_state.assert_not_called()
noop()
reset_graph.assert_called_once_with()
reset_state.assert_called_once_with()


@pytest.mark.parametrize("assets", simple_assets())
def test__task_final(assets):
for a in iotaa._listify(assets):
assert getattr(a, "taskname", None) is None
assets = iotaa._task_final("task", assets)
assets = iotaa._task_final(False, "task", assets)
for a in iotaa._listify(assets):
assert getattr(a, "taskname") == "task"

Expand All @@ -663,7 +648,7 @@ def f(taskname, n):
assert next(g) == 88


# Graph tests
# _Graph tests


def test__Graph___repr__(capsys):
Expand All @@ -688,11 +673,34 @@ def test__Graph___repr__(capsys):
assert 1 == len([x for x in out if "fillcolor=%s," % iotaa._graph.color[False] in x])


def test__Graph_color():
assert isinstance(iotaa._graph.color, dict)


def test__Graph_name():
name = "foo"
assert iotaa._graph.name(name) == "_%s" % md5(name.encode("utf-8")).hexdigest()


def test__Graph_shape():
assert iotaa._graph.shape.asset == "box"
assert iotaa._graph.shape.task == "ellipse"


def test__Graph_reset():
with patch.object(iotaa, "_graph", iotaa._Graph()) as _graph:
_graph.assets["some"] = "asset"
_graph.edges.add("some-edge")
_graph.tasks.add("some-task")
assert _graph.assets
assert _graph.edges
assert _graph.tasks
_graph.reset()
assert not _graph.assets
assert not _graph.edges
assert not _graph.tasks


@pytest.mark.parametrize("assets", simple_assets())
def test__Graph_update_from_requirements(assets, empty_graph):
taskname_req = "req"
Expand Down Expand Up @@ -720,3 +728,26 @@ def test__Graph_update_from_task(assets, empty_graph):
assert all(a() for a in iotaa._graph.assets.values())
assert iotaa._graph.tasks == {taskname}
assert iotaa._graph.edges == {(taskname, x.ref) for x in iotaa._listify(assets)}


# _State tests


def test__State():
with patch.object(iotaa, "_state", iotaa._State()) as _state:
assert not _state.dry_run
assert not _state.initialized


def test__State_initialize():
with patch.object(iotaa, "_state", iotaa._State()) as _state:
_state.initialize()
assert _state.initialized


def test__State_reset():
with patch.object(iotaa, "_state", iotaa._State()) as _state:
_state.initialize()
assert _state.initialized
_state.reset()
assert not _state.initialized
3 changes: 2 additions & 1 deletion src/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@
data_files=[(".", ["meta.json"])],
description="A simple workflow engine",
entry_points={"console_scripts": ["iotaa = %s:main" % name_py]},
long_description="A simple workflow engine with semantics inspired by Luigi and tasks expressed as decorated Python functions",
long_description=""" A simple workflow engine with semantics inspired by Luigi and tasks
expressed as decorated Python functions """,
name=name_conda,
packages=find_packages(include=[name_py, "%s.*" % name_py]),
url="https://github.com/maddenp/iotaa",
Expand Down

0 comments on commit c78ea7e

Please sign in to comment.