Home > Software design >  How to filter out NaN by pydantic
How to filter out NaN by pydantic

Time:06-02

How to filter out NaN in pytdantic float validation?

from pydantic import BaseModel

class MySchema(BaseModel):
    float_value: float

CodePudding user response:

You can use confloat and set higher and lower limits to infinity and minus infinity respectively. That will make pydantic check if the provided value is indeed a valid float and not NaN, while leaving all other behaviour identical (including parsing, conversion from int to float, ...).

from pydantic import BaseModel, confloat

class MySchema(BaseModel):
    float_value: confloat(ge=-float('inf'), le=float('inf'))

Testing:

m = MySchema(float_value=float('nan'))

Output:

pydantic.error_wrappers.ValidationError: 1 validation error for MySchema
float_value
  ensure this value is greater than or equal to -inf (type=value_error.number.not_ge; limit_value=-inf)

CodePudding user response:

Define your custom type for validations, is well documented at pydantic:

class NoNanFloat(float):
    
    @classmethod
    def __get_validators__(cls):
        yield cls.validate
        
    @classmethod
    def __modify_schema__(cls, field_schema):
        # you can ommit this method
        field_schema.update(
            examples=['24.2,15.2'],
        )

    @classmethod
    def validate(cls, v):
        if not isinstance(v, float):
            raise TypeError('float required')
        if v!=v: # you can use here also maths.isnan(v):
            raise ValueError("value can't be Not-a-Number (NaN)")
        return cls(v)

    def __repr__(self):
        # you can also ommit this method, but it looks good when printing. 
        return f'NoNanFloat({super().__repr__()})'
    
class MySchema(BaseModel):
    no_nan_float_value: NoNanFloat
    other_float_value: float
    other: Any

This approach has many advantages, as it allows you to have two types of "floats" depending on your needs, so you can have some allowing nan and others that doesn't.

I also allows you to have the "Any" type accepting nans, and unions of types behaving as expected.

CodePudding user response:

import math
from pydantic import BaseModel, validator

class MySchema(BaseModel):
    float_value: float

    @validator('*', pre=True)
    def split_str(cls, v):
        if isinstance(v, float):
            if math.isnan(v):
                raise ValueError("value can't be Not-a-Number (NaN)")
            return v
        return v   
  • Related