Scramblings

Dev scratchpad. Digital garden

Python Helpers - Closures

Nov 9, 2019 | Reading Time: 2 min

A script to chain multiple closures in python with example usage.

Closure chaining

 1from functools import wraps
 2
 3
 4# A new closure will be returned as a chain of Closures in the incoming list order
 5def chain_closures(closure_list=None):
 6    if not closure_list:
 7        return None
 8
 9    def chain(f):
10        @wraps(f)
11        def r(*args, **kwargs):
12            newfunc = f
13            for c in reversed(closure_list):
14                newfunc = c(newfunc)
15            return newfunc(*args, **kwargs)
16
17        return r
18
19    return chain

Example

 1from functools import wraps
 2from closures import chain_closures
 3import logging
 4
 5logger = logging.getLogger()
 6
 7def x(a):
 8    def request_logger(f):
 9        @wraps(f)
10        def rlog(*args, **kwargs):
11            logger.info("%s x entry", a)
12            ret = f(*args, **kwargs)
13            logger.info("%s x exit", a)
14            return ret
15
16        return rlog
17
18    return request_logger
19
20
21def y(b):
22    def request_logger(f):
23        @wraps(f)
24        def rlog(*args, **kwargs):
25            logger.info("%s y entry", b)
26            ret = f(*args, **kwargs)
27            logger.info("%s y exit", b)
28            return ret
29
30        return rlog
31
32    return request_logger
33
34
35def z(a, b):
36    c1 = x(a)
37    c2 = y(b)
38
39    def request_logger(f):
40        @wraps(f)
41        def rlog(*args, **kwargs):
42            q = c1(c2(f))
43            logger.info("z entry")
44            ret = q(*args, **kwargs)
45            logger.info("z exit")
46            return ret
47
48        return rlog
49
50    return request_logger
51
52
53def w(a, b, f):
54    c1 = x(a)
55    c2 = y(b)
56    q = c1(c2(f))
57    return q
58
59
60def get_w(a, b):
61    c1 = x(a)
62    c2 = y(b)
63    q = chain_closures([c1, c2])
64    return q
65
66
67def f1(a, b):
68    logger.info("%s %s", a, b)
69    # raise Exception("me except")
70
71
72def smain():
73    c4 = get_w(10, 11)
74    f2 = c4(f1)
75    f2(12, 13)
76    logger.info(f2.__name__)
77
78
79if __name__ == "__main__":
80    logging.basicConfig(format='%(asctime)s - %(message)s', datefmt='%d-%b-%y %H:%M:%S', level=logging.DEBUG)
81    smain()