o
    qoiw                     @  s  d Z ddlmZ ddlZddlZddlZddlZddlmZm	Z	m
Z
mZmZmZmZmZmZmZ G dd dejdZejdd	G d
d deZejdd	G dd deZejdd	G dd deZdkddZG dd deZejdd	G dd deZeedf Zeedf Zeeee e!e"ej#e$de$e%e$e&f
Z'G dd dZ(eedf Z)e	ege)f Z*eeef Z+e	egee+ f Z,e	egeeedf ef f Z-e	ee egef Z.edZ/ejdd	G dd dZ0G dd  d Z1e1d!d"Z2e2j3Z3e2j4Z4e2j5Z5e3e6d#d$ d%d$ d&d$ d' d(d) Z7d*d+ Z8e3ej9e7e8d,d$ d' e3e:d-d$ d.d$ d/d$ d0d$ d1 e3e(d2d$ d3d$ d4d$ d' e3e;d5d$ d6d$ d7d$ d8d$ d1 dld<d=Z<dmd@dAZ=dndDdEZ>dodJdKZ?dpdNdOZ@dpdPdQZAdqdTdUZBejG dVdW dWejdZCedXZDejdd	G dYdZ dZZEejdd	G d[d\ d\ZFejG d]d^ d^eCZGejG d_d` d`eGZH	!dre2dadsdddeZIdde2fdtdidjZJdS )uzLibrary for manipulating DAGs.    )annotationsN)
AnyCallableDictIterableListOptionalTupleTypeTypeVarUnionc                   @  s>   e Zd ZdZeejdddZejdddZdddZ	dS )PathElementzElement of a path.returnstrc                 C     t  )z'Generates code for accessing this path.NotImplementedErrorself r   G/home/ubuntu/.local/lib/python3.10/site-packages/fiddle/_src/daglish.pycode      zPathElement.coder   c                 C  s   dS )zBReturns the element of `container` specified by this path element.Nr   r   	containerr   r   r   follow$   s    zPathElement.followotherboolc                 C  s0   t | t |urtt | tt |k S td)z.Define the less than relation for PathElement.z?__lt__ relation should be handled by subclasses of PathElement.)typer   r   r   r   r   r   r   __lt__(   s
   zPathElement.__lt__Nr   r   )r   r   r   r   r   r   )
__name__
__module____qualname____doc__propertyabcabstractmethodr   r   r    r   r   r   r   r      s    r   )	metaclassT)frozenc                      sD   e Zd ZU dZded< edddZdddZd fddZ  Z	S )Indexz)An index into a sequence (list or tuple).intindexr   r   c                 C  s   d| j  dS N[]r.   r   r   r   r   r   7      z
Index.coder   !Union[List[Any], Tuple[Any, ...]]r   c                 C  
   || j  S Nr2   r   r   r   r   r   ;      
zIndex.followr   r   r   c                   (   t | t |u r| j|jk S t |S r6   )r   r.   superr    r   	__class__r   r   r    >      zIndex.__lt__r!   )r   r4   r   r   r"   
r#   r$   r%   r&   __annotations__r'   r   r   r    __classcell__r   r   r:   r   r,   2      
 
