
    ? ia                     F   d Z ddlZddlZddlZddlZddlZddlZddlZddl	Z	ddl
Z
ddlZddlZddlZddlmZ ddlmZ ddej                  j(                  fdZ G d d      Z G d	 d
      Zd ZddZd Zd Zd Zd Zd Zd Zd Z d Z!d Z"ddZ#ddZ$d Z%d Z&ddZ'y)z~This library gathers utilities for hyperpyyaml loading

Authors
 * Peter Plantinga 2020
 * Aku Rouhe 2020
 * Jianchen Li 2022
    N)StringIO)RepresenterErrorTc                 j   t        | ||      } |j                  dt               t        j                  d      }|j                  d|d       |j                  dt               |j                  dt               |j                  dt               |j                  d	t               d
t        j                  j                  j                  _        d
t         j                  j                  j                  j                  _        t        j"                  | |      }dt        j                  j                  j                  _        dt         j                  j                  j                  j                  _        |j%                         D cg c]  }|j'                  d      s| }}|D ]  }||=  |S c c}w )a  This function implements the HyperPyYAML syntax

    The purpose for this syntax is a compact, structured hyperparameter and
    function definition. This function implements a few extensions to the yaml
    syntax, listed below.

    **PyYAML complex tag shortcuts**

    Part of our clean structured hyperparameter interface is being able to
    specify python objects easily and cleanly. This is possible with
    native YAML using the following syntax:

    .. code-block:: yaml

        alignment_saver: !!python/object/new:speechbrain.data_io.TensorSaver
            kwargs: {save_dir: results/asr/ali}

    However, due to the extensive use within speechbrain yaml files, we have
    added a shortcut for this that has the following syntax:

    .. code-block:: yaml

        alignment_saver: !new:speechbrain.data_io.TensorSaver
            save_dir: results/asr/ali

    In this example, the alignment_saver will be an instance of the
    ``TensorSaver`` class, with ``'exp/asr/ali'`` passed to the
    ``__init__()`` method as a keyword argument. This is equivalent to:

    .. code-block:: python

        import speechbrain.data_io.data_io
        alignment_saver = speechbrain.data_io.TensorSaver(
            save_dir='exp/asr/ali'
        )

    We have also implemented a few more shortcuts:::

        !!python/name: => !name:
        !!python/module: => !module:
        !!python/object/apply: => !apply:

    **References and copies**

    Allows internal references to any node in the file. Any node with
    tag ``!ref`` will create an object reference to the yaml object at the
    ``<key.subkey>`` location within the yaml itself,
    following reference chains.

    .. code-block:: yaml

        output_folder: results/asr
        alignment_saver: !new:speechbrain.data_io.TensorSaver
            save_dir: !ref <output_folder>

    Strings values are handled specially: references are substituted but
    the rest of the string is left in place, allowing filepaths to be
    easily extended:

    .. code-block:: yaml

        output_folder: results/asr
        alignment_saver: !new:speechbrain.data_io.TensorSaver
            save_dir: !ref <output_folder>/ali  # results/asr/ali

    A more complex example for demonstration purposes:

    .. code-block:: yaml

        key1: {a: !new:object {arg1: 1}}
        key2: !ref <key1[a]>

    Here, ``key2`` will contain a reference to the ``a`` object, so changing
    ``a.arg1`` will also change ``key2.arg1``. If you need a
    deep copy of the object instead of a shallow reference, you
    can use a similar syntax with the tag ``!copy``. For example:

    .. code-block:: yaml

        key1: {a: !new:object {arg1: 1}}
        key2: !copy <key1[a]>

    These will also implement very basic arithmetic, so:

    .. code-block:: yaml

        key1: 1
        key2: !ref <key1> + 3  # this is 4

    **Tuples**

    One last minor enhancement is an implicit tuple resolver. Passing
    a string value of ``(3, 4)`` will be given a tag of ``!tuple`` which is
    then interpreted as a tuple.

    Arguments
    ---------
    yaml_stream : stream
        A file-like object or string from which to read.
    overrides : mapping or str
        A set of overrides for the values read from the stream.
        As yaml implements a nested structure, so can the overrides.
        See `speechbrain.utils.data_utils.recursive_update`
    overrides_must_match : bool
        Whether an error will be thrown when an override does not match
        a corresponding key in the yaml_stream.
    return_dict : bool
        Whether to return a dictionary rather than the default namespace.
    loader : Loader
        Use to parse the yaml_stream, could be `ruamel.yaml.Loader`,
        `yaml.Loader`, etc.

    Returns
    -------
    hparams : dict
        Reflects the structure of ``yaml_stream``.

    Example
    -------
    >>> yaml_string = """
    ... a: 3
    ... thing: !new:collections.Counter
    ...     b: !ref <a>
    ... """
    >>> params = load_hyperpyyaml(yaml_string)
    >>> params["thing"]
    Counter({'b': 3})
    z!tuple)tagconstructorz^\(.*\)$()first!new:!name:!module:!apply:)TLoaderF__)resolve_referencesadd_constructor_make_tuplerecompileadd_implicit_resolveradd_multi_constructor_construct_object_construct_name_construct_module_apply_functionyamlr   BaseConstructorconstruct_object__defaults__ruamelloadkeys
