o
    $iY                     @  s   d dl mZ d dlZd dlZd dlm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mZmZ d dlmZ d d	lmZmZmZmZ d d
lmZ d dlmZmZmZ d dlm Z m!Z! ered dl"Z"ned\Z"Z#dgZ$dddddZ%G dd dZ&dS )    )annotationsN)Sequence)deepcopy)Path)TYPE_CHECKINGAny)ComponentLocatorConfigComponentConfigExpression
ConfigItem)ReferenceResolver)
ID_REF_KEY
ID_SEP_KEY	MACRO_KEYmerge_kv)PathLike)ensure_tuplelook_up_optionoptional_import)CheckKeyDuplicatesYamlLoadercheck_key_duplicatesyamlConfigParsermonaitorchnumpy)r   r   npr   c                	   @  sJ  e Zd ZdZdZdde dZde dZe	de
 de de d	Zd
Z			dVdWddZdd Zdd ZdXddZdYddZdZd[d$d%Zd\d]d)d*Zd^d-d.Zd_d/d0Zd`dad2d3Zdbdcd5d6Zddd9d:Zddd;d<Zdbded=d>Zd?d@ ZdbdfdAdBZedgdFdGZ edhdIdJZ!edidjdMdNZ"edkdQdRZ#edldTdUZ$dS )mr   a	  
    The primary configuration parser. It traverses a structured config (in the form of nested Python dict or list),
    creates ``ConfigItem``, and assign unique IDs according to the structures.

    This class provides convenient access to the set of ``ConfigItem`` of the config by ID.
    A typical workflow of config parsing is as follows:

        - Initialize ``ConfigParser`` with the ``config`` source.
        - Call ``get_parsed_content()`` to get expected component with `id`.

    .. code-block:: python

        from monai.bundle import ConfigParser

        config = {
            "my_dims": 2,
            "dims_1": "$@my_dims + 1",
            "my_xform": {"_target_": "LoadImage"},
            "my_net": {"_target_": "BasicUNet", "spatial_dims": "@dims_1", "in_channels": 1, "out_channels": 4},
            "trainer": {"_target_": "SupervisedTrainer", "network": "@my_net", "preprocessing": "@my_xform"}
        }
        # in the example $@my_dims + 1 is an expression, which adds 1 to the value of @my_dims
        parser = ConfigParser(config)

        # get/set configuration content, the set method should happen before calling parse()
        print(parser["my_net"]["in_channels"])  # original input channels 1
        parser["my_net"]["in_channels"] = 4  # change input channels to 4
        print(parser["my_net"]["in_channels"])

        # instantiate the network component
        parser.parse(True)
        net = parser.get_parsed_content("my_net", instantiate=True)
        print(net)

        # also support to get the configuration content of parsed `ConfigItem`
        trainer = parser.get_parsed_content("trainer", instantiate=False)
        print(trainer)

    Args:
        config: input config source to parse.
        excludes: when importing modules to instantiate components,
            excluding components from modules specified in ``excludes``.
        globals: pre-import packages as global variables to ``ConfigExpression``,
            so that expressions, for example, ``"$monai.data.list_data_collate"`` can use ``monai`` modules.
            The current supported globals and alias names are
            ``{"monai": "monai", "torch": "torch", "np": "numpy", "numpy": "numpy"}``.
            These are MONAI's minimal dependencies. Additional packages could be included with `globals={"itk": "itk"}`.
            Set it to ``False`` to disable `self.globals` module importing.

    See also:

        - :py:class:`monai.bundle.ConfigItem`
        - :py:class:`monai.bundle.scripts.run`

    )jsonr   ymlz.*\.(|)(z$)z(?:+Z_meta_Nconfigr   excludesSequence[str] | str | Noneglobalsdict[str, Any] | None | boolc                 C  s   d | _ i | _t }t|tr|dvr|| |d ur9|dur9| D ]\}}t|tr3t	|d n|| j|< q$t
|d| _t | _|d u rL| ji i}| j| j|d d S )N)NFFr   )r$   r#   )r#   r&   _default_globalscopy
isinstancedictupdateitemsstrr   r   locatorr   ref_resolvermeta_keysetnormalize_meta_id)selfr#   r$   r&   _globalskv r9   \/home/dell461/cl/sdc2/last_ska_mid/HISourceFinder-master-l/src/monai/bundle/config_parser.py__init__f   s   
"
zConfigParser.__init__c                 C  s   | j  S )Nr(   r5   r9   r9   r:   __repr__{   s   zConfigParser.__repr__c                 C  s
   |  |S )a,  
        Get the parsed result of ``ConfigItem`` with the specified ``id``
        with default arguments (e.g. ``lazy=True``, ``instantiate=True`` and ``eval_expr=True``).

        Args:
            id: id of the ``ConfigItem``.

        See also:
             :py:meth:`get_parsed_content`
        )get_parsed_content)r5   idr9   r9   r:   __getattr__~   s   
zConfigParser.__getattr__r?   	str | intreturnc                 C  s   |dkr| j S | j }t|D ]A}t|ttfs(td| dt| d| dzt|tr5t||ddn|t	| }W q tyP } zt
d| |d	}~ww |S )
a  
        Get the config by id.

        Args:
            id: id of the ``ConfigItem``, ``"::"`` (or ``"#"``) in id are interpreted as special characters to
                go one level further into the nested structures.
                Use digits indexing from "0" for list or other strings for dict.
                For example: ``"xform::5"``, ``"net::channels"``. ``""`` indicates the entire ``self.config``.

         z%config must be dict or list for key `z`, but got z: .F)print_all_optionszquery key: N)r#   r   split_idr+   r,   list
ValueErrortyper   intKeyError)r5   r?   r#   r7   er9   r9   r:   __getitem__   s    "zConfigParser.__getitem__Nonec                 C  sb   |dkr|| _ | j  dS tj|dd\}}| | }t|tr"|nt|}|||< | j  dS )at  
        Set config by ``id``.  Note that this method should be used before ``parse()`` or ``get_parsed_content()``
        to ensure the updates are included in the parsed content.

        Args:
            id: id of the ``ConfigItem``, ``"::"`` (or ``"#"``) in id are interpreted as special characters to
                go one level further into the nested structures.
                Use digits indexing from "0" for list or other strings for dict.
                For example: ``"xform::5"``, ``"net::channels"``. ``""`` indicates the entire ``self.config``.
            config: config to set at location ``id``.

        rC   NT)last)r#   r1   resetr   rF   r+   r,   rJ   )r5   r?   r#   Zlast_idbase_idconf_indexingr9   r9   r:   __setitem__   s   

zConfigParser.__setitem__rC   r/   default
Any | Nonec              
   C  s(   z| | W S  t ttfy   | Y S w )z
        Get the config by id.

        Args:
            id: id to specify the expected position. See also :py:meth:`__getitem__`.
            default: default value to return if the specified ``id`` is invalid.

        rK   
IndexErrorrH   )r5   r?   rU   r9   r9   r:   get   s
   	
zConfigParser.getT	recursiveboolc                 C  s   t |}|  }|r7|du ri  | _}|dd D ]}t|tr)||vr)i ||< |t|tr1|nt| }q| j|| t 	|< dS )a  
        Set config by ``id``.

        Args:
            config: config to set at location ``id``.
            id: id to specify the expected position. See also :py:meth:`__setitem__`.
            recursive: if the nested id doesn't exist, whether to recursively create the nested items in the config.
                default to `True`. for the nested id, only support `dict` for the missing section.

        N)
r   rF   rY   r#   r+   r,   rJ   r1   r4   normalize_id)r5   r#   r?   rZ   keysrR   r7   r9   r9   r:   r3      s   

zConfigParser.setpairsdict[str, Any]c                 C  s   |  D ]\}}|| |< qdS )a	  
        Set the ``id`` and the corresponding config content in pairs, see also :py:meth:`__setitem__`.
        For example, ``parser.update({"train::epoch": 100, "train::lr": 0.02})``

        Args:
            pairs: dictionary of `id` and config pairs.

        N)r.   )r5   r_   r7   r8   r9   r9   r:   r-      s   	
zConfigParser.updatec              
   C  s*   z| | }W dS  t ttfy   Y dS w )z
        Returns True if `id` is stored in this configuration.

        Args:
            id: id to specify the expected position. See also :py:meth:`__getitem__`.
        TFrW   )r5   r?   _r9   r9   r:   __contains__   s   zConfigParser.__contains__rP   c                 C  s*   |r| j   |   | j|  d dS )aE  
        Recursively resolve `self.config` to replace the macro tokens with target content.
        Then recursively parse the config source, add every item as ``ConfigItem`` to the reference resolver.

        Args:
            reset: whether to reset the ``reference_resolver`` before parsing. Defaults to `True`.

        r(   N)r1   rP   resolve_macro_and_relative_ids	_do_parserY   )r5   rP   r9   r9   r:   parse   s   	
zConfigParser.parsekwargsc                 K  sP   | j  s| jdd n|dds| j|dd d | j jdd|i|S )a  
        Get the parsed result of ``ConfigItem`` with the specified ``id``.

            - If the item is ``ConfigComponent`` and ``instantiate=True``, the result is the instance.
            - If the item is ``ConfigExpression`` and ``eval_expr=True``, the result is the evaluated output.
            - Else, the result is the configuration content of `ConfigItem`.

        Args:
            id: id of the ``ConfigItem``, ``"::"`` (or ``"#"``) in id are interpreted as special characters to
                go one level further into the nested structures.
                Use digits indexing from "0" for list or other strings for dict.
                For example: ``"xform::5"``, ``"net::channels"``. ``""`` indicates the entire ``self.config``.
            kwargs: additional keyword arguments to be passed to ``_resolve_one_item``.
                Currently support ``lazy`` (whether to retain the current config cache, default to `True`),
                ``instantiate`` (whether to instantiate the `ConfigComponent`, default to `True`) and
                ``eval_expr`` (whether to evaluate the `ConfigExpression`, default to `True`), ``default``
                (the default config item if the `id` is not in the config content).

        T)rP   lazyr?   Nr9   )r1   Zis_resolvedre   rY   Zget_resolved_content)r5   r?   rf   r9   r9   r:   r>   	  s
   
zConfigParser.get_parsed_contentf$PathLike | Sequence[PathLike] | dictc                 K  s    |  | j|fi || j dS )a  
        Read the metadata from specified JSON or YAML file.
        The metadata as a dictionary will be stored at ``self.config["_meta_"]``.

        Args:
            f: filepath of the metadata file, the content must be a dictionary,
                if providing a list of files, will merge the content of them.
                if providing a dictionary directly, use it as metadata.
            kwargs: other arguments for ``json.load`` or ``yaml.safe_load``, depends on the file format.

        N)r3   load_config_filesr2   )r5   rh   rf   r9   r9   r:   	read_meta$  s    zConfigParser.read_metac                 K  s<   | j | | j i i}|| j|fi | | j|d dS )a  
        Read the config from specified JSON/YAML file or a dictionary and
        override the config content in the `self.config` dictionary.

        Args:
            f: filepath of the config file, the content must be a dictionary,
                if providing a list of files, wil merge the content of them.
                if providing a dictionary directly, use it as config.
            kwargs: other arguments for ``json.load`` or ``yaml.safe_load``, depends on the file format.

        r(   N)r2   rY   r-   rj   r3   )r5   rh   rf   contentr9   r9   r:   read_config2  s   zConfigParser.read_configc           	      C  s   t |ttfr| jj||dD ]\}}}| ||||< qt |trN| ||}|t	rNt
|tt	d \}}t
|sA|  nt
|d}t|| S |S )a^  
        Recursively resolve `self.config` to replace the relative ids with absolute ids, for example,
        `@##A` means `A` in the upper level. and replace the macro tokens with target content,
        The macro tokens start with "%", can be from another structured file, like:
        ``"%default_net"``, ``"%/data/config.json#net"``.
        Note that the macro replacement doesn't support recursive macro tokens.

        Args:
            config: input config file to resolve.
            id: id of the ``ConfigItem``, ``"::"`` (or ``"#"``) in id are interpreted as special characters to
                go one level further into the nested structures.
                Use digits indexing from "0" for list or other strings for dict.
                For example: ``"xform::5"``, ``"net::channels"``. ``""`` indicates the entire ``self.config``.

        r?   r#   Nr(   )r+   r,   rG   r1   iter_subconfigs_do_resolver/   resolve_relative_ids
