Skip to content

Commit

Permalink
Add 'handle_once' property to EventHandler
Browse files Browse the repository at this point in the history
This is useful for implementing "one shot" events.
The property can be set via the constructor or setter.
If 'handle_once' is set to True, then the EventHandler unregisters itself from the context after the first time it is handled.
Tests included.
  • Loading branch information
jacobperron committed Sep 21, 2018
1 parent a086837 commit 0a67450
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 1 deletion.
23 changes: 22 additions & 1 deletion launch/launch/event_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ def __init__(
self,
*,
matcher: Callable[[Event], bool],
entities: Optional[SomeActionsType] = None
entities: Optional[SomeActionsType] = None,
handle_once: bool = False
) -> None:
"""
Constructor.
Expand All @@ -48,15 +49,33 @@ def __init__(
the event should be handled by this event handler, False otherwise.
:param: entities is an LaunchDescriptionEntity or list of them, and is
returned by handle() unconditionally if matcher returns True.
:param: handle_once is a flag that, if True, unregisters this EventHandler
after being handled once.
"""
self.__matcher = matcher
self.__entities = entities
self.handle_once = handle_once

@property
def entities(self):
"""Getter for entities."""
return self.__entities

@property
def handle_once(self):
"""Getter for handle_once flag."""
return self.__handle_once

@handle_once.setter
def handle_once(self, value):
"""Setter for handle_once flag."""
if isinstance(value, bool):
self.__handle_once = value
else:
raise TypeError(
'handle_once expects type "bool", but received {} instead.'.format(type(value))
)

# TODO(wjwwood): setup standard interface for describing event handlers

def matches(self, event: Event) -> bool:
Expand All @@ -66,4 +85,6 @@ def matches(self, event: Event) -> bool:
def handle(self, event: Event, context: 'LaunchContext') -> Optional[SomeActionsType]:
"""Handle the given event."""
context.extend_locals({'event': event})
if self.handle_once:
context.unregister_event_handler(self)
return self.__entities
44 changes: 44 additions & 0 deletions launch/test/launch/test_event_handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,18 @@
from launch import LaunchDescriptionEntity
from launch import SomeActionsType_types_tuple

import pytest


def test_event_handler_constructors():
"""Test the constructors for EventHandler class."""
EventHandler(matcher=lambda event: False)
EventHandler(matcher=lambda event: False, entities=[LaunchDescriptionEntity])
EventHandler(matcher=lambda event: True, handle_once=True)

# Construct with bad type
with pytest.raises(TypeError):
EventHandler(matcher=lambda event: True, handle_once='Bad type')


def test_event_handler_matches_and_handle():
Expand All @@ -39,3 +46,40 @@ class MockEvent:
assert isinstance(entities, SomeActionsType_types_tuple)
assert len(entities) == 1
assert context.locals.event == mock_event


def test_event_handler_handle_once_property():
eh = EventHandler(matcher=lambda event: True, handle_once=True)
assert eh.handle_once
eh.handle_once = False
assert not eh.handle_once
# Set bad type
with pytest.raises(TypeError):
eh.handle_once = 'Not a bool'


def test_event_handler_handle_once():
"""Test the option for handling events once for the EventHandler class."""
class MockEvent:
...

mock_event = MockEvent()

# Test handling multiple events with handle_once=False (default)
eh_multiple = EventHandler(matcher=lambda event: True, handle_once=False)
context_multiple = LaunchContext()
context_multiple.register_event_handler(eh_multiple)
eh_multiple.handle(mock_event, context_multiple)
assert context_multiple.locals.event == mock_event
# Attempt to handle a second event
eh_multiple.handle(mock_event, context_multiple)

# Test handling multiple events with handle_once=True
eh_once = EventHandler(matcher=lambda event: True, handle_once=True)
context_once = LaunchContext()
context_once.register_event_handler(eh_once)
eh_once.handle(mock_event, context_once)
assert context_once.locals.event == mock_event
# Attempt to handle a second event, this time expect ValueError because it is unregistered
with pytest.raises(ValueError):
eh_once.handle(mock_event, context_once)

0 comments on commit 0a67450

Please sign in to comment.