RollTime
RollTime

Reputation: 25

pydantic_settings isn't calling 'before' validators properly

I'm writing a pydantic_settings class to read data from a .env file/environment variables. The relevant parts look like this:

from pydantic_settings import BaseSettings
from pydantic import Field, field_validator
from typing import Tuple

class JobSettings(BaseSettings):
    wp_generate_funnel_box: bool = Field(True)
    wp_funnel_box_dims_mm: Tuple[int, int, int] = Field((380, 90, 380))

    @field_validator('wp_funnel_box_dims_mm', mode='before')
    @classmethod
    def parse_int_tuple(cls, v) -> tuple[int, int, int]:
        output = tuple(int(x.strip()) for x in v.split(','))
        assert len(output) == 3
        return output

    model_config = {
        "env_file": ".env",
        "env_file_encoding": "utf-8",
        "extra": "ignore",
    }

I need to create a 3-tuple of ints from the string in the environment variable:

wp_generate_funnel_box="True"
wp_funnel_box_dims_mm="380, 90, 380"

Pydantic doens't seem to have a native parser for this datatype, so I implemented this field validator to properly parse and validate the data. However, Pydantic doesn't appear to be running the code in the method; print statements don't produce any output, and simply returning tuple(1, 2, 3) doesn't do anything either. I think I'm misunderstanding how Pydantic handles fields, validators, and ordering. The particular error raised is:

SettingsError: error parsing value for field "wp_funnel_box_dims_mm" from source "DotEnvSettingsSource"

I've tried various configurations of decorators, modes, and logic structures. Nothing so far has changed the type of error generated. Pydantic doesn't run the validator before its own internal logic.

Upvotes: 1

Views: 354

Answers (1)

ACE Fly
ACE Fly

Reputation: 482

method 1:
Write in the env file:

wp_funnel_box_dims_mm=[380, 90, 380]

method 2:
If you want to preprocess this value, write this in the env file:
wp_funnel_box_dims_mm='"1,2,3"'
pydantic treats this value as a complex type and executes json.loads().

    class JobSettings(BaseSettings):
        wp_funnel_box_dims_mm: Tuple[int, int, int] = Field((380, 90, 380))
    
        @model_validator(mode="before")
        @classmethod
        def parse_int_tuple(cls, v) -> tuple[int, int, int]:
            v["wp_funnel_box_dims_mm"] = tuple(int(x.strip()) for x in v["wp_funnel_box_dims_mm"].split(","))
            return v
    
        model_config = {
            "env_file": ".env",
            "env_file_encoding": "utf-8",
            "extra": "ignore",
        }

Upvotes: 0

Related Questions