startswithr   r   split_path_idlenrY   load_config_filer   )	r5   r#   r?   r7   sub_idr8   pathidsparserr9   r9   r:   rp   B  s   

zConfigParser._do_resolvec                 C  s   |  | j|  d dS )ao  
        Recursively resolve `self.config` to replace the relative ids with absolute ids, for example,
        `@##A` means `A` in the upper level. and replace the macro tokens with target content,
        The macro tokens are marked as starting with "%", can be from another structured file, like:
        ``"%default_net"``, ``"%/data/config.json::net"``.

        r(   N)r3   rp   rY   r<   r9   r9   r:   rc   ^  s   z+ConfigParser.resolve_macro_and_relative_idsc                 C  s   t |ttfr| jj||dD ]\}}}| j||d qt|r/| jt||| j	d dS t
|rB| jt
||| jd dS | jt||d dS )a!  
        Recursively parse the nested data in config source, add every item as `ConfigItem` to the resolver.

        Args:
            config: config source to parse.
            id: id of the ``ConfigItem``, ``"::"`` (or ``"#"``) in id are interpreted as special characters to
                go one level further into the nested structures.
                Use digits indexing from "0" for list or other strings for dict.
                For example: ``"xform::5"``, ``"net::channels"``. ``""`` indicates the entire ``self.config``.

        rn   )r#   r?   )r#   r?   r0   )r#   r?   r&   N)r+   r,   rG   r1   ro   rd   r	   is_instantiableZadd_itemr0   r
   is_expressionr&   r   )r5   r#   r?   ra   rv   r8   r9   r9   r:   rd   h  s   

