Skip to content

Commit 59b4bea

Browse files
committed
api: Add consume_first for convenience
A lot of actors are expecting only one type and are using next to retrieve the value. This patch introduces a convenience function that is simplifying the usage for actors that have a need for this. Additionally it covers some gotchas that are mostly not handled, mainly if there is no message available then an empty tuple is returned that is not iterable by next. Usage example: -------------- ``` from leapp.libraries.stdlib import api from leapp.models import SomeModel def some_function_previously(): value = next(api.consume(SomeModel), None) value_other = next(api.consume(SomeModel), SomeModel()) value_different = next(api.consume(SomeModel), 'yadda') def some_function_now(): value = api.consume_first(SomeModel) value_other api.consume_first_default(SomeModel) value_different = api.consume_first(SomeModel, 'yadda') ``` Signed-off-by: Vinzenz Feenstra <[email protected]>
1 parent f766a84 commit 59b4bea

File tree

4 files changed

+70
-1
lines changed

4 files changed

+70
-1
lines changed

leapp/actors/__init__.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,35 @@ def produce(self, *models):
324324
'explicitely in the actor\'s "produces" tuple. The message will be ignored'.format(
325325
type(model)))
326326

327+
def consume_first_default(self, model):
328+
"""
329+
Retrieves the first message found as specified in the actors :py:attr:`consumes` attribute, and filter message
330+
types by model. This additionally returns as fallback value an instance of model with the default
331+
initialization
332+
333+
:param model: Model to use as a filter for the messages to return
334+
:type model: A derived class from :py:class:`leapp.models.Model`
335+
:return: The first message of the specified model produced by other a fallback instance of model
336+
:rtype: The message or a default initialized instance of the model type.
337+
"""
338+
return self.consume_first(model, default=model())
339+
340+
def consume_first(self, model, default=None):
341+
"""
342+
Retrieves the first message found as specified in the actors :py:attr:`consumes` attribute, and filter message
343+
types by model.
344+
345+
:param model: Model to use as a filter for the messages to return
346+
:type model: A derived class from :py:class:`leapp.models.Model`
347+
:param default: Fallback value in case there are no messages
348+
:type default: Any
349+
:return: The first message of the specified model produced by other actors or the value passed as `default`
350+
:rtype: The message or the value passed as ``default``
351+
"""
352+
# One cannot iterate over tuples, so we make a generator that iterates over the value returned by self.consume.
353+
# This way we can workaround both cases.
354+
return next((message for message in self.consume(model)), default)
355+
327356
def consume(self, *models):
328357
"""
329358
Retrieve messages specified in the actors :py:attr:`consumes` attribute, and filter message types by

leapp/libraries/stdlib/api.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,35 @@ def produce(*model_instances):
6565
return current_actor().produce(*model_instances)
6666

6767

68+
def consume_first_default(model):
69+
"""
70+
Retrieves the first message found as specified in the actors :py:attr:`consumes` attribute, and filter message
71+
types by model. This additionally returns as fallback value an instance of model with the default
72+
initialization
73+
74+
:param model: Model to use as a filter for the messages to return
75+
:type model: A derived class from :py:class:`leapp.models.Model`
76+
:return: The first message of the specified model produced by other a fallback instance of model
77+
:rtype: The message or a default initialized instance of the model type.
78+
"""
79+
return current_actor().consume_first_default(model)
80+
81+
82+
def consume_first(model, default=None):
83+
"""
84+
Retrieves the first message found as specified in the actors :py:attr:`consumes` attribute, and filter message
85+
types by model.
86+
87+
:param model: Model to use as a filter for the messages to return
88+
:type model: A derived class from :py:class:`leapp.models.Model`
89+
:param default: Fallback value in case there are no messages
90+
:type default: Any
91+
:return: The first message of the specified model produced by other actors or the value passed as `default`
92+
:rtype: The message or the value passed as ``default``
93+
"""
94+
return current_actor().consume_first(model=model, default=default)
95+
96+
6897
def consume(*models):
6998
"""
7099
Retrieve messages specified in the actors :py:attr:`consumes` attribute, and filter message types by

tests/data/actor-api-tests/models/apitest.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
class ApiTest(Model):
66
topic = ApiTestTopic
77

8-
data = fields.String()
8+
data = fields.String(default='not-filled')
99

1010

1111
class ApiTestProduce(ApiTest):

tests/scripts/test_actor_api.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,13 +72,24 @@ def test_actor_messaging_paths(leapp_forked, repository, actor_name):
7272
messaging = _TestableMessaging()
7373
with _with_loaded_actor(repository, actor_name, messaging) as (_unused, actor):
7474
from leapp.models import ApiTestConsume, ApiTestProduce
75+
assert len(list(actor.consume(ApiTestConsume))) == 0
76+
assert next(actor.consume(ApiTestConsume), None) is None
77+
assert actor.consume_first(ApiTestConsume) is None
78+
assert api.consume_first(ApiTestConsume) is None
79+
assert api.consume_first_default(ApiTestConsume).data == 'not-filled'
80+
assert actor.consume_first_default(ApiTestConsume).data == 'not-filled'
81+
assert actor.consume_first(ApiTestConsume, default='default') == 'default'
82+
assert api.consume_first(ApiTestConsume, default='default') == 'default'
83+
7584
messaging.feed(ApiTestConsume(data='prefilled'), actor)
7685

7786
assert len(list(actor.consume(ApiTestConsume))) == 1
7887
assert next(actor.consume(ApiTestConsume)).data == 'prefilled'
88+
assert actor.consume_first(ApiTestConsume).data == 'prefilled'
7989

8090
assert len(list(api.consume(ApiTestConsume))) == 1
8191
assert next(api.consume(ApiTestConsume)).data == 'prefilled'
92+
assert api.consume_first(ApiTestConsume).data == 'prefilled'
8293

8394
actor_message = 'Actor {} sent message via Actor'.format(actor_name)
8495
api_message = 'Actor {} sent message via API'.format(actor_name)

0 commit comments

Comments
 (0)