# Path: paco/times.py
# @decorate
# def times(coro, limit=1, raise_exception=False, return_value=None):
#     """
#     Wraps a given coroutine function to be executed only a certain amount
#     of times.
# 
#     If the execution limit is exceeded, the last execution return value will
#     be returned as result.
# 
#     You can optionally define a custom return value on exceeded via
#     `return_value` param.
# 
#     This function can be used as decorator.
# 
#     arguments:
#         coro (coroutinefunction): coroutine function to wrap.
#         limit (int): max limit of coroutine executions.
#         raise_exception (bool): raise exception if execution times exceeded.
#         return_value (mixed): value to return when execution times exceeded.
# 
#     Raises:
#         TypeError: if coro argument is not a coroutine function.
#         RuntimeError: if max execution excedeed (optional).
# 
#     Returns:
#         coroutinefunction
# 
#     Usage::
# 
#         async def mul_2(num):
#             return num * 2
# 
#         timed = paco.times(mul_2, 3)
#         await timed(2)
#         # => 4
#         await timed(3)
#         # => 6
#         await timed(4)
#         # => 8
#         await timed(5)  # ignored!
#         # => 8
#     """
#     assert_corofunction(coro=coro)
# 
#     # Store call times
#     limit = max(limit, 1)
#     times = limit
# 
#     # Store result from last execution
#     result = None
# 
#     @asyncio.coroutine
#     def wrapper(*args, **kw):
#         nonlocal limit
#         nonlocal result
# 
#         # Check execution limit
#         if limit == 0:
#             if raise_exception:
#                 raise RuntimeError(ExceptionMessage.format(times))
#             if return_value:
#                 return return_value
#             return result
# 
#         # Decreases counter
#         limit -= 1
# 
#         # If return_value is present, do not memoize result
#         if return_value:
#             return (yield from coro(*args, **kw))
# 
#         # Schedule coroutine and memoize result
#         result = yield from coro(*args, **kw)
#         return result
# 
#     return wrapper
# 
# Path: paco/decorator.py
# def decorate(fn):
#     """
#     Generic decorator for coroutines helper functions allowing
#     multiple variadic initialization arguments.
# 
#     This function is intended to be used internally.
# 
#     Arguments:
#         fn (function): target function to decorate.
# 
#     Raises:
#         TypeError: if function or coroutine function is not provided.
# 
#     Returns:
#         function: decorated function.
#     """
#     if not isfunction(fn):
#         raise TypeError('paco: fn must be a callable object')
# 
#     @functools.wraps(fn)
#     def decorator(*args, **kw):
#         # If coroutine object is passed
#         for arg in args:
#             if iscoro_or_corofunc(arg):
#                 return fn(*args, **kw)
# 
#         # Explicit argument must be at least a coroutine
#         if len(args) and args[0] is None:
#             raise TypeError('paco: first argument cannot be empty')
# 
#         def wrapper(coro, *_args, **_kw):
#             # coro must be a valid type
#             if not iscoro_or_corofunc(coro):
#                 raise TypeError('paco: first argument must be a '
#                                 'coroutine or coroutine function')
# 
#             # Merge call arguments
#             _args = ((coro,) + (args + _args))
#             kw.update(_kw)
# 
#             # Trigger original decorated function
#             return fn(*_args, **kw)
#         return wrapper
#     return decorator


