
    IR-ec                         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 ddlm	Z	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	gZ G d
 d	          ZdS )    N)
urlunparse   )SAMP_STATUS_OKSAMP_STATUS_WARNING)SAMPClientErrorSAMPWarning)SAMPHubServer)ThreadingXMLRPCServer)get_num_argsinternet_on
SAMPClientc                      e Zd ZdZ	 	 	 	 	 	 d$dZd Zd%dZed	             Zed
             Z	d Z
d Zd Zd Zd Zd Zd Zd Zd Zd Zd&dZd&dZd&dZd Zd'dZd'dZd Zd(dZd Zd Zd Zd(d Zd(d!Z d" Z!d# Z"dS ))r   a  
    Utility class which provides facilities to create and manage a SAMP
    compliant XML-RPC server that acts as SAMP callable client application.

    Parameters
    ----------
    hub : :class:`~astropy.samp.SAMPHubProxy`
        An instance of :class:`~astropy.samp.SAMPHubProxy` to be
        used for messaging with the SAMP Hub.

    name : str, optional
        Client name (corresponding to ``samp.name`` metadata keyword).

    description : str, optional
        Client description (corresponding to ``samp.description.text`` metadata
        keyword).

    metadata : dict, optional
        Client application metadata in the standard SAMP format.

    addr : str, optional
        Listening address (or IP). This defaults to 127.0.0.1 if the internet
        is not reachable, otherwise it defaults to the host name.

    port : int, optional
        Listening XML-RPC server socket port. If left set to 0 (the default),
        the operating system will select a free port.

    callable : bool, optional
        Whether the client can receive calls and notifications. If set to
        `False`, then the client can send notifications and calls, but can not
        receive any.
    Nr   Tc                 v   d| _         d| _        |i }|||d<   |||d<   || _        || _        || _        d | _        || _        d | _        d | _        d | _	        d | _
        i | _        | j        i g| j        i gd| _        i | _        d| _        t#                      rY	 t%          j                    | _        t%          j        | j        p| j        | j        pd           n# t*          $ r
 d| _        Y nw xY w|| _        | j        r6t/          j        | j                  | _        d| j        _        t9          | j        p| j        | j        fdd	          | _        | j                                         | j                            | j        d
           | j                            | j         d           | j                            | j!        d           | j        dk    r)| j        j        "                                d         | _        d}tG          || j        p| j         d| j         ddddf          | _        d S d S )NFz	samp.namezsamp.description.text)zsamp.app.pingzclient.env.getz	127.0.0.1r   )targetT)logRequests
allow_nonezsamp.client.receiveNotificationzsamp.client.receiveCallzsamp.client.receiveResponser   http: )$_is_running_is_registered	_metadata_addr_port_xmlrpcAddr	_callableclient
_public_id_private_key_hub_id_notification_bindings_ping_client_env_get_call_bindings_response_bindings
_host_namer   socketgetfqdngetaddrinfoOSErrorhub	threadingThread_serve_forever_threaddaemonr
    register_introspection_functionsregister_functionreceive_notificationreceive_callreceive_responsegetsocknamer   )	selfr+   namedescriptionmetadataaddrportcallableprotocols	            3lib/python3.11/site-packages/astropy/samp/client.py__init__zSAMPClient.__init__:   s    !#H$(H[!"0;H,-!

!  &(#"j"-#3R8
 
 #%%== 	.."(."2"2"4:#@$*/PQRRRR . . ."-. > #	$+43FGGGDL"&DL/.t
;!  DK K88:::K)))+L   K))$*;=VWWWK))%'D   zQ![/;;==a@
H)z4T_CCtzCC	  	 D5#	 #	s   A C C10C1c                 N    | j         rd| _        |                                  dS dS )z
        Start the client in a separate thread (non-blocking).

        This only has an effect if ``callable`` was set to `True` when
        initializing the client.
        TN)r   r   _run_clientr7   s    r?   startzSAMPClient.start   s8     > 	#D	 	          $@c                     d| _         | j        r3| j                                        r| j                            |           | j                                        rt          d| d          dS )z
        Stop the client.

        Parameters
        ----------
        timeout : float
            Timeout after which to give up if the client cannot be cleanly
            shut down.
        Fz/Client was not shut down successfully (timeout=zs)N)r   r   r/   is_alivejoinr   )r7   timeouts     r?   stopzSAMPClient.stop   s     !> 	'dl3355 	'Lg&&&<  "" 	!M'MMM  	 	rE   c                     | j         S )z:
        Whether the client is currently running.
        )r   rC   s    r?   
