@@ -102,3 +102,57 @@ def wrapper(param, *args, **kwargs):
102102 return func (param , * args , ** kwargs )
103103
104104 return wrapper
105+
106+
107+ # From jaraco.functools 4.0.2
108+ def compose (* funcs ):
109+ """
110+ Compose any number of unary functions into a single unary function.
111+
112+ Comparable to
113+ `function composition <https://en.wikipedia.org/wiki/Function_composition>`_
114+ in mathematics:
115+
116+ ``h = g ∘ f`` implies ``h(x) = g(f(x))``.
117+
118+ In Python, ``h = compose(g, f)``.
119+
120+ >>> import textwrap
121+ >>> expected = str.strip(textwrap.dedent(compose.__doc__))
122+ >>> strip_and_dedent = compose(str.strip, textwrap.dedent)
123+ >>> strip_and_dedent(compose.__doc__) == expected
124+ True
125+
126+ Compose also allows the innermost function to take arbitrary arguments.
127+
128+ >>> round_three = lambda x: round(x, ndigits=3)
129+ >>> f = compose(round_three, int.__truediv__)
130+ >>> [f(3*x, x+1) for x in range(1,10)]
131+ [1.5, 2.0, 2.25, 2.4, 2.5, 2.571, 2.625, 2.667, 2.7]
132+ """
133+
134+ def compose_two (f1 , f2 ):
135+ return lambda * args , ** kwargs : f1 (f2 (* args , ** kwargs ))
136+
137+ return functools .reduce (compose_two , funcs )
138+
139+
140+ def apply (transform ):
141+ """
142+ Decorate a function with a transform function that is
143+ invoked on results returned from the decorated function.
144+
145+ >>> @apply(reversed)
146+ ... def get_numbers(start):
147+ ... "doc for get_numbers"
148+ ... return range(start, start+3)
149+ >>> list(get_numbers(4))
150+ [6, 5, 4]
151+ >>> get_numbers.__doc__
152+ 'doc for get_numbers'
153+ """
154+
155+ def wrap (func ):
156+ return functools .wraps (func )(compose (transform , func ))
157+
158+ return wrap
0 commit comments