r,   c                      sD   e Zd ZU dZded< edddZdd
dZd fddZ  Z	S )Keyz A key of a mapping (e.g., dict).r   keyr   r   c                 C  s   d| j dS r/   rB   r   r   r   r   r   J   r3   zKey.coder   Dict[Any, Any]c                 C  r5   r6   rC   r   r   r   r   r   N   r7   z
Key.followr   r   r   c                   r8   r6   )r   rB   r9   r    r   r:   r   r   r    Q   r<   z
Key.__lt__r!   )r   rD   r   r   r"   r=   r   r   r:   r   rA   E   r@   rA   c                      sD   e Zd ZU dZded< edddZdd	d
Zd fddZ  Z	S )AttrzAn attribute of an object.r   namer   c                 C  s   d| j  S )N.)rF   r   r   r   r   r   ]   s   z	Attr.coder   r   c                 C  s   t || jS r6   )getattrrF   r   r   r   r   r   a      zAttr.followr   r   r   c                   r8   r6   )r   rF   r9   r    r   r:   r   r   r    d   r<   zAttr.__lt__r!   )r   r   r   r   r"   r=   r   r   r:   r   rE   X   r@   rE   rB   Union[str, int]r   c                 C  s   t | tr	t| S t| S )z<Returns an Attr for a string key or an Index for an int key.)
isinstancer-   r,   rE   rC   r   r   r   attr_or_indexk   s   rL   c                   @  s   e Zd ZdZdS )BuildableAttrzAn attribute of a Buildable.N)r#   r$   r%   r&   r   r   r   r   rM   p   s    rM   c                      s    e Zd ZdZ fddZ  ZS )BuildableFnOrClsz1The callable (__fn_or_cls__) for a fdl.Buildable.c                   s   t  d d S )N__fn_or_cls__)r9   __init__r   r:   r   r   rP   x   s   zBuildableFnOrCls.__init__)r#   r$   r%   r&   rP   r?   r   r   r:   r   rN   t   s    rN   .c                   @  s   e Zd ZdS )NamedTupleTypeN)r#   r$   r%   r   r   r   r   rQ      s    rQ   Tc                   @  s6   e Zd ZU dZded< ded< ded< dZd	ed
< dS )NodeTraverserz<Contains information required to traverse a given node type.	FlattenFnflattenUnflattenFn	unflattenPathElementsFnpath_elementsNOptimizedFlattenFn | Noneflatten_with_paths)r#   r$   r%   r&   r>   r[   r   r   r   r   rS      s   
 rS   c                   @  s<   e Zd ZdZdddZ	ddddZdddZdddZdS ) NodeTraverserRegistryzA registry of `NodeTraverser`s.use_fallback$Union['NodeTraverserRegistry', bool]c                C  s.   i | _ t|tr|}n|rt}nd}|| _dS )a  Initializes the instance.

    Args:
      use_fallback: Whether to use a fallback registry for lookups if a
        traverser can't be found in this registry. If `True`, this uses the
        default daglish registry for fallbacks. If `False`, fallback behavior is
        disabled. This can also be a `NodeTraverserRegistry` instance, in which
        case it will be used for fallback lookups.
    N)_node_traversersrK   r\   _default_traverser_registryfallback_registry)r   r]   ra   r   r   r   rP      s   


zNodeTraverserRegistry.__init__N	node_type	Type[Any]
flatten_fnrT   unflatten_fnrV   path_elements_fnrX   flatten_with_paths_fnrZ   r   Nonec                 C  sN   t |tstd| d|| jv rtd| dt||||d| j|< dS )a  Registers a node traverser for `node_type`.

    Args:
      node_type: The node type to regiser a traverser for. The traverser will be
        used *only* for nodes of this type, not subclasses (with the exception
        of the special-cased `daglish.NamedTupleType`).
      flatten_fn: A function that flattens values for traversal. This should
        accept an instance of `node_type`, and return a tuple of `(values,
        metadata)`, where `values` is a sequence of values and `metadata` is
        arbitrary traverser-specific data.
      unflatten_fn: A function that unflattens values, which should accept
        `values` and `metadata` and return a new instance of `node_type`.
      path_elements_fn: A function that returns `PathElement` instances for the
        flattened values returned by `flatten_fn`. This should accept an
        instance of `node_type`, and return a sequence of `PathElement`s aligned
        with the values returned by `flatten_fn`.
      flatten_with_paths_fn: A version of `flatten_fn` that returns an iterable
        of `(value, path)` pairs, where `value` is a child value and `path` is a
        `Path` to the value.
    `node_type` () must be a type.zA node traverser for z has already been registered.)rU   rW   rY   r[   N)rK   r   	TypeErrorr_   
ValueErrorrS   )r   rb   rd   re   rf   rg   r   r   r   register_node_traverser   s   


z-NodeTraverserRegistry.register_node_traverserOptional[NodeTraverser]c                 C  sP   t |tstd| dt|rt}| j|}|du r&| jr&| j|}|S )aE  Finds a `NodeTraverser` for the given `node_type`.

    This simply looks up `node_type`, with one special case: if `node_type` is a
    `NamedTuple` (as determined by the `is_namedtuple_subclass` function), then
    `daglish.NamedTupleType` is looked up in `registry` instead.

    If this node traverser registry doesn't contain a traverser for `node_type`,
    then it will be looked up in `self.fallback_registry`.

    Args:
      node_type: The node type to find a traverser for.

    Returns:
      A `NodeTraverser` instance for `node_type`, if it exists, else `None`.
    ri   rj   N)	rK   r   rk   is_namedtuple_subclassrQ   r_   getra   find_node_traverser)r   rb   	traverserr   r   r   rq      s   
