U
    PhoY                     @  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 d d
lmZ d dlmZmZmZ d dlmZm Z  erd 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)PathLike)ensure_tuplelook_up_optionoptional_import)CheckKeyDuplicatesYamlLoadercheck_key_duplicatesyamlConfigParsermonaitorchnumpy)r   r   npr   c                	   @  s  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dUddddddZdd Zdd ZdddddZddddddZdVdd dd!d"d#ZdWddd%dd&d'd(Zd)dd*d+d,Zdd%dd-d.ZdXd%dd/d0d1ZdYdddd2d3d4Zd5ddd6d7d8Zd5ddd6d9d:ZdZdddd;d<d=Zd>d? Zd[dddd;d@dAZedBddCdDdEdFZ ed5ddCdGdHdIZ!ed\dCdBddddKdLdMZ"eddNdOdPdQZ#eddddRdSdTZ$dS )]r   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_Nr   zSequence[str] | str | Nonezdict[str, Any] | None | bool)configexcludesglobalsc                 C  s   d | _ i | _t }t|tr0|dkr0|| |d k	rr|dk	rr| D ](\}}t|trft	|d n|| j|< qHt
|d| _t | _|d kr| ji i}| 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)selfr"   r#   r$   _globalskv r5   O/home/dell461/cl/sdc2/HISourceFinder-master-l/src/monai/bundle/config_parser.py__init__f   s    
"
zConfigParser.__init__c                 C  s   | j  S )Nr%   r1   r5   r5   r6   __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)r1   idr5   r5   r6   __getattr__~   s    zConfigParser.__getattr__z	str | int)r;   returnc                 C  s   |dkr| j S | j }t|D ]}t|ttfsPtd| dt| d| dz(t|trjt||ddn
|t	| }W q tk
r } zt
d| |W 5 d	}~X Y qX q|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)r1   r;   r"   r3   er5   r5   r6   __getitem__   s     "$zConfigParser.__getitem__None)r;   r"   r=   c                 C  sb   |dkr|| _ | j  dS tj|dd\}}| | }t|trD|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``.

        r>   NT)last)r"   r.   resetr   rA   r(   r)   rE   )r1   r;   r"   Zlast_idbase_idconf_indexingr5   r5   r6   __setitem__   s    

zConfigParser.__setitem__r>   r,   z
Any | None)r;   defaultr=   c              
   C  s.   z
| | W S  t ttfk
r(   | Y S X dS )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.

        NrF   
IndexErrorrC   )r1   r;   rP   r5   r5   r6   get   s    	
zConfigParser.getTbool)r"   r;   	recursiver=   c                 C  s   t |}|  }|rn|dkr(i  | _}|dd D ]8}t|trR||krRi ||< |t|trb|nt| }q4|| 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   rA   rS   r"   r(   r)   rE   normalize_id)r1   r"   r;   rU   keysrM   r3   r5   r5   r6   r0      s    

zConfigParser.setzdict[str, Any])pairsr=   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+   )r1   rY   r3   r4   r5   r5   r6   r*      s    	zConfigParser.updatec              
   C  s0   z| | }W dS  t ttfk
r*   Y dS X dS )z
        Returns True if `id` is stored in this configuration.

        Args:
            id: id to specify the expected position. See also :py:meth:`__getitem__`.
        TFNrQ   )r1   r;   _r5   r5   r6   __contains__   s
    zConfigParser.__contains__)rK   r=   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)r.   rK   resolve_macro_and_relative_ids	_do_parserS   )r1   rK   r5   r5   r6   parse   s    	
zConfigParser.parse)r;   kwargsr=   c                 K  sP   | j  s| jdd n"|dds:| j|dd d | j jf 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)rK   lazyr;   )r.   Zis_resolvedr^   rS   Zget_resolved_content)r1   r;   r_   r5   r5   r6   r:   	  s
    
zConfigParser.get_parsed_contentz$PathLike | Sequence[PathLike] | dict)fr_   r=   c                 K  s   |  | j|f|| 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)r0   load_config_filesr/   )r1   ra   r_   r5   r5   r6   	read_meta$  s    zConfigParser.read_metac                 K  s8   | j | | j i i}|| j|f| | 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)r/   rS   r*   rb   r0   )r1   ra   r_   contentr5   r5   r6   read_config2  s    zConfigParser.read_config)r"   r;   r=   c           	      C  s   t |ttfr:| jj||dD ]\}}}| ||||< qt |tr| ||}|t	rt
|tt	d \}}t
|s|  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)   rB   r.   iter_subconfigs_do_resolver,   resolve_relative_ids
startswithr   r   split_path_idlenrS   load_config_filer   )	r1   r"   r;   r3   sub_idr4   pathidsparserr5   r5   r6   rh   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)r0   rh   rS   r8   r5   r5   r6   r\   ^  s    z+ConfigParser.resolve_macro_and_relative_idsc                 C  s   t |ttfr8| jj||dD ]\}}}| j||d qt|r\| jt||| j	d n8t
|r| jt
||| jd n| 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``.

        rf   )r"   r;   )r"   r;   r-   )r"   r;   r$   N)r(   r)   rB   r.   rg   r]   r	   is_instantiableZadd_itemr-   r
   is_expressionr$   r   )r1   r"   r;   rZ   rn   r4   r5   r5   r6   r]   h  s    

zConfigParser._do_parser   r)   )filepathr_   r=   c              
   K  s   |si S t t|}t| jtj|s:td| dt|}|	 
| jd rztj|fdti|W  5 Q R  S |	 
| jdd rtj|tf|W  5 Q R  S td| dW 5 Q R X 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_hook   Nz7only support JSON or YAML config file so far, got name r?   )r,   r   recompile
path_match
IGNORECASEfindallrC   openlowerendswithsuffixesr   loadr   r   r   )clsrt   r_   	_filepathra   r5   r5   r6   rm     s    

"zConfigParser.load_config_file)filesr_   r=   c                 K  sz   t |tr|S ti d}t |tr@t| s@d|kr@|d}t|D ](}| j|f|	 D ]\}}|||< q^qH|
 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   rm   r+   rS   )r   r   r_   rq   ir3   r4   r5   r5   r6   rb     s    


zConfigParser.load_config_filesr   )r"   rt   fmtr_   r=   c              
   K  s   t t|}t| dddh}t|dh}|dkrRtj||f| W 5 Q R  dS |dksb|dkr~tj||f|W  5 Q R  S t	d| dW 5 Q R X 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   wNz2only support JSON or YAML config file so far, got r?   )
r,   r   r   r~   r}   r   dumpr   	safe_dumprC   )r   r"   rt   r   r_   r   writerra   r5   r5   r6   export_config_file  s    zConfigParser.export_config_fileztuple[str, str])srcr=   c                 C  sx   t |}td| j dt dtj|}|s:d|fS |d d }||d\}}||	trr|t
td n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.*)|$))r>   r   rw   N)r   rW   rx   ry   suffix_matchr   r{   r|   rsplitrj   rl   )r   r   result	path_namerZ   rp   r5   r5   r6   rk     s    
$zConfigParser.split_path_id)r;   valuer=   c           	      C  s   t |}tt | j|dd}|t}|D ]}t	|krDt	nt
}|t|d t}|t|krztd| d|t|krd}nt|d|  t }|||| }q4|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.r>   )r   rW   sortedr0   unionrelative_id_prefixr|   r   r   r   r   rl   countrC   joinreplace)	r   r;   r   prefixesZ
current_idpsymlengthnewr5   r5   r6   ri     s    

z!ConfigParser.resolve_relative_ids)NNN)r>   N)r>   T)T)r>   )r>   )r>   )r   )%__name__
__module____qualname____doc__r   r   r   rz   rx   ry   r   r   r   r   r/   r7   r9   r<   rH   rO   rS   r0   r*   r[   r^   r:   rc   re   rh   r\   r]   classmethodrm   rb   r   rk   ri   r5   r5   r5   r6   r   &   sF   8   
)&
__future__r   r   rx   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   monai.configr   monai.utilsr   r   r   monai.utils.miscr   r   r   rZ   __all__r&   r   r5   r5   r5   r6   <module>   s$   
