U
    9%e9                     @   s  U d Z ddlZddlZddlZddlmZ ddlmZmZm	Z	m
Z
 ddlmZmZ er^ddlZddlmZmZ ddlmZ dae
d	 ed
< eddkZeG dd	 d	Zede
e edddZedddZe
e ddddZeeedddZeeedddZdS )zSContains `WebhooksServer` and `webhook_endpoint` to create a webhook server easily.    N)wraps)TYPE_CHECKINGCallableDictOptional   )experimentalis_gradio_available)FastAPIRequest)JSONResponseWebhooksServer_global_appZSYSTEMspacesc                       st   e Zd ZdZd d fddZded ee dddd	Zdee ed
ddZ	ddddZ
ddddZ  ZS )r   a<  
    The [`WebhooksServer`] class lets you create an instance of a Gradio app that can receive Huggingface webhooks.
    These webhooks can be registered using the [`~WebhooksServer.add_webhook`] decorator. Webhook endpoints are added to
    the app as a POST endpoint to the FastAPI router. Once all the webhooks are registered, the `run` method has to be
    called to start the app.

    It is recommended to accept [`WebhookPayload`] as the first argument of the webhook function. It is a Pydantic
    model that contains all the information about the webhook event. The data will be parsed automatically for you.

    Check out the [webhooks guide](../guides/webhooks_server) for a step-by-step tutorial on how to setup your
    WebhooksServer and deploy it on a Space.

    <Tip warning={true}>

    `WebhooksServer` is experimental. Its API is subject to change in the future.

    </Tip>

    <Tip warning={true}>

    You must have `gradio` installed to use `WebhooksServer` (`pip install --upgrade gradio`).

    </Tip>

    Args:
        ui (`gradio.Blocks`, optional):
            A Gradio UI instance to be used as the Space landing page. If `None`, a UI displaying instructions
            about the configured webhooks is created.
        webhook_secret (`str`, optional):
            A secret key to verify incoming webhook requests. You can set this value to any secret you want as long as
            you also configure it in your [webhooks settings panel](https://huggingface.co/settings/webhooks). You
            can also set this value as the `WEBHOOK_SECRET` environment variable. If no secret is provided, the
            webhook endpoints are opened without any security.

    Example:

        ```python
        import gradio as gr
        from huggingface_hub import WebhooksServer, WebhookPayload

        with gr.Blocks() as ui:
            ...

        app = WebhooksServer(ui=ui, webhook_secret="my_secret_key")

        @app.add_webhook("/say_hello")
        async def hello(payload: WebhookPayload):
            return {"message": "hello"}

        app.run()
        ```
    returnc                    s   t  stdt | S )NzjYou must have `gradio` installed to use `WebhooksServer`. Please run `pip install --upgrade gradio` first.)r	   ImportErrorsuper__new__)clsargskwargs	__class__ _/var/www/html/Darija-Ai-API/env/lib/python3.8/site-packages/huggingface_hub/_webhooks_server.pyr   \   s
    zWebhooksServer.__new__Nz	gr.Blocks)uiwebhook_secretr   c                 C   s*   || _ |ptd| _i | _t| j d S )NZWEBHOOK_SECRET)_uiosgetenvr   registered_webhooks_warn_on_empty_secret)selfr   r   r   r   r   __init__d   s    zWebhooksServer.__init__pathr   c                    s0   t  r  S ttj fdd}|S )au  
        Decorator to add a webhook to the [`WebhooksServer`] server.

        Args:
            path (`str`, optional):
                The URL path to register the webhook function. If not provided, the function name will be used as the
                path. In any case, all webhooks are registered under `/webhooks`.

        Raises:
            ValueError: If the provided path is already registered as a webhook.

        Example:
            ```python
            from huggingface_hub import WebhooksServer, WebhookPayload

            app = WebhooksServer()

            @app.add_webhook
            async def trigger_training(payload: WebhookPayload):
                if payload.repo.type == "dataset" and payload.event.action == "update":
                    # Trigger a training job if a dataset is updated
                    ...

            app.run()
        ```
        c                     sF   | d }d p|j d }|jkr8td| d|j|< d S )Nr   z
