U
    diO                     @   s2  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mZmZ d dl	m	Z	 d dl
mZ d dlmZ d dlmZ d dlmZmZmZmZmZmZmZmZmZmZmZ d dlZedeZed	eZd
d Ze ee!gef dddZ"dde j#e j#ddee!ee! f e!eeg ef e$e j%dddZ&G dd deZ'dS )    N)ArgumentDefaultsHelpFormatterArgumentParserArgumentTypeError)copy)Enum)isclassPath)AnyCallableDictIterableListLiteralNewTypeOptionalTupleUnionget_type_hints	DataClassDataClassTypec                 C   sB   t | tr| S |  dkrdS |  dkr.dS td|  dd S )N)yestruety1T)nofalsefn0FzTruthy value expected: got zJ but expected one of yes/no, true/false, t/f, y/n, 1/0 (case insensitive).)
isinstanceboollowerr   )v r%   =/tmp/pip-unpacked-wheel-bm_b0l5e/transformers/hf_argparser.pystring_to_bool#   s    

r'   )choicesreturnc                    s   dd | D   fddS )aN  
    Creates a mapping function from each choices string representation to the actual value. Used to support multiple
    value types for a single argument.

    Args:
        choices (list): List of choices.

    Returns:
        Callable[[str], Any]: Mapping function from string representation to actual value for each choice.
    c                 S   s   i | ]}t ||qS r%   )str).0choicer%   r%   r&   
<dictcomp>;   s      z-make_choice_type_function.<locals>.<dictcomp>c                    s     | | S )N)get)argZstr_to_choicer%   r&   <lambda><       z+make_choice_type_function.<locals>.<lambda>r%   )r(   r%   r0   r&   make_choice_type_function0   s    r3   )aliaseshelpdefaultdefault_factorymetadata)r4   r5   r6   r7   r8   r)   c                 K   sD   |dkri }| dk	r| |d< |dk	r,||d< t jf |||d|S )a  Argument helper enabling a concise syntax to create dataclass fields for parsing with `HfArgumentParser`.

    Example comparing the use of `HfArg` and `dataclasses.field`:
    ```
    @dataclass
    class Args:
        regular_arg: str = dataclasses.field(default="Huggingface", metadata={"aliases": ["--example", "-e"], "help": "This syntax could be better!"})
        hf_arg: str = HfArg(default="Huggingface", aliases=["--example", "-e"], help="What a nice syntax!")
    ```

    Args:
        aliases (Union[str, List[str]], optional):
            Single string or list of strings of aliases to pass on to argparse, e.g. `aliases=["--example", "-e"]`.
            Defaults to None.
        help (str, optional): Help string to pass on to argparse that can be displayed with --help. Defaults to None.
        default (Any, optional):
            Default value for the argument. If not default or default_factory is specified, the argument is required.
            Defaults to dataclasses.MISSING.
        default_factory (Callable[[], Any], optional):
            The default_factory is a 0-argument function called to initialize a field's value. It is useful to provide
            default values for mutable types, e.g. lists: `default_factory=list`. Mutually exclusive with `default=`.
            Defaults to dataclasses.MISSING.
        metadata (dict, optional): Further metadata to pass on to `dataclasses.field`. Defaults to None.

    Returns:
        Field: A `dataclasses.Field` with the desired properties.
    Nr4   r5   )r8   r6   r7   )dataclassesfield)r4   r5   r6   r7   r8   kwargsr%   r%   r&   HfArg?   s    $r<   c                       s   e Zd ZU dZee ed< eeee f d fddZe	e
ejdddZed	d
dZdeedf dddZdeeef eeedf dddZdeeejf eeedf dddZdeeejf eeedf dddZ  ZS ) HfArgumentParsera  
    This subclass of `argparse.ArgumentParser` uses type hints on dataclasses to generate arguments.

    The class is designed to play well with the native argparse. In particular, you can add more (non-dataclass backed)
    arguments to the parser after initialization and you'll get the output back after parsing as an additional
    namespace. Optional: To create sub argument groups use the `_argument_group_name` attribute in the dataclass.
    dataclass_types)r>   c                    sR   d|krt |d< t jf | t|r.|g}t|| _| jD ]}| | q>dS )a%  
        Args:
            dataclass_types:
                Dataclass type, or list of dataclass types for which we will "fill" instances with the parsed args.
            kwargs (`Dict[str, Any]`, *optional*):
                Passed to `argparse.ArgumentParser()` in the regular way.
        formatter_classN)r   super__init__r9   Zis_dataclasslistr>   _add_dataclass_arguments)selfr>   r;   dtype	__class__r%   r&   rA   y   s    	


zHfArgumentParser.__init__)parserr:   c                 C   s  d|j  g}d|j kr2|d|j dd  |j }t|jtrPtd|	dg }t|trl|g}t
|jd|j}|tksttdrxt|tjrxt|jjkrt|jjdkstd |jjkrtd	|j  d
td |jjkr,|jjd tkr|jjd n
|jjd |_t
|jd|j}nLt|jjkrxtd |jjd rZ|jjd n
|jjd |_t
|jd|j}i }|tkst|jtrt|jtr|tkr|jj|d< ndd |jD |d< t|d |d< |jtjk	r|j|d< nd|d< n.|jtks |jtt krt|}t|d< |jtksV|jd k	r2|jtjk	r2|jtjkrhdn|j}||d< d|d< d|d< nt|rt|tr|jjd |d< d|d< |jtjk	r| |d< n|jtjkr2d|d< nH|j|d< |jtjk	r|j|d< n$|jtjk	r*| |d< nd|d< | j||| |jdkr|jtksj|jtt krd|d< | jd|j  d|j dd fd|j d| d S )Nz--_-zpUnresolved type detected, which should have been done with the help of `typing.get_type_hints` method by defaultr4   
__origin__	UnionType   zOnly `Union[X, NoneType]` (i.e., `Optional[X]`) is allowed for `Union` because the argument parser only supports one type per argument. Problem encountered in field 'z'.   r   r(   c                 S   s   g | ]
}|j qS r%   )value)r+   xr%   r%   r&   
<listcomp>   s     z;HfArgumentParser._parse_dataclass_field.<locals>.<listcomp>typer6   TrequiredF?nargsconst+z--no_z--no-store_false)actiondest) nameappendreplacer8   r   r!   rR   r*   RuntimeErrorpopgetattrr   hasattrtypesrL   __args__len
ValueErrorr"   r   
issubclassr   r3   r6   r9   MISSINGr   r'   r   rB   r7   add_argument)rH   r:   Zlong_optionsr;   r4   Zorigin_typeZbool_kwargsr6   r%   r%   r&   _parse_dataclass_field   s    


",*&
&


(
z'HfArgumentParser._parse_dataclass_field)rE   c              
   C   s   t |dr| |j}n| }zt|}W n tk
rL   td| dY nt tk
r } zVtjd d dk rdt	|krd
tt	tjd d }td| d	| d
| W 5 d }~X Y nX t|D ]$}|jsq||j |_| || qd S )N_argument_group_namezType resolution failed for z. Try declaring the class in global scope or removing line of `from __future__ import annotations` which opts in Postponed Evaluation of Annotations (PEP 563)rM   )   
   z!unsupported operand type(s) for |.rk   z on Python a6  . Try removing line of `from __future__ import annotations` which opts in union types as `X | Y` (PEP 604) via Postponed Evaluation of Annotations (PEP 563). To support Python versions that lower than 3.10, you need to use `typing.Union[X, Y]` instead of `X | Y` and `typing.Optional[X]` instead of `X | None`.)ra   add_argument_grouprj   r   	NameErrorr^   	TypeErrorsysversion_infor*   joinmapr9   fieldsinitr[   rR   ri   )rD   rE   rH   Z
type_hintsexpython_versionr:   r%   r%   r&   rC      s.    

z)HfArgumentParser._add_dataclass_argumentsNFT.)r)   c                    s  |s|s|rt tjrg }|r.|t| n(|rVt tjrV|ttjd d |rt }|j|tdd |j	|d\}}t
||dd}	|	r|dd	 |	D  g }
|D ]}| r|
|  7 }
q|dk	r|
| n|
tjd
d  }| j	|d\}}g }| jD ]^}dd t|D   fddt
| D } D ]}t|| qF|f |}|| qt |jdkr|| |r||fS |rtd| |S dS )a'  
        Parse command-line args into instances of the specified dataclass types.

        This relies on argparse's `ArgumentParser.parse_known_args`. See the doc at:
        docs.python.org/3.7/library/argparse.html#argparse.ArgumentParser.parse_args

        Args:
            args:
                List of strings to parse. The default is taken from sys.argv. (same as argparse.ArgumentParser)
            return_remaining_strings:
                If true, also return a list of remaining argument strings.
            look_for_args_file:
                If true, will look for a ".args" file with the same base name as the entry point script for this
                process, and will append its potential content to the command line args.
            args_filename:
                If not None, will uses this file instead of the ".args" file specified in the previous argument.
            args_file_flag:
                If not None, will look for a file in the command-line args specified with this flag. The flag can be
                specified multiple times and precedence is determined by the order (last one wins).

        Returns:
            Tuple consisting of:

                - the dataclass instances in the same order as they were passed to the initializer.abspath
                - if applicable, an additional namespace for more (non-dataclass backed) arguments added to the parser
                  after initialization.
                - The potential list of remaining argument strings. (same as argparse.ArgumentParser.parse_known_args)
        r   z.argsr\   )rR   rY   )argsrJ   Nc                 S   s   g | ]}t |qS r%   r   )r+   pr%   r%   r&   rQ   O  s     z@HfArgumentParser.parse_args_into_dataclasses.<locals>.<listcomp>rN   c                 S   s   h | ]}|j r|jqS r%   rv   r[   r+   r   r%   r%   r&   	<setcomp>\  s      z?HfArgumentParser.parse_args_into_dataclasses.<locals>.<setcomp>c                    s   i | ]\}}| kr||qS r%   r%   r+   kr$   keysr%   r&   r-   ]  s       z@HfArgumentParser.parse_args_into_dataclasses.<locals>.<dictcomp>z?Some specified arguments are not used by the HfArgumentParser: )rd   rq   argvr\   r	   with_suffixr   rh   r*   parse_known_argsvarsr.   lstripextendexists	read_textsplitr>   r9   ru   itemsdelattr__dict__re   )rD   ry   Zreturn_remaining_stringsZlook_for_args_fileZargs_filenameZargs_file_flagZ
