#!/usr/bin/env python
"""Benchmark for stack_context functionality."""
import collections
import contextlib
import functools
import subprocess
import sys

from tornado import stack_context


class Benchmark(object):
    def enter_exit(self, count):
        """Measures the overhead of the nested "with" statements
        when using many contexts.
        """
        if count < 0:
            return
        with self.make_context():
            self.enter_exit(count - 1)

    def call_wrapped(self, count):
        """Wraps and calls a function at each level of stack depth
        to measure the overhead of the wrapped function.
        """
        # This queue is analogous to IOLoop.add_callback, but lets us
        # benchmark the stack_context in isolation without system call
        # overhead.
        queue = collections.deque()
        self.call_wrapped_inner(queue, count)
        while queue:
            queue.popleft()()

    def call_wrapped_inner(self, queue, count):
        if count < 0:
            return
        with self.make_context():
            queue.append(stack_context.wrap(
                functools.partial(self.call_wrapped_inner, queue, count - 1)))


class StackBenchmark(Benchmark):
    def make_context(self):
        return stack_context.StackContext(self.__context)

    @contextlib.contextmanager
    def __context(self):
        yield


class ExceptionBenchmark(Benchmark):
    def make_context(self):
        return stack_context.ExceptionStackContext(self.__handle_exception)

    def __handle_exception(self, typ, value, tb):
        pass


def main():
    base_cmd = [
        sys.executable, '-m', 'timeit', '-s',
        'from stack_context_benchmark import StackBenchmark, ExceptionBenchmark']
    cmds = [
        'StackBenchmark().enter_exit(50)',
        'StackBenchmark().call_wrapped(50)',
        'StackBenchmark().enter_exit(500)',
        'StackBenchmark().call_wrapped(500)',

        'ExceptionBenchmark().enter_exit(50)',
        'ExceptionBenchmark().call_wrapped(50)',
        'ExceptionBenchmark().enter_exit(500)',
        'ExceptionBenchmark().call_wrapped(500)',
    ]
    for cmd in cmds:
        print(cmd)
        subprocess.check_call(base_cmd + [cmd])


if __name__ == '__main__':
    main()
