o
    tfl                     @   s  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
mZmZmZmZmZmZmZmZ d dlZd dlmZ ddlmZ ddlmZmZ ddlmZmZ ejjd	Z erd d
l!m"Z" d dlm#Z# d dl$m%Z& d dl$m'Z( d dl)m*Z* ddlm+Z+ eeege,f Z-n1e rd dl.m/Z& d dlm(Z( nd dl$m%Z& d dl$m'Z( zd dl)m*Z* W n e0y   dd Z*Y nw e1 Z2dZ3dZ4dZ5dZ6eded fddZ7e sdeej8eej8 f de
e9ef fddZ:dej8ddfdd Z;dej8de
e9ej<j=f fd!d"Z>dej8de?fd#d$Z@nCedej8de
e9ef fd%dZ:G d&d' d'ZAedeBddfd(d Z;G d)d* d*eZ=edeBde
e9e=f fd+d"Z>dej8de?fd,d$Z@G d-d. d.ZCG d/d0 d0e&jDZEdd0d1e?d2e?de
e9ee9 f fd3d4ZFe*d5ejGfd6G d7d8 d8ej8eEd9ZHdS ):    N)contextmanagersuppress)TYPE_CHECKINGAnyCallableClassVarDictIteratorMapping
NamedTupleSetTypeUnioncastno_type_check)PrivateAttr   )SignalGroup)_check_field_equality_pick_equality_operator)ReemissionModeSignal1)	Signature)
ConfigDict)_model_construction)_utils)dataclass_transform)SignalInstance)utilsc                  O   s   dd S )Nc                 S   s   | S N )ar!   r!   _/var/www/html/software/conda/envs/catlas/lib/python3.10/site-packages/psygnal/_evented_model.py<lambda>6       z%dataclass_transform.<locals>.<lambda>r!   )argskwargsr!   r!   r#   r   5      r   Zallow_property_settersZfield_dependenciesZguess_property_dependencies
reemissionreturnc                  c   sR    dt jvrdV  dS dtddddfdd} | t_z
dV  W tjt_dS tjt_w )	a}  Context in which pydantic_main.ClassAttribute just passes value 2.

    Due to a very annoying decision by PySide2, all class ``__signature__``
    attributes may only be assigned **once**.  (This seems to be regardless of
    whether the class has anything to do with PySide2 or not).  Furthermore,
    the PySide2 ``__signature__`` attribute seems to break the python
    descriptor protocol, which means that class attributes that have a
    ``__get__`` method will not be able to successfully retrieve their value
    (instead, the descriptor object itself will be accessed).

    This plays terribly with Pydantic, which assigns a ``ClassAttribute``
    object to the value of ``cls.__signature__`` in ``ModelMetaclass.__new__``
    in order to avoid masking the call signature of object instances that have
    a ``__call__`` method (https://github.com/samuelcolvin/pydantic/pull/1466).

    So, because we only get to set the ``__signature__`` once, this context
    manager basically "opts-out" of pydantic's ``ClassAttribute`` strategy,
    thereby directly setting the ``cls.__signature__`` to an instance of
    ``inspect.Signature``.

    For additional context, see:
    - https://github.com/napari/napari/issues/2264
    - https://github.com/napari/napari/pull/2265
    - https://bugreports.qt.io/browse/PYSIDE-1004
    - https://codereview.qt-project.org/c/pyside/pyside-setup/+/261411
    ZPySide2Nxyr   r*   c                 S   s   |S r    r!   )r+   r,   r!   r!   r#   _return2b   s   z%no_class_attributes.<locals>._return2)sysmodulesstrpydantic_mainZClassAttributer   )r-   r!   r!   r#   no_class_attributes@   s   
r2   objc                 C   sX   i }| j  D ]"\}}| }|du r%t|jtr%t|jtjr%t	|j}|||< q|S z6Get possibly nested default values for a Model object.N)
model_fieldsitemsget_default
isinstance
annotationtype
issubclasspydantic	BaseModel_get_defaultsr3   Zdfltkvdr!   r!   r#   r>   o   s   


r>   clsr   c                 C      | j S r    )model_configrC   r!   r!   r#   _get_config      rG   c                 C   rD   r    )r5   rF   r!   r!   r#   _get_fields   rH   rI   c                 C      |   S r    )Z
model_dumpr3   r!   r!   r#   _model_dump   r(   rL   c                 C   sL   i }| j  D ]\}}| }|du rt|jtjrt|j}|||< q|S r4   )
__fields__r6   r7   r8   type_r1   ModelMetaclassr>   r?   r!   r!   r#   r>      s   