/webhooks//zWebhook z already exists.)__name__stripr!   
ValueError)r   r   funcabs_pathr&   r#   r   r   _inner_post   s
    
z/WebhooksServer.add_webhook.<locals>._inner_post)callableadd_webhookr   r
   post)r#   r&   r.   r   r-   r   r0   o   s
    zWebhooksServer.add_webhookc                    s   | j p|  }|jdtd\| _}}| j D ]0\}}| jdk	rNt|| jd}| j	|| q.|j
pj|jd d}|dd fdd	| jD  7 }|d
7 }t| |  dS )zIStarts the Gradio app with the FastAPI server and registers the webhooks.T)Zprevent_thread_lockshareNr   r'   z/
Webhooks are correctly setup and ready to use:
c                 3   s   | ]}d   | V  qdS )z	  - POST Nr   ).0webhookurlr   r   	<genexpr>   s     z%WebhooksServer.run.<locals>.<genexpr>zG
Go to https://huggingface.co/settings/webhooks to setup your webhooks.)r   _get_default_uiZlaunch	_is_localZfastapi_appr!   itemsr   _wrap_webhook_to_check_secretr1   Z	share_urlZ	local_urlr)   joinprintZblock_thread)r#   r   _r&   r+   messager   r7   r   run   s    
"zWebhooksServer.runc              	   C   sz   ddl }| `}|d |d |t| j dd ddd	 | j D   |trfd
nd W 5 Q R X |S )zLDefault UI if not provided (lists webhooks and provides basic instructions).r   Nu)   # This is an app to process 🤗 WebhooksaT  Webhooks are a foundation for MLOps-related features. They allow you to listen for new changes on specific repos or to all repos belonging to particular set of users/organizations (not just your repos, but any repo). Check out this [guide](https://huggingface.co/docs/hub/webhooks) to get to know more about webhooks on the Huggingface Hub.z webhook(s) are registered:z

z
 c                 s   s,   | ]$\}}d | dt |j| dV  qdS )z- [z]()N)_get_webhook_doc_urlr(   )r5   webhook_pathr6   r   r   r   r9      s   z1WebhooksServer._get_default_ui.<locals>.<genexpr>zGo to https://huggingface.co/settings/webhooks to setup your webhooks.
You app is running locally. Please look at the logs to check the full URL you need to set.z
This app is running on a Space. You can find the corresponding URL in the options menu (top-right) > 'Embed the Space'. The URL looks like 'https://{username}-{repo_name}.hf.space'.)gradioZBlocksMarkdownlenr!   r>   r<   r;   )r#   grr   r   r   r   r:      s*    


	zWebhooksServer._get_default_ui)NN)N)r(   
__module____qualname____doc__r   r   strr$   r   r0   rB   r:   __classcell__r   r   r   r   r   %   s   5
  +r%   c                    s4   t  rt  S ttjttd fdd}|S )a  Decorator to start a [`WebhooksServer`] and register the decorated function as a webhook endpoint.

    This is a helper to get started quickly. If you need more flexibility (custom landing page or webhook secret),
    you can use [`WebhooksServer`] directly. You can register multiple webhook endpoints (to the same server) by using
    this decorator multiple times.

    Check out the [webhooks guide](../guides/webhooks_server) for a step-by-step tutorial on how to setup your
    server and deploy it on a Space.

    <Tip warning={true}>

    `webhook_endpoint` is experimental. Its API is subject to change in the future.

    </Tip>

    <Tip warning={true}>

    You must have `gradio` installed to use `webhook_endpoint` (`pip install --upgrade gradio`).

    </Tip>

    Args:
        path (`str`, optional):
            The URL path to register the webhook function. If not provided, the function name will be used as the path.
            In any case, all webhooks are registered under `/webhooks`.

    Examples:
        The default usage is to register a function as a webhook endpoint. The function name will be used as the path.
        The server will be started automatically at exit (i.e. at the end of the script).

        ```python
        from huggingface_hub import webhook_endpoint, WebhookPayload

        @webhook_endpoint
        async def trigger_training(payload: WebhookPayload):
            if payload.repo.type == "dataset" and payload.event.action == "update":
                # Trigger a training job if a dataset is updated
                ...

        # Server is automatically started at the end of the script.
        ```

        Advanced usage: register a function as a webhook endpoint and start the server manually. This is useful if you
        are running it in a notebook.

        ```python
        from huggingface_hub import webhook_endpoint, WebhookPayload

        @webhook_endpoint
        async def trigger_training(payload: WebhookPayload):
            if payload.repo.type == "dataset" and payload.event.action == "update":
                # Trigger a training job if a dataset is updated
                ...

        # Start the server manually
        trigger_training.run()
        ```
    )r+   r   c                    sN   t    |  t jdkr.t j t j fdd}|| _| S )Nr   c                      s   t  j    d S N)atexit
