wrap a function as a node

def wrap_as_node(self, func):
        'wrap a function as a node'
        name = self.get_name(func)

        @wraps(func)
        def wrapped(*args, **kwargs):
            'wrapped version of func'
            message = self.get_message_from_call(*args, **kwargs)
            self.logger.info('calling "%s" with %r', name, message)
            result = func(message)

            # functions can return multiple values ("emit" multiple times)
            # by yielding instead of returning. Handle this case by making
            # a list of the results and processing them all after the
            # generator successfully exits. If we were to process them as
            # they came out of the generator, we might get a partially
            # processed input sent down the graph. This may be possible in
            # the future via a flag.
            if isinstance(result, GeneratorType):
                results = [
                    self.wrap_result(name, item)
                    for item in result
                    if item is not NoResult
                ]
                self.logger.debug(
                    '%s returned generator yielding %d items', func, len(results)
                )

                [self.route(name, item) for item in results]
                return tuple(results)

            # the case of a direct return is simpler. wrap, route, and
            # return the value.
            else:
                if result is NoResult:
                    return result

                result = self.wrap_result(name, result)
                self.logger.debug(
                    '%s returned single value %s', func, result
                )
                self.route(name, result)
                return result

        return wrapped