o
    ;iw                     @   s^  U d dl Z d dlZd dlZd dlZd dlZd dlZd dlZd dlZd dl	Z	d dl
Z
d dlZd dlZd dlmZmZmZmZmZ d dlmZ d dlmZ d dlmZmZmZmZmZmZmZ d dlZd dlmZ d dl m!Z! d dl"m#Z# d d	l$m%Z%m&Z& d d
l'm(Z( d dl)m*Z* ddl+m,Z,m-Z- ddl.m.Z. edZ/e%dZ0edZ1ej2dkre 3e 4  de5dede6e7e5f fddZ8eG dd dZ9dd Z:de
j;fddZ<de
j;fdd Z=ej>e=d!Z?dd"d#Z@dZAeeB eCd$< dd%d dd&d'd(d)ZDG d*d+ d+ZEd,d- ZFG d.d/ d/ee/ ZG	1dd2ee jHeGf fd3d4ZIG d5d6 d6ZJe@eJZKG d7d8 d8eJZLdd9ee5 fd:d;ZMd<ejNdde/f d=e5de/fd>d?ZOG d@dA dAZPg ZQdBdC ZRdDee0e/f dee0ejNdde/f f fdEdFZSdGee/ dee/df fdHdIZTedJdK ZUedLZVedMZWdNedOejeVeWf dejXeVeWdf fdPdQZYG dRdS dSeje/ ZZdTeee/ ee/ f dee/df fdUdVZ[ej\dWee/df dXee1df dee6e/e1f df fdYdZZ]ej\d[ee/df dee6e/d\f df fd]dZZ]d^dZ Z]eG d_d` d`eje/ Z^eG dadb dbZ_G dcdd ddZ`e` Zadedfd[ee/df dgebdee/df fdhdiZcdjeg ee/ f dee/df fdkdlZddmdn Zedodp Zf	eddqee/df dree/gee1 f dseBdgebdee1df f
dtduZg	ddqee/df dree/gee1 f dseBdveeB dee1df f
dwdxZhd[ee/df dee/df fdydzZid{e6eBeBeBf d|e5fd}d~ZjdS )    N)AsyncGeneratorAsyncIterable	AwaitableIterableIterator)asynccontextmanager)	dataclass)AnyCallableGenericOptionalTypeVarUnioncast)Runner)MethodWithAio)NestedEventLoops)	ParamSpecassert_type)is_interactive_ipythondeprecation_warning   )AsyncUsageWarningInvalidError   )loggerTPVwin32	code_lineoriginal_funcreturnc                  C   s`  ddl }|j}t|}|dkr | dr | ddd}d|fS |dkr4| d	r4| d	d
d}d|fS |dkrj|d|  }|rb| \}}}	d| d| d|	 d| d| d|	 d}d|fS dd| dfS |dkr|d|  }
|
r|
 \}}d| d| d| d| d	}d|fS dd| dfS |r|  drd|	| d}|
|| }|r| ddd}d|fS d|	| d}|
|| }|rd}|d| }|rt|d}| d| }| |d }|d  |  }d|fS d|	| d!}|
|| }|sdd| dfS g d"}|D ]}|
d#| d#| r/dd| df  S q| }|}|d }|dkrj| | }| sS|d$ksS|d%krZ|}|d8 }n| rd|d8 }nn|dks@| d| }| |d }|d&|}|rt|d}| d||  }| || d }|jd'|	| d(d)|dd*}|d  | }d|fS )+a  
    Rewrite a blocking call to use async/await syntax.

    Handles four patterns:
    1. __aiter__: for x in obj -> async for x in obj
    2. __aenter__: with obj as x -> async with obj as x
    3. Async generators in for loops: for x in obj.method(...) -> async for x in obj.method(...)
    4. Regular methods: obj.method() -> await obj.method.aio()

    Args:
        code_line: The line of code containing the blocking call
        original_func: The original function object being called

    Returns:
        A tuple of (success, rewritten_code):
        - success: True if the pattern was found and rewritten, False if falling back to generic
        - rewritten_code: The rewritten code or a generic suggestion
    r   N	__aiter__zfor z
async for r   T
__aenter__zwith zasync with __setitem__z(\w+)\[([^\]]+)\]\s*=\s*(.+)zYou can't use `[z] = z@` syntax asynchronously - there may be an alternative api, e.g. z	.put.aio(z, )Fz	await ...z	.aio(...)__getitem__z(\w+)\[([^\]]+)\]$zA]` syntax asynchronously - there may be an alternative api, e.g. z	.get.aio(z(for\s+\w+\s+in\s+.*\.)z(\s*\()z\.z	(?!\s*\()z'^(\s*(?:\w+\s*=|return|yield|raise)\s+)zawait z\s*\()	ifelifwhileandornotinisforz\b_.z^(return|yield|raise)\s+z(\.z)\s*\(z\1.aio()count)re__name__inspectisasyncgenfunction
startswithreplacematchstripgroupsescapesearchlengrouplstripstartisalnumisspacesub) r!   r"   r6   	func_nameis_async_gen
suggestionsetitem_matchobjkeyvaluegetitem_matchfor_pattern	for_matchproperty_patternproperty_matchstatement_startprefix_matchbefore_exprafter_prefixmethod_patternmethod_matchunsafe_keywordskeywordmethod_start
expr_startic
before_objobj_and_restkeyword_matchkeyword_lenrewritten_expr re   L/home/ubuntu/.local/lib/python3.10/site-packages/modal/_utils/async_utils.pyrewrite_sync_to_async0   s   





 rg   c                   @   s.   e Zd ZU dZeed< eed< ee ed< dS )
_CallFramez0Simple dataclass to hold call frame information.filenamelinenolineN)r7   
__module____qualname____doc__str__annotations__intr   re   re   re   rf   rh      s
   
 rh   c                  C   s   ddl } ddl}t }|jt}|D ]:}|j}|jjd |jj |v s9|jjd |jj |v s9|j||kr:q| 	||j
}t||j
|rI|ndd  S dS )z
    Extract the call frame from user code by filtering out frames from synchronicity and asyncio.

    Returns a _CallFrame with the filename, line number, and source line, or None if not found.
    r   Nsynchronicityasyncio)ri   rj   rk   )	linecacheosr8   stackpathabspath__file__ri   sepgetlinerj   rh   )rt   ru   rv   	this_file
