Greengen is a python package that allows implementing generators without yielding items all the way up. Instead, any inner function called from a greengen generator, can yield items directly to the outer generator.
pip install greengen
For example, suppose we want to implement a function that performs some heavy business logic, and generates logs. We want to handle the logs the moment they are created. But our function has to call a deep stack of helper functions, which in their turn call more functions, and logs may be written anywhere inside any function. The implementation with regular generators will be utterly annoying:
import time
def do_business_logic_and_yield_logs(some_input):
yield from _helper_function(some_input)
yield from _more_helper_function(some_input)
def _helper_function(some_input):
for i in range(some_input):
logs_and_result = _inner_helper_function(i)
# Notice the enormous effort needed in order to retrieve the last result (using StopIteration etc.)
while True:
try:
yield next(logs_and_result) # This is a log
except StopIteration as e:
result = e.value # This is the result
yield log('Result for {} is {}'.format(i, result))
break
def _more_helper_function(some_input):
yield from _helper_function(some_input * 100)
yield from _helper_function(some_input ** 2)
def _inner_helper_function(some_input):
yield log('Started calculating')
result = 2 ** some_input # (Or whatever other heavy calculation)
yield log('Finished calculating')
return result # Will be raised as StopIteration
def log(stuff):
return {'message': str(stuff), 'timestamp': time.time()}
def main():
for l in do_business_logic_and_yield_logs(42):
# Consume the logs however we want
print('{}: {}'.format(l['timestamp'], l['message']))
Using Greengen, this example can be simplified into the following:
import time
import greengen
@greengen.greengen
def do_business_logic_and_yield_logs(some_input):
# Notice how we don't need the "yield from" anymore
_helper_function(some_input)
_more_helper_function(some_input)
def _helper_function(some_input):
for i in range(some_input):
# Notice how easy it is to retrieve the result now
result = _inner_helper_function(i)
log('Result for {} is {}'.format(i, result))
def _more_helper_function(some_input):
_helper_function(some_input * 100)
_helper_function(some_input ** 2)
def _inner_helper_function(some_input):
log('Started calculating')
result = 2 ** some_input # (Or whatever other heavy calculation)
log('Finished calculating')
return result
def log(stuff):
# This is the only place in the entire code where we need to be aware of the fact that we are inside a generator.
# This will directly yield the log as the next item in the outer generator ("do_business_logic_and_yield_logs")
greengen.yield_({'message': str(stuff), 'timestamp': time.time()})
def main():
for l in do_business_logic_and_yield_logs(42):
# Consume the logs however we want
print('{}: {}'.format(l['timestamp'], l['message']))
- Fork it!
- Create your feature branch:
git checkout -b my-new-feature
- Commit your changes:
git commit -am 'Add some feature'
- Push to the branch:
git push origin my-new-feature
- Submit a pull request :D
TODO: Write history
TODO: Write credits
TODO: Write license