args_filesZargs_file_parsercfgZcmd_args_file_pathsZ	file_args	args_file	namespaceremaining_argsoutputsrE   inputsr   objr%   r   r&   parse_args_into_dataclasses  sD    %"



z,HfArgumentParser.parse_args_into_dataclasses)ry   allow_extra_keysr)   c                    s   t | }g }| jD ]P}dd t|D   fdd| D }||  |f |}|| q|s|rtdt	| t
|S )a<  
        Alternative helper method that does not use `argparse` at all, instead uses a dict and populating the dataclass
        types.

        Args:
            args (`dict`):
                dict containing config values
            allow_extra_keys (`bool`, *optional*, defaults to `False`):
                Defaults to False. If False, will raise an exception if the dict contains keys that are not parsed.

        Returns:
            Tuple consisting of:

                - the dataclass instances in the same order as they were passed to the initializer.
        c                 S   s   h | ]}|j r|jqS r%   r{   r|   r%   r%   r&   r}     s      z.HfArgumentParser.parse_dict.<locals>.<setcomp>c                    s   i | ]\}}| kr||qS r%   r%   r~   r   r%   r&   r-     s       z/HfArgumentParser.parse_dict.<locals>.<dictcomp>z0Some keys are not used by the HfArgumentParser: )setr   r>   r9   ru   r   difference_updater\   re   sortedtuple)rD   ry   r   Zunused_keysr   rE   r   r   r%   r   r&   
parse_dictm  s    

zHfArgumentParser.parse_dict)	json_filer   r)   c              	   C   s@   t t|dd}t| }W 5 Q R X | j||d}t|S )at  
        Alternative helper method that does not use `argparse` at all, instead loading a json file and populating the
        dataclass types.

        Args:
            json_file (`str` or `os.PathLike`):
                File name of the json file to parse
            allow_extra_keys (`bool`, *optional*, defaults to `False`):
                Defaults to False. If False, will raise an exception if the json file contains keys that are not
                parsed.

        Returns:
            Tuple consisting of:

                - the dataclass instances in the same order as they were passed to the initializer.
        zutf-8)encodingr   )openr	   jsonloadsreadr   r   )rD   r   r   Zopen_json_filedatar   r%   r%   r&   parse_json_file  s    z HfArgumentParser.parse_json_file)	yaml_filer   r)   c                 C   s$   | j tt| |d}t|S )at  
        Alternative helper method that does not use `argparse` at all, instead loading a yaml file and populating the
        dataclass types.

        Args:
            yaml_file (`str` or `os.PathLike`):
                File name of the yaml file to parse
            allow_extra_keys (`bool`, *optional*, defaults to `False`):
                Defaults to False. If False, will raise an exception if the json file contains keys that are not
                parsed.

        Returns:
            Tuple consisting of:

                - the dataclass instances in the same order as they were passed to the initializer.
        r   )r   yamlZ	safe_loadr	   r   r   )rD   r   r   r   r%   r%   r&   parse_yaml_file  s    z HfArgumentParser.parse_yaml_file)NFTNN)F)F)F)__name__
__module____qualname____doc__r   r   __annotations__r   rA   staticmethodr   r9   Fieldri   rC   r   r   r   r   r*   r
   r"   r   osPathLiker   r   __classcell__r%   r%   rF   r&   r=   n   s6   
i$     
V$  
  
r=   )(r9   r   r   rq   rb   argparser   r   r   r   enumr   inspectr   pathlibr	   typingr
   r   r   r   r   r   r   r   r   r   r   r   r   r   r'   rB   r*   r3   rg   dictr   r<   r=   r%   r%   r%   r&   <module>   s:   4


/