frame_infori   rk   re   re   rf   _extract_user_call_frame   s   r~   c           	      C   s   t  rd S dd l}| rt| dt| }|dv rd S t }g d}d }d }| r7|r7|jr7|j }t|| \}}|rD|rD|d| d |rdt	j
t	j
|jd }|jd|t|j|j|d d S |d|t d S )	Nr   r7   )	__aexit__	__anext__)z=A blocking Modal interface is being used in an async context.z,

This may cause performance issues or bugs.z4 Consider rewriting to use Modal's async interfaces:z#
https://modal.com/docs/guide/asyncz

Suggested rewrite:
  z

Original line: )ri   rj   module)r   warningsgetattrro   r~   rk   r=   rg   appendru   rw   splitextbasenameri   warn_explicitjoinr   rj   warn)	r"   r   rH   
call_framemessage_partsrJ   r!   r3   module_namere   re   rf   _blocking_in_async_warning   s4   

	r   c                 C   sL   ddl m } |dsdS zt|  W dS  ty     ty%   Y dS w )aD  
    Safety wrapper around _blocking_in_async_warning to ensure it never raises exceptions.

    This is non-critical functionality (just a warning), so we don't want it to break user code.
    However, if the warning has been configured to be treated as an error (via filterwarnings),
    we should let that propagate.
    r   )configasync_warningsN)r   getr   r   	Exception)r"   r   re   re   rf   _safe_blocking_in_async_warning.  s   
r   )blocking_in_async_callbackc                 C   sZ   t | s
t | r| jd}nt| trd| j }nd }|d u r%| j}tj	| ||dS )Nr3   
_BLOCKING_)target_module)
r8   isclass
isfunctionr7   rC   
isinstancer   rl   synchronizercreate_blocking)rL   r   blocking_namere   re   rf   synchronize_apiH  s   
r   RETRY_N_ATTEMPTS_OVERRIDE   Z   )
n_attempts
base_delaydelay_factortimeoutc                   s&    fdd}| dur|| S |S )a  Decorator that calls an async function multiple times, with a given timeout.

    If a `base_delay` is provided, the function is given an exponentially
    increasing delay on each run, up until the maximum number of attempts.

    Usage:

    ```
    @retry
    async def may_fail_default():
        # ...
        pass

    @retry(n_attempts=5, base_delay=1)
    async def may_fail_delay():
        # ...
        pass
    ```
    c                    s"   t   fdd}|S )Nc                     s   t d urt }n} }t|D ]l}t }ztj| i |dI d H W   S  tjy:   td d   tyo } z*||d krH td d| dt |  d| d	|| d  d
 W Y d }~nd }~ww t	|I d H  |9 }qd S )Nr   z	Function z was cancelledr   zFailed invoking function z: z (took zs, sleeping zs and trying z more times))
