'How do I pass kwargs to pydantic validator

I'm trying to write pydantic validators, but I can't seem to understand how to make use of the kwargs argument that is mentioned in the docs. I would like to pass conditional parameters for validation. Here's a toy example:

from pydantic import validator
import pydantic.dataclasses as pyd_dc


@pyd_dc.dataclass
class Point_t:
    x: int = 0
    y: int = 1

    @validator("y")
    def quadrant(cls, val, values, **kwargs):
        pt = x, y = values.get("x", None), val
        if x is None:
            raise ValueError(f"invalid point: {x}, {y}")

        signs = kwargs.get("signs", None)
        if signs is None:
            raise ValueError("'signs' parameter missing")

        if all(c * s >= 0 for c, s in zip(pt, signs) if s != 0):
            return val

        raise ValueError(f"{pt} not in quadrant: {signs}")

This doesn't seem to work, instantiating a Point_t object leads to a validation error:

ValidationError: 1 validation error for Point_t
y
  'signs' parameter missing (type=value_error)

How do I pass on the signs parameter in the above example? If there's no way, what's the point of allowing for **kwargs? What am I missing?



Solution 1:[1]

Because Pydantic.validators are class methods here.

what's the point of allowing for **kwargs?

Since validators are “class methods”,and full signature here is equal to (cls, value, *, values, config, field)

In other word, your def quadrant(..., **kwargs): is euqal to config, field

How do I pass kwargs to pydantic validator

1) Create custom config and read from config

2) Fields hack

class Point_t:
    signs = dict
    x: int = 0

    def quadrant(cls, val, values, **kwargs):
        signs = values[signs]
        if signs is None:
            raise ValueError("'signs' parameter missing")

        if all(c * s >= 0 for c, s in zip(pt, signs) if s != 0):
            return val

Solution 2:[2]

I solved this using Config on the pydantic model. This is especially relevant when creating reusable validators.

from pydantic import validator
import pydantic.dataclasses as pyd_dc


def name_must_match(cls, v: str) -> str:
    """
    Throw an error if the provided value does not match
    the class name (`cls.Config.name`)

    Note: for this validator to work, provided pydantic model (`cls`)
    must have a Config attribute, with a `name` attribute.
    """
    if v != cls.Config.name:
        raise ValueError('value does not equal class name')
    return v

# Now to use the validator

@pyd_dc.dataclass
class Point:
    """Some pydantic model"""

    class Config:
        """Store extra info here"""
        name = 'foo'

    # define your fields
    name: str = 'foo'
    x: int = 0
    y: int = 1
    
    # Fields with underscores can be hidden from json encoder
    _name =  validator('name',
                        allow_reuse=True,
                        pre=True,
                        always=True
                        )(name_must_match)

# To test it out
>> Point(name='foo', x=1, y=2)
Point(name='foo', x=1, y=2)

>> Point(name='bar', x=3, y=4)
pydantic.error_wrappers.ValidationError: 1 validation error for Point
name
  value does not equal class name (type=value_error)

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Tyler
Solution 2 ybressler_simon