U nfF@sdZddlZddlZddlZddlmZddlmZzddlmZWne k rXYnXddl m Z m Z m Z mZmZmZmZmZmZmZmZddlmZddlmZdd lmZmZmZmZdd lm Z dd l!m"Z"dd l#m$Z$m%Z%m&Z&m'Z'dd l(m)Z)m*Z*ddl+m,Z,e rVddl(m-Z-ddl.m/Z/m0Z0edddZ1eeddfZ2GdddZ3dddddgZ4edZ5ej6dkrVeej7e$fdedddddddddd d! e8e8e8e8e8e8eeee9dfee8ee8e8e ee5gd"fd# d$dZ:eej7e$fdedddddddddd d! ee5e8e8e8e8e8e8eeee9dfee8ee8e8d"d% d&dZ:neej7e$fdedddddddddd' e8e8e8e8e8e8eeee9dfee8ee8e ee5gd"fd( d)dZ:eej7e$fdedddddddddd' ee5e8e8e8e8e8e8eeee9dfee8ee8d"d* d+dZ:eej7e$fddLddddddddddd! eee5e8e8e8e8e8e8eeee9dfee8ee8e8ee ee5gd"fd"fd% d,dZ:eede8eedddfd-d.dZ;Gd/ddZd"d3d4d5d6Z?ede dd7d8d9Z@edfedee ee=ed:d;dd?d@ZBnde=e8d>dAd@ZBdddBdCdDZCde=e ddEdFdGZDee e8dHdIdZEedeed3dJdKdZFdS)MaX The main purpose is to enhance stdlib dataclasses by adding validation A pydantic dataclass can be generated from scratch or from a stdlib one. Behind the scene, a pydantic dataclass is just like a regular one on which we attach a `BaseModel` and magic methods to trigger the validation of the data. `__init__` and `__post_init__` are hence overridden and have extra logic to be able to validate input data. When a pydantic dataclass is generated from scratch, it's just a plain dataclass with validation triggered at initialization The tricky part if for stdlib dataclasses that are converted after into pydantic ones e.g. ```py @dataclasses.dataclass class M: x: int ValidatedM = pydantic.dataclasses.dataclass(M) ``` We indeed still want to support equality, hashing, repr, ... as if it was the stdlib one! ```py assert isinstance(ValidatedM(x=1), M) assert ValidatedM(x=1) == M(x=1) ``` This means we **don't want to create a new dataclass that inherits from it** The trick is to create a wrapper around `M` that will act as a proxy to trigger validation without altering default `M` behaviour. N)contextmanager)wraps)cached_property) TYPE_CHECKINGAnyCallableClassVarDict GeneratorOptionalTypeTypeVarUnionoverload)dataclass_transform)gather_all_validators) BaseConfig ConfigDictExtra get_config)ValidationError)DataclassTypeError)Field FieldInfoRequired Undefined) create_modelvalidate_model)ClassAttribute) BaseModel)CallableGeneratorNoArgAnyCallable DataclassT Dataclass)boundDataclassProxyc@seZdZUeeeefed<eeed<eeded<ee ed<eeded<ee ed<ee e ed<eedgd fed <ee ed <e e d d d dZ ee dddddZee deddddZd S)r#__dataclass_fields____dataclass_params__).N __post_init____pydantic_run_validation____post_init_post_parse____pydantic_initialised____pydantic_model__N__pydantic_validate_values__#__pydantic_has_field_info_default__argskwargsreturncOsdSNselfr0r1r4r4E/opt/hc_python/lib/python3.8/site-packages/pydantic/v1/dataclasses.py__init__PszDataclass.__init__r clsr2cCsdSr3r4r:r4r4r7__get_validators__SszDataclass.__get_validators__r"r:vr2cCsdSr3r4r:r>r4r4r7 __validate__WszDataclass.__validate__)__name__ __module__ __qualname__rr strr__annotations__rboolr robjectr8 classmethodr<r@r4r4r4r7r#Bs      dataclassset_validation$create_pydantic_model_from_dataclassis_builtin_dataclassmake_dataclass_validator_T )field_specifiersTF. initrepreqorder unsafe_hashfrozenconfigvalidate_on_init use_proxykw_onlyDataclassClassOrWrapper) rTrUrVrWrXrYrZr[r\r]r2c CsdSr3r4rSr4r4r7rIhs) _clsrTrUrVrWrXrYrZr[r\r]r2c CsdSr3r4) r_rTrUrVrWrXrYrZr[r\r]r4r4r7rIys rTrUrVrWrXrYrZr[r\) rTrUrVrWrXrYrZr[r\r2c CsdSr3r4r`r4r4r7rIs) r_rTrUrVrWrXrYrZr[r\r2c CsdSr3r4) r_rTrUrVrWrXrYrZr[r\r4r4r7rIsc sFt|ttdd f dd } |dkr>| S| |S)a Like the python standard lib dataclasses but with type validation. The result is either a pydantic dataclass that will validate input data or a wrapper that will trigger validation around a stdlib dataclass to avoid modifying it directly r^r9c sdk r n2t|o>|jdtkp>tt|tt|jdk}|rVd}t|}d}nL|jp^d}tjdkrt j |d}nt j |d}d} dkr|n }t ||||j j f|j|i|S)NrFrO)rTrUrVrWrXrYr])rTrUrVrWrXrYT)rL __bases__rGsetdirr%__doc__sys version_info dataclassesrI#_add_pydantic_validation_attributesr,Z__try_update_forward_refs__rA)r:Zshould_use_proxy dc_cls_docdc_clsZdefault_validate_on_initZshould_validate_on_init rVrYrTr]rWrUZ the_configrXr\r[r4r7wrapsF*   zdataclass..wrapN)rr r) r_rTrUrVrWrXrYrZr[r\r]rmr4rlr7rIs *%)r:valuer2ccs$|j}z||_|VW5||_XdSr3)r))r:rnZoriginal_run_validationr4r4r7rJs  c@seZdZdZedddddZeeeddd Zeed d d Z eedd ddZ ee dddZ ddddZ eddddZdS)r% __dataclass__r#N)rkr2cCst|d|dS)Nro)rG __setattr__)r6rkr4r4r7r8szDataclassProxy.__init__r/c Os0t|jd|j||W5QRSQRXdS)NT)rJror5r4r4r7__call__szDataclassProxy.__call__)namer2cCs t|j|Sr3)getattrro)r6rrr4r4r7 __getattr__szDataclassProxy.__getattr__)_DataclassProxy__name_DataclassProxy__valuer2cCst|j||Sr3)setattrro)r6rurvr4r4r7rpszDataclassProxy.__setattr__)instancer2cCs t||jSr3) isinstancero)r6rxr4r4r7__instancecheck__ sz DataclassProxy.__instancecheck__)r2cCstt|jSr3)r%copyror6r4r4r7__copy__szDataclassProxy.__copy__)memor2cCstt|j|Sr3)r%r{deepcopyro)r6r~r4r4r7 __deepcopy__szDataclassProxy.__deepcopy__)rArBrC __slots__r r8rrqrDrtrprFrzr}rr4r4r4r7r%s)rkrZr[rjr2csF|jtdttddfdd t|drz |jjWntk rX|jYnXtdttddfdd }t|d t|d|n,tdttddfd d }t|d |t|d td |t|d dt|dt ||t|dt t|dt t t|dt t |jjjrB|jjsBt|dtdS)a We need to replace the right method. If no `__post_init__` has been set in the stdlib dataclass it won't even exist (code is generated on the fly by `dataclasses`) By default, we run validation after `__init__` or `__post_init__` if defined r#N)r6r0r1r2csjtjkr0f|fdd|Dn`jtjkr|D]\}}j||qDf|fdd|Dnf||dS)Ncs i|]\}}|jkr||qSr4r&.0kr>r|r4r7 %s zR_add_pydantic_validation_attributes..handle_extra_init..cs i|]\}}|jkr||qSr4rrr|r4r7r*s )extrarignoreitemsZallow__dict__ setdefault)r6r0r1rr>)rZrTr|r7handle_extra_init"s $ $z>_add_pydantic_validation_attributes..handle_extra_initr(cs^jdkr|f|||jjr@|t|dr@|j||jdkrZ|f||dS)NZbefore_validationr*Zafter_validation)Zpost_init_call __class__r)r-hasattrr*r5)rZ post_initr4r7 new_post_init5s    z:_add_pydantic_validation_attributes..new_post_initr8c s|f|||jjr |t|dri}t|jjD]R\}}|jtj kr>z||||j <Wq>t k r| |j |j ||j <Yq>Xq>|jf|dS)Nr*)rr)r-r enumerater&values _field_typerh_FIELD_INITVARrr IndexErrorgetdefaultr*)r6r0r1Zinitvars_and_valuesif)rr4r7new_initGs  z5_add_pydantic_validation_attributes..new_initr)r+Fr,r-r@r<rp)r8rrrr( __wrapped__AttributeErrorrwrrK_dataclass_validate_valuesrH_validate_dataclass_get_validatorsr, __config__Zvalidate_assignmentr'rY&_dataclass_validate_assignment_setattr)rkrZr[rjrrr4)rZrrTrr7ris.        rir r9ccs |jVdSr3)r@r;r4r4r7rksrr=c Cst|dvt||r.||W5QRSt|ttfrP||W5QRSt|trp|f|W5QRSt|jdW5QRXdS)NT) class_name)rJryr-listtupledictrrAr?r4r4r7ros   rr)rkrZrjr2c Csi}t|D]t}t}d}|jtjk r.|j}n|jtjk rB|j}nt}t|tr\|}d|_ nt f||d|j }|j |f||j <qt|}t|jf||j|ddid|} |dk r|n|jpd| _| S)NT)rdefault_factoryZ__resolve_forward_refs__F)rrBZ__validators__Z__cls_kwargs__ra)rhfieldsrrMISSINGrrryrr.rmetadatatyperrrrrArBre) rkrZrjZfield_definitionsfieldrrZ field_infoZ validatorsmodelr4r4r7rK|s6   )rP)objrr2cCsttt||dtSr3)ryrsrrrrr4r4r7_is_field_cached_propertysrcCsdS)NFr4rr4r4r7rs)r6r2cstdrdStddr4fddjD}nfddjD}tj|jd\}}}|rl|j|tdddS) Nr+r.Fcs*i|]"\}}t|tst|s||qSr4)ryrrrr|r4r7rs  z._dataclass_validate_values..cs i|]\}}t|s||qSr4)rrr|r4r7rs r;T) rsrrrr,rupdaterGrp)r6Z input_datad_Zvalidation_errorr4r|r7rs    r)r6rrrnr2cCsl|jrZt|j}||d|jj|d}|rZ|j||||jd\}}|rZt |g|jt |||dS)N)locr:) r+rrpopr,Z __fields__rvalidaterrrGrp)r6rrrnrZ known_fieldZerror_r4r4r7rs  r)r_r2cCs2t|o0t|d o0t|jtt|diS)a Whether a class is a stdlib dataclass (useful to discriminated a pydantic dataclass that is actually a wrapper around a stdlib dataclass) we check that - `_cls` is a dataclass - `_cls` is not a processed pydantic dataclass (with a basemodel attached) - `_cls` is not a pydantic dataclass inheriting directly from a stdlib dataclass e.g. ``` @dataclasses.dataclass class A: x: int @pydantic.dataclasses.dataclass class B(A): y: int ``` In this case, when we first check `B`, we make an extra check and look at the annotations ('y'), which won't be a superset of all the dataclass fields (only the stdlib fields i.e. 'x') r,rE)rh is_dataclassrrcr& issupersetrs)r_r4r4r7rLs   )rkrZr2ccstt||ddEdHdS)z Create a pydantic.dataclass from a builtin dataclass to add type validation and yield the validators It retrieves the parameters of the dataclass and forwards them to the newly created dataclass T)rZr\N)rrI)rkrZr4r4r7rMs)N)Grer{rhrf contextlibr functoolsrr ImportErrortypingrrrrr r r r r rrtyping_extensionsrZpydantic.v1.class_validatorsrZpydantic.v1.configrrrrZpydantic.v1.error_wrappersrZpydantic.v1.errorsrZpydantic.v1.fieldsrrrrZpydantic.v1.mainrrZpydantic.v1.utilsrrZpydantic.v1.typingr r!r"r^r#__all__rNrgrrFrGrIrJr%rDrirrrKrrrrLrMr4r4r4r7sn!  4         A&  V '