o
    qoiM'                     @  s  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	m
Z
mZmZmZmZ ddlmZ ddlmZ ddlmZ edZeed	ef ee f Zejd
dG dd dZd&ddZd'ddZd(ddZd)d d!ZG d"d# d#e
e eje ZG d$d% d%e
e eje ZdS )*zHDefines the `Partial` and `ArgFactory` classes and associated functions.    )annotationsN)AnyCallableDictGenericTupleTypeTypeVarUnion)arg_factory)config)daglishT.T)frozenc                   @  s   e Zd ZU dZded< dS )_BuiltArgFactoryzThe result of building an ``ArgFactory``.

  This wrapper is returned by ``ArgFactory.__build__``, and then consumed by the
  ``__build__`` method of the containing ``Partial`` or ``ArgFactory`` object.
  Callable[..., Any]factoryN)__name__
__module____qualname____doc____annotations__ r   r   G/home/ubuntu/.local/lib/python3.10/site-packages/fiddle/_src/partial.pyr   "   s   
 r   valuer   returnboolc                 C     ddd}t j|| S )zFReturns true if ``value`` contains any ``_BuiltArgFactory`` instances.statedaglish.Statec                 S  s*   t | trdS || rt|| S dS )NTF)
isinstancer   is_traversableanyyield_map_child_values)noder   r   r   r   visit0   s
   

z$_contains_arg_factory.<locals>.visitNr   r   r   MemoizedTraversalrunr   r%   r   r   r   _contains_arg_factory-   s   
r+   c                 C  r   )a  Makes a copy of value with any _BuiltArgFactory ``f`` replaced by ``f()``.

  The copy is "shallow" in the sense that only containers that (directly or
  indirectly) contain _BuiltArgFactories are replaced.  E.g.:

  >>> x = []
  >>> value = [[], _BuiltArgFactory(list)]
  >>> result = _invoke_arg_factories(value)
  >>> assert value[0] is result[0]  # not replaced

  Args:
    value: The structured value containing _BuiltArgFactories.

  Returns:
    A copy of ``value``.
  r   r   c                 S  sd   t | tr	|  S || r0|| }|j| \}}tdd t||j	D }|r,| S |
 S | S )Nc                 S  s   g | ]\}}||u qS r   r   ).0oldnewr   r   r   
<listcomp>T   s    z8_invoke_arg_factories.<locals>.visit.<locals>.<listcomp>)r    r   r   r!   flattened_map_childrennode_traverserflattenallzipvalues	unflatten)r$   r   subtraversalold_vals_no_child_changedr   r   r   r%   M   s   


z$_invoke_arg_factories.<locals>.visitNr&   r'   r*   r   r   r   _invoke_arg_factories;   s   
r;   argc                 C  s&   t | ts	t| s| S ttt| S )a  Converts a structure-of-ArgFactory's to an ArgFactory-of-structure.

  If `arg` is a `_BuiltArgFactory`, or is a nested structure that doesn't
  contain any `_BuiltArgFactory`s, then return it as-is.

  Othewise, return a new `_BuiltArgFactory` whose factory returns a copy of
  `arg` with any `_BuiltArgFactory` `f` replaced by `f()`.

  Args:
    arg: The argument value to convert.

  Returns:
    `arg`; or a `_BuiltArgFactory` whose factory returns a copy of `arg` with
    any `_BuiltArgFactory` `f` replaced by `f()`.
  )r    r   r+   	functoolspartialr;   )r<   r   r   r   _promote_arg_factory]   s   r?   fnr   args
Tuple[Any]kwargsDict[str, Any]functools.partialc                   s   dd  dd |D }dd |  D } fdd|  D }dd |  D }| }t| D ]*\}}|rLd	d |D }tj|g|R i |}i }q0tj|g|R i |}i }q0|rftj|fi |}tj|fi |S )
ag  Returns `functools.partial` or `arg_factory.partial` for `fn`.

  If `args` or `kwargs` contain any `ArgFactory` instances, then return
  `arg_factory.partial(fn, *args, **kwargs)`.  Otherwise, return
  `functools.partial(fn, *args, **kwargs)`.  If any `args` or `kwargs`
  are nested structures that contain one or more `ArgFactory`s, then
  convert them to an `ArgFactory` that returns a copy of that structure with
  the nested `ArgFactory`s invoked.

  Args:
    fn: The function argument for the partial.
    args: The positional arguments for the partial.
    kwargs: The keyword arguments for the partial.
  c                 S  s
   t | tS Nr    r   )r   r   r   r   is_arg_factory   s   
z&_build_partial.<locals>.is_arg_factoryc                 S  s   g | ]}t |qS r   r?   r,   r<   r   r   r   r/      s    z"_build_partial.<locals>.<listcomp>c                 S  s   i | ]	\}}|t |qS r   rI   r,   namer<   r   r   r   
<dictcomp>   s    z"_build_partial.<locals>.<dictcomp>c                   s    i | ]\}} |r||j qS r   r   rK   rH   r   r   rM      s
    c                 S  s    i | ]\}}t |ts||qS r   rG   rK   r   r   r   rM      s    c                 S  s   g | ]}|j qS r   rN   rJ   r   r   r   r/      s    )items	itertoolsgroupbyr   r>   r=   )r@   rA   rC   arg_factory_kwargsfunctool_kwargsresult
is_factory
arg_valuesr   rO   r   _build_partials   s(   
rX   c                   @  "   e Zd ZU dZded< dd ZdS )Partiala  A ``Partial`` config creates a partial function or class when built.

  In some cases, it may be desired to leave a function or class uninvoked, and
  instead output a corresponding ``functools.partial`` object. Where the
  ``Config`` base class calls its underlying ``__fn_or_cls__`` when built, this
  ``Partial`` instead results in a partially bound function or class::

      partial_config = Partial(SampleClass)
      partial_config.arg = 1
      partial_config.kwarg = 'kwarg'
      partial_class = build(partial_config)
      instance = partial_class(arg=2)  # Keyword arguments can be overridden.
      assert instance.arg == 2
      assert instance.kwarg == 'kwarg'

  A ``Partial`` can also be created from an existing ``Config``, by using the
  ``fdl.cast()`` function. This results in a shallow copy that is decoupled from
  the ``Config`` used to create it. In the example below, any further changes to
  ``partial_config`` are not reflected by ``class_config`` (and vice versa)::

      partial_config = fdl.cast(Partial, class_config)
      class_config.arg = 'new value'  # Further modification to `class_config`.
      partial_class = build(partial_config)
      instance = partial_class()
      assert instance.arg == 'arg'  # The instance config is still 'arg'.

  ``Partial`` also supports configuring function/class with positional
  arguments. Please see docstring of ``Config`` class for details.
  TypeOrCallableProducingT[T]__fn_or_cls__c                 O  s   t | j||S )a%  Builds this ``Partial`` for the given ``args`` and ``kwargs``.

    This method is called during ``build`` to get the output for this
    ``Partial``.

    Args:
      *args: Positional arguments to partially bind to ``self.__fn_or_cls__``.
      **kwargs: Keyword arguments to partially bind to ``self.__fn_or_cls__``.

    Returns:
      A partial object (``functools.partial`` or ``arg_factory.partial``) for
      the callable ``self.__fn_or_cls__``, which binds the positional arguments
      ``args`` and the keyword arguments ``kwargs``.
    )rX   r\   selfrA   rC   r   r   r   	__build__   s   zPartial.__build__Nr   r   r   r   r   r_   r   r   r   r   rZ      s   
 rZ   c                   @  rY   )
ArgFactorya  A configuration that creates an argument factory when built.

  When an ``ArgFactory`` is used as a parameter for a ``fdl.Partial``, the
  partial function built from that ``fdl.Partial`` will construct a new value
  for the parameter each time it is called.  For example:

  >>> def f(x, noise): return x + noise
  >>> cfg = fdl.Partial(f, noise=fdl.ArgFactory(random.random))
  >>> p = fdl.build(cfg)
  >>> p(5) == p(5)  # noise has a different value for each call to `p`.
  False

  In contrast, if we replaced ``fdl.ArgFactory`` with ``fdl.Config`` in the
  above example, then the same noise value would be added each time ``p`` is
  called, since ``random.random`` would be called when ``fdl.build(cfg)`` is
  called.

  ``ArgFactory``'s can also be nested inside containers that are parameter
  values for a ``Partial``.  In this case, the partial function will construct
  the parameter value by copying the containers and replacing any ``ArgFactory``
  with the result of calling its factory.  Only the containers that (directly or
  indirectly) contain ``ArgFactory``'s are copied; any elements of the
  containers that do not contain ``ArgFactory``'s are not copied.

  ``ArgFactory`` can also be used as the parameter for another ``ArgFactory``,
  in which case a new value will be constructed for the child argument
  each time the parent argument is created.

  ``ArgFactory`` should *not* be used as a top-level configuration object, or
  as the argument to a ``fdl.Config``.
  r[   r\   c                 O  s$   |s|rt t| j||S t | jS rF   )r   rX   r\   r]   r   r   r   r_     s   
zArgFactory.__build__Nr`   r   r   r   r   ra      s   
 $ra   )r   r   r   r   )r   r   r   r   )r<   r   r   r   )r@   r   rA   rB   rC   rD   r   rE   )r   
__future__r   dataclassesr=   rQ   typingr   r   r   r   r   r   r	   r
   fiddle._srcr   r   r   r   TypeOrCallableProducingT	dataclassr   r+   r;   r?   rX   	BuildablerZ   ra   r   r   r   r   <module>   s&   (




"
9 4