a
    4ñ»jŸ*  ã                   @   sx   d Z ddlmZ ddlZddlmZ zddlZdZW n eyJ   dZY n0 ddd„Z	e	Z
G d	d
„ d
eƒZddd„ZdS )a8  
This module provides functionality to support "async steps" (coroutines)
in a step-module with behave. This functionality simplifies to test
frameworks and protocols that make use of `asyncio.coroutines`_ or
provide `asyncio.coroutines`_.

EXAMPLE:

.. code-block:: python

    # -- FILE: features/steps/my_async_steps.py
    # EXAMPLE REQUIRES: Python >= 3.5
    from behave import step
    from behave.api.async_step import async_run_until_complete

    @step('an async coroutine step waits {duration:f} seconds')
    @async_run_until_complete
    async def step_async_step_waits_seconds(context, duration):
        await asyncio.sleep(duration)

.. code-block:: python

    # -- FILE: features/steps/my_async_steps2.py
    # EXAMPLE REQUIRES: Python >= 3.4
    from behave import step
    from behave.api.async_step import async_run_until_complete
    import asyncio

    @step('a tagged-coroutine async step waits {duration:f} seconds')
    @async_run_until_complete
    @asyncio.coroutine
    def step_async_step_waits_seconds2(context, duration):
        yield from asyncio.sleep(duration)


.. requires:: Python 3.5 (or 3.4) or :mod:`asyncio` backport (like :pypi:`trollius`)
.. seealso::
    https://docs.python.org/3/library/asyncio.html

.. _asyncio.coroutines: https://docs.python.org/3/library/asyncio-task.html#coroutines
é    )Úprint_functionN)Ústring_typesTFc                    s^   t  ˆ ¡dd„ ƒ‰ˆ du r2‡‡‡‡‡fdd„}|S tˆ ƒs>J ‚t  ˆ ¡‡ ‡fdd„ƒ}|S dS )aç  Provides a function decorator for async-steps (coroutines).
    Provides an async event loop and runs the async-step until completion
    (or timeout, if specified).

    .. code-block:: python

        from behave import step
        from behave.api.async_step import async_run_until_complete
        import asyncio

        @step("an async step is executed")
        @async_run_until_complete
        async def astep_impl(context)
            await asycio.sleep(0.1)

        @step("an async step is executed")
        @async_run_until_complete(timeout=1.2)
        async def astep_impl2(context)
            # -- NOTE: Wrapped event loop waits with timeout=1.2 seconds.
            await asycio.sleep(0.3)

    Parameters:
        astep_func: Async step function (coroutine)
        loop (asyncio.EventLoop):   Event loop to use or None.
        timeout (int, float):       Timeout to wait for async-step completion.
        async_context (name):       Async_context name or object to use.
        should_close (bool):        Indicates if event lopp should be closed.

    .. note::

        * If :param:`loop` is None, the default event loop will be used
          or a new event loop is created.
        * If :param:`timeout` is provided, the event loop waits only the
          specified time.
        * :param:`async_context` is only used, if :param:`loop` is None.
        * If :param:`async_context` is a name, it will be used to retrieve
          the real async_context object from the context.

    c                 _   s:  |  dd ¡}|  dd ¡}|  dd ¡}|  dd ¡}t|tƒrHt||d ƒ}n8|r€t|tƒrl|}t||ƒ}|j}nt|tƒszJ ‚|j}|d u r˜t ¡ p–t 	¡ }z†|d u rÀ| 
| |g|¢R i |¤Ž¡ nH| | |g|¢R i |¤Ž¡}	| 
tj|	g|d�¡\}
}|�rJ d| ƒ‚W |�r6|�r6| ¡  n|�r4|�r4| ¡  0 d S )NÚ_loopÚ_timeoutÚ_async_contextÚ_should_close)ÚtimeoutzTIMEOUT-OCCURED: timeout=%s)ÚpopÚ
isinstancer   ÚgetattrÚuse_or_create_async_contextÚloopÚAsyncContextÚasyncioÚget_event_loopÚnew_event_loopÚrun_until_completeZcreate_taskÚwaitÚclose)Ú
astep_funcÚcontextÚargsÚkwargsr   r   Úasync_contextÚshould_closeÚnameÚtaskÚdoneÚpending© r   ú;E:\soccer\ml_env\Lib\site-packages\behave/api/async_step.pyÚstep_decoratore   s6    