r   rangetimers   wait_forCancelledErrorr   debugr   sleep)argskwargslocal_n_attemptsdelayr^   t0e)r   r   fnr   r   re   rf   	f_wrappedn  s:   $


z+retry.<locals>.decorator.<locals>.f_wrapped)	functoolswraps)r   r   r   r   r   r   )r   rf   	decoratorm  s   zretry.<locals>.decoratorNre   )	direct_fnr   r   r   r   r   re   r   rf   retryX  s   r   c                	   @   s   e Zd ZU dZeej ed< d"dddee	 de	fdd	Z
d
d ZedefddZdd Zdd Zdd ZdejfddZ	d#dee	 de	dedejfddZededefd d!ZdS )$TaskContexta{  A structured group that helps manage stray tasks.

    This differs from the standard library `asyncio.TaskGroup` in that it *cancels* tasks still
    running after exiting the context manager, rather than waiting for them to finish.

    Arguments:
    `grace: float`: period in seconds, which will wait for a certain amount of time before cancelling
    all remaining tasks. This is useful for allowing tasks to finish after the context exits.

    `cancellation_grace: float = 1.0`: period in seconds that cancelled tasks are allowed to stall before
    they exit once they get cancelled (e.g. if they do async handling of the CancelledError). If tasks
    take longer than this to exit the tasks are left dangling when the context exits.

    Usage:

    ```python notest
    async with TaskContext(grace=1.0) as task_context:
        task = task_context.create_task(coro())
    ```
    _loopsNg      ?)cancellation_gracegracer   c                C   s   || _ || _t | _d S N)_grace_cancellation_gracesetr   )selfr   r   re   re   rf   __init__  s   zTaskContext.__init__c                    s   t  | _t | _d S r   )r   _tasksrs   Event_exitedr   re   re   rf   rD     s   zTaskContext.startr#   c                 C   
   | j  S r   )r   is_setr   re   re   rf   exited  s   
zTaskContext.exitedc                    s   |   I d H  | S r   )rD   r   re   re   rf   r%     s   zTaskContext.__aenter__c                    s|  | j   tdI dH  dd | jD }d}zz| jdur4|r4tj|ddi}tj|| jdI dH  W n
 tjy?   Y nw W |r^t	
tj |I dH  W d   n1 sYw   Y  g }| jD ])}| rt	
t |  W d   n1 s}w   Y  qc|  || qctj|ddi}ztj|| jdI dH  W n tjy   td|  Y nw tdI dH  dS |rt	
tj |I dH  W d   n1 sw   Y  g }| jD ]*}| rt	
t |  W d   n1 sw   Y  q|  || qtj|ddi}ztj|| jdI dH  W n tjy4   td|  Y nw tdI dH  w )	ag  This is called when exiting the TaskContext

        Two important properties that we need to maintain here:
        * Should never raise exceptions as a result
        of exceptions (incl. cancellations) in the contained tasks
        * Should not have an open-ended runtime, even if
        the contained tasks are uncooperative with cancellations.
        r   Nc                 S      g | ]}|  s|qS re   done.0tre   re   rf   
<listcomp>      z$TaskContext.stop.<locals>.<listcomp>return_exceptionsTr   z;Internal warning: Tasks did not cancel in a timely manner: )r   r   rs   r   r   r   gatherr   TimeoutError
contextlibsuppressr   r   BaseExceptionresultcancelr   r   r   r   )r   unfinished_tasksgather_futurecancelled_taskstaskcancellation_gatherre   re   rf   stop  sl   
	




zTaskContext.stopc                    sx   t |  }zt |I dH  W dS  t jy;   | s:tt j |I dH  W d    1 s5w   Y   w )a  
        This is a bit involved:
        * If there is an exception within the "context", we typically always want to reraise that
        * If a cancellation comes in *during* aexit/stop execution itself, we don't actually cancel
          the exit logic (it's already performing cancellation logic of sorts), but we do reraise
          the CancelledError to prevent muting cancellation chains
        N)rs   ensure_futurer   shieldr   r   r   r   )r   exc_typerN   tb	stop_taskre   re   rf   r     s   