zConfigParser._do_parsefilepathr   r,   c                 K  s   |si S t t|}t| jtj|std| dt|B}|	 
| jd r>tj|fdti|W  d   S |	 
| jdd r[tj|tfi |W  d   S td| d1 sfw   Y  dS )	aI  
        Load a single config file with specified file path (currently support JSON and YAML files).

        Args:
            filepath: path of target file to load, supported postfixes: `.json`, `.yml`, `.yaml`.
            kwargs: other arguments for ``json.load`` or ```yaml.safe_load``, depends on the file format.

        zunknown file input: ""r   object_pairs_hookN   z7only support JSON or YAML config file so far, got name rD   )r/   r   recompile
path_match
IGNORECASEfindallrH   openlowerendswithsuffixesr   loadr   r   r   )clsr|   rf   	_filepathrh   r9   r9   r:   ru     s   

zConfigParser.load_config_filefilesc                 K  s   t |tr|S ti d}t |tr t| s d|v r |d}t|D ]}| j|fi |}|	 D ]
\}}t
||| q3q$| S )a  
        Load multiple config files into a single config dict.
        The latter config file in the list will override or add the former config file.
        ``"::"`` (or ``"#"``) in the config keys are interpreted as special characters to go one level
        further into the nested structures.

        Args:
            files: path of target files to load, supported postfixes: `.json`, `.yml`, `.yaml`.
                if providing a list of files, will merge the content of them.
                if providing a string with comma separated file paths, will merge the content of them.
                if providing a dictionary, return it directly.
            kwargs: other arguments for ``json.load`` or ```yaml.safe_load``, depends on the file format.
        r(   ,)r+   r,   r   r/   r   is_filesplitr   ru   r.   r   rY   )r   r   rf   ry   iconfig_dictr7   r8   r9   r9   r:   rj     s   


