From c374ac91d192f6a49e8c15781c49c37c9fb7b5f3 Mon Sep 17 00:00:00 2001 From: Aalekh Patel Date: Wed, 28 Jul 2021 12:22:36 -0500 Subject: [PATCH 1/2] Add dunder add method to allow composing callables through '+' operator. --- normal.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/normal.py b/normal.py index a5f8a94..be34ef2 100644 --- a/normal.py +++ b/normal.py @@ -59,6 +59,15 @@ def __call__(self, /, *args, **kwargs): result = function(result) return result + def __add__(self, other): + """Compose using '+' operator.""" + if isinstance(other, compose): + return compose(*self._functions, other._functions) + elif callable(other): + return compose(*self._functions, other) + else: + raise ValueError("Can only compose callables and 'compose' instances.") + @_recursive_repr_if_available def __repr__(self): """Represent the composed function as an unambiguous string.""" From 853fa9684663f94b3dd0f37d441819fc55dd4ee1 Mon Sep 17 00:00:00 2001 From: Aalekh Patel Date: Wed, 28 Jul 2021 13:57:47 -0500 Subject: [PATCH 2/2] Add tests for add operator and fix access of functions property. --- normal.py | 4 ++-- test.py | 40 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/normal.py b/normal.py index be34ef2..b18e0ec 100644 --- a/normal.py +++ b/normal.py @@ -62,9 +62,9 @@ def __call__(self, /, *args, **kwargs): def __add__(self, other): """Compose using '+' operator.""" if isinstance(other, compose): - return compose(*self._functions, other._functions) + return compose(*self.functions, *other.functions) elif callable(other): - return compose(*self._functions, other) + return compose(*self.functions, other) else: raise ValueError("Can only compose callables and 'compose' instances.") diff --git a/test.py b/test.py index f59bd19..dd4a6ad 100644 --- a/test.py +++ b/test.py @@ -11,6 +11,46 @@ def g(s): assert f_g.functions == (g, f) # functions exposed in execution order +def test_compose_add_compose(): + def f(s): + return s + 'f' + def g(s): + return s + 'g' + f_wrapped = compose(f) + g_wrapped = compose(g) + f_g_wrapped = f_wrapped + g_wrapped + + assert f_g_wrapped('') == 'gf' # Remember that it's f(g(...)) so g runs first + assert f_g_wrapped.functions == (g, f) # functions exposed in execution order + + +def test_compose_add_function(): + def f(s): + return s + 'f' + def g(s): + return s + 'g' + f_wrapped = compose(f) + f_g_composed = f_wrapped + g + assert f_g_composed('') == 'gf' # Remember that it's f(g(...)) so g runs first + assert f_g_composed.functions == (g, f) # functions exposed in execution order + + +def test_compose_add_neither_compose_nor_function(): + def f(s): + return s + 'f' + f_wrapped = compose(f) + g = 'string' + + raised = False + + try: + f_g_wrapped = f_wrapped + g + except ValueError as err: + raised = True + finally: + assert raised + + def test_inlining(): def f(_): return None