startswith)	yaml_stream	overridesoverrides_must_matchloadertuple_patternhparamskremoval_keyskeys	            e/Volumes/fast/ai/experiments/voice-extract-mac/.venv/lib/python3.12/site-packages/hyperpyyaml/core.pyload_hyperpyyamlr/      sc   F %[)=QRK x[AJJ{+M
  = D   *;<
  ?;
  ->?
  O<FD$$55BMFKK++<<I iiF3GFD$$55BMFKK++<<I
  '||~D~!d1CA~LDCL  N	 Es   F0F0c                   *    e Zd ZdZdZd Zed        Zy)RefTagzClass for dumping !ref tags to yaml

    Arguments
    ---------
    ref_str : str
        String including yaml keys in `<key>` notation

    Example
    -------
    See ``dump_hyperpyyaml``
    !refc                     || _         y N)ref_str)selfr5   s     r.   __init__zRefTag.__init__   s	        c                 N    |j                  | j                  |j                        S r4   )represent_scalaryaml_tagr5   clsrepresenternodes      r.   to_yamlzRefTag.to_yaml   s    ++CLL$,,GGr8   N)__name__
__module____qualname____doc__r;   r7   classmethodr@    r8   r.   r1   r1      s)    
 H H Hr8   r1   c                   $    e Zd ZdZdZed        Zy)PlaceholderzfClass for dumping !PLACEHOLDER tags to yaml

    Example
    -------
    See ``dump_hyperpyyaml``
    !PLACEHOLDERc                 :    |j                  | j                  d      S )N )r:   r;   r<   s      r.   r@   zPlaceholder.to_yaml   s    ++CLL"==r8   N)rA   rB   rC   rD   r;   rE   r@   rF   r8   r.   rH   rH      s      H> >r8   rH   c                 &   t         j                  j                         }|j                  j	                  t
        t
        j                         |j                  j	                  t        t        j                          |j                  | |g|i | y)a  Dump yaml including placeholder and reference tags.

    Arguments
    ---------
    yaml_tree : dict
        An object to dump
    output_stream : stream
        A file stream for putting the yaml
    *args, **kwargs
        Arguments to forward to ruamel.yaml.YAML().dump()

    Example
    -------
    >>> to_yaml = {'a': Placeholder(), 'b': RefTag('<a>')}
    >>> stringio = StringIO()
    >>> dump_hyperpyyaml(to_yaml, stringio)
    >>> stringio.getvalue()
    'a: !PLACEHOLDER\nb: !ref <a>\n'
    N)	r!   r   YAMLr>   add_representerr1   r@   rH   dump)	yaml_treeoutput_streamargskwargsruamel_yamls        r.   dump_hyperpyyamlrU      sh    ( ++""$K++FFNNC++K9L9LMKY???r8   c                    d}t        | d      rFt        j                  j                  t        j                  j	                  | j
                              }t        j                  j                         }|j                  |       }|>t        |      dkD  r0t        |t              r|j                  |      }	 t        |||       t!        d|||       t#               } 	 |j%                  ||        | j'                  d       | S # t        $ r t        d|      w xY w# t(        $ r8}|j*                  d   dz   f|_        |j-                  |j.                        d}~ww xY w)a  Resolves inter-document references, a component of HyperPyYAML.

    Arguments
    ---------
    yaml_stream : stream
        A file-like object or string with the contents of a yaml file
        written with the HyperPyYAML syntax.
    overrides : mapping or str
        Replacement values, either in a yaml-formatted string or a dict.
    overrides_must_match : bool
        Whether an error will be thrown when an override does not match
        a corresponding key in the yaml_stream. This is the opposite
        default from ``load_hyperpyyaml`` because ``resolve_references``
        doesn't need to be as strict by default.

    Returns
    -------
    stream
        A yaml-formatted stream with all references and overrides resolved.

    Example
    -------
    >>> yaml_string = """
    ... constants:
    ...     a: 3
    ...     b: !ref <constants[a]>
    ... """
    >>> overrides = {'constants': {'a': 4}}
    >>> resolve_references(yaml_string, overrides).getvalue()
    'constants:\n  a: 4\n  b: 4\n'
    Nnamer   )
must_matchzLThe structure of the overrides doesn't match the structure of the document: rootz$. Please use the !apply tag instead.)hasattrospathdirnamerealpathrW   r!   r   rM   r"   len
isinstancestrrecursive_update	TypeError
ValueError_walk_tree_and_resolver   rO   seekr   rR   with_traceback__traceback__)r%   r&   r'   	file_pathrT   previewes          r.   r   r     sD   B I{F#GGOOBGG$4$4[5E5E$FG	 ++""$K{+G Y!!3i%#((3I	Wi<PQ 67GY?*K0+.
 '  	2 	  0&&)DDFq//0s$   6D #D D	E$3EEc                    t        |t              r4t        |      D ]%  \  }}| dk(  r|n|  d| d}t        ||||      ||<   ' nHt        |t              r8|j                         D ]%  \  }}| dk(  r|n|  d| d}t        ||||      ||<   ' t        |d      r|j                  j                  xs d}|dk(  rt        d|  d      |d	v r |d
k(  }	t        |j                  g ||	      }|S |j                  d      r|t        d      d }
| t        j                  j                  ||
      }
	 t	        |      }t#        |
      5 }t%        ||      }ddd       t&        j(                  j+                         }|j-                        }|S |j                  d      r|t        d      d }t/        ||      }|S # t         $ r i }Y w xY w# 1 sw Y   xxY w)a  A recursive function for resolving ``!ref``, ``!copy`` and ``!applyref`` tags.

    Loads additional yaml files if ``!include:`` tags are used.
    Also throws an error if ``!PLACEHOLDER`` tags are encountered.

    Arguments
    ---------
    key : str
        The fully-qualified path to current node.
    current_node : node
        A node in the yaml tree loaded with ruamel.yaml.
    tree : node
        The base node in the yaml tree loaded with ruamel.yaml.
    file_path : str
        The location of the directory storing the main yaml file

    Returns
    -------
    yaml.Node
        A yaml tree with all references resolved.
    rY   []r   rK   rI   'z)' is a !PLACEHOLDER and must be replaced.)r2   !copyrp   )	referencereference_list	full_tree	copy_modez	!include:N
!applyref:)r`   list	enumeratere   dictitemsrZ   r   valuerd   recursive_resolver$   r_   r[   r\   joinrc   openr   r!   r   rM   r"   _applyref_function)r-   current_nodetreeri   isub_nodesub_keyr+   	tag_valuert   filenamer&   fincluded_yamlrT   functions                   r.   re   re   T  s    0 ,%$\2KAx&=aQqcmG4WhiXLO 3
 
L$	''--/KAx&=aQqcmG4WhiXLO 0
 |U# $$**0b	 &q%NOPP ++!W,I,&,,!#	LB 3 !!+. [!1!23H$77<<	8< .	 h1 21i @   !++**,K&++M:L  !!,/ \!2!34H-hEL !  	  s   .F7 G7GGGc                     | j                  |      }d|dd z   dz   }t        j                  |t        j                        }t	        |      S )z-Parse scalar node as a list, convert to tuplerm      rn   r   )construct_scalarr   r"   r   tuple)r(   r?   tuple_stringlist_stringparsed_lists        r.   r   r     sG    **40LQr**S0K))K<Kr8   c                 ^   t        |t        j                        s$t        |t        j                  j                        r| j	                  |d      }g |fS t        |t        j
                        s$t        |t        j                  j
                        r| j                  |d      }|i fS g i fS )NT)deep)r`   r   MappingNoder!   construct_mappingSequenceNodeconstruct_sequence)r(   r?   rS   rR   s       r.   
_load_noder     s    tT--.tV[[445))$T):6ztT../tV[[556((D(9Rxr6Mr8   c                     t        |       sg i fS t        | t              r$d| v rd| v rt        |       dk(  r
| d   | d   fS g | fS t        | t              r| i fS t
        )N_args_kwargs   )ra   r`   rx   r_   rv   rd   )r?   s    r.   	_get_argsr     sk    t92v$ d?yD0SY!^=$y/11t8O	D$	Rxr8   c                     t        j                  |      }|t        d|z        t        j                  |      st        d| d|       	 t        | |      \  }} ||i |S # t        $ r }d|z  }|g|j                  |_         d }~ww xY w)NzThere is no such class as %sr
   z should be a class, but is zInvalid argument to class %s)	pydoclocateImportErrorinspectisclassrd   r   rc   rR   r(   callable_stringr?   	callable_rR   rS   rk   err_msgs           r.   r   r     s    _-I8?JKK??9%O$$?	{K
 	
!&$/f$)&)) 0?B#AFF#   A$ $	B-BBc                    t        j                  |      }|t        d|z        t        j                  |      s;t        j
                  |      s&t        | |      \  }}|s|rt        d| d|       |S 	 t        | |      \  }}|s|rt        j                  |g|i |S |S # t        $ r }d|z  }|g|j                  |_         d }~ww xY w)NzThere is no such entity as %sr   zK should be class or function, if you specify args or kwargs. Instead it is Invalid argument to callable %s)r   r   r   r   r   	isroutiner   rd   	functoolspartialrc   rR   )r(   r   r?   rW   rR   rS   rk   r   s           r.   r   r     s    <<(D|9OKLLOOD!W%6%6t%<!&$/f6) *@@DvG  !&$/f6$$T;D;F;; 3oE#AFF#s   7+B% #B% %	C.C		Cc                     t        j                  |      }|t        d|z        t        | |      \  }}|g k7  s|i k7  rt	        d      t        j                  |      st	        d| d|       |S )NzThere is no such module as %szCannot pass args to moduler   z should be module, but is )r   r   r   r   rd   r   ismodule)r(   module_namer?   modulerR   rS   s         r.   r   r     s{    \\+&F~9KGHHfd+LD&rzVr\566F#8K=0J6(STTMr8   c                     t        j                  |      }|t        d|z        t        j                  |      st        d| d|       	 t        | |      \  }} ||i |S # t        $ r }d|z  }|g|j                  |_         d }~ww xY w)NThere is no such callable as %sr    should be a callable, but is r   )	r   r   r   r   r   rd   r   rc   rR   r   s           r.   r   r     s    _-I;oMNNY'o&&DYKP
 	
!&$/f$)&)) 3oE#AFF#r   c                 "   t        j                  |       }|t        d| z        t        j                  |      st        d|  d|       	 t        |      \  }} ||i |}|S # t        $ r }d| z  }|g|j                  |_         d }~ww xY w)Nr   ru   r   r   )	r   r   r   r   r   rd   r   rc   rR   )r   r?   r   rR   rS   outrk   r   s           r.   r~   r~   #  s    _-I;oMNNY'))G	{S
 	
 f((
 3oE#AFF#s   A% %	B.B		Bc                    d}d| v r| j                  dd      \  } }|}| j                  d      D ]*  }|j                  d      }||vrt        d| z        ||   }, |rt        j                  |      S |`t
        j                  j                  j                         }|||gz  }|j                  t
        j                  j                  d	             |S |S )
a	  Find the value referred to by a reference in dot-notation

    Arguments
    ---------
    ref : str
        The location of the requested value, e.g. 'constants.param'
    full_tree : dict
        The dictionary to use for finding values
    copy_mode : bool
        Whether to copy the node before dereferencing.

    Returns
    -------
    node
        The node in the full_tree dictionary referenced by ``ref``.

    Example
    -------
    >>> deref('constants[a][b]', {'constants': {'a': {'b': 'c'}}})
    'c'
    N.r   )maxsplitrm   rn   zThe reference "%s" is not validz!apply:getattr)suffix)splitstriprd   copydeepcopyr!   r   commentsCommentedSeqyaml_set_ctagTag)refrs   rt   attrbranchpartr?   s          r.   derefr   8  s    0 D
czIIcAI.	T F		#zz#v>DEE	  }}V$$ {{##002KKOO#3O4	
 Mr8   c                    t        j                  d      }t        | t              r|j	                  |       s| S t        |      dkD  r| |dd v rt        d|      |j                  |       r0t        | j                  d      ||      }|| gz  }t        ||||      S |j                  |       }||D cg c]  }|d   	 c}z  }||fd}|j                  ||       }	t        |	|||      } t        |       S c c}w )a  Resolve a reference to a value, following chained references

    Arguments
    ---------
    reference : str
        a string containing '<x[y]>' in it where x[y] refers
        to a scalar node in the file.
    reference_list : list
        list of prior references in the chain, in order
        to catch circular references.
    full_tree : dict
        the dictionary in which to find all references and their values.
    copy_mode : bool
        Whether to perform a deep copy of the referenced node, rather than
        a shallow reference to the same object.

    Returns
    -------
    scalar
        The dereferenced value, with possible string interpolation and
        arithmetic parsing.

    Example
    -------
    >>> tree = {'a': 3, 'b': 'x', 'c': '<a>', 'd': '<c>/<c>', 'e': '<b>/<b>'}
    >>> recursive_resolve('<d>', [], tree)
    1.0
    >>> recursive_resolve('<e>', [], tree)
    'x/x'
    z<[^>]*>r   NzCircular reference detected: <>r   c                 T    t        t        | d   j                  d      ||            S )Nr   r   )rs   rt   )ra   r   r   )xr   rt   s      r.   