c                   @   s6   e Zd ZdeddfddZd
dededefdd	ZdS )GetAttrAsItemr3   r*   Nc                 C   
   || _ d S r    )_obj)selfr3   r!   r!   r#   __init__      
zGetAttrAsItem.__init__keydefaultc                 C   s   t | j||S r    )getattrrR   )rS   rV   rW   r!   r!   r#   get   s   zGetAttrAsItem.getr    )__name__
__module____qualname__r   rT   r0   rY   r!   r!   r!   r#   rP      s    rP   c                 C   s
   t | jS r    )rP   
__config__rF   r!   r!   r#   rG      s   
c                   @   s2   e Zd ZU eee df ed< eedf ed< dS )	FieldInfoNr9   frozen)rZ   r[   r\   r   r   r   __annotations__boolr!   r!   r!   r#   r^      s   
 r^   c                 C   s   dd | j  D S )Nc                 S   s&   i | ]\}}|t |j|jj d qS ))r9   r_   )r^   rN   Z
field_infoZallow_mutation).0r@   fr!   r!   r#   
<dictcomp>   s    z_get_fields.<locals>.<dictcomp>)rM   r6   rF   r!   r!   r#   rI      s   c                 C   rJ   r    )dictrK   r!   r!   r#   rL      r(   c                   @   s:   e Zd ZdZdddZddd	Zd
ededdfddZdS )ComparisonDelayerz8Context that delays before/after comparisons until exit.targetEventedModelr*   Nc                 C   rQ   r    )_target)rS   rg   r!   r!   r#   rT      rU   zComparisonDelayer.__init__c                 C   s   | j  jd7  _d S Nr   )ri   _delay_check_semaphorerS   r!   r!   r#   	__enter__   s   zComparisonDelayer.__enter_____c                 O   s   | j  jd8  _| j   d S rj   )ri   rk   +_check_if_values_changed_and_emit_if_needed)rS   rn   ro   r!   r!   r#   __exit__   s   zComparisonDelayer.__exit__)rg   rh   r*   Nr*   N)rZ   r[   r\   __doc__rT   rm   r   rq   r!   r!   r!   r#   rf      s
    

rf   c                       sP   e Zd ZU dZeeef ed< ede	dede
dededd f fd	d
Z  ZS )EventedMetaclassa  pydantic ModelMetaclass that preps "equality checking" operations.

    A metaclass is the thing that "constructs" a class, and ``ModelMetaclass``
    is where pydantic puts a lot of it's type introspection and ``ModelField``
    creation logic.  Here, we simply tack on one more function, that builds a
    ``cls.__eq_operators__`` dict which is mapping of field name to a function
    that can be called to check equality of the value of that field with some
    other object.  (used in ``EventedModel.__eq__``)

    This happens only once, when an ``EventedModel`` class is created (and not
    when each instance of an ``EventedModel`` is instantiated).
    __property_setters__mcsnamebases	namespacer'   r*   c              
      s  t   t j| |||fi |}W d   n1 sw   Y  i |_i }t|}t|}|ti }	tj	}
i }t
|	ttfrEt|	}
n2zdd |	 D }W n& ttfyv } zddd t D }td|	d| d	|d}~ww | D ]C\}}t|j|j|< |js|||
}t|j|d
||< trt|jdr|jj}||jj|j< |jD ]}t|dr||jj|j< qq{|td}i |_|rt|jD ]}t|dr|j|j q| D ]#\}}t
|t r|j!dur||j|< |||
}tt"|d
||< qn2|jD ].}t#t$ t|}|r(|tdr(td| dW d   n	1 s3w   Y  qt%||||_&t'| dt(f||_)|j&s\t|dr\|j*|_+|S t|drf|j,|_+|S )zCreate new EventedModel class.Nc                 S   s   i | ]
\}}|t |qS r!   )r   validate)rb   r@   rA   r!   r!   r#   rd      s    z,EventedMetaclass.__new__.<locals>.<dictcomp>z, c                 s   s    | ]}t |V  qd S r    )repr)rb   r+   r!   r!   r#   	<genexpr>   s    z+EventedMetaclass.__new__.<locals>.<genexpr>zInvalid reemission value z-. Must be a mapping of field names to one of .)r)   _json_encoder]   Fru   z?Cannot set 'allow_property_setters' to 'False' when base class z sets it to Truer   _setattr_no_dependants_setattr_with_dependents)-r2   super__new____eq_operators__rI   rG   rY   
REEMISSIONr   ZLATESTr8   r0   rz   r6   
ValueError	TypeErrorjoinZ_membersr   r9   r_   r   PYDANTIC_V1hasattrr~   r]   json_encoders	__bases__ALLOW_PROPERTY_SETTERSru   reversedupdatepropertyfsetobjectr   AttributeError_get_field_dependents__field_dependents__r:   r   __signal_group__r   _setattr_defaultr   )rv   rw   rx   ry   r'   rC   Zsignalsr5   rE   Zemission_cfgZdefault_strategyZemission_mapevalidnrc   	recursionencoderbaseZallow_propsbrV   attrconf	__class__r!   r#   r      s   