zTaskContext.__aexit__c                 C   sb   t |tjr	|}nt|rt }||}n
tdt| d| j	| |
| jj |S )NzObject of type z is not a coroutine or Task)r   rs   Taskiscoroutineget_event_loopcreate_taskr   typer   addadd_done_callbackdiscard)r   coro_or_taskr   loopre   re   rf   r     s   
zTaskContext.create_taskr   
   Tr   r   log_exceptionc                    sn   t  tjr jjn jd fdd}| }| d j| |	jj
 |S )Nr#   c               
      s   t d  jsfztj  dI d H  W n/ tyH }  z#r3t| tjr3t d d nr>t 	d d W Y d } ~ nd } ~ ww ztjj
 dI d H  W n
 tjyb   Y q	w jrt d  d S )NzStarting infinite loop r   zLoop attempt for z
 timed outz failedzExiting infinite loop for )r   r   r   rs   r   r   r   r   error	exceptionr   wait)excasync_ffunction_namer   r   r   r   re   rf   	loop_coro  s&   z,TaskContext.infinite_loop.<locals>.loop_coroz loop)r#   N)r   r   partialfuncrm   r   set_namer   r   r   r   )r   r   r   r   r   r   r   re   r   rf   infinite_loop  s   
zTaskContext.infinite_loopcorosc               	      sb   t  4 I dH   fdd| D }tj| I dH W  d  I dH  S 1 I dH s*w   Y  dS )u  Wait for a sequence of coroutines to finish, concurrently.

        This is similar to `asyncio.gather()`, but it uses TaskContext to cancel all remaining tasks
        if one fails with an exception other than `asyncio.CancelledError`. The native `asyncio`
        function does not cancel remaining tasks in this case, which can lead to surprises.

        For example, if you use `asyncio.gather(t1, t2, t3)` and t2 raises an exception, then t1 and
        t3 would continue running. With `TaskContext.gather(t1, t2, t3)`, they are cancelled.

        It's still useful to use `asyncio.gather()` if you don't need cancellation — for
        example, if you're just gathering quick coroutines with no side-effects. Or if you're
        gathering the tasks with `return_exceptions=True`.

        Usage:

        ```python notest
        # Example 1: Await three coroutines
        created_object, other_work, new_plumbing = await TaskContext.gather(
            create_my_object(),
            do_some_other_work(),
            fix_plumbing(),
        )

        # Example 2: Gather a list of coroutines
        coros = [a.load() for a in objects]
        results = await TaskContext.gather(*coros)
        ```
        Nc                    s   g | ]}  |qS re   )r   )r   corotcre   rf   r   M      z&TaskContext.gather.<locals>.<listcomp>)r   rs   r   )r   tasksre   r   rf   r   .  s
   0zTaskContext.gatherr   )r   r   T)r7   rl   rm   rn   r   rs   r   rp   r   floatr   rD   propertyboolr   r%   r   r   r   r   staticmethodr   r	   r   re   re   re   rf   r     s.   
 1
!r   c                 C   sH   t jjdd}|tj| }| W  d   S 1 sw   Y  dS )a  Fairly hacky thing that's needed in some extreme cases.

    It's basically works like asyncio.run but unlike asyncio.run it also works
    with in the case an event loop is already running. It does this by basically
    moving the whole thing to a separate thread.
    r   )max_workersN)
concurrentfuturesThreadPoolExecutorsubmitrs   runr   )r   executorfutre   re   rf   run_coro_blockingQ  s   $r  c                   @   s   e Zd ZdZedZddefddZdd Zd	ed
e	e
df fddZde	e
df fddZdefddZdefddZdd Zdd ZdS )TimestampPriorityQueuezW
    A priority queue that schedules items to be processed at specific timestamps.
    infr   maxsizec                 C   s&   t  | _t j|d| _t | _d S )Nr  )rs   	Condition	conditionPriorityQueue_queue	itertoolsr5   _counter)r   r  re   re   rf   r   d  s   
zTimestampPriorityQueue.__init__c                    s   |  | jd I d H  d S r   )put_MAX_PRIORITYr   re   re   rf   closej  s   zTimestampPriorityQueue.close	timestampitemNc              	      sj   | j |t| j|fI dH  | j4 I dH  | j  W d  I dH  dS 1 I dH s.w   Y  dS )zS
        Add an item to the queue to be processed at a specific timestamp.
        N)r  r  nextr  r  
notify_all)r   r  r  re   re   rf   r  m  s
   .zTimestampPriorityQueue.putr#   c              	      s   	 | j 4 I dH { |  r| j  I dH  |  s| j I dH \}}}t }||k r9|W  d  I dH  S || jkrJ	 W d  I dH  dS || }| j|||f ztj	| j  |dI dH  W n tj
yy   Y W d  I dH  qw W d  I dH  n1 I dH sw   Y  q)zQ
        Get the next item from the queue that is ready to be processed.
        TNr   )r  emptyr   r  r   r   r  