z)NodeTraverserRegistry.find_node_traverserr   c                 C  s   |  |duS )z-Returns whether `node_type` can be traversed.N)rq   r   rb   r   r   r   is_traversable_type  r3   z)NodeTraverserRegistry.is_traversable_type)r]   r^   r6   )rb   rc   rd   rT   re   rV   rf   rX   rg   rZ   r   rh   rb   rc   r   rn   )rb   rc   r   r   )r#   r$   r%   r&   rP   rm   rq   rt   r   r   r   r   r\      s    

(r\   F)r]   c                 C  s   t |  t |  fS r6   )tuplevalueskeysxr   r   r   <lambda>  s    r{   c                 C  s   t t|| S r6   )dictzip)rw   rx   r   r   r   r{     s    c                 C  s   dd |   D S )Nc                 S  s   g | ]}t |qS r   rA   .0rB   r   r   r   
<listcomp>      z<lambda>.<locals>.<listcomp>)rx   ry   r   r   r   r{         )rd   re   rf   c                 C  s   t |  | jt |  ffS r6   )rv   rw   default_factoryrx   )noder   r   r   flatten_defaultdict  s   r   c                 C  s   |\}}t |t|| S r6   )collectionsdefaultdictr}   )rw   metadatar   rx   r   r   r   unflatten_defaultdict  s   r   c                 C  s   t dd |  D S )Nc                 s      | ]}t |V  qd S r6   r~   r   r   r   r   	<genexpr>$      <lambda>.<locals>.<genexpr>)rv   rx   ry   r   r   r   r{   $  s    c                 C  s   | d fS r6   r   ry   r   r   r   r{   (      c                 C     t | S r6   rv   rz   _r   r   r   r{   )  r   c                 C     t dd tt| D S )Nc                 s  r   r6   r,   r   ir   r   r   r   *  r   r   rv   rangelenry   r   r   r   r{   *      c                 C     dd t | D S )Nc                 s       | ]\}}|t |fV  qd S r6   r   r   r   rz   r   r   r   r   +      r   	enumeratexsr   r   r   r{   +  r   )rd   re   rf   rg   c                 C  s   t | t| fS r6   )rv   r   ry   r   r   r   r{   0  s    c                 C  s   ||  S r6   r   )rw   rb   r   r   r   r{   1  r   c                 C  s   t dd |   D S )Nc                 s  r   r6   )rE   )r   rF   r   r   r   r   2  r   r   )rv   _asdictrx   ry   r   r   r   r{   2  r   c                 C  s   t | d fS r6   r   ry   r   r   r   r{   6  s    c                 C  r   r6   )listr   r   r   r   r{   7  r   c                 C  r   )Nc                 s  r   r6   r   r   r   r   r   r   8  r   r   r   ry   r   r   r   r{   8  r   c                 C  r   )Nc                 s  r   r6   r   r   r   r   r   r   9  r   r   r   r   r   r   r   r{   9  r   prefix_pathPathcontaining_pathc                 C  s   | |dt |  kS )a%  Returns `True` if `prefix_path` is a prefix of `containing_path`.

  Args:
    prefix_path: the `Path` that may be a prefix of `containing_path`.
    containing_path: the `Path` that may be prefixed by `prefix_path`.

  Returns:
    `True` if `prefix_path` is a prefix of `containing_path`.
  N)r   )r   r   r   r   r   	is_prefix=  s   
r   pathr   c                 C  s   d dd | D S )N c                 s  s    | ]}|j V  qd S r6   )r   )r   rz   r   r   r   r   K  s    zpath_str.<locals>.<genexpr>)join)r   r   r   r   path_strJ  s   r   rootr   c                 C  st   | }t |D ]1\}}z||}W q ttttfy7 } zt| dt|d|  d|d| |d}~ww |S )a  Follows the path from a root item to a contained item, and returns it.

  Equivalent to `functools.reduce(lambda v, p: p.follow(v), root, path)`,
  but gives better error messages.

  Args:
    root: The starting point for the path.
    path: A sequence of `PathElement`s, indicating how to get from `root` to the
      contained item.

  Returns:
    The contained item identified by `path`.

  Raises:
    ValueError: If `path` is not compatible with `root`.
  z is not compatible with rootN=z: )r   r   KeyError