unregisterrB   r   Zappr   r   _run_now  s    z2webhook_endpoint.<locals>._inner.<locals>._run_now)_get_global_appr0   rH   r!   rP   registerrB   r   )r+   rS   r&   rR   r   _inner  s    z webhook_endpoint.<locals>._inner)r/   webhook_endpointr   r   r0   r   )r&   rW   r   rV   r   rX      s
    <
rX   r   c                   C   s   t d krt a t S rO   )r   r   r   r   r   r   rT   )  s    rT   )r   r   c                 C   s.   | d kr"t d t d t d nt d d S )NzZWebhook secret is not defined. This means your webhook endpoints will be open to everyone.zTo add a secret, set `WEBHOOK_SECRET` as environment variable or pass it at initialization: 
	`app = WebhooksServer(webhook_secret='my_secret', ...)`zpFor more details about webhook secrets, please refer to https://huggingface.co/docs/hub/webhooks#webhook-secret.z$Webhook secret is correctly defined.)r?   r3   r   r   r   r"   0  s    r"   )webhook_namerE   r   c                 C   s   d|  | dd d S )z@Returns the anchor to a given webhook in the docs (experimental)z/docs#/default/r'   r@   _post)replace)rY   rE   r   r   r   rD   ?  s    rD   )r+   r   r   c                    sd   t  t td fdd}djkr`jt jdt jjtdftj	  d|_
|S )a  Wraps a webhook function to check the webhook secret before calling the function.

    This is a hacky way to add the `request` parameter to the function signature. Since FastAPI based itself on route
    parameters to inject the values to the function, we need to hack the function signature to retrieve the `Request`
    object (and hence the headers). A far cleaner solution would be to use a middleware. However, since
    `fastapi==0.90.1`, a middleware cannot be added once the app has started. And since the FastAPI app is started by
    Gradio internals (and not by us), we cannot add a middleware.

    This method is called only when a secret has been defined by the user. If a request is sent without the
    "x-webhook-secret", the function will return a 401 error (unauthorized). If the header is sent but is incorrect,
    the function will return a 403 error (forbidden).

    Inspired by https://stackoverflow.com/a/33112180.
    )requestc                    sv   | j d}|d kr$tddiddS |kr<tddiddS djkrN| |d< t rh f |I d H S  f |S d S )	Nzx-webhook-secreterrorz x-webhook-secret header not set.i  )status_codezInvalid webhook secret.i  r\   )headersgetr   
parametersinspectiscoroutinefunction)r\   r   Zrequest_secretr+   Zinitial_sigr   r   r   _protected_funcU  s    

z6_wrap_webhook_to_check_secret.<locals>._protected_funcr\   )namekind
annotation)ra   )rb   	signaturer   r   ra   r[   	ParameterPOSITIONAL_OR_KEYWORDtuplevalues__signature__)r+   r   re   r   rd   r   r=   D  s    

r=   )N) rL   rP   rb   r   	functoolsr   typingr   r   r   r   utilsr   r	   rF   rI   Zfastapir
   r   Zfastapi.responsesr   r   __annotations__r    r;   r   rM   rX   rT   r"   rD   r=   r   r   r   r   <module>   s*    0S