from .times import times
from .decorator import decorate
    arguments:
        coro (coroutinefunction): coroutine function to wrap.
        raise_exception (bool): raise exception if execution times exceeded.
        return_value (mixed): value to return when execution times exceeded,
            instead of the memoized one from last invocation.

    Raises:
        TypeError: if coro argument is not a coroutine function.

    Returns:
        coroutinefunction

    Usage::

        async def mul_2(num):
            return num * 2

        once = paco.once(mul_2)
        await once(2)
        # => 4
        await once(3)
        # => 4

        once = paco.once(mul_2, return_value='exceeded')
        await once(2)
        # => 4
        await once(3)
        # => 'exceeded'

    """

# Path: paco/times.py
# @decorate
# def times(coro, limit=1, raise_exception=False, return_value=None):
#     """
#     Wraps a given coroutine function to be executed only a certain amount
#     of times.
# 
#     If the execution limit is exceeded, the last execution return value will
#     be returned as result.
# 
#     You can optionally define a custom return value on exceeded via
#     `return_value` param.
# 
#     This function can be used as decorator.
# 
#     arguments:
#         coro (coroutinefunction): coroutine function to wrap.
#         limit (int): max limit of coroutine executions.
#         raise_exception (bool): raise exception if execution times exceeded.
#         return_value (mixed): value to return when execution times exceeded.
# 
#     Raises:
#         TypeError: if coro argument is not a coroutine function.
#         RuntimeError: if max execution excedeed (optional).
# 
#     Returns:
#         coroutinefunction
# 
#     Usage::
# 
#         async def mul_2(num):
#             return num * 2
# 
#         timed = paco.times(mul_2, 3)
#         await timed(2)
#         # => 4
#         await timed(3)
#         # => 6
#         await timed(4)
#         # => 8
#         await timed(5)  # ignored!
#         # => 8
#     """
#     assert_corofunction(coro=coro)
# 
#     # Store call times
#     limit = max(limit, 1)
#     times = limit
# 
#     # Store result from last execution
#     result = None
# 
#     @asyncio.coroutine
#     def wrapper(*args, **kw):
#         nonlocal limit
#         nonlocal result
# 
#         # Check execution limit
#         if limit == 0:
#             if raise_exception:
#                 raise RuntimeError(ExceptionMessage.format(times))
#             if return_value:
#                 return return_value
#             return result
# 
#         # Decreases counter
#         limit -= 1
# 
#         # If return_value is present, do not memoize result
#         if return_value:
#             return (yield from coro(*args, **kw))
# 
#         # Schedule coroutine and memoize result
#         result = yield from coro(*args, **kw)
#         return result
# 
#     return wrapper
# 
# Path: paco/decorator.py
# def decorate(fn):
#     """
#     Generic decorator for coroutines helper functions allowing
#     multiple variadic initialization arguments.
# 
#     This function is intended to be used internally.
# 
#     Arguments:
#         fn (function): target function to decorate.
# 
#     Raises:
#         TypeError: if function or coroutine function is not provided.
# 
#     Returns:
#         function: decorated function.
#     """
#     if not isfunction(fn):
#         raise TypeError('paco: fn must be a callable object')
# 
#     @functools.wraps(fn)
#     def decorator(*args, **kw):
#         # If coroutine object is passed
#         for arg in args:
#             if iscoro_or_corofunc(arg):
#                 return fn(*args, **kw)
# 
#         # Explicit argument must be at least a coroutine
#         if len(args) and args[0] is None:
#             raise TypeError('paco: first argument cannot be empty')
# 
#         def wrapper(coro, *_args, **_kw):
#             # coro must be a valid type
#             if not iscoro_or_corofunc(coro):
#                 raise TypeError('paco: first argument must be a '
#                                 'coroutine or coroutine function')
# 
#             # Merge call arguments
#             _args = ((coro,) + (args + _args))
#             kw.update(_kw)
# 
#             # Trigger original decorated function
#             return fn(*_args, **kw)
#         return wrapper
#     return decorator


# Path: paco/once.py
from .times import times
from .decorator import decorate

    arguments:
        coro (coroutinefunction): coroutine function to wrap.
        raise_exception (bool): raise exception if execution times exceeded.
        return_value (mixed): value to return when execution times exceeded,
            instead of the memoized one from last invocation.

    Raises:
        TypeError: if coro argument is not a coroutine function.

    Returns:
        coroutinefunction

    Usage::

        async def mul_2(num):
            return num * 2

        once = paco.once(mul_2)
        await once(2)
        # => 4
        await once(3)
        # => 4

        once = paco.once(mul_2, return_value='exceeded')
        await once(2)
        # => 4
        await once(3)
        # => 'exceeded'

    """

    return times(coro,