a
    ߙfbuG                     @   s   d dl Zd dlmZ d dlmZ g dZG dd dZG dd deZ	G d	d
 d
eZ
G dd deZG dd deZG dd deZG dd deZddlmZ ddlmZmZ dS )    N)units)ShapedLikeNDArray)	AttributeTimeAttributeQuantityAttributeEarthLocationAttributeCoordinateAttribute CartesianRepresentationAttributeDifferentialAttributec                       sH   e Zd ZdZdZd fdd	Zdd Zd	d
 ZdddZdd Z	  Z
S )r   aU  A non-mutable data descriptor to hold a frame attribute.

    This class must be used to define frame attributes (e.g. ``equinox`` or
    ``obstime``) that are included in a frame class definition.

    Examples
    --------
    The `~astropy.coordinates.FK4` class uses the following class attributes::

      class FK4(BaseCoordinateFrame):
          equinox = TimeAttribute(default=_EQUINOX_B1950)
          obstime = TimeAttribute(default=None,
                                  secondary_attribute='equinox')

    This means that ``equinox`` and ``obstime`` are available to be set as
    keyword arguments when creating an ``FK4`` class instance and are then
    accessible as instance attributes.  The instance value for the attribute
    must be stored in ``'_' + <attribute_name>`` by the frame ``__init__``
    method.

    Note in this example that ``equinox`` and ``obstime`` are time attributes
    and use the ``TimeAttributeFrame`` class.  This subclass overrides the
    ``convert_input`` method to validate and convert inputs into a ``Time``
    object.

    Parameters
    ----------
    default : object
        Default value for the attribute if not provided
    secondary_attribute : str
        Name of a secondary instance attribute which supplies the value if
        ``default is None`` and no value was supplied during initialization.
    z	<unbound>N c                    s   || _ || _t   d S N)defaultsecondary_attributesuper__init__)selfr   r   	__class__ =lib/python3.9/site-packages/astropy/coordinates/attributes.pyr   6   s    zAttribute.__init__c                 C   s
   || _ d S r   )name)r   ownerr   r   r   r   __set_name__;   s    zAttribute.__set_name__c                 C   s   |dfS )a  
        Validate the input ``value`` and convert to expected attribute class.

        The base method here does nothing, but subclasses can implement this
        as needed.  The method should catch any internal exceptions and raise
        ValueError with an informative message.

        The method returns the validated input along with a boolean that
        indicates whether the input value was actually converted.  If the input
        value was already the correct type then the ``converted`` return value
        should be ``False``.

        Parameters
        ----------
        value : object
            Input value to be converted.

        Returns
        -------
        output_value : object
            The ``value`` converted to the correct type (or just ``value`` if
            ``converted`` is False)
        converted : bool
            True if the conversion was actually performed, False otherwise.

        Raises
        ------
        ValueError
            If the input is not valid for this attribute.

        Fr   r   valuer   r   r   convert_input>   s     zAttribute.convert_inputc              	   C   s   |d u r| j }n,t|d| j | j }|d u r<t|| j| j }| |\}}|d urt|dd }|d urt|ddr|j|krz2t|tr|jt	j