zEventedMetaclass.__new__)rZ   r[   r\   rs   r   r0   r   r`   r   r:   tuplere   r   r   __classcell__r!   r!   r   r#   rt      s"   
 rt   rE   r5   c           
      C   s  i }| ti }|s| di }|rtjdtdd |r_t|ts(td|| D ]2\}}|h || j	vrAt
d|d|D ]}||vrStjd|dd ||t | qCq,| td	r| j	 D ]\}}|jd
ur|jjjD ]}	|	|v r||	t | qxqj|S )a  Return mapping of field name -> dependent set of property names.

    Dependencies may be declared in the Model Config to emit an event
    for a computed property when a model field that it depends on changes
    e.g.  (@property 'c' depends on model fields 'a' and 'b')

    Examples
    --------
        class MyModel(EventedModel):
            a: int = 1
            b: int = 1

            @property
            def c(self) -> List[int]:
                return [self.a, self.b]

            @c.setter
            def c(self, val: Sequence[int]):
                self.a, self.b = val

            class Config:
                property_dependencies={'c': ['a', 'b']}
    Zproperty_dependenciesz]The 'property_dependencies' configuration key is deprecated. Use 'field_dependencies' instead   )
stacklevelz1Config property_dependencies must be a dict, not z<Fields with dependencies must be fields or property.setters.z is not.zUnrecognized field dependency: FN)rY   FIELD_DEPENDENCIESwarningswarnDeprecationWarningr8   re   r   r6   ru   r   
setdefaultsetaddGUESS_PROPERTY_DEPENDENCIESfget__code__co_names)
rC   rE   r5   depsZcfg_depspropfieldsfieldsetterrw   r!   r!   r#   r   #  sJ   


r   T)Zkw_only_defaultZfield_specifiersc                       s,  e Zd ZU dZe Zee ed< ee	e
ef  ed< ee	e
ee
 f  ed< ee	e
df  ed< dhZeee  ed< eed	Ze	e
ef ed
< eed	Zee
 ed< edZeed< erdG dd dZdeddf fddZedefddZede	e
ef fddZdedefddZd7ded ef deddfdd Zd8d!d"Zd8d#d$Z d%e
d&eddfd'd(Z!d%e
d&eddf fd)d*Z"d%e
d&eddfd+d,Z#d%e
d&eddfd-d.Z$d%e
d&eddfd/d0Z%d%e
d&eddfd1d2Z&ere'd7d3ede(d fd4d5Z)  Z+S e*e'	d7d3ede(d fd6d5Z)  Z+S )9rh   aD  A pydantic BaseModel that emits a signal whenever a field value is changed.

    !!! important

        This class requires `pydantic` to be installed.
        You can install directly (`pip install pydantic`) or by using the psygnal
        extra: `pip install psygnal[pydantic]`

    In addition to standard pydantic `BaseModel` properties
    (see [pydantic docs](https://pydantic-docs.helpmanual.io/usage/models/)),
    this class adds the following:

    1. gains an `events` attribute that is an instance of [`psygnal.SignalGroup`][].
       This group will have a signal for each field in the model (excluding private
       attributes and non-mutable fields).  Whenever a field in the model is mutated,
       the corresponding signal will emit with the new value (see example below).

    2. Gains support for properties and property.setters (not supported in pydantic's
       BaseModel).  Enable by adding `allow_property_setters = True` to your model
       `Config`.

    3. If you would like properties (i.e. "computed fields") to emit an event when
       one of the model fields it depends on is mutated you must set one of the
       following options in the `Config`:

        - `property_dependencies` may be a `Dict[str, List[str]]`, where the
          keys are the names of properties, and the values are a list of field names
          (strings) that the property depends on for its value
        - `guess_property_dependencies` may be set to `True` to "guess" property
          dependencies by inspecting the source code of the property getter for.

    4. If you would like to allow custom fields to provide their own json_encoders, you
       can either use the standard pydantic method of adding json_encoders to your
       model, for each field type you'd like to support:
       https://pydantic-docs.helpmanual.io/usage/exporting_models/#json_encoders
       This `EventedModel` class will additionally look for a `_json_encode` method
       on any field types in the model.  If a field type declares a `_json_encode`
       method, it will be added to the
       [`json_encoders`](https://pydantic-docs.helpmanual.io/usage/exporting_models/#json_encoders)
       dict in the model `Config`.

    Examples
    --------
    Standard EventedModel example:

    ```python
    class MyModel(EventedModel):
        x: int = 1


    m = MyModel()
    m.events.x.connect(lambda v: print(f"new value is {v}"))
    m.x = 3  # prints 'new value is 3'
    ```

    An example of using property_setters and emitting signals when a field dependency
    is mutated.

    ```python
    class MyModel(EventedModel):
        a: int = 1
        b: int = 1

        @property
        def c(self) -> List[int]:
            return [self.a, self.b]

        @c.setter
        def c(self, val: Sequence[int]) -> None:
            self.a, self.b = val

        class Config:
            allow_property_setters = True
            field_dependencies = {"c": ["a", "b"]}


    m = MyModel()
    assert m.c == [1, 1]
    m.events.c.connect(lambda v: print(f"c updated to {v}"))
    m.a = 2  # prints 'c updated to [2, 1]'
    ```

    _eventsru   r   
EqOperatorr   __weakref__r   )default_factory_changes_queue_primary_changesr   rk   c                   @   s"   e Zd ZU ddiZee ed< dS )zEventedModel.ConfigZ____Nr   )rZ   r[   r\   r   r   re   r`   r!   r!   r!   r#   Config  s   
 r   datar*   Nc                    s&   t  jdi | | j}|| | _d S )Nr!   )r   rT   r   r   )Z_model_self_r   Groupr   r!   r#   rT     s   zEventedModel.__init__c                 C   rD   )z>Return the `SignalGroup` containing all events for this model.)r   rl   r!   r!   r#   events  s   zEventedModel.eventsc                 C   s   t | S r    )r>   rl   r!   r!   r#   	_defaults  s   zEventedModel._defaultsotherc                 C   sx   t |tstt| |kS | j D ]'\}}t| |r t||s# dS t| |}t||}tt	| |||s9 dS qdS )aM  Check equality with another object.

        We override the pydantic approach (which just checks
        ``self.model_dump() == other.model_dump()``) to accommodate more complicated
        types like arrays, whose truth value is often ambiguous. ``__eq_operators__``
        is constructed in ``EqualityMetaclass.__new__``
        FT)
