a
    ijd                     @  sX  d Z ddlmZ ddlZddlZddlZddlmZmZ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Zed
ZedZejd#ddddddddddddddddddZejdddddddd
ddddddd
dddZd$dddddddddZd
d
dddZd
d
dddZdddddd Zd
d
dd!d"Ze
jZdS )%zDataclass utils.    )annotationsN)AnyCallableTypeVar)epy)
cast_utils)context)frozen_utils)helpers_ClsT_T.)kw_onlyreplacerepr	auto_castcontextvarsallow_unfrozenNoneboolzCallable[[_ClsT], _ClsT])clsr   r   r   r   r   r   returnc                C  s   d S N r   r   r   r   r   r   r   r   r   W/home/ghrups/robot-bench/.venv/lib/python3.9/site-packages/etils/edc/dataclass_utils.py	dataclass#   s    r   c                C  s   d S r   r   r   r   r   r   r   1   s    FTc                C  s   | du rt jt|||||dS |r*t| } |r6t| } |rBt| } |rPt| } g }|rp|t	j
tjtjd |r|t	j
tjtjd t	| |} | S )a  Augment a dataclass with additional features.

  `auto_cast`: Auto-convert init assignements to the annotated class.

  ```python
  @edc.dataclass
  class A:
    path: edc.AutoCast[epath.Path]
    some_enum: edc.AutoCast[MyEnum]
    x: edc.AutoCast[str]

  a = A(
      path='/some/path',
      some_enum='A',
      x=123
  )
  # Fields annotated with `AutoCast` are automatically casted to their type
  assert a.path == epath.Path('/some/path')
  assert a.some_enum is MyEnum.A
  assert a.x == '123'
  ```

  `allow_unfrozen`: allow nested dataclass to be updated. This add two methods:

   * `.unfrozen()`: Create a lazy deep-copy of the current dataclass. Updates
     to nested attributes will be propagated to the top-level dataclass.
   * `.frozen()`: Returns the frozen dataclass, after it was mutated.

  Example:

  ```python
  old_x = X(y=Y(z=123))

  x = old_x.unfrozen()
  x.y.z = 456
  x = x.frozen()

  assert x == X(y=Y(z=123))  # Only new x is mutated
  assert old_x == X(y=Y(z=456))  # Old x is not mutated
  ```

  Note:

  * Only the last `.frozen()` call resolve the dataclass by calling `.replace`
    recursivelly.
  * Dataclass returned by `.unfrozen()` and nested attributes are not the
    original dataclass but proxy objects which track the mutations. As such,
    those object are not compatible with `isinstance()`, `jax.tree_map`,...
  * Only the top-level dataclass need to be `allow_unfrozen=True`
  * Avoid using `unfrozen` if 2 attributes of the dataclass point to the
    same nested dataclass. Updates on one attribute might not be reflected on
    the other.

    ```python
    y = Y(y=123)
    x = X(x0=y, x1=y)  # Same instance assigned twice in `x0` and `x1`
    x = x.unfrozen()
    x.x0.y = 456  # Changes in `x0` not reflected in `x1`
    x = x.frozen()

    assert x == X(x0=Y(y=456), x1=Y(y=123))
    ```

    This is because only attributes which are accessed are tracked, so `etils`
    do not know the object exist somewhere else in the attribute tree.

  * After `.frozen()` has been called, any of the temporary sub-attribute
    become invalid:

    ```python
    a = a.unfrozen()
    y = a.y
    a = a.frozen()

    y.x  # Raise error (created between the unfrozen/frozen call)
    a.y.x  # Work
    ```

  `contextvars`: Fields annotated as `edc.ContextVar` are wrapped in
  a `contextvars.ContextVar`. Afterward each thread / asyncio coroutine will
  have its own version of the fields (similarly to `threading.local`).

  The contextvars are lazily initialized at first usage.

  Example:

  ```python
  @edc.dataclass
  @dataclasses.dataclass
  class Context:
    thread_id: edc.ContextVar[int] = dataclasses.field(
        default_factory=threading.get_native_id
    )
    stack: edc.ContextVar[list[str]] = dataclasses.field(default_factory=list)

  # Global context object
  context = Context(thread_id=0)

  def worker():
    # Inside each thread, the worker use its own context
    assert context.thread_id != 0
    context.stack.append(1)

  with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:
    for _ in range(10):
      executor.submit(worker)
  ```

  Args:
    cls: The dataclass to decorate
    kw_only: If True, make the dataclass `__init__` keyword-only.
    replace: If `True`, add a `.replace(` alias of `dataclasses.replace`.
    repr: If `True`, the class `__repr__` will return a pretty-printed `str`
      (one attribute per line)
    auto_cast: If `True`, fields annotated as `x: edc.AutoCast[Cls]` will be
      converted to `x: Cls = edc.field(validator=Cls)`.
    contextvars: It `True`, fields annotated as `x: edc.AutoCast[T]` are
      converted to `contextvars`. This allow to have a `threading.local`-like
      API for contextvars.
    allow_unfrozen: If `True`, add `.frozen`, `.unfrozen` methods.

  Returns:
    Decorated class
  N)r   r   r   r   r   )
annotationZdescriptor_fn)	functoolspartialr   _make_kw_onlyadd_repr_add_replacer	   Zadd_unfrozenappendr
   ZDescriptorInfor   ZAutoCastZmake_auto_cast_descriptorr   Z
ContextVarZmake_contextvar_descriptorZwrap_new)r   r   r   r   r   r   r   Zdescriptor_fnsr   r   r   r   ?   sF     	
)r   r   c                   s4   d| j vr| S | j t  fdd}|| _| S )z1Replace the `__init__` by a keyword-only version.__init__c                   s0   |r t | jj dt| d | fi |S )Nz! contructor is keyword-only. Got z positional arguments.)	TypeError	__class____name__len)selfargskwargsZold_initr   r   r#      s    
z_make_kw_only.<locals>.__init__)__dict__r#   r   wraps)r   r#   r   r+   r   r      s    
r   c                 C  s   t | dst| _| S )z=Add a `.replace` method to the class, if not already present.r   )hasattrr   r   r   r   r   r!   
  s    
r!   r   )r(   r*   r   c                 K  s   t j| fi |S )z!Similar to `dataclasses.replace`.)dataclassesr   )r(   r*   r   r   r   r     s    r   c                 C  s$   d| j vr| S tj| r t| _| S )z>Add a `.__repr__` method to the class, if not already present.__repr__)r,   r   Z
text_utilsZhas_default_reprr1   r/   r   r   r   r      s
    
r    ).)N)__doc__
__future__r   r0   r   typingr   r   r   Zetilsr   Z	etils.edcr   r   r	   r
   Z_Clsr   r   overloadr   r   r!   r   r    pretty_reprr1   r   r   r   r   <module>   s\    ""  4