replace_fnz%recursive_resolve.<locals>.replace_fn  s#    51D)TYOPPr8   )r   r   r`   ra   searchr_   rd   	fullmatchr   r   r{   findallsubparse_arithmetic)
rq   rr   rs   rt   reference_finderrz   matchesmatchr   r   s
             r.   r{   r{   l  s   B zz*- i%-=-D-DY-O
>Q9qr0B#B8.II !!),iood+Y	B9+% 	9MM &&y1GW5WEuQxW55N %	 Q 

z9
5C!#~y)LI I&& 6s   1C3c                     	 t        t        j                  | d      j                        S # t        t
        t        f$ r | cY S w xY w)a  Parses simple arithmetic operations in references

    Adapted from https://stackoverflow.com/a/9558001/1761970

    Arguments
    ---------
    reference_string : str
        A string with references and possible arithmetic operations.

    Returns
    -------
    str
        Result of parsing and applying the arithmetic.

    Example
    -------
    >>> parse_arithmetic('2 * 6')
    12
    eval)mode)	_ast_evalastparsebodyrc   SyntaxErrorKeyError)reference_strings    r.   r   r     s@    ( #3&AFFGG{H-   s   ), AAc                    t         j                  t        j                  t         j                  t        j
                  t         j                  t        j                  t         j                  t        j                  t         j                  t        j                  t         j                  t        j                  t         j                  t        j                  i}t         j"                  dk\  r&t%        | t         j&                        r2| j(                  S t%        | t         j*                        r| j,                  S t%        | t         j.                        rE |t1        | j                           t3        | j4                        t3        | j6                              S t%        | t         j8                        r1 |t1        | j                           t3        | j:                              S t=        |       )N)      )r   AddopaddSubr   MultmulDivtruedivFloorDivfloordivPowpowModmodsysversion_infor`   Constantrz   NumnBinOptyper   leftrightUnaryOpoperandrc   )r?   opss     r.   r   r     s   "&&bkkC 6!dCLL):: dCGG$66M$		"!s4=!)DII"6	$**8MNN	D#++	&!s4=!)DLL"9::or8   c           
          t        | t        j                  j                        st	        d|        t        |t        j                  j                        st	        d|       |j                         D ]  \  }}t        |t        j                  j                        r!|| v rt        | j                  |i       |       K|r1|| vr-t        d| d| j                         D cg c]  }| c}       || |<    yc c}w )a  Similar function to `dict.update`, but for a nested `dict`.

    From: https://stackoverflow.com/a/3233356

    If you have to a nested mapping structure, for example:

        {"a": 1, "b": {"c": 2}}

    Say you want to update the above structure with:

        {"b": {"d": 3}}

    This function will produce:

        {"a": 1, "b": {"c": 2, "d": 3}}

    Instead of:

        {"a": 1, "b": {"d": 3}}

    Arguments
    ---------
    d : dict
        mapping to be updated
    u : dict
        mapping to update with
    must_match : bool
        Whether to throw an error if the key in `u` does not exist in `d`.

    Example
    -------
    >>> d = {'a': 1, 'b': {'c': 2}}
    >>> recursive_update(d, {'b': {'d': 3}})
    >>> d
    {'a': 1, 'b': {'c': 2, 'd': 3}}
    z'Expected to update a mapping, but got: z.Expected a mapping to use for update, but got z
Override 'z' not found in: N)
r`   collectionsabcMappingrc   ry   rb   getr   r#   )durX   r+   vr-   s         r.   rb   rb     s    N a001A!EFFa001HLMM 	1a001a1fQUU1b\1-AQJZs*:1668;T8CC8;T:UVWWAaD  <Us   !	C;)NFr   )(rD   r   r   r   r   r   r   os.pathr[   r   r   r   ruamel.yamlr!   operatorr   ior   ruamel.yaml.representerr   r   r/   r1   rH   rU   r   re   r   r   r   r   r   r   r   r~   r   r{   r   r   rb   rF   r8   r.   <module>r     s    
 
 
           4  d6;;CUCUpfH H.> >@4CLOd 0&2&*1h<'~ 4:4r8   