IndexErrorrk   AttributeErrorrl   r   )r   r   valuer   path_elter   r   r   follow_pathN  s"   r   pathsIterable[Path]elementPathsc                   s   t  fdd| D S )Nc                 3  s    | ]}| f V  qd S r6   r   r   r   r   r   r   r   j      z#add_path_element.<locals>.<genexpr>r   )r   r   r   r   r   add_path_elementi  s   r   r   r   c                 C  s   t | t o	| dkS )af  Determines what values can be memoized.

  A primary concern is whether `value` may be subject to Python's interning
  optimizations, which could lead to confusing results under some circumstances
  if memoization is allowed. For the purposes of this function then, immutable
  types that can't contain references (including strings and ints) are excluded
  from memoization. Instances of such types are guaranteed to maintain an
  equality relationship with themselves over time.

  Args:
    value: A candidate value to check for memoizability.

  Returns:
    A bool indicating whether `value` is memoizable.
  r   )rK   _IMMUTABLE_NONCONTAINER_TYPESr   r   r   r   is_memoizablem  s   r   c                 C  s(   t |  pt| tu otdd | D S )a  Returns true if Python can apply an interning optimization to `value`.

  If this is false, then `x is y` is only true if they point to the same object,
  created at the same place.

  If this is true, then `x is y` may be true for unrelated but equal values
  (i.e., values that were created at different places).

  The most common examples of values that the interning optimization can
  apply to are constants, such as booleans, strings, and small integers.

  The interning optimization may be applied to (nested) tuples whose
  values are constants.

  Args:
    value: any value, it can be a Fiddle buildable or a regular Python value.

  Returns:
    A bool indicating whether interning optimization is applicable to `value`.
  c                 s  r   r6   )is_internable)r   r   r   r   r   r     r   z is_internable.<locals>.<genexpr>)r   r   rv   allr   r   r   r   r     s   
r   type_rc   c                 C  s2   t | tot| dot| dotdd | jD S )Nr   _fieldsc                 s  s    | ]}t |tV  qd S r6   )rK   r   )r   fr   r   r   r     r   z)is_namedtuple_subclass.<locals>.<genexpr>)
issubclassrv   hasattrr   r   )r   r   r   r   ro     s   
ro   c                   @  s~   e Zd ZU dZded< ded< eZded< d"ddZej	d#ddZ
ej	d$ddZd%ddZed&ddZed'dd Zd!S )(	TraversalzDefines an API that traversers must implement.

  Please note that users of a traverser will mostly interact with the State
  API, which bundles a Traverser with the current paths in the traversal.
  Callable[..., Any]traversal_fnr   root_objr\   registryrb   rc   r   rn   c                 C  s   | j |S )z5Uses the configured registry to find a NodeTraverser.)r   rq   rs   r   r   r   rq     s   zTraversal.find_node_traverserr   stateStatec                 C  r   )z5Calls the underlying function bound to the traversal.r   r   r   r   r   r   r   apply  s   zTraversal.apply	object_idr-   allow_cachingr   
List[Path]c                 C  r   )z!Returns all paths to a container.r   )r   r   r   r   r   r   all_paths_to_object  r   zTraversal.all_paths_to_objectc                 C  s   t | d| jddS )zReturns an initial state for this traversal.

    The initial state has a reference to the traversal, the empty path, and no
    parent states.
    r   N)parent)r   r   r   r   r   r   initial_state  s   zTraversal.initial_statefnc                 C  s   | ||d  S )a  Creates a new traversal and returns the initial state.

    Args:
      fn: Function which is applied at each node during the traversal.
      root_obj: Root object being traversed.

    Returns:
      The initial state (from `initial_state`) of a new traversal instance.
    )r   r   r   )clsr   r   r   r   r   begin  s   zTraversal.beginc                 C  s   | j ||d}|||S )zCCreates a traversal and state, and then calls/returns `fn` on this.)r   r   )r   )r   r   r   r   r   r   r   run  s   
zTraversal.runNru   )r   r   r   r   r   r   r   r-   r   r   r   r   )r   r   )r   r   r   r   r   r   )r   r   r   r   r   r   )r#   r$   r%   r&   r>   r`   r   rq   r(   r)   r   r   r   classmethodr   r   r   r   r   r   r     s   
 