r8   rh   ra   rL   r   r6   r   rX   r   r:   )rS   r   f_namern   r"   r   r!   r!   r#   __eq__  s   


zEventedModel.__eq__Tvaluesrecursec                 C   s   t |tjr
t|}t |tstdt| | jj	 , |
 D ]\}}t| |}t |tr;|r;|j||d q#t| || q#W d   dS 1 sMw   Y  dS )a  Update a model in place.

        Parameters
        ----------
        values : Union[dict, EventedModel]
            Values to update the model with. If an EventedModel is passed it is
            first converted to a dictionary. The keys of this dictionary must
            be found as attributes on the current model.
        recurse : bool
            If True, recursively update fields that are EventedModels.
            Otherwise, just update the immediate fields of this EventedModel,
            which is useful when the declared field type (e.g. ``Union``) can have
            different realized types with different fields.
        z(values must be a dict or BaseModel. got )r   N)r8   r<   r=   rL   re   r   r:   r   _psygnal_relaypausedr6   rX   rh   r   setattr)rS   r   r   rV   valuer   r!   r!   r#   r     s   

"zEventedModel.updatec                 C   sh   t | }t| }| j D ]$\}}t|tr!tdt| |  q|	ds1|| j
s1t| || qdS )z/Reset the state of the model to default values.rh   r_   N)rG   rI   r   r6   r8   rh   r   rX   resetrY   r_   r   )rS   rE   r5   rw   r   r!   r!   r#   r     s   
zEventedModel.resetc                 C   s&  | j dkst| jdkrdS g }| jD ]"}| j| }t| |}tt| |||s/|||f | j| q|sD| j	  | j	  dS | j
 D ]\}}t| |}tt| |||sb|||f qI| j	  | j	  t|  |D ]\}}t| j|| qtW d   dS 1 sw   Y  dS )z
        Check if field values changed and emit events if needed.

        The advantage of moving this to the end of all the modifications is
        that comparisons will be performed only once for every potential change.
        r   N)rk   lenr   r   rX   r   r:   appendpopclearr6   rf   r   )rS   Zto_emitrw   	old_value	new_valuer!   r!   r#   rp     s2   








