B
    Afxa50                 @  s   d Z ddlmZ ddlZddlZddlmZmZ ddlm	Z	m
Z
mZmZmZ ddlmZmZ ddlmZ dd	lmZmZmZ e	rdd
lmZ ddlmZ ddlmZ edZedZdddddZG dd dZ G dd deZ!dS )z
    pint.context
    ~~~~~~~~~~~~

    Functions and classes related to context definitions and application.

    :copyright: 2016 by Pint Authors, see AUTHORS for more details..
    :license: BSD, see LICENSE for more details.
    )annotationsN)ChainMapdefaultdict)TYPE_CHECKINGAnyCallableOptionalTuple   )
DefinitionUnitDefinition)DefinitionSyntaxError)ParserHelperSourceIteratorto_units_container)Quantity)UnitRegistry)UnitsContainerzF@context\s*(?P<defaults>\(.*\))?\s+(?P<name>\w+)\s*(=(?P<aliases>.*))*z[A-Za-z_][A-Za-z0-9_]*strzCallable[..., Quantity[Any]])eqreturnc               s   ddddd fdd}|S )Nr   r   zQuantity[Any])uregvaluekwargsr   c               s   | j  fd|i|S )Nr   )Zparse_expression)r   r   r   )r    +lib/python3.7/site-packages/pint/context.pyfunc%   s    z%_expression_to_function.<locals>.funcr   )r   r   r   )r   r   _expression_to_function$   s    r   c               @  s   e Zd ZdZd!dddddd	d
Zed d dddZedefd dddZddddZ	ddddZ
eddddZdd ZdddddZdddd ZdS )"Contexta  A specialized container that defines transformation functions from one
    dimension to another. Each Dimension are specified using a UnitsContainer.
    Simple transformation are given with a function taking a single parameter.


    Conversion functions may take optional keyword arguments and the context
    can have default values for these arguments.


    Additionally, a context may host redefinitions:


    A redefinition must be performed among units that already exist in the registry. It
    cannot change the dimensionality of a unit. The symbol and aliases are automatically
    inherited from the registry.

    Parameters
    ----------
    name : str or None, optional
        Name of the context (must be unique within the registry).
        Use None for anonymous Context. (Default value = None).
    aliases : iterable of str
        Other names for the context.
    defaults : None or dict
        Maps variable names to values.

    Example
    -------

    >>> from pint.util import UnitsContainer
    >>> from pint import Context, UnitRegistry
    >>> ureg = UnitRegistry()
    >>> timedim = UnitsContainer({'[time]': 1})
    >>> spacedim = UnitsContainer({'[length]': 1})
    >>> def time_to_len(ureg, time):
    ...     'Time to length converter'
    ...     return 3. * time
    >>> c = Context()
    >>> c.add_transformation(timedim, spacedim, time_to_len)
    >>> c.transform(timedim, spacedim, ureg, 2)
    6.0
    >>> def time_to_len_indexed(ureg, time, n=1):
    ...     'Time to length converter, n is the index of refraction of the material'
    ...     return 3. * time / n
    >>> c = Context(defaults={'n':3})
    >>> c.add_transformation(timedim, spacedim, time_to_len_indexed)
    >>> c.transform(timedim, spacedim, ureg, 2)
    2.0
    >>> c.redefine("pound = 0.5 kg")
    Nr   zOptional[str]zTuple[str, ...]zOptional[dict]None)namealiasesdefaultsr   c             C  s6   || _ || _i | _|pi | _g | _d| _t | _d S )NF)	r    r!   funcsr"   redefinitionscheckedweakrefWeakValueDictionaryrelation_to_context)selfr    r!   r"   r   r   r   __init___   s    
