From d03387379d426bb988046743d081c21947186aef Mon Sep 17 00:00:00 2001 From: Nicolas Marier Date: Fri, 27 May 2022 15:17:12 -0400 Subject: [PATCH 1/2] fix: stop leaking processors with multiple invoke This commit fixes a bug where a processor would be invoked succesively more and more times because the list of processors associated with a given filter was accidentally mutated during the invocation. --- src/event_processor/event_processor.py | 5 ++++- src/tests/test_event_processor.py | 23 +++++++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/src/event_processor/event_processor.py b/src/event_processor/event_processor.py index 201073a..e09e245 100644 --- a/src/event_processor/event_processor.py +++ b/src/event_processor/event_processor.py @@ -1,5 +1,6 @@ """Contains the EventProcessor class.""" import inspect +from copy import copy from types import ModuleType from typing import Dict, Callable, Tuple, List, Union @@ -103,7 +104,9 @@ def invoke(self, event: Dict) -> Union[Result, List[Result]]: for (filter_, rank), processors in self.processors.items(): if filter_.matches(event): if rank > highest_rank: - matching, highest_rank = processors, rank + # We take a copy here to avoid mutating the list of processors associated with a filter if we also + # end up hitting the elif just below. + matching, highest_rank = copy(processors), rank elif rank == highest_rank: matching.extend(processors) diff --git a/src/tests/test_event_processor.py b/src/tests/test_event_processor.py index cd30688..1128af0 100644 --- a/src/tests/test_event_processor.py +++ b/src/tests/test_event_processor.py @@ -118,6 +118,29 @@ def a_test(): assert called is True +def test_invoke_calls_processors_once_per_invoke_with_multiple_matched_processors_and_all_matches_invocation_strategy(): + event_processor = EventProcessor(invocation_strategy=InvocationStrategies.ALL_MATCHES) + filter_a = Exists("a") + filter_b = Exists("b") + a_calls = 0 + b_calls = 0 + + @event_processor.processor(filter_a) + def a_test(): + nonlocal a_calls + a_calls += 1 + + @event_processor.processor(filter_b) + def b_test(): + nonlocal b_calls + b_calls += 1 + + event_processor.invoke({"a": 0, "b": 0}) + event_processor.invoke({"a": 0, "b": 0}) + assert a_calls == 2 + assert b_calls == 2 + + def test_invoke_raises_for_no_matching_processors(event_processor): with pytest.raises(InvocationError): From d6139079185061e355a322e2e12f221ebc2f68bc Mon Sep 17 00:00:00 2001 From: Nicolas Marier Date: Fri, 27 May 2022 15:23:57 -0400 Subject: [PATCH 2/2] docs: update docs --- docs/content/changelog.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/content/changelog.rst b/docs/content/changelog.rst index 924cd04..7c8f263 100644 --- a/docs/content/changelog.rst +++ b/docs/content/changelog.rst @@ -1,3 +1,6 @@ +- v3.1.3: Fix a bug with invocation when an event matches several filters. +- v3.1.2: Fix an incorrect import. +- v3.1.1: Relax pydantic requirements to help dependency management for downstream projects. - v3.1.0: Add error handling strategies for processors. - v3.0.1: Improve the import path for results. - v3.0.0: