U
    diC                     @   s  d dl Z d dlmZmZmZmZ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mZmZmZ ddlmZmZmZ ee
gdf ZedZeee ee dd	d
Zeddeeee gee f ddd
Zd5ddeee  eeee eee gee f f ddd
Zeedddddfeee ef ee
ef eeg e
f ef eeee eee e
e
d
ddZ edddZ!e"e
e
ddddZ#e"e
ee
df ddddZ$e"e
ee
df dddd Z%e"e
ee
df ddd!d"Z&e"e
ee
df ddd#d$Z'e"e
ee
df ddd%d&Z(e"e
ee
df ddd'd(Z)e"e
e*ddd)d*Z+eed+d,d-Z,e
ed.d/d0Z-ee$ee%e.e&e/e'e0e(e1e)iZ2d
dd1d2d3d4gZ3dS )6    N)_MISSING_TYPEMISSINGFieldfieldfields)wraps)AnyCallableDict
ForwardRefListLiteralOptionalTupleTypeTypeVarUnionget_args
get_originoverload   )#StrictDataclassClassValidationErrorStrictDataclassDefinitionError#StrictDataclassFieldValidationErrorTclsreturnc                 C   s   d S N r   r   r   ?/tmp/pip-unpacked-wheel-ohruig0i/huggingface_hub/dataclasses.pystrict!   s    r"   Faccept_kwargs)r$   r   c                 C   s   d S r   r   r#   r   r   r!   r"   %   s    )r   r$   r   c                   s0   t t t t d fdd}| dk	r,|| S |S )a  
    Decorator to add strict validation to a dataclass.

    This decorator must be used on top of `@dataclass` to ensure IDEs and static typing tools
    recognize the class as a dataclass.

    Can be used with or without arguments:
    - `@strict`
    - `@strict(accept_kwargs=True)`

    Args:
        cls:
            The class to convert to a strict dataclass.
        accept_kwargs (`bool`, *optional*):
            If True, allows arbitrary keyword arguments in `__init__`. Defaults to False.

    Returns:
        The enhanced dataclass with strict validation on field assignment.

    Example:
    ```py
    >>> from dataclasses import dataclass
    >>> from huggingface_hub.dataclasses import as_validated_field, strict, validated_field

    >>> @as_validated_field
    >>> def positive_int(value: int):
    ...     if not value >= 0:
    ...         raise ValueError(f"Value must be positive, got {value}")

    >>> @strict(accept_kwargs=True)
    ... @dataclass
    ... class User:
    ...     name: str
    ...     age: int = positive_int(default=10)

    # Initialize
    >>> User(name="John")
    User(name='John', age=10)

    # Extra kwargs are accepted
    >>> User(name="John", age=30, lastname="Doe")
    User(name='John', age=30, *lastname='Doe')

    # Invalid type => raises
    >>> User(name="John", age="30")
    huggingface_hub.errors.StrictDataclassFieldValidationError: Validation error for field 'age':
        TypeError: Field 'age' expected int, got str (value: '30')

    # Invalid value => raises
    >>> User(name="John", age=-1)
    huggingface_hub.errors.StrictDataclassFieldValidationError: Validation error for field 'age':
        ValueError: Value must be positive, got -1
    ```
    r   c                    s  t  dstd j di }t D ]x}g }|t| |jd}|d k	rt|t	sb|g}|D ]$}t
|sftd|j d| dqf|| |||j< q(| _ jtttd dfd	d
}| _r$ jttd d fdd}| _ jttd fdd}| _g }	t D ]f}
|
dsDq0t |
}t|s\q0tt|jdkrtd j d|
 d|	| q0|	 _td d fdd}d|_t  drt jddstd j d| _ jtd d fdd}t  d|  S ) N__dataclass_fields__zClass 'z.' must be a dataclass before applying @strict.	validatorzInvalid validator for field '': z.. Must be a callable taking a single argument.)selfnamevaluer   c                    sf   | j |g D ]F}z|| W q ttfk
