5

I want to validate since the instance creation if the type is right or wrong, i tried using @dataclass decorator but doesn't allow me to use the __init__ method, i also tried using a custom like class type

also in order of the type made some validations (if is a int, that field>0 or if is a str clean whitespaces, for example), i could use a dict to validate the type, but i want to know if there's a way to do it in pythonic way

class Car(object): """ My class with many fields """ color: str name: str wheels: int def __init__(self): """ Get the type of fields and validate """ pass 
1
  • Related: Validating complex types in dataclasses. If you run into problems because your types aren't something straight forward like str, int, or float, the answer in that post might help. Commented Nov 7, 2018 at 9:25

4 Answers 4

8

You can use the __post_init__ method of dataclasses to do your validations.

Below I just confirm that everything is an instance of the indicated type

from dataclasses import dataclass, fields def validate(instance): for field in fields(instance): attr = getattr(instance, field.name) if not isinstance(attr, field.type): msg = "Field {0.name} is of type {1}, should be {0.type}".format(field, type(attr)) raise ValueError(msg) @dataclass class Car: color: str name: str wheels: int def __post_init__(self): validate(self) 
Sign up to request clarification or add additional context in comments.

Comments

1

An alternative to @dataclass is to use pyfields. It provides validation and conversion out of the box, and is directly done at the field level so you can use fields inside any class, without modifying them in any way.

from pyfields import field, init_fields from valid8.validation_lib import is_in ALLOWED_COLORS = ('blue', 'yellow', 'brown') class Car(object): """ My class with many fields """ color: str = field(check_type=True, validators=is_in(ALLOWED_COLORS)) name: str = field(check_type=True, validators={'should be non-empty': lambda s: len(s) > 0}) wheels: int = field(check_type=True, validators={'should be positive': lambda x: x > 0}) @init_fields def __init__(self, msg="hello world!"): print(msg) c = Car(color='blue', name='roadie', wheels=3) c.wheels = 'hello' # <-- (1) type validation error, see below c.wheels = 0 # <-- (2) value validation error, see below 

yields the following two errors

TypeError: Invalid value type provided for '<...>.Car.wheels'. Value should be of type <class 'int'>. Instead, received a 'str': 'hello' 

and

valid8.entry_points.ValidationError[ValueError]: Error validating [<...>.Car.wheels=0]. InvalidValue: should be positive. Function [<lambda>] returned [False] for value 0. 

See pyfields documentation for details. I'm the author by the way :)

Comments

0

Dataclasses do not check the data. But I made a small superstructure for dataclasses, and you can use it this way:

import json from dataclasses import dataclass from validated_dc import ValidatedDC @dataclass class Car(ValidatedDC): color: str name: str wheels: int # This string was received by api data = '{"color": "gray", "name": "Delorean", "wheels": 4}' # Let's upload this json-string to the dictionary data = json.loads(data) car = Car(**data) assert car.get_errors() is None # Let's say the key "color" got the wrong value: data['color'] = 11111 car = Car(**data) assert car.get_errors() print(car.get_errors()) # { # 'color': [ # BasicValidationError( # value_repr='11111', value_type=<class 'int'>, # annotation=<class 'str'>, exception=None # ) # ] # } # fix car.color = 'gray' # is_valid() - Starting validation of an already created instance # (if True returns, then there are no errors) assert car.is_valid() assert car.get_errors() is None 

ValidatedDC: https://github.com/EvgeniyBurdin/validated_dc

Comments

0

Use pydantic. In this example, the field password1 is only validated for being a string, while other fields have custom validator functions.

from pydantic import BaseModel, ValidationError, validator class UserModel(BaseModel): name: str username: str password1: str password2: str @validator('name') def name_must_contain_space(cls, v): if ' ' not in v: raise ValueError('must contain a space') return v.title() @validator('password2') def passwords_match(cls, v, values, **kwargs): if 'password1' in values and v != values['password1']: raise ValueError('passwords do not match') return v @validator('username') def username_alphanumeric(cls, v): assert v.isalnum(), 'must be alphanumeric' return v user = UserModel( name='samuel colvin', username='scolvin', password1='zxcvbn', password2='zxcvbn', ) print(user) #> name='Samuel Colvin' username='scolvin' password1='zxcvbn' password2='zxcvbn' try: UserModel( name='samuel', username='scolvin', password1='zxcvbn', password2='zxcvbn2', ) except ValidationError as e: print(e) """ 2 validation errors for UserModel name must contain a space (type=value_error) password2 passwords do not match (type=value_error) """ 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.