put_nowaitrs   r   r   )r   r  counterr  now
sleep_timere   re   rf   r   u  s2   
(zTimestampPriorityQueue.getc                 C   r   r   )r  r"  r   re   re   rf   r"       
zTimestampPriorityQueue.emptyc                 C   r   r   r  qsizer   re   re   rf   r)    r'  zTimestampPriorityQueue.qsizec                    s(   |   s|  I dH  |   rdS dS )z
        Clear the retry queue. Used for testing to simulate reading all elements from queue using queue_batch_iterator.
        N)r"  r   r   re   re   rf   clear  s   zTimestampPriorityQueue.clearc                 C   r   r   r(  r   re   re   rf   __len__  r'  zTimestampPriorityQueue.__len__)r   )r7   rl   rm   rn   r  r  rq   r   r  r   r   r  r   r  r"  r)  r*  r+  re   re   re   rf   r  ]  s    r  d   Q?qc                 C  s   g }	 |   rt|dkr|V  g }t|I dH  |  I dH }t||kr-|V  g }|du r<t|dkr:|V  dS || q)zx
    Read from a queue but return lists of items when queue is large

    Treats a None value as end of queue items
    Tr   N)r"  rA   rs   r   r   r   )r.  max_batch_sizedebounce_time	item_listresre   re   rf   queue_batch_iterator  s"   
r3  c                   @   sR   e Zd ZdefddZdd Zdd Zdd	 Zd
d Zdd Z	dd Z
dd ZdS )_WarnIfGeneratorIsNotConsumedr   c                 C   s"   || _ || _d| _d| _|| _d S )NF)genr   iteratedwarned__wrapped__)r   r5  r   re   re   rf   r     s
   
z&_WarnIfGeneratorIsNotConsumed.__init__c                 C      d| _ | j S NT)r6  r5  r$   r   re   re   rf   r$        
z'_WarnIfGeneratorIsNotConsumed.__aiter__c                    s   d| _ | j I d H S r:  )r6  r5  r   r   re   re   rf   r     s   z'_WarnIfGeneratorIsNotConsumed.__anext__c                    s   d| _ | j|I d H S r:  )r6  r5  asendr   rN   re   re   rf   r<    s   z#_WarnIfGeneratorIsNotConsumed.asendc                 C   s
   t | jS r   )reprr5  r   re   re   rf   __repr__  r'  z&_WarnIfGeneratorIsNotConsumed.__repr__c                 C   s:   | j s| jsd| _td| j d| j d d S d S d S )NTz"Warning: the results of a call to zZ was not consumed, so the call will never be executed. Consider a for-loop like `for x in z3(...)` or unpacking the generator using `list(...)`)r6  r7  r   warningr   r   re   re   rf   __del__  s   
z%_WarnIfGeneratorIsNotConsumed.__del__c                    s   | j |I d H S r   )r5  athrow)r   r   re   re   rf   rB       z$_WarnIfGeneratorIsNotConsumed.athrowc                    s   | j  I d H S r   )r5  acloser   re   re   rf   rD    s   z$_WarnIfGeneratorIsNotConsumed.acloseN)r7   rl   rm   ro   r   r$   r   r<  r?  rA  rB  rD  re   re   re   rf   r4    s    
r4  c                   @   s$   e Zd Zdd Zdd Zdd ZdS )'_WarnIfNonWrappedGeneratorIsNotConsumedc                 C   s   d| _ t| jS r:  )r6  iterr5  r   re   re   rf   __iter__  r;  z0_WarnIfNonWrappedGeneratorIsNotConsumed.__iter__c                 C   r9  r:  )r6  r5  __next__r   re   re   rf   rH    r;  z0_WarnIfNonWrappedGeneratorIsNotConsumed.__next__c                 C   s   d| _ | j|S r:  )r6  r5  sendr=  re   re   rf   rI    s   z,_WarnIfNonWrappedGeneratorIsNotConsumed.sendN)r7   rl   rm   rG  rH  rI  re   re   re   rf   rE    s    rE  r   c                    s    fdd}|S )Nc                    s.   d urn j t  fdd}|S )Nc                     s,    | i |}t |rt|S t|S r   )r8   
isasyncgenr4  rE  )r   r   r5  gen_fpresented_func_namere   rf   r     s   