ÿ
þz0async_run_until_complete.<locals>.step_decoratorNc                    s0   t  ˆ ¡‡ ‡‡‡‡‡fdd„ƒ}tˆ ƒs,J ‚|S )Nc                    s"   ˆˆ | g|¢R ˆˆˆˆdœ|¤ŽS )N)r   r   r   r   r   ©r   r   r   ©r   r   r   r   r!   r   r   r    Úwrapped_decorator2�   s    üüzPasync_run_until_complete.<locals>.wrapped_decorator1.<locals>.wrapped_decorator2©Ú	functoolsÚwrapsÚcallable)r   r$   )r   r   r   r!   r   )r   r    Úwrapped_decorator1Œ   s    z4async_run_until_complete.<locals>.wrapped_decorator1c                    s   ˆˆ | g|¢R i |¤ŽS ©Nr   r"   )r   r!   r   r    Úwrapped_decorator›   s    z3async_run_until_complete.<locals>.wrapped_decoratorr%   )r   r   r   r   r   r)   r+   r   r#   r    Úasync_run_until_complete<   s    )
#
r,   c                   @   s.   e Zd ZdZdZddd„Zdd„ Zd	d
„ ZdS )r   a/  Provides a context object for "async steps" to keep track:

    * which event loop is used
    * which (asyncio) tasks are used or of interest

    .. attribute:: loop
        Event loop object to use.
        If none is provided, the current event-loop is used
        (or a new one is created).

    .. attribute:: tasks
        List of started :mod:`asyncio` tasks (of interest).

    .. attribute:: name

        Optional name of this object (in the behave context).
        If none is provided, :attr:`AsyncContext.default_name` is used.

    .. attribute:: should_close
        Indicates if the :attr:`loop` (event-loop) should be closed or not.

    EXAMPLE:

    .. code-block:: python

        # -- FILE: features/steps/my_async_steps.py
        # REQUIRES: Python 3.5
        from behave import given, when, then, step
        from behave.api.async_step import AsyncContext

        @when('I dispatch an async-call with param "{param}"')
        def step_impl(context, param):
            async_context = getattr(context, "async_context", None)
            if async_context is None:
                async_context = context.async_context = AsyncContext()
            task = async_context.loop.create_task(my_async_func(param))
            async_context.tasks.append(task)

        @then('I wait at most {duration:f} seconds until all async-calls are completed')
        def step_impl(context, duration):
            async_context = context.async_context
            assert async_context.tasks
            done, pending = async_context.loop.run_until_complete(asyncio.wait(
                async_context.tasks, loop=async_context.loop, timeout=duration))
            assert len(pending) == 0

        # -- COROUTINE:
        async def my_async_func(param):
            await asyncio.sleep(0.5)
            return param.upper()
    r   NFc                 C   s6   |pt  ¡ pt  ¡ | _|pg | _|p(| j| _|| _d S r*   )r   r   r   r   ÚtasksÚdefault_namer   r   )Úselfr   r   r   r-   r   r   r    Ú__init__Þ   s    
zAsyncContext.__init__c                 C   s   | j r| jr|  ¡  d S r*   )r   r   r   ©r/   r   r   r    Ú__del__ä   s    zAsyncContext.__del__c                 C   s$   | j r | j  ¡ s | j  ¡  d | _ d S r*   )r   Z	is_closedr   r1   r   r   r    r   è   s    
zAsyncContext.close)NNFN)Ú__name__Ú
__module__Ú__qualname__Ú__doc__r.   r0   r2   r   r   r   r   r    r   ¦   s
   4
r   c                 K   sj   |du rt j}t| |dƒ}|du rDt f ||dœ|¤Ž}t| |j|ƒ t|t ƒsRJ ‚t| |jƒ|u sfJ ‚|S )aX  Utility function to be used in step implementations to ensure that an
    :class:`AsyncContext` object is stored in the :param:`context` object.

    If no such attribute exists (under the given name),
    a new :class:`AsyncContext` object is created with the provided args.
    Otherwise, the existing context attribute is used.

    EXAMPLE:

    .. code-block:: python

        # -- FILE: features/steps/my_async_steps.py
        # EXAMPLE REQUIRES: Python 3.5
        from behave import when
        from behave.api.async_step import use_or_create_async_context

        @when('I dispatch an async-call with param "{param}"')
        def step_impl(context, param):
            async_context = use_or_create_async_context(context, "async_context")
            task = async_context.loop.create_task(my_async_func(param))
            async_context.tasks.append(task)

        # -- COROUTINE:
        async def my_async_func(param):
            await asyncio.sleep(0.5)
            return param.upper()

    :param context:     Behave context object to use.
    :param name:        Optional name of async-context object (as string or None).
    :param loop:        Optional event_loop object to use for create call.
    :param kwargs:      Optional :class:`AsyncContext` params for create call.
    :return: :class:`AsyncContext` object from the param:`context`.
    N)r   r   )r   r.   r   Úsetattrr   r
   )r   r   r   r   r   r   r   r    r   ñ   s    "r   )NNNNF)NN)r6   Ú
__future__r   r&   Úsixr   r   Zhas_asyncioÚImportErrorr,   r   Úobjectr   r   r   r   r   r    Ú<module>   s   +
  ÿ
eK