# Path: paco/generator.py
# @asyncio.coroutine
# def consume(generator):  # pragma: no cover
#     """
#     Helper function to consume a synchronous or asynchronous generator.
# 
#     Arguments:
#         generator (generator|asyncgenerator): generator to consume.
# 
#     Returns:
#         list
#     """
#     # If synchronous generator, just consume and return as list
#     if hasattr(generator, '__next__'):
#         return list(generator)
# 
#     if not PY_35:
#         raise RuntimeError(
#             'paco: asynchronous iterator protocol not supported')
# 
#     # If asynchronous generator, consume it generator protocol manually
#     buf = []
#     while True:
#         try:
#             buf.append((yield from generator.__anext__()))
#         except StopAsyncIteration:  # noqa
#             break
# 
#     return buf
# 
# Path: paco/assertions.py
# def iscoro_or_corofunc(x):
#     """
#     Returns ``True`` if the given value is a coroutine or a coroutine function.
# 
#     Arguments:
#         x (mixed): object value to assert.
# 
#     Returns:
#         bool: returns ``True`` if ``x` is a coroutine or coroutine function.
#     """
#     return asyncio.iscoroutinefunction(x) or asyncio.iscoroutine(x)
# 
# def isgenerator(x):
#     """
#     Returns `True` if the given value is sync or async generator coroutine.
# 
#     Arguments:
#         x (mixed): value to check if it is an iterable.
# 
#     Returns:
#         bool
#     """
#     return any([
#         hasattr(x, '__next__'),
#         hasattr(x, '__anext__')
#     ])
# 
# Path: paco/pipe.py
# def overload(fn):
#     """
#     Overload a given callable object to be used with ``|`` operator
#     overloading.
# 
#     This is especially used for composing a pipeline of
#     transformation over a single data set.
# 
#     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')
# 
#     spec = getargspec(fn)
#     args = spec.args
#     if not spec.varargs and (len(args) < 2 or args[1] != 'iterable'):
#         raise ValueError('paco: invalid function signature or arity')
# 
#     @functools.wraps(fn)
#     def decorator(*args, **kw):
#         # Check function arity
#         if len(args) < 2:
#             return PipeOverloader(fn, args, kw)
#         # Otherwise, behave like a normal wrapper
#         return fn(*args, **kw)
# 
#     return decorator


import asyncio
import functools
from inspect import isfunction
from .generator import consume
from .assertions import iscoro_or_corofunc, isgenerator
from .pipe import overload  # noqa
    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

# Path: paco/generator.py
# @asyncio.coroutine
# def consume(generator):  # pragma: no cover
#     """
#     Helper function to consume a synchronous or asynchronous generator.
# 
#     Arguments:
#         generator (generator|asyncgenerator): generator to consume.
# 
#     Returns:
#         list
#     """
#     # If synchronous generator, just consume and return as list
#     if hasattr(generator, '__next__'):
#         return list(generator)
# 
#     if not PY_35:
#         raise RuntimeError(
#             'paco: asynchronous iterator protocol not supported')
# 
#     # If asynchronous generator, consume it generator protocol manually
#     buf = []
#     while True:
#         try:
#             buf.append((yield from generator.__anext__()))
#         except StopAsyncIteration:  # noqa
#             break
# 
#     return buf
# 
# Path: paco/assertions.py
# def iscoro_or_corofunc(x):
#     """
#     Returns ``True`` if the given value is a coroutine or a coroutine function.
# 
#     Arguments:
#         x (mixed): object value to assert.
# 
#     Returns:
#         bool: returns ``True`` if ``x` is a coroutine or coroutine function.
#     """
#     return asyncio.iscoroutinefunction(x) or asyncio.iscoroutine(x)
# 
# def isgenerator(x):
#     """
#     Returns `True` if the given value is sync or async generator coroutine.
# 
#     Arguments:
#         x (mixed): value to check if it is an iterable.
# 
#     Returns:
#         bool
#     """
#     return any([
#         hasattr(x, '__next__'),
#         hasattr(x, '__anext__')
#     ])
# 
# Path: paco/pipe.py
# def overload(fn):
#     """
#     Overload a given callable object to be used with ``|`` operator
#     overloading.
# 
#     This is especially used for composing a pipeline of
#     transformation over a single data set.
# 
#     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')
# 
#     spec = getargspec(fn)
#     args = spec.args
#     if not spec.varargs and (len(args) < 2 or args[1] != 'iterable'):
#         raise ValueError('paco: invalid function signature or arity')
# 
#     @functools.wraps(fn)
#     def decorator(*args, **kw):
#         # Check function arity
#         if len(args) < 2:
#             return PipeOverloader(fn, args, kw)
#         # Otherwise, behave like a normal wrapper
#         return fn(*args, **kw)
# 
#     return decorator


# Path: paco/decorator.py
import asyncio
import functools
from inspect import isfunction
from .generator import consume
from .assertions import iscoro_or_corofunc, isgenerator
from .pipe import overload  # noqa

    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):