r   _Tc                   @  s:   e Zd ZU dZded< ded< ded< ded	< d
d ZdS )SubTraversalResultnode_traverserrw   r   rY   rS   r   z	List[Any]rw   r   r   PathElementsrY   c                 C  s   | j | j| jS r6   )r   rW   rw   r   r   r   r   r   rW     s   zSubTraversalResult.unflattenN)r#   r$   r%   	__slots__r>   rW   r   r   r   r   r     s   
 r   c                   @  s   e Zd ZU dZdZded< ded< ded< d	ed
< ed3ddZed4ddZedd Z	ed5ddZ
d6d7ddZd8ddZd9d"d#Zd:d%d&Zd;d'd(Z	)d<d=d,d-Zd>d0d1Zd2S )?r   as  Contains a current traversal state.

  Attributes:
    traversal: Reference to main traversal object.
    current_path: A path that can be followed to the current object. In the case
      of shared objects, there will be other paths to the current object, and
      often these are determined by a somewhat arbitrary DAG traversal order.
    parent: The parent state.
  )	traversalcurrent_path_valuer   r   r   r   r   r   r   zOptional[State]r   r   r-   c                 C  
   t | jS r6   )idr   r   r   r   r   
_object_id     
zState._object_idr   c                 C  r   r6   )r   r   r   r   r   r   _is_memoizable	  r   zState._is_memoizablec                 C  s   | j S )a&  Original value constructed with this state.

    Generally please don't use this value, it's much more clear to use the
    first argument of your `traverse(value, state)` function, especially since
    for post-order traversals, you'll often write `value =
    state.map_children(value)`.
    )r   r   r   r   r   original_value  s   	zState.original_valueIterable[State]c                 c  s(    | V  | j dur| j jE dH  dS dS )z,Gets ancestors, including the current state.N)r   ancestors_inclusiver   r   r   r   r     s
   
zState.ancestors_inclusiveTr   r   c                   sh   dg}d}| j D ]}|jr| jj|j|d} n|d7 }q|dkr)| j| d nd  fdd|D S )a  Gets all paths to the current value.

    Args:
      allow_caching: Whether paths can be cached. Most traverers will compute
        paths to all objects from the root config object. But this can fail to
        reflect objects that are being added by the traversal, so occasionally
        one may want to take a big performance hit to reflect any newly added
        objects.

    Returns:
      List of paths.
    r   r   )r      Nc                   s   g | ]}|  qS r   r   r   path_suffixr   r   r   8  r   z'State.get_all_paths.<locals>.<listcomp>)r   r   r   r   r   r   )r   r   
base_paths
suffix_lenr   r   r   r   get_all_paths  s   

zState.get_all_pathsr   c                 C  s   | j t|duS )z'Returns whether a value is traversable.N)r   rq   r   )r   r   r   r   r   is_traversable:  s   zState.is_traversabler   rS   r   c                   s@   | |\}}||} fddt||D }t||||dS )z:Shared function for map_children / flattened_map_children.c                   s   g | ]
\}}  ||qS r   )call)r   subvaluepath_elementr   r   r   r   C  s    
z1State._flattened_map_children.<locals>.<listcomp>r   )rU   rY   r}   r   )r   r   r   	subvaluesr   rY   new_subvaluesr   r   r   _flattened_map_children>  s   

zState._flattened_map_childrenr   c                 C  s8   | j t|}|du r|S | ||}||j|jS )zMaps over children for traversable values, otherwise returns it.

    Args:
      value: Value to map over. Non-traversable values are returned unmodified.

    Returns:
      A mapped value of the same type.
    N)r   rq   r   r   rW   rw   r   )r   r   r   resultr   r   r   map_childrenM  s
   	zState.map_childrenc                 C  s4   | j t|}|du rtd| d| ||S )a)  Maps over children for traversable values, but doesn't unflatten results.

    Unlike `map_children`, we've found that there's not an easy natural choice
    for handling the case where `value` is not a traversable type. Generally
    calling code expects a SubTraversalResult, which contains an iterable of
    values and metadata. The transformation of these values and choice of
    metadata is non-trivial, and client code would probably have to branch on it
    anyway. Therefore, we throw an error for non-traversable values; please test
    whether `value` is traversable beforehand.

    Args:
      value: Value to map over.

    Returns:
      Sub-traversal results.

    Raises:
      ValueError: If `value` is not traversable. Please test beforehand by
      calling `state.is_traversable()`.
    NNo node traverser found for L, please register traverser or pre-process the non-traversable values first.)r   rq   r   rl   r   )r   r   r   r   r   r   flattened_map_children\  s   
zState.flattened_map_childrenFignore_leavesIterable[Any]c           
      c  s    | j t|}|du r|rdS td| d|jdur1||D ]\}}| ||V  q#dS ||\}}||}t||D ]\}}	| ||	V  qBdS )a  Maps over children for traversable values, but doesn't unflatten results.

    This method only returns result values, so use it in place of
    `state.flattened_map_children(value).values` when the result is consumed
    once. If you are calling this for an "effectful" computation (including ones
    that just gather results in `nonlocal` variables) then consider the
    following pattern,

    for _ in state.yield_map_child_values(node, ignore_leaves=True):
      pass  # Run lazy iterator.

    Args:
      value: Value to map over.
      ignore_leaves: If True, then this function will return an empty iterable
        if `value` is not traversable. Otherwise, it will raise a ValueError.

    Yields:
      Sub-traversal results, the same type as returned by your _traverse
      function.

    Raises:
      ValueError: If `value` is not traversable and `ignore_leaves` is `False`.
      Please test beforehand by calling `state.is_traversable()`.
    Nr   r   )	r   rq   r   rl   r[   r   rU   rY   r}   )
r   r   r  r   r   
sub_valuesunused_metarY   	sub_valuer   r   r   r   yield_map_child_valuesy  s"   


zState.yield_map_child_valuesadditional_pathr   c                 G  s*   t | jg | j|R || }| j||S )a  Low-level function to execute a sub-traversal.

    This creates a new state, and then applies the function bound to the
    traverser.

    Args:
      value: Sub-value to run the traversal on.
      *additional_path: Additional path elements relating the sub-value to the
        value referred to by `self`.

    Returns:
      Result of sub-traversal (whatever `self.traversal.traversal_fn` returns).
    )r   r   r   r   )r   r   r  	new_stater   r   r   r     s   z
State.callN)r   r-   )r   r   )r   r   T)r   r   r   r   r   r   r   r   )r   r   r   rS   r   r   )r   r   r   r   )r   r   r   r   F)r   r   r  r   r   r  )r  r   )r#   r$   r%   r&   r   r>   r'   r   r   r   r   r   r   r   r   r  r  r   r   r   r   r   r     s.   
 	





-r   c                   @  s8   e Zd ZU dZejedZded< dd Z	dddZ
dS )BasicTraversalzBasic traversal.

  This traversal will go through shared objects multiple times, for each path
  that they are reachable from.
  r   Dict[int, List[Path]]paths_cachec                 C  s   |  ||S r6   )r   r   r   r   r   r     rI   zBasicTraversal.applyr   r-   r   r   r   r   c                 C  s8   |r|| j v r| j | S t| jd| jd }| _ || S )NT)memoizable_onlyr   )r  collect_paths_by_idr   r   )r   r   r   	all_pathsr   r   r   r     s   

z"BasicTraversal.all_paths_to_objectNr   )r#   r$   r%   r&   dataclassesfieldr|   r  r>   r   r   r   r   r   r   r    s
   
 r  c                   @  s`   e Zd ZU dZdZded< ejedZ	ded< ejedZ
