o
    ;i!                     @   s   d dl Z d dlZd dlZd dlZd dlmZmZ d dlmZ d dlm	Z	m
Z
 d dlZd dlmZmZ d dlmZ ejrAd dlmZ dd	 ZG d
d deZG dd dZG dd dZdS )    N)Counterdefaultdict)	Awaitable)AnyCallable)	GRPCErrorStatus)logger)MockClientServicerc                    s   dt f fdd}tjdd }| _d _dd }t D ]-}t |}t|d	d
r7t ||||| q|d  rKt	
|rKt |||| qt  _ S )a  Adds an `.intercept()` context manager method

    This allows for context-local tracking and assertions of all calls
    performed on the servicer during a context, e.g.:

    ```python notest
    with servicer.intercept() as ctx:
        await some_complex_method()
    assert ctx.calls == [("SomeMethod", MyMessage(foo="bar"))]
    ```
    Also allows to set a predefined queue of responses, temporarily replacing
    a mock servicer's default responses for a method:

    ```python notest
    with servicer.intercept() as ctx:
        ctx.add_response("SomeMethod", [
            MyResponse(bar="baz")
        ])
        ctx.add_response("SomeMethod", [
            MyResponse(bar="baz2")
        ])
        await service_stub.SomeMethod(Empty())  # receives MyResponse(bar="baz")
        await service_stub.SomeMethod(Empty())  # receives MyResponse(bar="baz2")
    ```

    Also patches all unimplemented abstract methods in a mock servicer with default error implementations.
    namec                    s   d fdd}|S )Nreturnc                    s   t tj dt  )Nz" not implemented in mock servicer )r   r   UNIMPLEMENTEDrepr)selfstream)clsr    M/home/ubuntu/.local/lib/python3.10/site-packages/modal/_utils/grpc_testing.py	_fallback1   s   z8patch_mock_servicer.<locals>.fallback.<locals>._fallback)r   Nr   )r   r   r   )r   r   fallback0   s   z%patch_mock_servicer.<locals>.fallbackc                 s   s(    t | }|| _|V  |  d | _d S N)InterceptionContextinterception_context_assert_responses_consumed)servicerctxr   r   r   	intercept6   s   
z&patch_mock_servicer.<locals>.interceptNc                    s    fdd}|S )Nc                    s   z4| j }|r-t| | I d H }| |j}|r$|| |I d H W S | |I d H W S | |I d H W S  ty=     tyI   td  w )Nz!Error in mock servicer responder:)	r   InterceptedStream
initialize_next_custom_responderrequest_messager   	Exceptionr	   	exception)servicer_selfr   r   intercepted_streamcustom_respondermethod_nameoriginal_methodr   r   patched_methodB   s    
zFpatch_mock_servicer.<locals>.patch_grpc_method.<locals>.patched_methodr   )r(   r)   r*   r   r'   r   patch_grpc_methodA   s   z.patch_mock_servicer.<locals>.patch_grpc_method__isabstractmethod__Fr   )str
contextlibcontextmanagerr   r   dirgetattrsetattrisupperinspect
isfunction	frozenset__abstractmethods__)r   r   r   r+   r   methodr   r   r   patch_mock_servicer   s   

r9   c                       s&   e Zd Zdee f fddZ  ZS )ResponseNotConsumedunconsumed_requestsc                    s$   || _ t|}t d|  d S )Nz5Expected but did not receive the following requests: )r;   r   super__init__)r   r;   request_count	__class__r   r   r=   d   s   zResponseNotConsumed.__init__)__name__
__module____qualname__listr-   r=   __classcell__r   r   r?   r   r:   c   s    r:   c                   @   s   e Zd Zdd Zdd ddedeegef fdd	Zded
ede	j
jged f fddZdd Zdedee fddZdefddZdd Zdd ZdS )r   c                 C   s    || _ g | _tt| _i | _d S r   )	_servicercallsr   rD   custom_responsescustom_defaults)r   r   r   r   r   r=   k   s   

