# 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/assertions.py
# def assert_corofunction(**kw):
#     """
#     Asserts if a given values are a coroutine function.
# 
#     Arguments:
#         **kw (mixed): value to check if it is an iterable.
# 
#     Raises:
#         TypeError: if assertion fails.
#     """
#     for name, value in kw.items():
#         if not asyncio.iscoroutinefunction(value):
#             raise TypeError(
#                 'paco: {} must be a coroutine function'.format(name))


import asyncio
from .decorator import decorate
from .assertions import assert_corofunction
        interval (int/float): number of seconds to repeat the coroutine
            execution.
        times (int): optional maximum time of executions. Infinite by default.
        loop (asyncio.BaseEventLoop, optional): loop to run.
            Defaults to asyncio.get_event_loop().

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

    Returns:
        future (asyncio.Task): coroutine wrapped as task future.
            Useful for cancellation and state checking.

    Usage::

        # Usage as function
        future = paco.interval(coro, 1)

        # Cancel it after a while...
        await asyncio.sleep(5)
        future.cancel()

        # Usage as decorator
        @paco.interval(10)
        async def metrics():
            await send_metrics()

        future = await metrics()

    """

# 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/assertions.py
# def assert_corofunction(**kw):
#     """
#     Asserts if a given values are a coroutine function.
# 
#     Arguments:
#         **kw (mixed): value to check if it is an iterable.
# 
#     Raises:
#         TypeError: if assertion fails.
#     """
#     for name, value in kw.items():
#         if not asyncio.iscoroutinefunction(value):
#             raise TypeError(
#                 'paco: {} must be a coroutine function'.format(name))


# Path: paco/interval.py
import asyncio
from .decorator import decorate
from .assertions import assert_corofunction

        interval (int/float): number of seconds to repeat the coroutine
            execution.
        times (int): optional maximum time of executions. Infinite by default.
        loop (asyncio.BaseEventLoop, optional): loop to run.
            Defaults to asyncio.get_event_loop().

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

    Returns:
        future (asyncio.Task): coroutine wrapped as task future.
            Useful for cancellation and state checking.

    Usage::

        # Usage as function
        future = paco.interval(coro, 1)

        # Cancel it after a while...
        await asyncio.sleep(5)
        future.cancel()

        # Usage as decorator
        @paco.interval(10)
        async def metrics():
            await send_metrics()

        future = await metrics()

    """

    assert_corofunction(coro=coro)