is_runningzSAMPClient.is_running   s    
 rE   c                     | j         S )z=
        Whether the client is currently registered.
        )r   rC   s    r?   is_registeredzSAMPClient.is_registered   s    
 ""rE   c                 J    | j         r| j                                         d S d S N)r   r/   rD   rC   s    r?   rB   zSAMPClient._run_client   s0    > 	!L     	! 	!rE   c                 F   | j         r	 t          j        | j        j        gg g d          d         }|r| j                                         n4# t
          $ r'}t          j        d| t                     Y d }~nd }~ww xY w| j         | j        	                                 d S )Ng?r   z%Call to select in SAMPClient failed: )
r   selectr   r'   handle_requestr*   warningswarnr   server_close)r7   
read_readyexcs      r?   r.   zSAMPClient._serve_forever   s     		11#]DK,>+?RMMaP
  1K..000    ACAA;         		1 	  """""s   (A 
A>A99A>c                 R    t           i d}| j                            |||           d S )Nsamp.statussamp.result)r   r+   replyr7   private_key	sender_idmsg_id	msg_mtype
msg_paramsmessager^   s           r?   r"   zSAMPClient._ping   s-     .rBB{FE22222rE   c                     |d         t           j        v r#t          dt           j        |d                  id}nt          ddiddid}| j                            |||           d S )Nr8   valuer[   r   zsamp.errortxtz!Environment variable not defined.)r\   r]   z
samp.error)osenvironr   r   r+   r^   r_   s           r?   r#   zSAMPClient._client_env_get   s|     f++- 'Jv4F)GH EE  3 '}.0ST E 	{FE22222rE   c           	      :   ||                                  k    rd|v r~|d         }|d= |d         }|d= t          j        |          }|D ]Q}|| j        v rF| j        |         d         }t	          |          dk    r ||||||           A |||d |||           RdS )N
samp.mtypesamp.paramsr      r   )get_private_keyr	   get_mtype_subtypesr!   r   )	r7   r`   ra   re   rc   rd   msubsmtype
bound_funcs	            r?   _handle_notificationzSAMPClient._handle_notification   s    $..0000\W5L5L-I% /J&!4Y??E 
 
D777!%!<U!CA!FJ#J//144"
'Iz7    #
'D)ZQX   rrE   c                 0    |                      |||          S )a^  
        Standard callable client ``receive_notification`` method.

        This method is automatically handled when the
        :meth:`~astropy.samp.client.SAMPClient.bind_receive_notification`
        method is used to bind distinct operations to MTypes. In case of a
        customized callable client implementation that inherits from the
        :class:`~astropy.samp.SAMPClient` class this method should be
        overwritten.

        .. note:: When overwritten, this method must always return
                  a string result (even empty).

        Parameters
        ----------
        private_key : str
            Client private key.

        sender_id : str
            Sender public ID.

        message : dict
            Received message.

        Returns
        -------
        confirmation : str
            Any confirmation string.
        )rs   )r7   r`   ra   re   s       r?   r3   zSAMPClient.receive_notification   s    < ((iIIIrE   c           	          ||                                  k    r]d|v rY|d         }|d= |d         }|d= t          j        |          }|D ],}|| j        v r! | j        |         d         ||||||           -dS )Nrk   rl   r   r   )rn   r	   ro   r$   )	r7   r`   ra   rb   re   rc   rd   rp   rq   s	            r?   _handle_callzSAMPClient._handle_call  s    $..0000\W5L5L-I% /J&!4Y??E  D///1D'.q1#Y	:w   rrE   c                 2    |                      ||||          S )a  
        Standard callable client ``receive_call`` method.

        This method is automatically handled when the
        :meth:`~astropy.samp.client.SAMPClient.bind_receive_call` method is
        used to bind distinct operations to MTypes. In case of a customized
        callable client implementation that inherits from the
        :class:`~astropy.samp.SAMPClient` class this method should be
        overwritten.

        .. note:: When overwritten, this method must always return
                  a string result (even empty).

        Parameters
        ----------
        private_key : str
            Client private key.

        sender_id : str
            Sender public ID.

        msg_id : str
            Message ID received.

        message : dict
            Received message.

        Returns
        -------
        confirmation : str
            Any confirmation string.
        )rv   )r7   r`   ra   rb   re   s        r?   r4   zSAMPClient.receive_call0  s    B   iIIIrE   c                 z    ||                                  k    r"|| j        v r | j        |         ||||           dS )Nr   )rn   r%   r7   r`   responder_idmsg_tagresponses        r?   _handle_responsezSAMPClient._handle_responseS  sR    $..0000W@W5W5W,D#G,\7H   rrE   c                 2    |                      ||||          S )a  
        Standard callable client ``receive_response`` method.

        This method is automatically handled when the
        :meth:`~astropy.samp.client.SAMPClient.bind_receive_response` method
        is used to bind distinct operations to MTypes. In case of a customized
        callable client implementation that inherits from the
        :class:`~astropy.samp.SAMPClient` class this method should be
        overwritten.

        .. note:: When overwritten, this method must always return
                  a string result (even empty).

        Parameters
        ----------
        private_key : str
            Client private key.

        responder_id : str
            Responder public ID.

        msg_tag : str
            Response message tag.

        response : dict
            Received response.

        Returns
        -------
        confirmation : str
            Any confirmation string.
        )r}   ry   s        r?   r5   zSAMPClient.receive_responseZ  s    B $$[,RRRrE   c                 j    |                      ||||           |                     ||||           dS )a  
        Bind a specific MType to a function or class method, being intended for
        a call or a notification.

        The function must be of the form::

            def my_function_or_method(<self,> private_key, sender_id, msg_id,
                                      mtype, params, extra)

        where ``private_key`` is the client private-key, ``sender_id`` is the
        notification sender ID, ``msg_id`` is the Hub message-id (calls only,
        otherwise is `None`), ``mtype`` is the message MType, ``params`` is the
        message parameter set (content of ``"samp.params"``) and ``extra`` is a
        dictionary containing any extra message map entry. The client is
        automatically declared subscribed to the MType by default.

        Parameters
        ----------
        mtype : str
            MType to be caught.

        function : callable
            Application function to be used when ``mtype`` is received.

        declare : bool, optional
            Specify whether the client must be automatically declared as
            subscribed to the MType (see also
            :meth:`~astropy.samp.client.SAMPClient.declare_subscriptions`).

        metadata : dict, optional
            Dictionary containing additional metadata to declare associated
            with the MType subscribed to (see also
            :meth:`~astropy.samp.client.SAMPClient.declare_subscriptions`).
        )declarer:   N)bind_receive_callbind_receive_notificationr7   rq   functionr   r:   s        r?   bind_receive_messagezSAMPClient.bind_receive_message}  sR    F 	uh(SSS&&8Wx 	' 	
 	
 	
 	
 	
rE   c                     | j         r*|si }||g| j        |<   |r|                                  dS dS t          d          )a  
        Bind a specific MType notification to a function or class method.

        The function must be of the form::

            def my_function_or_method(<self,> private_key, sender_id, mtype,
                                      params, extra)

        where ``private_key`` is the client private-key, ``sender_id`` is the
        notification sender ID, ``mtype`` is the message MType, ``params`` is
        the notified message parameter set (content of ``"samp.params"``) and
        ``extra`` is a dictionary containing any extra message map entry. The
        client is automatically declared subscribed to the MType by default.

        Parameters
        ----------
        mtype : str
            MType to be caught.

        function : callable
            Application function to be used when ``mtype`` is received.

        declare : bool, optional
            Specify whether the client must be automatically declared as
            subscribed to the MType (see also
            :meth:`~astropy.samp.client.SAMPClient.declare_subscriptions`).

        metadata : dict, optional
            Dictionary containing additional metadata to declare associated
            with the MType subscribed to (see also
            :meth:`~astropy.samp.client.SAMPClient.declare_subscriptions`).
        Client not callable.Nr   r!   _declare_subscriptionsr   r   s        r?   r   z$SAMPClient.bind_receive_notification  si    B > 	: 2:H1ED'. .++-----. . ""8999rE   c                     | j         r*|si }||g| j        |<   |r|                                  dS dS t          d          )a<  
        Bind a specific MType call to a function or class method.

        The function must be of the form::

            def my_function_or_method(<self,> private_key, sender_id, msg_id,
                                      mtype, params, extra)

        where ``private_key`` is the client private-key, ``sender_id`` is the
        notification sender ID, ``msg_id`` is the Hub message-id, ``mtype`` is
        the message MType, ``params`` is the message parameter set (content of
        ``"samp.params"``) and ``extra`` is a dictionary containing any extra
        message map entry. The client is automatically declared subscribed to
        the MType by default.

        Parameters
        ----------
        mtype : str
            MType to be caught.

        function : callable
            Application function to be used when ``mtype`` is received.

        declare : bool, optional
            Specify whether the client must be automatically declared as
            subscribed to the MType (see also
            :meth:`~astropy.samp.client.SAMPClient.declare_subscriptions`).

        metadata : dict, optional
            Dictionary containing additional metadata to declare associated
            with the MType subscribed to (see also
            :meth:`~astropy.samp.client.SAMPClient.declare_subscriptions`).
        r   Nr   r$   r   r   r   s        r?   r   zSAMPClient.bind_receive_call  si    D > 	: *2H)=D& .++-----. . ""8999rE   c                 F    | j         r|| j        |<   dS t          d          )a  
        Bind a specific msg-tag response to a function or class method.

        The function must be of the form::

            def my_function_or_method(<self,> private_key, responder_id,
                                      msg_tag, response)

        where ``private_key`` is the client private-key, ``responder_id`` is
        the message responder ID, ``msg_tag`` is the message-tag provided at
        call time and ``response`` is the response received.

        Parameters
        ----------
        msg_tag : str
            Message-tag to be caught.

        function : callable
            Application function to be used when ``msg_tag`` is received.
        r   Nr   r%   r   )r7   r{   r   s      r?   bind_receive_responsez SAMPClient.bind_receive_response  s0    * > 	:/7D#G,,,!"8999rE   c                 r    | j         r"| j        |= |r|                                  dS dS t          d          )a  
        Remove from the notifications binding table the specified MType and
        unsubscribe the client from it (if required).

        Parameters
        ----------
        mtype : str
            MType to be removed.

        declare : bool
            Specify whether the client must be automatically declared as
            unsubscribed from the MType (see also
            :meth:`~astropy.samp.client.SAMPClient.declare_subscriptions`).
        r   Nr   r7   rq   r   s      r?   unbind_receive_notificationz&SAMPClient.unbind_receive_notification  sT     > 	:+E2 .++-----. . ""8999rE   c                 r    | j         r"| j        |= |r|                                  dS dS t          d          )a  
        Remove from the calls binding table the specified MType and unsubscribe
        the client from it (if required).

        Parameters
        ----------
        mtype : str
            MType to be removed.

        declare : bool
            Specify whether the client must be automatically declared as
            unsubscribed from the MType (see also
            :meth:`~astropy.samp.client.SAMPClient.declare_subscriptions`).
        r   Nr   r   s      r?   unbind_receive_callzSAMPClient.unbind_receive_call+  sT     > 	:#E* .++-----. . ""8999rE   c                 B    | j         r
| j        |= dS t          d          )z
        Remove from the responses binding table the specified message-tag.

        Parameters
        ----------
        msg_tag : str
            Message-tag to be removed.
        r   Nr   )r7   r{   s     r?   unbind_receive_responsez"SAMPClient.unbind_receive_responseA  s.     > 	:'000!"8999rE   c                 \    | j         r|                     |           dS t          d          )a  
        Declares the MTypes the client wishes to subscribe to, implicitly
        defined with the MType binding methods
        :meth:`~astropy.samp.client.SAMPClient.bind_receive_notification`
        and :meth:`~astropy.samp.client.SAMPClient.bind_receive_call`.

        An optional ``subscriptions`` map can be added to the final map passed
        to the :meth:`~astropy.samp.hub_proxy.SAMPHubProxy.declare_subscriptions`
        method.

        Parameters
        ----------
        subscriptions : dict, optional
            Dictionary containing the list of MTypes to subscribe to, with the
            same format of the ``subscriptions`` map passed to the
            :meth:`~astropy.samp.hub_proxy.SAMPHubProxy.declare_subscriptions`
            method.
        r   N)r   r   r   )r7   subscriptionss     r?   declare_subscriptionsz SAMPClient.declare_subscriptionsO  s7    & > 	:''66666!"8999rE   c                     | j         j        r| j        t          d          | j                             | j         j        d                   }|d         dk    rt          d          |d         dk    rt          d          |d         | _        |d         | _        |d	         | _        | j        r(| 	                                 | 
                                 | j        i k    r|                                  d
| _        dS t          d          )z6
        Register the client to the SAMP Hub.
        NzClient already registeredzsamp.secretzsamp.self-idr   z:Registration failed - samp.self-id was not set by the hub.zsamp.private-keyz>Registration failed - samp.private-key was not set by the hub.zsamp.hub-idTz<Unable to register to the SAMP Hub. Hub proxy not connected.)r+   is_connectedr   r   registerlockfiler   r    r   _set_xmlrpc_callbackr   r   declare_metadatar   )r7   results     r?   r   zSAMPClient.registerg  s/    8   	 ,%&ABBBX&&tx'8'GHHFn%++%P   ()R//%T   %^4DO &'9 :D!-0DL~ .))+++++---~##%%'''"&D "N  rE   c                     | j         j        r=d| _        | j                             | j                   d| _        d| _        d| _        dS t          d          )z:
        Unregister the client from the SAMP Hub.
        FNz@Unable to unregister from the SAMP Hub. Hub proxy not connected.)r+   r   r   
unregisterr   r    r   r   rC   s    r?   r   zSAMPClient.unregister  sc     8  		"'DH 1222DL"DO $D!R  rE   c                 ~    | j         j        r.| j        )| j                             | j        | j                   d S d S d S rQ   )r+   r   r   set_xmlrpc_callbackr   rC   s    r?   r   zSAMPClient._set_xmlrpc_callback  sN    8  	NT%6%BH(():D<LMMMMM	N 	N%B%BrE   c                    | j         j        r| j        i }| j                                        D ]*}t          j        | j        |         d                   ||<   +| j                                        D ]*}t          j        | j        |         d                   ||<   +|r'|                    t          j        |                     | j         	                    | j        |           d S t          d          )Nr   z[Unable to declare subscriptions. Hub unreachable or not connected or client not registered.)r+   r   r   r!   keyscopydeepcopyr$   updater   r   )r7   r   mtypes_dictrq   s       r?   r   z!SAMPClient._declare_subscriptions  s   8  	T%6%BK499;;  %)]/6q9& &E""
 ,1133 R R%)]43Fu3Ma3P%Q%QE""  A""4=#?#?@@@H**4+<kJJJJJ ""  rE   c                     | j         j        rJ| j        C|| j                            |           | j                             | j        | j                   dS t          d          )a>  
        Declare the client application metadata supported.

        Parameters
        ----------
        metadata : dict, optional
            Dictionary containing the client application metadata as defined in
            the SAMP definition document. If omitted, then no metadata are
            declared.
        NzVUnable to declare metadata. Hub unreachable or not connected or client not registered.)r+   r   r   r   r   r   r   )r7   r:   s     r?   r   zSAMPClient.declare_metadata  so     8  		T%6%B#%%h///H%%d&7HHHHH!"  rE   c                     | j         S )z
        Return the client private key used for the Standard Profile
        communications obtained at registration time (``samp.private-key``).

        Returns
        -------
        key : str
            Client private key.
        )r   rC   s    r?   rn   zSAMPClient.get_private_key  s       rE   c                     | j         S )z
        Return public client ID obtained at registration time
        (``samp.self-id``).

        Returns
        -------
        id : str
            Client public ID.
        )r   rC   s    r?   get_public_idzSAMPClient.get_public_id  s     rE   )NNNNr   T)rF   )TN)TrQ   )#__name__
__module____qualname____doc__r@   rD   rK   propertyrM   rO   rB   r.   r"   r#   rs   r3   rv   r4   r}   r5   r   r   r   r   r   r   r   r   r   r   r   r   r   rn   r    rE   r?   r   r      sW          N W W W Wr	 	 	   *     X  # # X#! ! !# # #3 3 3
3 3 3"  ,J J J@  "!J !J !JF  !S !S !SF'
 '
 '
 '
R(: (: (: (:T): ): ): ):V: : :4: : : :,: : : :,: : :: : : :0$ $ $L  N N N   4   ,
! 
! 
!
 
 
 
 
rE   )r   rh   rS   r'   r,   rU   urllib.parser   	constantsr   r   errorsr   r   r+   r	   standard_profiler
   utilsr   r   __all__r   r   rE   r?   <module>r      s    				        # # # # # # : : : : : : : : 0 0 0 0 0 0 0 0       3 3 3 3 3 3 , , , , , , , ,.Q Q Q Q Q Q Q Q Q QrE   