|dd}nt	j
||dd}W n( ty   td| j||jY n0 d}|rt|d| j | |S )N_shaper   T)r   subokr   z_attribute {} should be scalar or have shape {}, but is has shape {} and could not be broadcast.)r   getattrr   r   r   r   
isinstancer   Z_applynpbroadcast_to
ValueErrorformatsetattr)r   instanceZ	frame_clsout	convertedZinstance_shaper   r   r   __get__`   s6    



zAttribute.__get__c                 C   s   t dd S )NzCannot set frame attribute)AttributeError)r   r'   valr   r   r   __set__   s    zAttribute.__set__)Nr   )N)__name__
__module____qualname____doc__r   r   r   r   r*   r-   __classcell__r   r   r   r   r      s   ""
"r   c                   @   s   e Zd ZdZdd ZdS )r   a  
    Frame attribute descriptor for quantities that are Time objects.
    See the `~astropy.coordinates.Attribute` API doc for further
    information.

    Parameters
    ----------
    default : object
        Default value for the attribute if not provided
    secondary_attribute : str
        Name of a secondary instance attribute which supplies the value if
        ``default is None`` and no value was supplied during initialization.
    c              
   C   s   ddl m} |du rdS t||r,|}d}nPz||}W n> tyv } z&td| j d|d|W Y d}~n
d}~0 0 d	}|jrd|_||fS )
aU  
        Convert input value to a Time object and validate by running through
        the Time constructor.  Also check that the input was a scalar.

        Parameters
        ----------
        value : object
            Input value to be converted.

        Returns
        -------
        out, converted : correctly-typed object, boolean
            Tuple consisting of the correctly-typed object and a boolean which
            indicates if conversion was actually performed.

        Raises
        ------
        ValueError
            If the input is not valid for this attribute.
        r   )TimeNNFFzInvalid time input =.T)Zastropy.timer3   r!   	Exceptionr$   r   r   Z	writeable)r   r   r3   r(   r)   errr   r   r   r      s$    
zTimeAttribute.convert_inputNr.   r/   r0   r1   r   r   r   r   r   r      s   r   c                       s*   e Zd ZdZd fdd	Zdd Z  ZS )	r	   a  
    A frame attribute that is a CartesianRepresentation with specified units.

    Parameters
    ----------
    default : object
        Default value for the attribute if not provided
    secondary_attribute : str
        Name of a secondary instance attribute which supplies the value if
        ``default is None`` and no value was supplied during initialization.
    unit : unit-like or None
        Name of a unit that the input will be converted into. If None, no
        unit-checking or conversion is performed
    Nr   c                    s   t  || || _d S r   )r   r   unit)r   r   r   r:   r   r   r   r      s    z)CartesianRepresentationAttribute.__init__c                 C   s   t |trJt|dkrJtdd |D rJ| jdurJttd| j dfS t|drj|j	j| jkrj|dfS d}t
|d|}t|dstd	| jj|| j}t|dd
}||fS dS )a   
        Checks that the input is a CartesianRepresentation with the correct
        unit, or the special value ``[0, 0, 0]``.

        Parameters
        ----------
        value : object
            Input value to be converted.

        Returns
        -------
        out : object
            The correctly-typed object.
        converted : boolean
            A boolean which indicates if conversion was actually performed.

        Raises
        ------
        ValueError
            If the input is not valid for this attribute.
           c                 s   s   | ]}|d kV  qdS )r   Nr   ).0vr   r   r   	<genexpr>       zACartesianRepresentationAttribute.convert_input.<locals>.<genexpr>NTxyzFr:   z;tried to set a {} with something that does not have a unit.copy)r!   listlenallr:   CartesianRepresentationr"   Zzeroshasattrr@   r    	TypeErrorr%   r   r.   to)r   r   r)   Zcartrepr   r   r   r      s"    
z.CartesianRepresentationAttribute.convert_input)Nr   Nr.   r/   r0   r1   r   r   r2   r   r   r   r   r	      s   r	   c                       s*   e Zd ZdZd fdd	Zdd Z  ZS )	r   a  
    A frame attribute that is a quantity with specified units and shape
    (optionally).

    Can be `None`, which should be used for special cases in associated
    frame transformations like "this quantity should be ignored" or similar.

    Parameters
    ----------
    default : number or `~astropy.units.Quantity` or None, optional
        Default value for the attribute if the user does not supply one. If a
        Quantity, it must be consistent with ``unit``, or if a value, ``unit``
        cannot be None.
    secondary_attribute : str, optional
        Name of a secondary instance attribute which supplies the value if
        ``default is None`` and no value was supplied during initialization.
    unit : unit-like or None, optional
        Name of a unit that the input will be converted into. If None, no
        unit-checking or conversion is performed
    shape : tuple or None, optional
        If given, specifies the shape the attribute must be
    Nr   c                    sZ   |d u r|d u rt d|d ur.|d u r.|j}|| _|| _| |d }t || d S )NzkEither a default quantity value must be provided, or a unit must be provided to define a QuantityAttribute.r   )r$   r:   r   r   r   r   )r   r   r   r:   r   r   r   r   r     s    zQuantityAttribute.__init__c                 C   s   |du rdS t |ds8| jtjkr8t|dkr8td|}tj|| jdd}| jdur|j| jkr|jdkr|dkrtj	|| jd	d
}nt
d|j d| j d||u}||fS ),  
        Checks that the input is a Quantity with the necessary units (or the
        special value ``0``).

        Parameters
        ----------
        value : object
            Input value to be converted.

        Returns
        -------
        out, converted : correctly-typed object, boolean
            Tuple consisting of the correctly-typed object and a boolean which
            indicates if conversion was actually performed.

        Raises
        ------
        ValueError
            If the input is not valid for this attribute.
        Nr4   r:   r   zJTried to set a QuantityAttribute with something that does not have a unit.FrA   r   Tr   zThe provided value has shape "z", but should have shape "")rG   r:   uZdimensionless_unscaledr"   anyrH   ZQuantityr   r#   r$   )r   r   Zoldvaluer)   r   r   r   r   ,  s$    
zQuantityAttribute.convert_input)Nr   NNrJ   r   r   r   r   r     s
     r   c                   @   s   e Zd ZdZdd ZdS )r   a)  
    A frame attribute that can act as a `~astropy.coordinates.EarthLocation`.
    It can be created as anything that can be transformed to the
    `~astropy.coordinates.ITRS` frame, but always presents as an `EarthLocation`
    when accessed after creation.

    Parameters
    ----------
    default : object
        Default value for the attribute if not provided
    secondary_attribute : str
        Name of a secondary instance attribute which supplies the value if
        ``default is None`` and no value was supplied during initialization.
    c                 C   s\   |du rdS t |tr|dfS ddlm} t|dsBtd||| }|jdfS dS )	rK   Nr4   F   )ITRStransform_tozZ"{}" was passed into an EarthLocationAttribute, but it does not have "transform_to" methodT)	r!   EarthLocationZbuiltin_framesrP   rG   r$   r%   rQ   Zearth_location)r   r   rP   Zitrsobjr   r   r   r   i  s    

z$EarthLocationAttribute.convert_inputNr9   r   r   r   r   r   Y  s   r   c                       s*   e Zd ZdZd fdd	Zdd Z  ZS )	r   a*  
    A frame attribute which is a coordinate object.  It can be given as a
    `~astropy.coordinates.SkyCoord` or a low-level frame instance.  If a
    low-level frame instance is provided, it will always be upgraded to be a
    `~astropy.coordinates.SkyCoord` to ensure consistent transformation
    behavior.  The coordinate object will always be returned as a low-level
    frame instance when accessed.

    Parameters
    ----------
    frame : `~astropy.coordinates.BaseCoordinateFrame` class
        The type of frame this attribute can be
    default : object
        Default value for the attribute if not provided
    secondary_attribute : str
        Name of a secondary instance attribute which supplies the value if
        ``default is None`` and no value was supplied during initialization.
    Nr   c                    s   || _ t || d S r   )_framer   r   )r   framer   r   r   r   r   r     s    zCoordinateAttribute.__init__c                 C   sN   ddl m} |du rdS t|| jr,|dfS ||}|| j}|jdfS dS )a/  
        Checks that the input is a SkyCoord with the necessary units (or the
        special value ``None``).

        Parameters
        ----------
        value : object
            Input value to be converted.

        Returns
        -------
        out, converted : correctly-typed object, boolean
            Tuple consisting of the correctly-typed object and a boolean which
            indicates if conversion was actually performed.

        Raises
        ------
        ValueError
            If the input is not valid for this attribute.
        r   )SkyCoordNr4   FT)Zastropy.coordinatesrU   r!   rS   rQ   rT   )r   r   rU   Ztransformedobjr   r   r   r     s    z!CoordinateAttribute.convert_input)Nr   rJ   r   r   r   r   r     s   r   c                       s*   e Zd ZdZd fdd	Zdd Z  ZS )	r
   a  A frame attribute which is a differential instance.

    The optional ``allowed_classes`` argument allows specifying a restricted
    set of valid differential classes to check the input against. Otherwise,
    any `~astropy.coordinates.BaseDifferential` subclass instance is valid.

    Parameters
    ----------
    default : object
        Default value for the attribute if not provided
    allowed_classes : tuple, optional
        A list of allowed differential classes for this attribute to have.
    secondary_attribute : str
        Name of a secondary instance attribute which supplies the value if
        ``default is None`` and no value was supplied during initialization.
    Nr   c                    s,   |d urt || _nt| _t || d S r   )tupleallowed_classesBaseDifferentialr   r   )r   r   rW   r   r   r   r   r     s    zDifferentialAttribute.__init__c                 C   sR   |du rdS t || jsJt| jdkr6| jd |}ntd|j| j|dfS )a  
        Checks that the input is a differential object and is one of the
        allowed class types.

        Parameters
        ----------
        value : object
            Input value.

        Returns
        -------
        out, converted : correctly-typed object, boolean
            Tuple consisting of the correctly-typed object and a boolean which
            indicates if conversion was actually performed.

        Raises
        ------
        ValueError
            If the input is not valid for this attribute.
        Nr4   rO   r   zfTried to set a DifferentialAttribute with an unsupported Differential type {}. Allowed classes are: {}T)r!   rW   rD   rH   r%   r   r   r   r   r   r     s    z#DifferentialAttribute.convert_input)NNr   rJ   r   r   r   r   r
     s
     
r
   rO   )rR   )rF   rX   )Znumpyr"   Zastropyr   rM   Zastropy.utilsr   __all__r   r   r	   r   r   r   r
   ZearthrR   ZrepresentationrF   rX   r   r   r   r   <module>   s   u<BU69C