Commit 12b7a9cc authored by Wout De Nolf's avatar Wout De Nolf
Browse files

add .ows format to load_graph, save_graph and convert_graph

parent 2041220a
Pipeline #71754 passed with stages
in 3 minutes and 9 seconds
from .bindings import load_graph # noqa: F401
from .bindings import save_graph # noqa: F401
from .bindings import convert_graph # noqa: F401
from .bindings import execute_graph # noqa: F401
from .bindings import graph_is_supported # noqa: F401
from .bindings.owsignal_manager import patch_signal_manager
patch_signal_manager()
from .bindings import execute_graph # noqa F401
__version__ = "0.1.0-rc"
import os
import sys
import tempfile
from typing import Optional, List
from typing import Any, Optional, List
from ewokscore.events import job_decorator as execute_graph_decorator
from .owsconvert import ewoks_to_ows
import ewokscore
from ewokscore.graph import TaskGraph
from . import owsconvert
from ..canvas.main import main as launchcanvas
__all__ = ["execute_graph"]
__all__ = [
"execute_graph",
"load_graph",
"save_graph",
"convert_graph",
]
@execute_graph_decorator(binding="orange")
@ewokscore.execute_graph_decorator(binding="orange")
def execute_graph(
graph,
inputs: Optional[List[dict]] = None,
load_options: Optional[dict] = None,
**execute_options
**execute_options,
):
if isinstance(graph, str) and graph.lower().endswith(".ows"):
argv = [sys.argv[0], graph]
launchcanvas(argv=argv)
if load_options is None:
load_options = dict()
representation = _get_representation(graph, options=load_options)
if representation == "ows":
ows_filename = graph
if inputs or load_options or execute_options:
# Already an .ows file but modify it before launching the GUI
with tempfile.TemporaryDirectory(prefix="ewoksorange_") as tmpdirname:
basename = os.path.splitext(os.path.basename(ows_filename))[0]
ows_filename2 = os.path.join(tmpdirname, f"{basename}.ows")
graph = owsconvert.ows_to_ewoks(ows_filename)
owsconvert.ewoks_to_ows(
graph,
ows_filename2,
inputs=inputs,
**load_options,
**execute_options,
)
argv = [sys.argv[0], ows_filename2]
launchcanvas(argv=argv)
else:
# Already an .ows file
argv = [sys.argv[0], ows_filename]
launchcanvas(argv=argv)
else:
# We do not have a mapping between OWS and the runtime representation.
# So map to a (temporary) persistent representation first.
with tempfile.TemporaryDirectory(prefix="ewoksorange") as tmpdirname:
filename = os.path.join(tmpdirname, "ewokstaskgraph.ows")
if load_options is None:
load_options = dict()
# Note: execute options are saved in the temporary file
ewoks_to_ows(
graph, filename, inputs=inputs, **load_options, **execute_options
# Convert to an .ows file before launching the GUI
with tempfile.TemporaryDirectory(prefix="ewoksorange_") as tmpdirname:
ows_filename = os.path.join(tmpdirname, "graph.ows")
owsconvert.ewoks_to_ows(
graph, ows_filename, inputs=inputs, **load_options, **execute_options
)
argv = [sys.argv[0], filename]
argv = [sys.argv[0], ows_filename]
launchcanvas(argv=argv)
def load_graph(
graph: Any, inputs: Optional[List[dict]] = None, **load_options
) -> TaskGraph:
representation = _get_representation(graph, options=load_options)
if representation == "ows":
load_options.pop("representation", None)
return owsconvert.ows_to_ewoks(graph, inputs=inputs, **load_options)
else:
return ewokscore.load_graph(graph, inputs=inputs, **load_options)
def save_graph(graph: TaskGraph, destination, **save_options) -> Optional[str]:
representation = _get_representation(destination, options=save_options)
if representation == "ows":
return owsconvert.ewoks_to_ows(graph, destination, **save_options)
else:
return graph.dump(destination, **save_options)
def convert_graph(
source,
destination,
inputs: Optional[List[dict]] = None,
load_options: Optional[dict] = None,
save_options: Optional[dict] = None,
):
if load_options is None:
load_options = dict()
if save_options is None:
save_options = dict()
graph = load_graph(source, inputs=inputs, **load_options)
return save_graph(graph, destination, **save_options)
def _get_representation(graph: Any, options: Optional[dict] = None):
representation = None
if options:
representation = options.get("representation")
if (
representation is None
and isinstance(graph, str)
and graph.lower().endswith(".ows")
):
representation = "ows"
return representation
......@@ -27,7 +27,7 @@ from .owsignals import signal_orange_to_ewoks_name
from .owwidgets import is_ewoks_widget_class
from ..ewoks_addon.orangecontrib.ewoks_defaults import default_owwidget_class
__all__ = ["ows_to_ewoks", "ewoks_to_ows"]
__all__ = ["ows_to_ewoks", "ewoks_to_ows", "graph_is_supported"]
ReadSchemeType = readwrite._scheme
......@@ -190,6 +190,17 @@ def ows_to_ewoks(
return load_graph(graph, **load_graph_options)
def graph_is_supported(graph: TaskGraph) -> bool:
all_explicit_datamapping = all(
link_attrs.get("data_mapping") for link_attrs in graph.graph.edges.values()
)
return (
not graph.is_cyclic
and not graph.has_conditional_links
and all_explicit_datamapping
)
def ewoks_to_ows(
graph,
destination: Union[str, IO],
......@@ -204,9 +215,13 @@ def ewoks_to_ows(
"""
ewoksgraph = load_graph(graph, **load_graph_options)
if ewoksgraph.is_cyclic:
raise RuntimeError("Orange can only execute DAGs")
raise RuntimeError("Orange can only handle DAGs")
if ewoksgraph.has_conditional_links:
raise RuntimeError("Orange cannot handle conditional links")
if not all(
link_attrs.get("data_mapping") for link_attrs in ewoksgraph.graph.edges.values()
):
raise RuntimeError("Orange cannot handle links without explicit data mapping")
owsgraph = OwsSchemeWrapper(
ewoksgraph,
varinfo=varinfo,
......
from ewokscore.tests.examples.tasks.sumtask import SumTask
class SumTaskEvaluate1(SumTask):
pass
class SumTaskEvaluate2(SumTask):
pass
class SumTaskCategory1(SumTask):
pass
class SumTaskCategory2(SumTask):
pass
class SumTaskSubCategory1(SumTask):
pass
class SumTaskSubCategory2(SumTask):
pass
......@@ -11,13 +11,13 @@ else:
from ewoksorange.bindings import OWEwoksWidgetNoThread
from ewoksorange.gui.parameterform import ParameterForm
from ewokscore.tests.examples.tasks.sumtask import SumTask
from ewoks_example_addon import SumTaskEvaluate1
__all__ = ["Adder1"]
class Adder1(OWEwoksWidgetNoThread, ewokstaskclass=SumTask):
class Adder1(OWEwoksWidgetNoThread, ewokstaskclass=SumTaskEvaluate1):
name = "Adder1"
description = "Adds two numbers"
icon = "icons/mywidget.svg"
......
......@@ -12,13 +12,13 @@ else:
from ewoksorange.bindings import OWEwoksWidgetNoThread
from ewoksorange.gui.parameterform import ParameterForm
from ewokscore.tests.examples.tasks.sumtask import SumTask
from ewoks_example_addon import SumTaskEvaluate2
__all__ = ["Adder2"]
class Adder2(OWEwoksWidgetNoThread, ewokstaskclass=SumTask):
class Adder2(OWEwoksWidgetNoThread, ewokstaskclass=SumTaskEvaluate2):
name = "Adder2"
description = "Adds two numbers"
icon = "icons/mywidget.svg"
......
......@@ -11,13 +11,13 @@ else:
from ewoksorange.bindings import OWEwoksWidgetNoThread
from ewoksorange.gui.parameterform import ParameterForm
from ewokscore.tests.examples.tasks.sumtask import SumTask
from ewoks_example_addon import SumTaskCategory1
__all__ = ["Adder1"]
class Adder1(OWEwoksWidgetNoThread, ewokstaskclass=SumTask):
class Adder1(OWEwoksWidgetNoThread, ewokstaskclass=SumTaskCategory1):
name = "Adder1"
description = "Adds two numbers"
icon = "icons/mywidget.svg"
......
......@@ -11,13 +11,13 @@ else:
from ewoksorange.bindings import OWEwoksWidgetNoThread
from ewoksorange.gui.parameterform import ParameterForm
from ewokscore.tests.examples.tasks.sumtask import SumTask
from ewoks_example_addon import SumTaskCategory2
__all__ = ["Adder2"]
class Adder2(OWEwoksWidgetNoThread, ewokstaskclass=SumTask):
class Adder2(OWEwoksWidgetNoThread, ewokstaskclass=SumTaskCategory2):
name = "Adder2"
description = "Adds two numbers"
icon = "icons/mywidget.svg"
......
......@@ -11,13 +11,13 @@ else:
from ewoksorange.bindings import OWEwoksWidgetNoThread
from ewoksorange.gui.parameterform import ParameterForm
from ewokscore.tests.examples.tasks.sumtask import SumTask
from ewoks_example_addon import SumTaskSubCategory1
__all__ = ["Adder1"]
class Adder1(OWEwoksWidgetNoThread, ewokstaskclass=SumTask):
class Adder1(OWEwoksWidgetNoThread, ewokstaskclass=SumTaskSubCategory1):
name = "Adder1"
description = "Adds two numbers"
icon = "icons/mywidget.svg"
......
......@@ -11,13 +11,13 @@ else:
from ewoksorange.bindings import OWEwoksWidgetNoThread
from ewoksorange.gui.parameterform import ParameterForm
from ewokscore.tests.examples.tasks.sumtask import SumTask
from ewoks_example_addon import SumTaskSubCategory2
__all__ = ["Adder2"]
class Adder2(OWEwoksWidgetNoThread, ewokstaskclass=SumTask):
class Adder2(OWEwoksWidgetNoThread, ewokstaskclass=SumTaskSubCategory2):
name = "Adder2"
description = "Adds two numbers"
icon = "icons/mywidget.svg"
......
......@@ -4,9 +4,12 @@ import pytest
from ewokscore.tests.examples.graphs import graph_names
from ewokscore.tests.examples.graphs import get_graph
from ewokscore.tests.test_examples import assert_convert_graph
from ewokscore.tests.utils.results import assert_execute_graph_all_tasks
from ewokscore import load_graph
from ewoksorange.bindings import ewoks_to_ows
from ewoksorange import convert_graph
from ewoksorange import graph_is_supported
@pytest.mark.parametrize("graph_name", graph_names())
......@@ -15,15 +18,7 @@ def test_execute_graph(graph_name, tmpdir, ewoks_orange_canvas):
graph, expected = get_graph(graph_name)
ewoksgraph = load_graph(graph)
varinfo = {"root_uri": str(tmpdir)}
no_explicit_datamapping = any(
not link_attrs.get("data_mapping")
for link_attrs in ewoksgraph.graph.edges.values()
)
if (
ewoksgraph.is_cyclic
or ewoksgraph.has_conditional_links
or no_explicit_datamapping
):
if not graph_is_supported(ewoksgraph):
pytest.skip("graph not supported by orange")
with tempfile.TemporaryDirectory() as tmpdirname:
......@@ -33,3 +28,28 @@ def test_execute_graph(graph_name, tmpdir, ewoks_orange_canvas):
ewoks_orange_canvas.wait_widgets(timeout=10)
assert_execute_graph_all_tasks(ewoksgraph, expected, varinfo=varinfo)
@pytest.mark.parametrize("graph_name", graph_names())
def test_convert_graph(graph_name, tmpdir):
graph, _ = get_graph(graph_name)
ewoksgraph = load_graph(graph)
for node_id, node_attrs in ewoksgraph.graph.nodes.items():
node_attrs["label"] = node_id
representations = [
(
{"representation": "ows", "title_as_node_id": True},
{"representation": "ows"},
"ows",
)
]
if graph_is_supported(ewoksgraph):
assert_convert_graph(
convert_graph, ewoksgraph, tmpdir, representations=representations
)
else:
with pytest.raises(RuntimeError):
assert_convert_graph(
convert_graph, ewoksgraph, tmpdir, representations=representations
)
......@@ -5,6 +5,7 @@ except ImportError:
import pytest
from ewoksorange.bindings import ows_to_ewoks
from ewoksorange.bindings import ewoks_to_ows
from ewoksorange.bindings import graph_is_supported
from ewokscore import load_graph
from ewokscore.tests.examples.graphs import graph_names
from ewokscore.tests.examples.graphs import get_graph
......@@ -45,7 +46,7 @@ def test_ewoks_to_ows(graph_name, tmpdir):
node_attrs["label"] = node_id
destination = str(tmpdir / "ewoksgraph2.ows")
if ewoksgraph.is_cyclic or ewoksgraph.has_conditional_links:
if not graph_is_supported(ewoksgraph):
with pytest.raises(RuntimeError):
ewoks_to_ows(ewoksgraph, destination)
return
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment