diff --git a/beat/backend/python/algorithm.py b/beat/backend/python/algorithm.py index 2cb4a95edc06315c0f318458d883a4ccdfa2be47..4d8c1679cf51cc6e20d8d21388b1ccb1107d9745 100644 --- a/beat/backend/python/algorithm.py +++ b/beat/backend/python/algorithm.py @@ -214,7 +214,10 @@ class Runner(object): # The method is optional if hasattr(self.obj, "prepare"): - if self.algorithm.type in [Algorithm.AUTONOMOUS, Algorithm.LOOP_USER]: + if self.algorithm.type in [ + Algorithm.AUTONOMOUS, + Algorithm.AUTONOMOUS_LOOP_USER, + ]: self.prepared = loader.run( self.obj, "prepare", self.exc, data_loaders.secondaries() ) @@ -264,35 +267,40 @@ class Runner(object): else: _check_argument(data_loaders, "data_loaders") - if self.algorithm.type == Algorithm.SEQUENTIAL: + if self.algorithm.is_sequential: _check_argument(inputs, "inputs") - return loader.run( - self.obj, "process", self.exc, inputs, data_loaders, outputs_to_use - ) + run_args = [ + self.obj, + "process", + self.exc, + inputs, + data_loaders, + outputs_to_use, + ] elif self.algorithm.is_autonomous: run_args = [self.obj, "process", self.exc, data_loaders, outputs_to_use] - has_loop_arg = utils.has_argument(self.obj.process, "loop_channel") - if loop_channel is not None: - if has_loop_arg: - run_args.append(loop_channel) - else: - raise exc( - "Algorithm '%s' is not a valid loop enabled algorithm" - % self.name - ) - elif has_loop_arg: + else: + raise exc("Unknown algorithm type: %s" % self.algorithm.type) + + has_loop_arg = utils.has_argument(self.obj.process, "loop_channel") + if loop_channel is not None: + if has_loop_arg: + run_args.append(loop_channel) + else: raise exc( - "Algorithm '%s' is a loop enabled algorithm but no loop_channel given" + "Algorithm '%s' is not a valid loop enabled algorithm" % self.name ) + elif has_loop_arg: + raise exc( + "Algorithm '%s' is a loop enabled algorithm but no loop_channel given" + % self.name + ) - return loader.run(*run_args) - - else: - raise exc("Unknown algorithm type: %s" % self.algorithm.type) + return loader.run(*run_args) def validate(self, result): """Validates the given results""" @@ -404,7 +412,8 @@ class Algorithm(object): SEQUENTIAL = "sequential" AUTONOMOUS = "autonomous" LOOP = "loop" - LOOP_USER = "loop_user" + SEQUENTIAL_LOOP_USER = "sequential_loop_user" + AUTONOMOUS_LOOP_USER = "autonomous_loop_user" def __init__(self, prefix, name, dataformat_cache=None, library_cache=None): @@ -672,7 +681,16 @@ class Algorithm(object): @property def is_autonomous(self): """ Returns whether the algorithm is in the autonomous category""" - return self.type in [Algorithm.AUTONOMOUS, Algorithm.LOOP_USER, Algorithm.LOOP] + return self.type in [ + Algorithm.AUTONOMOUS, + Algorithm.AUTONOMOUS_LOOP_USER, + Algorithm.LOOP, + ] + + @property + def is_sequential(self): + """ Returns whether the algorithm is in the sequential category""" + return self.type in [Algorithm.SEQUENTIAL, Algorithm.SEQUENTIAL_LOOP_USER] @language.setter def language(self, value): diff --git a/beat/backend/python/test/prefix/algorithms/autonomous/loop_user/1.json b/beat/backend/python/test/prefix/algorithms/autonomous/loop_user/1.json index ebd5c1252886fafa75b07570535a0d0980f52bf6..1cd6567396066fc77746db386afeb962abf3ba00 100644 --- a/beat/backend/python/test/prefix/algorithms/autonomous/loop_user/1.json +++ b/beat/backend/python/test/prefix/algorithms/autonomous/loop_user/1.json @@ -2,7 +2,7 @@ "schema_version": 3, "language": "python", "api_version": 2, - "type": "loop_user", + "type": "autonomous_loop_user", "splittable": false, "groups": [ { diff --git a/beat/backend/python/test/prefix/algorithms/sequential/loop_user/1.json b/beat/backend/python/test/prefix/algorithms/sequential/loop_user/1.json new file mode 100644 index 0000000000000000000000000000000000000000..4fc4a7846508e233687362c3d81428e8bfc6fca8 --- /dev/null +++ b/beat/backend/python/test/prefix/algorithms/sequential/loop_user/1.json @@ -0,0 +1,29 @@ +{ + "schema_version": 3, + "language": "python", + "api_version": 2, + "type": "sequential_loop_user", + "splittable": false, + "groups": [ + { + "inputs": { + "in": { + "type": "user/single_integer/1" + } + }, + "outputs": { + "out": { + "type": "user/single_integer/1" + } + }, + "loop": { + "request": { + "type": "user/single_integer/1" + }, + "answer": { + "type": "user/single_integer/1" + } + } + } + ] +} diff --git a/beat/backend/python/test/prefix/algorithms/sequential/loop_user/1.py b/beat/backend/python/test/prefix/algorithms/sequential/loop_user/1.py new file mode 100644 index 0000000000000000000000000000000000000000..d0ee50d0ce75ceff0aa4830635275da81d1bd171 --- /dev/null +++ b/beat/backend/python/test/prefix/algorithms/sequential/loop_user/1.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python +# vim: set fileencoding=utf-8 : + +################################################################################### +# # +# Copyright (c) 2019 Idiap Research Institute, http://www.idiap.ch/ # +# Contact: beat.support@idiap.ch # +# # +# Redistribution and use in source and binary forms, with or without # +# modification, are permitted provided that the following conditions are met: # +# # +# 1. Redistributions of source code must retain the above copyright notice, this # +# list of conditions and the following disclaimer. # +# # +# 2. Redistributions in binary form must reproduce the above copyright notice, # +# this list of conditions and the following disclaimer in the documentation # +# and/or other materials provided with the distribution. # +# # +# 3. Neither the name of the copyright holder nor the names of its contributors # +# may be used to endorse or promote products derived from this software without # +# specific prior written permission. # +# # +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND # +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE # +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE # +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR # +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER # +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, # +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # +# # +################################################################################### + + +import numpy as np + + +class Algorithm: + def process(self, inputs, data_loaders, outputs, loop_channel): + cnt = 10 + is_valid, _ = loop_channel.validate({"value": np.int32(cnt)}) + while not is_valid: + cnt = cnt - 1 + is_valid, _ = loop_channel.validate({"value": np.int32(cnt)}) + + value = inputs["in"].data.value + new_value = value + cnt + outputs["out"].write({"value": np.int32(new_value)}) + + return True diff --git a/beat/backend/python/test/prefix/algorithms/sequential/loop_user/1.rst b/beat/backend/python/test/prefix/algorithms/sequential/loop_user/1.rst new file mode 100644 index 0000000000000000000000000000000000000000..e62c1c42520de03eed49b89f3ed95fce122745de --- /dev/null +++ b/beat/backend/python/test/prefix/algorithms/sequential/loop_user/1.rst @@ -0,0 +1 @@ +Test documentation diff --git a/beat/backend/python/test/test_algorithm.py b/beat/backend/python/test/test_algorithm.py index 0e75b54f478e66c2af71d0e9466254f1898586bb..31c1197121ce457afe947af8317a77907c172673 100644 --- a/beat/backend/python/test/test_algorithm.py +++ b/beat/backend/python/test/test_algorithm.py @@ -234,6 +234,24 @@ class TestSequentialAPI_Loading(unittest.TestCase): self.assertFalse(runnable.ready) self.assertFalse(runnable.prepared) + def test_load_valid_loop_user(self): + algorithm = Algorithm(prefix, "sequential/loop_user/1") + self.assertEqual(algorithm.name, "sequential/loop_user/1") + self.assertTrue(algorithm.valid) + + self.assertFalse(algorithm.errors) + self.assertFalse(algorithm.results) # it is not an analyzer + self.assertFalse(algorithm.parameters) # does not contain parameters + self.assertTrue(algorithm.input_map) + self.assertTrue(algorithm.output_map) + self.assertEqual(algorithm.schema_version, 3) + self.assertEqual(algorithm.api_version, 2) + self.assertEqual(algorithm.type, Algorithm.SEQUENTIAL_LOOP_USER) + + runnable = algorithm.runner() + self.assertTrue(runnable.ready) + self.assertTrue(runnable.prepared) + # ---------------------------------------------------------- @@ -285,6 +303,24 @@ class TestAutonomousAPI_Loading(unittest.TestCase): self.assertFalse(runnable.ready) self.assertFalse(runnable.prepared) + def test_load_valid_loop_user(self): + algorithm = Algorithm(prefix, "autonomous/loop_user/1") + self.assertEqual(algorithm.name, "autonomous/loop_user/1") + self.assertTrue(algorithm.valid) + + self.assertFalse(algorithm.errors) + self.assertFalse(algorithm.results) # it is not an analyzer + self.assertFalse(algorithm.parameters) # does not contain parameters + self.assertTrue(algorithm.input_map) + self.assertTrue(algorithm.output_map) + self.assertEqual(algorithm.schema_version, 3) + self.assertEqual(algorithm.api_version, 2) + self.assertEqual(algorithm.type, Algorithm.AUTONOMOUS_LOOP_USER) + + runnable = algorithm.runner() + self.assertTrue(runnable.ready) + self.assertTrue(runnable.prepared) + # ----------------------------------------------------------