zGwarn_if_generator_is_not_consumed.<locals>.decorator.<locals>.f_wrapped)r7   r   r   )rL  r   r   rK  rf   r     s   z4warn_if_generator_is_not_consumed.<locals>.decoratorre   )r   r   re   rN  rf   !warn_if_generator_is_not_consumed  s   rO  r   nested_async_messagec                 C   sP   zt  }|| W  d   W S 1 sw   Y  W dS  ty'   t|w )a  Compatibility function to run an async coroutine in a temporary event loop.

    This is needed for compatibility with the async implementation of Function.spawn_map. The future plan is
    to have separate implementations so there is no issue with nested event loops.
    N)r   r  r   r   )r   rP  runnerre   re   rf   %run_coroutine_in_temporary_event_loop  s   (rR  c                   @   s@   e Zd ZdZdejedf fddZdd Zdd	 Z	d
d Z
dS )AsyncOrSyncIterablea  Compatibility class for non-synchronicity wrapped async iterables to get
    both async and sync interfaces in the same way that synchronicity does (but on the main thread)
    so they can be "lazily" iterated using either `for _ in x` or `async for _ in x`

    nested_async_message is raised as an InvalidError if the async variant is called
    from an already async context, since that would otherwise deadlock the event loop
    async_iterableNc                 C   s   || _ || _d S r   )_async_iterablerP  )r   rT  rP  re   re   rf   r     r;  zAsyncOrSyncIterable.__init__c                 C   s   | j S r   )rU  r   re   re   rf   r$   !  s   zAsyncOrSyncIterable.__aiter__c                 c   s^    z!t  }t|| jE d H  W d    W d S 1 sw   Y  W d S  ty.   t| jw r   )r   run_async_genrU  r   r   rP  )r   rQ  re   re   rf   rG  $  s   &
zAsyncOrSyncIterable.__iter__c                    s&   t | jdr| j I d H  d S d S )NrD  )hasattrrU  rD  r   re   re   rf   rD  +  s   zAsyncOrSyncIterable.aclose)r7   rl   rm   rn   typingr   r	   r   r$   rG  rD  re   re   re   rf   rS    s    rS  c                    s"    fdd}t t|  d S )Nc                      s.   zt dI d H  W  I d H    I d H   )Ng    _B)rs   r   re   r   re   rf   wrapper5  s   

zon_shutdown.<locals>.wrapper)_shutdown_tasksr   rs   r   )r   rZ  re   rY  rf   on_shutdown3  s   r\  fc                    s(   t  dtjdtjf fdd}|S )zNConvert a blocking function into one that runs in the current loop's executor.r   r   c                     s0   t  }|d tj g| R i |I d H S r   )rs   get_running_looprun_in_executorr   r   )r   r   r   r]  re   rf   rZ  B  s   &zasyncify.<locals>.wrapper)r   r   r   r   r   )r]  rZ  re   r`  rf   asyncify?  s   ra  iteratorc                 C  sB   t  }t }	 |dt| |I dH }||u rdS tt|V  q	)z5Iterate over a blocking iterator in an async context.TN)rs   r^  objectr_  r   r   r   )rb  r   DONErL   re   re   rf   iterate_blockingJ  s   re  c                  O  s   dV  dS )zAsync noop context manager.

    Note that for Python 3.10+ you can use contextlib.nullcontext() instead.

    Usage:
    async with asyncnullcontext():
        pass
    Nre   r   r   re   re   rf   asyncnullcontextV  s   

rg  
YIELD_TYPE	SEND_TYPErQ  r5  c              
   c   s    d}d}	 z|r|  ||}n|  ||}W n ty* } z|dd}~w ty3   Y dS w z|V }d}W n tyO } z|}W Y d}~nd}~ww q)z*Convert an async generator into a sync oneN)r  rB  r<  KeyboardInterruptStopAsyncIterationr   )rQ  r5  	next_sendr   
next_yieldr   errre   re   rf   rV  g  s.   rV  c                   @   s@   e Zd Zdeedf fddZdeedf fddZdd	 ZdS )
aclosingagenNc                 C   s
   || _ d S r   rp  )r   rp  re   re   rf   r     r'  zaclosing.__init__r#   c                    s   | j S r   rq  r   re   re   rf   r%     s   zaclosing.__aenter__c                    s   | j  I d H  d S r   )rp  rD  )r   r   r   r   re   re   rf   r     s   zaclosing.__aexit__)r7   rl   rm   r   r   r   r%   r   re   re   re   rf   ro    s    ro  rF  c              	   C  s   t | dr=tttd f | }z|2 z	3 d H W }|V  q6 W t |dr-| I d H  d S d S t |dr<| I d H  w w t | dsFJ dttt | D ]}|V  qNd S )Nr$   rD  rG  z9sync_or_async_iter requires an Iterable or AsyncGenerator)rW  rX  r   r   r   rD  r   )rF  rp  r  re   re   rf   sync_or_async_iter  s    