ded	< e	ddddZdd ZdS )MemoizedTraversala  Traversal that memoizes results.

  Attributes:
    memoize_internables: Whether to memoize applications of the function on
      internable (typically, immutable) values. For example, if "4" is a common
      leaf value, by default the traversal will only visit it once, and reuse
      the results. However, if you are writing a traversal that cares about
      paths to the value "4", you might want to set this to False so it is
      visited multiple times.
    memo: Memoization dictionary mapping `id(value)` to the tuple `(value,
      result)`, where `value` is a traversed node and `result` is the value
      returned by `apply` for `value`.
    _cycle_start: Dictionary used for cycle detection.  When the traversal is
      applied to a value, `_cycle_start[id(value)]` is temporarily set to the
      current the `state`.  Once the result has been computed,
      `_cycle_start[id(value)]` is deleted.  If a cycle is detected during
      traversal, `MemoizedTraversal` raises a `ValueError`.
  Tr   memoize_internablesr  zDict[int, Tuple[Any, Any]]memozDict[int, State]_cycle_startr   r   r   r   r   r   c                 C  s   | |||d  S )a  Creates a new traversal and returns the initial state.

    Args:
      fn: Function which is applied at each node during the traversal.
      root_obj: Root object being traversed.
      memoize_internables: `True` if internables should be memoized. Something
        is internable if it's declaration in separate code locations still cause
        references to it to use the same instance when the value is the same
        (for example, primitives are internable). This is a result of Python's
        internal optimization. See also `daglish.is_internable`.

    Returns:
      The initial state (from `initial_state`) of a new traversal instance.
    )r   r   r  r   )r   r   r   r  r   r   r   r     s   zMemoizedTraversal.beginc                 C  s   t |}| jst|r| ||S || jv r| j| d S || jv r8| j| }tdt|j dt|j d|| j|< | ||}||f| j|< | j|= |S )Nr   z8Fiddle detected a cycle while traversing a value: <root>z
 is <root>z/. Configurations with cycles are not supported.)	r   r  r   r   r  r  rl   r   r   )r   r   r   value_idoriginal_stater   r   r   r   r     s&   