zConfigParser.load_config_filesr   fmtc                 K  s   t t|}t| h d}t|d9}|dkr,tj||fi | 	 W d   dS |dks4|dkrEtj||fi |W  d   S t	d| d1 sPw   Y  dS )	a  
        Export the config content to the specified file path (currently support JSON and YAML files).

        Args:
            config: source config content to export.
            filepath: target file path to save.
            fmt: format of config content, currently support ``"json"`` and ``"yaml"``.
            kwargs: other arguments for ``json.dump`` or ``yaml.safe_dump``, depends on the file format.

        >   r   r   r   wr   Nr   r   z2only support JSON or YAML config file so far, got rD   )
r/   r   r   r   r   r   dumpr   	safe_dumprH   )r   r#   r|   r   rf   r   writerrh   r9   r9   r:   export_config_file  s   zConfigParser.export_config_filesrctuple[str, str]c                 C  sz   t |}td| j dt dtj|}|sd|fS |d d }||d\}}||	tr:|t
td fS dfS )a.  
        Split `src` string into two parts: a config file path and component id.
        The file path should end with `(json|yaml|yml)`. The component id should be separated by `::` if it exists.
        If no path or no id, return "".

        Args:
            src: source string to split.

        r!   z(?=(?:z.*)|$))rC   r   r   N)r   r]   r   r   suffix_matchr   r   r   rsplitrr   rt   )r   r   result	path_namera   rx   r9   r9   r:   rs     s   
$$zConfigParser.split_path_idvaluec           	      C  s   t |}tt | j|dd}|t}|D ]@}t	|v r"t	nt
}|t|d t}|t|kr=td| d|t|krFd}nt|d|  t }|||| }q|S )a"  
        To simplify the reference or macro tokens ID in the nested config content, it's available to use
        relative ID name which starts with the `ID_SEP_KEY`, for example, "@#A" means `A` in the same level,
        `@##A` means `A` in the upper level.
        It resolves the relative ids to absolute ids. For example, if the input data is:

        .. code-block:: python

            {
                "A": 1,
                "B": {"key": "@##A", "value1": 2, "value2": "%#value1", "value3": [3, 4, "@#1"]},
            }

        It will resolve `B` to `{"key": "@A", "value1": 2, "value2": "%B#value1", "value3": [3, 4, "@B#value3#1"]}`.

        Args:
            id: id name for current config item to compute relative id.
            value: input value to resolve relative ids.

        T)reverseNzthe relative id in `z(` is out of the range of config content.rC   )r   r]   sortedr3   unionrelative_id_prefixr   r   r   r   r   rt   countrH   joinreplace)	r   r?   r   prefixesZ
current_idpsymlengthnewr9   r9   r:   rq     s   

z!ConfigParser.resolve_relative_ids)NNN)r#   r   r$   r%   r&   r'   )r?   rA   rB   r   )r?   rA   r#   r   rB   rN   )rC   N)r?   r/   rU   rV   rB   r   )rC   T)r#   r   r?   r/   rZ   r[   rB   rN   )r_   r`   rB   rN   )r?   rA   rB   r[   )T)rP   r[   rB   rN   )rC   )r?   r/   rf   r   rB   r   )rh   ri   rf   r   rB   rN   )r#   r   r?   r/   rB   r   )r#   r   r?   r/   rB   rN   )r|   r   rf   r   rB   r,   )r   ri   rf   r   rB   r,   )r   )
r#   r,   r|   r   r   r/   rf   r   rB   rN   )r   r/   rB   r   )r?   r/   r   r/   rB   r/   )%__name__
__module____qualname____doc__r   r   r   r   r   r   r   r   r   r   r2   r;   r=   r@   rM   rT   rY   r3   r-   rb   re   r>   rk   rm   rp   rc   rd   classmethodru   rj   r   rs   rq   r9   r9   r9   r:   r   &   sH    8






)'
__future__r   r   r   collections.abcr   r*   r   pathlibr   typingr   r   Zmonai.bundle.config_itemr   r	   r
   r   Zmonai.bundle.reference_resolverr   monai.bundle.utilsr   r   r   r   monai.configr   monai.utilsr   r   r   monai.utils.miscr   r   r   ra   __all__r)   r   r9   r9   r9   r:   <module>   s&   