rR } zt||d|W 5 d}~X Y qX q | || dS )z1Custom __setattr__ method for strict dataclasses.)r   causeN)__validators__get
ValueError	TypeErrorr   )r(   r)   r*   r&   e)original_setattrr   r!   __strict_setattr__~   s    "z0strict.<locals>.wrap.<locals>.__strict_setattr__)kwargsr   c                    s^   dd t D   fdd| D }| f| | D ]\}}| kr<| || q<d S )Nc                 S   s   h | ]
}|j qS r   )r)   ).0fr   r   r!   	<setcomp>   s     z9strict.<locals>.wrap.<locals>.__init__.<locals>.<setcomp>c                    s   i | ]\}}| kr||qS r   r   r4   kvZdataclass_fieldsr   r!   
<dictcomp>   s       z:strict.<locals>.wrap.<locals>.__init__.<locals>.<dictcomp>)r   items__setattr__)r(   r3   Zstandard_kwargsr)   r*   )r   original_initr:   r!   __init__   s    z&strict.<locals>.wrap.<locals>.__init__)r   c                    sJ   | } fdd| j  D }d|}|rF|d d  d| dS |S )Nc                    s*   g | ]"\}}| j krd | d|qS )*=)r%   r7   r    r   r!   
<listcomp>   s   
z:strict.<locals>.wrap.<locals>.__repr__.<locals>.<listcomp>z, ))__dict__r<   join)r(   Zstandard_reprZadditional_kwargsZadditional_repr)r   original_reprr   r!   __repr__   s    

z&strict.<locals>.wrap.<locals>.__repr__Z	validate_r   z' has a class validator 'z' that takes more than one argument. Class validators must take only 'self' as an argument. Methods starting with 'validate_' are considered to be class validators.)r(   r   c                    sT    j D ]H}z||  W q ttfk
rL } zt|j|d|W 5 d}~X Y qX qdS )z%Run class validators on the instance.)r&   r+   N)__class_validators__r.   r/   r   __name__)r(   r&   r0   r    r   r!   validate   s
    
z&strict.<locals>.wrap.<locals>.validateTrK   "__is_defined_by_strict_decorator__Fz' already implements a method called 'validate'. This method name is reserved when using the @strict decorator on a dataclass. If you want to keep your own method, please rename it.c                    s   | f||   |  dS )z*Run class validators after initialization.N)rK   )r(   argsr3   )r   initial_initr   r!   init_with_validate   s    z0strict.<locals>.wrap.<locals>.init_with_validater?   )!hasattrr   rJ   r   append_create_type_validatormetadatar-   
isinstancelist_is_validatorr)   extendr,   r=   r   strr?   r   rH   dir
startswithgetattrcallableleninspect	signature
parametersrI   r   rL   rK   setattr)r   Zfield_validatorsr5   Z
validatorsZcustom_validatorr&   r2   r?   rH   Zclass_validatorsr)   methodrK   rO   r#   )r   rN   r>   rG   r1   r!   wrapc   st    





zstrict.<locals>.wrapN)r   r   )r   r$   rc   r   r#   r!   r"   )   s    : T)
r&   defaultdefault_factoryinitreprhashcomparerS   r3   r   c           	   
   K   sB   t | ts| g} |dkri }| |d< tf |||||||d|S )a<  
    Create a dataclass field with a custom validator.

    Useful to apply several checks to a field. If only applying one rule, check out the [`as_validated_field`] decorator.

    Args:
        validator (`Callable` or `List[Callable]`):
            A method that takes a value as input and raises ValueError/TypeError if the value is invalid.
            Can be a list of validators to apply multiple checks.
        **kwargs:
            Additional arguments to pass to `dataclasses.field()`.

    Returns:
        A field with the validator attached in metadata
    Nr&   rd   re   rf   rg   rh   ri   rS   )rT   rU   r   )	r&   rd   re   rf   rg   rh   ri   rS   r3   r   r   r!   validated_field   s     
rk   r&   c              
      sT   t t dddddftttf ttg tf tf tttt ttt td fdd}|S )a  
    Decorates a validator function as a [`validated_field`] (i.e. a dataclass field with a custom validator).

    Args:
        validator (`Callable`):
            A method that takes a value as input and raises ValueError/TypeError if the value is invalid.
    TNrd   re   rf   rg   rh   ri   rS   r3   c              
      s    t  f| ||||||d|S )Nrj   )rk   rm   rl   r   r!   _inner%  s    
	z"as_validated_field.<locals>._inner)r   r   r   r   r	   boolr   r
   )r&   rn   r   rl   r!   as_validated_field  s$    

rp   )r)   r*   expected_typer   c                 C   s   t |}t|}|tkrdS t| }r8|| || nDt|trPt| || n,t|tsdt|t	rhdS t
d|  d| dS )z.Validate that 'value' matches 'expected_type'.NzUnsupported type for field 'r'   )r   r   r   _BASIC_TYPE_VALIDATORSr-   rT   type_validate_simple_typer   rX   r/   )r)   r*   rq   originrM   r&   r   r   r!   type_validator>  s    
rv   .)r)   r*   rM   r   c                 C   s   g }|D ]J}zt | || W  dS  tk
rP } z|t| W 5 d}~X Y qX qtd|  dt| d| dd| dS )z8Validate that value matches one of the types in a Union.NField 'z' with value z doesn't match any type in z
. Errors: z; )rv   r/   rQ   rX   rg   rF   )r)   r*   rM   errorstr0   r   r   r!   _validate_unionO  s    "$rz   c                 C   s&   ||kr"t d|  d| d| dS )zValidate Literal type.rw   z' expected one of , got N)r/   )r)   r*   rM   r   r   r!   _validate_literal^  s    r|   c                 C   s   t |ts$td|  dt|j |d }t|D ]`\}}zt|  d| d|| W q4 tk
r } ztd| d|  d|W 5 d	}~X Y q4X q4d	S )
zValidate List[T] type.rw   z' expected a list, got r   []Invalid item at index z
 in list ''N)rT   rU   r/   rs   rJ   	enumeraterv   r)   r*   rM   Z	item_typeiitemr0   r   r   r!   _validate_listd  s    
r   c                 C   s   t |ts$td|  dt|j |\}}| D ]l\}}z.t|  d|| t|  d|d|| W q4 tk
r } ztd|  d|W 5 d}~X Y q4X q4dS )	zValidate Dict[K, V] type.rw   z' expected a dict, got z.keyr}   r~   zInvalid key or value in dict 'r   N)rT   dictr/   rs   rJ   r<   rv   )r)   r*   rM   Zkey_typeZ
value_typer8   r9   r0   r   r   r!   _validate_dictr  s    
r   c                 C   sZ  t |ts$td|  dt|j t|dkr|d tkrt|D ]d\}}z t|  d| d||d  W qD tk
r } ztd| d	|  d
|W 5 d}~X Y qDX qDnt|t|krtd|  dt| dt| nvtt	||D ]f\}\}}zt|  d| d|| W q tk
rR } ztd| d	|  d
|W 5 d}~X Y qX qdS )zValidate Tuple type.rw   z' expected a tuple, got    r   r}   r~   r   r   z in tuple 'r   Nz' expected a tuple of length r{   )
rT   tupler/   rs   rJ   r]   Ellipsisr   rv   zip)r)   r*   rM   r   r   r0   expectedr   r   r!   _validate_tuple  s    
 .$r   c                 C   s   t |ts$td|  dt|j |d }t|D ]T\}}zt|  d|| W q4 tk
r } ztd|  d|W 5 d}~X Y q4X q4dS )zValidate Set[T] type.rw   z' expected a set, got r   z itemzInvalid item in set 'r   N)rT   setr/   rs   rJ   r   rv   r   r   r   r!   _validate_set  s    
r   c              
   C   s<   t ||s8td|  d|j dt|j dt| d	dS )z&Validate simple type (int, str, etc.).rw   z' expected r{   z	 (value: rD   N)rT   r/   rJ   rs   rg   )r)   r*   rq   r   r   r!   rt     s    
(rt   )r   r   c                    s   t dd fdd}|S )z-Create a type validator function for a field.N)r*   r   c                    s   t  j|  j d S r   )rv   r)   rs   )r*   r   r   r!   r&     s    z)_create_type_validator.<locals>.validator)r   )r   r&   r   r   r!   rR     s    rR   )r&   r   c                 C   s   t | sdS t| }t|j }t|dkr4dS |d jtjj	tjj
tjjfkrXdS |dd D ]}|jtjjkrd dS qddS )zCheck if a function is a validator.

    A validator is a Callable that can be called with a single positional argument.
    The validator can have more arguments with default values.

    Basically, returns True if `validator(value)` is possible.
    Fr   r   NT)r\   r^   r_   rU   r`   valuesr]   kind	ParameterPOSITIONAL_OR_KEYWORDPOSITIONAL_ONLYVAR_POSITIONALrd   empty)r&   r_   r`   Z	parameterr   r   r!   rV     s     
rV   Validator_Tr   r   r   )N)4r^   Zdataclassesr   r   r   r   r   	functoolsr   typingr   r	   r
   r   r   r   r   r   r   r   r   r   r   r   rx   r   r   r   r   r   r"   ro   rk   rp   rX   rv   rz   r|   r   r   r   r   rs   rt   rR   rV   rU   r   r   r   rr   __all__r   r   r   r!   <module>   s   @* 
   K
+"
      