zMemoizedTraversal.applyNr
  )r   r   r   r   r  r   r   r   )r#   r$   r%   r&   r  r>   r  r  r|   r  r  r   r   r   r   r   r   r   r    s   
 r  r   r   r  c                  s@    st di d fdd}t|| |d}|| |  S )	a  Returns a dict mapping id(v)->paths for all `v` traversable from structure.

  I.e., if `result = collect_paths_by_id(structure)`, then `result[id(v)]` is
  the list of every path `p` such that `follow_path(structure, p) is v`.

  This dict only includes values `v` for which `is_memoizable(v)` is true.

  Args:
    structure: The structure for which the id->paths mapping should be created.
    memoizable_only: If true, then only include values `v` for which
      `is_memoizable(v)` is true.  Currently required to be True, to avoid bugs
      that can result from Python's interning optimizations.
    registry: Node traversal registry used to collect paths.
  zIncluding non-memoizable objects when collecting paths by id may cause problems, because of Python's interning optimizations.  If you are sure this is what you need, contact the Fiddle team, and we can look into enabling this flag.r   r   c                   s<    rt | rt| g |j |j| ddD ]}qd S NT)r  )r   
setdefaultr   appendr   r  )r   r   r   r  paths_by_idr   r   traverse4  s
   z%collect_paths_by_id.<locals>.traverser  Nr   r   )rl   r  r   )	structurer  r   r"  r   r   r   r   r    s   r  memoizedr  Iterable[Tuple[Any, Path]]c                 C  s<   ddd}|rt || ||d}nt|| |d}|| | S )	a  Iterates through values in a DAG.

  This API is suitable for read-only access and small/reasonable edits to a
  configuration DAG. If a transformation seeks to change the type of nodes, then
  we recommend using the functional traversal API.

  Often, when mutation can be used, it will preserve history better, so this
  API is not discouraged for modifying configuration.

  Args:
    value: fdl.Buildable or collection of buildables.
    memoized: Whether to yield shared nodes only once. Defaults to True. With
      this setting, you will only see one path (which is somewhat arbitrary) to
      shared nodes.
    memoize_internables: Whether to memoize Python internable values. Check the
      docstring of MemoizedTraversal for details.
    registry: Override to the NodeTraverserRegistry; this is a low-level setting
      for traversing into custom data types.

  Returns:
    Iterable of (value, path) tuples.
  r   r   c                 s  s0    | |j fV  |j| ddD ]}|E d H  qd S r  )r   r  )r   r   
sub_resultr   r   r   	_traverse\  s
   ziterate.<locals>._traverse)r   r  r  Nr#  )r  r  r   )r   r%  r  r   r(  r   r   r   r   iterate?  s   
r)  )rB   rJ   r   r   )r   r   r   r   )r   r   r   r   )r   r   r   r   )r   r   r   r   r   r   r  )r   rc   r   r   r  )r   r\   r   r  )
r   r   r%  r   r  r   r   r\   r   r&  )Kr&   
__future__r   r(   r   r  enumtypingr   r   r   r   r   r   r	   r
   r   r   ABCMetar   	dataclassr,   rA   rE   rL   rM   rN   r   r   r   r-   floatcomplexr   bytesEnumr   NotImplementedEllipsisr   rQ   r   rX   _ValueAndPathOptimizedFlattenFnrT   rV   rR   rS   r\   r`   rm   rq   rt   r|   r   r   r   rv   r   r   r   r   r   r   r   ro   r   r   r   r   r  r  r  r)  r   r   r   r   <module>   s   0






`
	





	7

 JH)