rr  g1g2c                C      d S r   re   )rs  rt  re   re   rf   	async_zip     rv  
generators.c                  G   ru  r   re   )rx  re   re   rf   rv    rw  c                    s  g }z	 zdd   fdd| D }t j| I d H }t|V  W n	 ty*   Y nw qW g }|D ]}| s@|  || q1z
t j| I d H  W n
 t jyU   Y nw d }| D ]+}z	| I d H  W qZ t	y } z|d u rs|}t
d|  W Y d }~qZd }~ww |d ur|d S g }|D ]}| s|  || qz
t j| I d H  W n
 t jy   Y nw d }| D ]+}z	| I d H  W q t	y } z|d u r|}t
d|  W Y d }~qd }~ww |d ur|w )NTc                    s   |   I d H S r   )r   )r5  re   re   rf   	next_item  s   zasync_zip.<locals>.next_itemc                    s   g | ]	}t  |qS re   rs   r   r   r5  ry  re   rf   r         zasync_zip.<locals>.<listcomp>Error closing async generator: )rs   r   tuplerk  r   r   r   r   rD  r   r   r   )rx  r  itemsr   r   first_exceptionr5  r   re   r|  rf   rv    sz   


c                   @      e Zd ZU eed< dS )ValueWrapperrN   N)r7   rl   rm   r   rp   re   re   re   rf   r       
 r  c                   @   r  )ExceptionWrapperrN   N)r7   rl   rm   r   rp   re   re   re   rf   r    r  r  c                   @   s   e Zd ZdS )StopSentinelTypeN)r7   rl   rm   re   re   re   rf   r    s    r        $@)cancellation_timeoutr  c                   s  t jt|d ddttdf ffdd  fdd|D }t  }z|rqt jg ||t jd	I dH \}}||v r]|	 }t
|trN|jV  nt|t |jt  }||@ }||8 }|D ]}|I dH  qg|s+ s|I dH }t
|tr|jV  nt|t |jt  } ruW d
d ||hB D }	|	D ]}
|
  qzt jt t j|	ddi| dI dH  W dS  t jy   td Y dS w d
d ||hB D }	|	D ]}
|
  qzt jt t j|	ddi| dI dH  W w  t jy
   td Y w w )a  
    Asynchronously merges multiple async generators into a single async generator.

    This function takes multiple async generators and yields their values in the order
    they are produced. If any generator raises an exception, the exception is propagated.

    Args:
        *generators: One or more async generators to be merged.

    Yields:
        The values produced by the input async generators.

    Raises:
        Exception: If any of the input generators raises an exception, it is propagated.

    Usage:
    ```python
    import asyncio
    from modal._utils.async_utils import async_merge

    async def gen1():
        yield 1
        yield 2

    async def gen2():
        yield "a"
        yield "b"

    async def example():
        values = set()
        async for value in async_merge(gen1(), gen2()):
            values.add(value)

        return values

    # Output could be: {1, "a", 2, "b"} (order may vary)
    values = asyncio.run(example())
    assert values == {1, "a", 2, "b"}
    ```
    r   r  	generatorNc              
      s   z7t | 4 I d H !}|2 z3 d H W } t|I d H  q6 W d   I d H  W d S 1 I d H s1w   Y  W d S  tyU } z t|I d H  W Y d }~d S d }~ww r   )ro  r  r  r   r  )r  streamr  r   )queuere   rf   producer  s   2"zasync_merge.<locals>.producerc                    s   h | ]	}t  |qS re   rz  r{  )r  re   rf   	<setcomp>  r}  zasync_merge.<locals>.<setcomp>)return_whenc                 S   r   re   r   r   re   re   rf   r   9  r   zasync_merge.<locals>.<listcomp>r   Tr   z'Timed out while cleaning up async_merge)rs   QueuerA   r   r   r   r   r   FIRST_COMPLETEDr   r   r  rN   r   r  r"  r   r   r   r   r   r   r   )r  rx  r  new_output_taskr   r3   r  finished_producersfinished_producerr   r   re   )r  r  rf   async_merge  st   +









r  	awaitablec                 C  s   |  I d H V  d S r   re   )r  re   re   rf   callable_to_agenI  rC  r  c                     s\   dd | D }z	t j| I d H W S  ty-   |D ]}|  qt j|ddiI d H   w )Nc                 S   s   g | ]}t |qS re   )rs   r   r   re   re   rf   r   N  r  z(gather_cancel_on_exc.<locals>.<listcomp>r   F)rs   r   r   r   )coros_or_futuresinput_tasksr   re   re   rf   gather_cancel_on_excM  s   
r  c                    sN   t | }z	t |I d H W S  t jy&   | r |  |I d H   w r   )rs   r   r   r   	cancelledr   )r   r   re   re   rf   prevent_cancellation_abortionX  s   