zInterceptionContext.__init__c                 C   s   dS )NTr   )reqr   r   r   <lambda>r   s    zInterceptionContext.<lambda>)request_filterr(   rL   c                C   s   | j | ||gf dS )aN  Adds one response payload to an expected queue of responses for a method.

        These responses will be used once each instead of calling the MockServicer's
        implementation of the method.

        The interception context will throw an exception on exit if not all of the added
        responses have been consumed.
        N)rH   append)r   r(   first_payloadrL   r   r   r   add_responseq   s   z InterceptionContext.add_response	responderr
   Nc                 C   s   || j |< dS )a  Replace the default responder from the MockClientServicer with a custom implementation

        ```python notest
        def custom_responder(servicer, stream):
            request = stream.recv_message()
            await stream.send_message(api_pb2.SomeMethodResponse(foo=123))

        with servicer.intercept() as ctx:
            ctx.set_responder("SomeMethod", custom_responder)
        ```

        Responses added via `.add_response()` take precedence over the use of this replacement
        N)rI   )r   r(   rP   r   r   r   set_responder~   s   z!InterceptionContext.set_responderc                 C   sL   t | jD ]\}\}}||kr| j|d d  | _|  S qtd| j )N   z&No message of that type in call list: )	enumeraterG   KeyError)r   r(   i_method_namemsgr   r   r   pop_request   s   zInterceptionContext.pop_requestr   c                    s.   t | j st  d fdd| jD S )Nz. not in MockServicer - did you spell it right?c                    s   g | ]
\}}| kr|qS r   r   ).0rV   rW   r(   r   r   
<listcomp>   s    z4InterceptionContext.get_requests.<locals>.<listcomp>)hasattrrF   
ValueErrorrG   )r   r(   r   rZ   r   get_requests   s   z InterceptionContext.get_requestsc                 C   s   | j ||f d S r   )rG   rM   )r   r(   rW   r   r   r   	_add_recv   s   zInterceptionContext._add_recvc           
   	      s   | j | }t|D ]1\}\}}z||}W n ty"   td  w |r:| |d | ||d d   | j |<  nq	| j|}|sEd S |S  fdd}	|	S )NzError when filtering requestsrR   c                    s.   |  I d H   D ]
}||I d H  q
d S r   )recv_messagesend_message)r$   r   rW   next_response_messagesr   r   rP      s
   z=InterceptionContext._next_custom_responder.<locals>.responder)rH   rS   r"   loggingr#   rI   get)
r   r(   requestmethod_responsesrU   rL   response_messagesrequest_matchescustom_defaultrP   r   rb   r   r       s&   

"z*InterceptionContext._next_custom_responderc                 C   s:   g }| j  D ]\}}||gt| 7 }q|rt|d S r   )rH   itemslenr:   )r   
unconsumedr(   queued_responsesr   r   r   r      s   z.InterceptionContext._assert_responses_consumed)rA   rB   rC   r=   r-   r   r   boolrO   grpclibserverStreamr   rQ   rX   rD   r^   r_   r    r   r   r   r   r   r   j   s$    

r   c                   @   s>   e Zd ZdedefddZdd Zdd Zd	d
 Zdd Z	dS )r   r   r(   c                 C   s   || _ || _|| _d | _d S r   )r   r(   r   r!   )r   r   r(   r   r   r   r   r=      s   
zInterceptedStream.__init__c                    s   |   I d H | _| S r   )r`   r!   )r   r   r   r   r      s   zInterceptedStream.initializec                    s<   | j r| j }d | _ |S | j I d H }| j| j| |S r   )r!   r   r`   r   r_   r(   )r   retrW   r   r   r   r`      s   zInterceptedStream.recv_messagec                    s   | j |I d H  d S r   )r   ra   )r   rW   r   r   r   ra      s   zInterceptedStream.send_messagec                 C   s   t | j|S r   )r1   r   )r   attrr   r   r   __getattr__   s   zInterceptedStream.__getattr__N)
rA   rB   rC   r   r-   r=   r   r`   ra   ru   r   r   r   r   r      s    
r   )r.   r4   rd   typingcollectionsr   r   collections.abcr   r   r   grpclib.serverrp   r   r   modal.configr	   TYPE_CHECKINGtest.conftestr
   r9   r"   r:   r   r   r   r   r   r   <module>   s    P^