# Path: paco/compose.py
# def compose(*coros):
#     """
#     Creates a coroutine function based on the composition of the passed
#     coroutine functions.
# 
#     Each function consumes the yielded result of the coroutine that follows.
# 
#     Composing coroutine functions f(), g(), and h() would produce
#     the result of f(g(h())).
# 
#     Arguments:
#         *coros (coroutinefunction): variadic coroutine functions to compose.
# 
#     Raises:
#         RuntimeError: if cannot execute a coroutine function.
# 
#     Returns:
#         coroutinefunction
# 
#     Usage::
# 
#         async def sum_1(num):
#             return num + 1
# 
#         async def mul_2(num):
#             return num * 2
# 
#         coro = paco.compose(sum_1, mul_2, sum_1)
#         await coro(2)
#         # => 7
# 
#     """
#     # Make list to inherit built-in type methods
#     coros = list(coros)
# 
#     @asyncio.coroutine
#     def reducer(acc, coro):
#         return (yield from coro(acc))
# 
#     @asyncio.coroutine
#     def wrapper(acc):
#         return (yield from reduce(reducer, coros,
#                                   initializer=acc, right=True))
# 
#     return wrapper
# 
# Path: paco/partial.py
# @decorate
# def partial(coro, *args, **kw):
#     """
#     Partial function implementation designed
#     for coroutines, allowing variadic input arguments.
# 
#     This function can be used as decorator.
# 
#     arguments:
#         coro (coroutinefunction): coroutine function to wrap.
#         *args (mixed): mixed variadic arguments for partial application.
# 
#     Raises:
#         TypeError: if ``coro`` is not a coroutine function.
# 
#     Returns:
#         coroutinefunction
# 
#     Usage::
# 
#         async def pow(x, y):
#             return x ** y
# 
#         pow_2 = paco.partial(pow, 2)
#         await pow_2(4)
#         # => 16
# 
#     """
#     assert_corofunction(coro=coro)
# 
#     @asyncio.coroutine
#     def wrapper(*_args, **_kw):
#         call_args = args + _args
#         kw.update(_kw)
#         return (yield from coro(*call_args, **kw))
# 
#     return wrapper
# 
# Path: tests/helpers.py
# def run_in_loop(coro, *args, **kw):
#     loop = asyncio.get_event_loop()
#     if asyncio.iscoroutinefunction(coro) or isfunction(coro):
#         coro = coro(*args, **kw)
#     return loop.run_until_complete(coro)


import time
import pytest
import asyncio
from paco import compose, partial
from .helpers import run_in_loop
# -*- coding: utf-8 -*-


@asyncio.coroutine
def coro(num, acc):
    yield from asyncio.sleep(0.1)
    return acc + (num,)


def test_compose():
    task = compose(partial(coro, 1), partial(coro, 2), partial(coro, 3))
    now = time.time()
    assert run_in_loop(task, (0,)) == (0, 3, 2, 1)
    assert time.time() - now >= 0.3


def test_compose_exception():
    count = 0

    @asyncio.coroutine
    def coro_exception(x):
        nonlocal count
        count += 1

        if count == 2:
            raise ValueError('foo')

        return x + 1


# Path: paco/compose.py
# def compose(*coros):
#     """
#     Creates a coroutine function based on the composition of the passed
#     coroutine functions.
# 
#     Each function consumes the yielded result of the coroutine that follows.
# 
#     Composing coroutine functions f(), g(), and h() would produce
#     the result of f(g(h())).
# 
#     Arguments:
#         *coros (coroutinefunction): variadic coroutine functions to compose.
# 
#     Raises:
#         RuntimeError: if cannot execute a coroutine function.
# 
#     Returns:
#         coroutinefunction
# 
#     Usage::
# 
#         async def sum_1(num):
#             return num + 1
# 
#         async def mul_2(num):
#             return num * 2
# 
#         coro = paco.compose(sum_1, mul_2, sum_1)
#         await coro(2)
#         # => 7
# 
#     """
#     # Make list to inherit built-in type methods
#     coros = list(coros)
# 
#     @asyncio.coroutine
#     def reducer(acc, coro):
#         return (yield from coro(acc))
# 
#     @asyncio.coroutine
#     def wrapper(acc):
#         return (yield from reduce(reducer, coros,
#                                   initializer=acc, right=True))
# 
#     return wrapper
# 
# Path: paco/partial.py
# @decorate
# def partial(coro, *args, **kw):
#     """
#     Partial function implementation designed
#     for coroutines, allowing variadic input arguments.
# 
#     This function can be used as decorator.
# 
#     arguments:
#         coro (coroutinefunction): coroutine function to wrap.
#         *args (mixed): mixed variadic arguments for partial application.
# 
#     Raises:
#         TypeError: if ``coro`` is not a coroutine function.
# 
#     Returns:
#         coroutinefunction
# 
#     Usage::
# 
#         async def pow(x, y):
#             return x ** y
# 
#         pow_2 = paco.partial(pow, 2)
#         await pow_2(4)
#         # => 16
# 
#     """
#     assert_corofunction(coro=coro)
# 
#     @asyncio.coroutine
#     def wrapper(*_args, **_kw):
#         call_args = args + _args
#         kw.update(_kw)
#         return (yield from coro(*call_args, **kw))
# 
#     return wrapper
# 
# Path: tests/helpers.py
# def run_in_loop(coro, *args, **kw):
#     loop = asyncio.get_event_loop()
#     if asyncio.iscoroutinefunction(coro) or isfunction(coro):
#         coro = coro(*args, **kw)
#     return loop.run_until_complete(coro)


# Path: tests/compose_test.py
import time
import pytest
import asyncio
from paco import compose, partial
from .helpers import run_in_loop

# -*- coding: utf-8 -*-


@asyncio.coroutine
def coro(num, acc):
    yield from asyncio.sleep(0.1)
    return acc + (num,)


def test_compose():
    task = compose(partial(coro, 1), partial(coro, 2), partial(coro, 3))
    now = time.time()
    assert run_in_loop(task, (0,)) == (0, 3, 2, 1)
    assert time.time() - now >= 0.3


def test_compose_exception():
    count = 0

    @asyncio.coroutine
    def coro_exception(x):
        nonlocal count
        count += 1

        if count == 2:
            raise ValueError('foo')

        return x + 1


    task = compose(*(coro_exception,) * 5)