r  input_generatorasync_mapper_funcconcurrencyc              	     s   t jd ddttd f ffdd}dttd f f fddttg fdd	tD | R d
|i4 I d H }|2 z	3 d H W }|V  qE6 W d   I d H  d S 1 I d H saw   Y  d S )Nr   r  r#   c               	     s   t 4 I d H } | 2 z3 d H W }t|I d H  q6 W d   I d H  n1 I d H s.w   Y  t D ]
}tI d H  q7d S r   )ro  r  r  r   STOP_SENTINEL)r  r  r3   )r  r  r  re   rf   r  p  s   (zasync_map.<locals>.producerc                    sX   	   I d H } t| trt | jI d H }|V  nt| tr$| jt| t d S qr   )r   r   r  r  rN   r  r   r  )r  r2  )r  r  re   rf   worker}  s   


zasync_map.<locals>.workerc                    s   g | ]}  qS re   re   )r   r^   )r  re   rf   r     s    zasync_map.<locals>.<listcomp>r  )rs   r  r   r   ro  r  r   )r  r  r  r  r  r  r  re   )r  r  r  r  r  rf   	async_maph  s   *.r  buffer_sizec              	     s   t |p|dtttf dtttf f fdd}dttd f ffdd}d}i }ttt	| | ||4 I d H 4}|2 z$3 d H W \}	}
|
||	< ||v rc|| V  
  ||= |d7 }||v sOq?6 W d   I d H  d S 1 I d H svw   Y  d S )	Ntupr#   c                    s   | d  | d I d H fS )Nr   r   re   )r  )r  re   rf   mapper_func_wrapper  s   z.async_map_ordered.<locals>.mapper_func_wrapperc                    s(   t  D ]}   I d H  | V  qd S r   )r  r5   acquire)r^   )	semaphorere   rf   r$    s
   z"async_map_ordered.<locals>.counterr   r   )rs   	Semaphorer  rq   r   r   r   ro  r  rv  release)r  r  r  r  r  r$  next_idxbufferr  
output_idxoutput_itemre   )r  r  rf   async_map_ordered  s$   &"
.r  c                  G  s  zK| D ]}|2 z	3 d H W }|V  q6 qW d }| D ]+}z	|  I d H  W q tyD } z|d u r2|}td|  W Y d }~qd }~ww |d urK|d S d }| D ]+}z	|  I d H  W qQ ty| } z|d u rj|}td|  W Y d }~qQd }~ww |d ur|w )Nr~  )rD  r   r   r   )rx  r5  r  r  r   re   re   rf   async_chain  s@   r  deprecation_datereadable_sync_callc                    s    fdd}|S )Nc                    s:   t  tr j d}nd} fdd}t |t|dS )NTFc                     s(   t  d d d | i |S )NzThe async constructor zV.aio(...) will be deprecated in a future version of Modal.
                Please use zY(...) instead (it doesn't perform any IO, and is safe in async contexts)
                r   rf  )r  r  sync_implementationre   rf   _async_proxy  s   z7deprecate_aio_usage.<locals>.deco.<locals>._async_proxy)is_classmethod)r   classmethod__func__r   r   )r  r  r  r  r  )r  rf   deco  s   
	z!deprecate_aio_usage.<locals>.decore   )r  r  r  re   r  rf   deprecate_aio_usage  s   r  r   )r,  r-  )r  )krs   concurrent.futuresr	  r   r   r8   r  ru   sysr   typesrX  r   collections.abcr   r   r   r   r   r   dataclassesr   r	   r
   r   r   r   r   r   rr   synchronicity.async_utilsr   synchronicity.combined_typesr   synchronicity.exceptionsr   typing_extensionsr   r   modal._ipythonr   modal._utils.deprecationr   r   r   r   r   r   r   r   platformset_event_loop_policyWindowsSelectorEventLoopPolicyro   r  r  rg   rh   r~   FunctionTyper   r   Synchronizerr   r   r   rq   rp   r   r   r  r  r  r3  r4  %_BlockingWarnIfGeneratorIsNotConsumedrE  rO  	CoroutinerR  rS  r[  r\  ra  re  rg  rh  ri  	GeneratorrV  ro  rr  overloadrv  r  r  r  r  r  r  r  r  r  r  r  r  r  re   re   re   rf   <module>   s   
$

 #:
: @B
(".



*8,%


&k


,


" 