"z8EventedModel._check_if_values_changed_and_emit_if_neededrw   r   c                 C   s8   |dkst | dr|| jvr| ||S | || d S Nr   )r   r   _super_setattr_r   rS   rw   r   r!   r!   r#   __setattr__D  s   
zEventedModel.__setattr__c                    sL   || j v r| j | | | d S |dkrt| || d S t || d S r   )ru   r   r   r   r   r   r   r!   r#   r   P  s
   
zEventedModel._super_setattr_c                 C   s   dS )zWill be overwritten by metaclass __new__.

        It will become either `_setattr_no_dependants` (if the class has no
        properties and `__field_dependents__`), or `_setattr_with_dependents` if it
        does.
        Nr!   r   r!   r!   r#   r   \  r%   zEventedModel._setattr_defaultc                 C   sj   | j }|| }t|dk r| ||S t| |t }| || tt| |||s3t| j || dS dS )z6__setattr__ behavior when the class has no properties.r   N)r   r   r   rX   r   r   r:   )rS   rw   r   groupsignal_instancer   r!   r!   r#   r   d  s   z#EventedModel._setattr_no_dependantsc                 C   s:   t |  | || W d   dS 1 sw   Y  dS )z4__setattr__ behavior when the class does properties.N)rf   _setattr_implr   r!   r!   r#   r   o  s   
"z%EventedModel._setattr_with_dependentsc                    s   | j   | } fdd| j|dD }t|dk r(|s(t js(| ||S | j| || jvr=t	| |t
 | j|< |D ]}|| jvrPt	| |t
 | j|< q?| || d S )Nc                    s   h | ]
}t  | r|qS r!   )r   )rb   dep_namer   r!   r#   	<setcomp>|  s    
z-EventedModel._setattr_impl.<locals>.<setcomp>r!   r   )r   r   rY   r   r   r   r   r   r   rX   r   )rS   rw   r   r   Zdeps_with_callbacksdepr!   r   r#   r   t  s&   


zEventedModel._setattr_impl	as_valuesc              	   c   sf    t | jdt}|| j_zdV  W |tur|| j_dS t| jd dS |tur,|| j_w t| jd w zTemporarily override how enums are retrieved.

            Parameters
            ----------
            as_values : bool
                Whether enums should be shown as values (or as enum objects),
                by default `True`
            use_enum_valuesN)rX   r   NULLr   delattr)rS   r   beforer!   r!   r#   enums_as_values  s   

zEventedModel.enums_as_valuesc              	   c   sx    | j dt}|| j d< zdV  W |tur tt|| j d< dS | j d dS |tur5tt|| j d< w | j d w r   )rE   rY   r   r   ra   r   )rC   r   r   r!   r!   r#   r     s   
)Trr   ),rZ   r[   r\   rs   r   r   r   r   r`   r   r0   r   r   	__slots__r   re   r   r   r   r   rk   intr   r   rT   r   r   ra   r   r   r   r   rp   r   r   r   r   r   r   r   r	   r   classmethodr   r!   r!   r   r#   rh   g  sN   
 U	 


%"rh   )	metaclass)Ir.   r   
contextlibr   r   typingr   r   r   r   r   r	   r
   r   r   r   r   r   r   r<   r   _groupr   Z_group_descriptorr   r   _signalr   r   versionVERSION
startswithr   inspectr   r   Zpydantic._internalr   r1   r   r   Ztyping_extensionsr   r   ra   r   Zpydantic.mainmainImportErrorr   r   r   r   r   r   r2   r=   r0   r>   rG   r   r^   rI   re   rL   rP   r:   rf   rO   rt   r   Fieldrh   r!   r!   r!   r#   <module>   s    <,

 
f
D