zContext.__init__)contextr   c             K  sT   |rPt |jf|}| |j|j|}|j|_|j|_x|jD ]}||j|< q:W |S |S )a  Creates a new context that shares the funcs dictionary with the
        original context. The default values are copied from the original
        context and updated with the new defaults.

        If defaults is empty, return the same context.

        Parameters
        ----------
        context : pint.Context
            Original context.
        **defaults


        Returns
        -------
        pint.Context
        )dictr"   r    r!   r#   r$   r(   )clsr+   r"   ZnewdefcZedger   r   r   from_contexty   s    zContext.from_context)r   c               s  t |}t|\}}y`t|}| d  }| d }|rbtdd | d dD }nd}| d }	W n4 tk
r }
 zt	d| |d	|
W d d }
~
X Y nX |	r:d
d |	}y0dd |	ddD }	fdd|	D }	W n> t
tfk
r* }
 zt	d| d|d	|
W d d }
~
X Y nX | |||	}n
| ||}t }x<|D ]2\}}d|krt|| qRy|d\}}|t| t|}d|kr fdd|dD \}}|r||}||}|||| |||| nPd|krB fdd|dD \}}|r2||}||}|||| ntW n: tk
r }
 zt	d||f |d	|
W d d }
~
X Y nX qRW |	r|	 t| }|rt	d| d|S )Nr    r!   c             s  s   | ]}|  V  qd S )N)strip).0ar   r   r   	<genexpr>   s    z%Context.from_lines.<locals>.<genexpr>=r   r"   z'Could not parse the Context header '%s')linenoc             S  s   t | } | js| jS | S )N)compleximagreal)valr   r   r   to_num   s    z"Context.from_lines.<locals>.to_numc             s  s   | ]}| d V  qdS )r4   N)split)r1   partr   r   r   r3      s    z(),c               s"   i | ]\}} |t | qS r   )r   r0   )r1   kv)r:   r   r   
<dictcomp>   s    z&Context.from_lines.<locals>.<dictcomp>z.Could not parse Context definition defaults: '':z<->c             3  s   | ]}t | V  qd S )N)r   from_string)r1   s)non_int_typer   r   r3      s   z->c             3  s   | ]}t | V  qd S )N)r   rC   )r1   rD   )rE   r   r   r3      s   z(Could not parse Context %s relation '%s'zContext parameters z not found in any equation)r   next
_header_research	groupdictr0   tupler;   	Exceptionr   
ValueError	TypeErrorsetredefineupdate_varname_refindallr   add_transformationkeys)r-   linesZto_base_funcrE   r5   headerrr    r!   r"   excZtxtctxnameslineZrelr   r   srcdstZmissing_parsr   )rE   r:   r   
from_lines   s~    
"








zContext.from_linesc             C  s$   |  ||}|| j|< | | j|< dS )z-Add a transformation function to the context.N)__keytransform__r#   r(   )r)   r\   r]   r   _keyr   r   r   rS      s    
zContext.add_transformationc             C  s    |  ||}| j|= | j|= dS )z-Add a transformation function to the context.N)r_   r#   r(   )r)   r\   r]   r`   r   r   r   remove_transformation   s    zContext.remove_transformationz%Tuple[UnitsContainer, UnitsContainer]c             C  s   t | t |fS )N)r   )r\   r]   r   r   r   r_      s    zContext.__keytransform__c             C  s"   |  ||}| j| ||f| jS )zTransform a value.)r_   r#   r"   )r)   r\   r]   registryr   r`   r   r   r   	transform  s    zContext.transformr   )
definitionr   c             C  sn   xh|  D ]\}t|}t|ts2td|  |j|jksD|j	rLtd|j
rZtd| j| q
W dS )zOverride the definition of a unit in the registry.

        Parameters
        ----------
        definition : str
            <unit> = <new definition>``, e.g. ``pound = 0.5 kg``
        z%Expected <unit> = <converter>; got %sz8Can't change a unit's symbol or aliases within a contextz(Can't define base units within a contextN)
splitlinesr   rC   
isinstancer   r   r0   symbolr    r!   Zis_baser$   append)r)   rd   r[   dr   r   r   rO     s    	

zContext.redefinezBTuple[Optional[str], Tuple[str, ...], frozenset, frozenset, tuple]c             C  s:   | j t| jtdd | j D t| j t| jfS )a.  Generate a unique hashable and comparable representation of self, which can
        be used as a key in a dict. This class cannot define ``__hash__`` because it is
        mutable, and the Python interpreter does cache the output of ``__hash__``.

        Returns
        -------
        tuple
        c             s  s   | ]\}}|t |fV  qd S )N)id)r1   r>   r?   r   r   r   r3   ,  s    z#Context.hashable.<locals>.<genexpr>)r    rJ   r!   	frozensetr#   itemsr"   r$   )r)   r   r   r   hashable  s
    zContext.hashable)Nr   N)__name__
__module____qualname____doc__r*   classmethodr/   floatr^   rS   ra   staticmethodr_   rc   rO   rm   r   r   r   r   r   +   s   2  Xr   c                  s`   e Zd ZdZ fddZdd Zdddd	d
Zedd Zedd Z	dd Z
dd Z  ZS )ContextChainzvA specialized ChainMap for contexts that simplifies finding rules
    to transform from one dimension to another.
    c               s$   t    g | _| j  d | _d S )N)superr*   contextsmapsclear_graph)r)   )	__class__r   r   r*   7  s    

zContextChain.__init__c             G  s8   t t|| j | _dd t|D | j | _d| _dS )a  Insert one or more contexts in reversed order the chained map.
        (A rule in last context will take precedence)

        To facilitate the identification of the context with the matching rule,
        the *relation_to_context* dictionary of the context is used.
        c             S  s   g | ]
}|j qS r   )r(   )r1   rY   r   r   r   
<listcomp>F  s    z0ContextChain.insert_contexts.<locals>.<listcomp>N)listreversedrw   rx   rz   )r)   rw   r   r   r   insert_contexts=  s    zContextChain.insert_contextsNint)nc             C  s"   | j d|= | jd|= d| _dS )zRemove the last n inserted contexts from the chain.

        Parameters
        ----------
        n: int
            (Default value = None)
        N)rw   rx   rz   )r)   r   r   r   r   remove_contextsI  s    	zContextChain.remove_contextsc             C  s   x|   D ]}|jS W i S )N)valuesr"   )r)   rY   r   r   r   r"   V  s    zContextChain.defaultsc             C  s<   | j dkr6tt| _ x | D ]\}}| j | | qW | j S )zThe graph relatingN)rz   r   rN   add)r)   Zfr_Zto_r   r   r   graph\  s
    

zContextChain.graphc             C  s   | ||f  ||||S )z|Transform the value, finding the rule in the chained context.
        (A rule in last context will take precedence)
        )rc   )r)   r\   r]   rb   r   r   r   r   rc   e  s    zContextChain.transformc             C  s   t dd | jD S )zGenerate a unique hashable and comparable representation of self, which can
        be used as a key in a dict. This class cannot define ``__hash__`` because it is
        mutable, and the Python interpreter does cache the output of ``__hash__``.
        c             s  s   | ]}|  V  qd S )N)rm   )r1   rY   r   r   r   r3   p  s    z(ContextChain.hashable.<locals>.<genexpr>)rJ   rw   )r)   r   r   r   rm   k  s    zContextChain.hashable)N)rn   ro   rp   rq   r*   r   r   propertyr"   r   rc   rm   __classcell__r   r   )r{   r   ru   2  s   	ru   )"rq   Z
__future__r   rer&   collectionsr   r   typingr   r   r   r   r	   Zdefinitionsr   r   errorsr   utilr   r   r   Zquantityr   rb   r   r   compilerG   rQ   r   r   ru   r   r   r   r   <module>	   s(   
  	