Skip to content

Planner cannot handle default values in scalar fields #91

Description

@lu-pl

The planner cannot handle default values in scalar fields, e.g for the following model and data

class Model(BaseModel):
    x: int | None = None
    y: int | None = None

data = [
    {"z": 1},
    {"z": 2, "x": 3},
]

the planner tries to look up x and y in the dataframe and crashes with polars.exceptions.ColumnNotFoundError.

Note that this concerns default values in general, i.e. also default factories.

Both the ungrouped and grouped code path for scalar handling are likely affected.

Activity

  1. self-assigned this
    on Sep 29, 2026
  2. added 3 commits that reference this issue on Sep 30, 2026
    d9e6577
    7da0f5f
    34814d3
  3. lu-pl commented on Oct 6, 2026

    @lu-pl
    OwnerAuthor

    This is possibly fixed by #96; separate investigation pending.

  4. lu-pl commented on Oct 6, 2026

    @lu-pl
    OwnerAuthor

    This is possibly fixed by #96; separate investigation pending.

    I can confirm that the now merged #96 remedies the problem described in OP.

    For

    class Model(BaseModel):
        x: int | None = None
        y: int | None = None
    
    data = [
        {"z": 1},
        {"z": 2, "x": 3},
    ]

    the planner now produces

    bindings = [
        {
            "x": null
        },
        {
            "x": 3
        }
    ]
    
    model_dump = [
        {
            "x": null,
            "y": null
        },
        {
            "x": 3,
            "y": null
        }
    ]
  5. lu-pl commented on Oct 6, 2026

    @lu-pl
    OwnerAuthor

    An better example would be

    class Model(BaseModel):
        x: int
        y: int = 42
        z: int = Field(default_factory=lambda: 0)
    
    data = [
        {"x": 1},
        {"x": 2},
        {"x": 3},
    ]

    Previously this also produced a polars.exceptions.ColumnNotFoundError; with #96 however, responsibiltiy for default resolution is passed to Pydantic, so the above produces

    bindings = [
        {
            "x": 1
        },
        {
            "x": 2
        },
        {
            "x": 3
        }
    ]
    
    model_dump = [
        {
            "x": 1,
            "y": 42,
            "z": 0
        },
        {
            "x": 2,
            "y": 42,
            "z": 0
        },
        {
            "x": 3,
            "y": 42,
            "z": 0
        }
    ]
  6. lu-pl commented on Oct 6, 2026

    @lu-pl
    OwnerAuthor

    This is possibly fixed by #96; separate investigation pending.

    I can confirm that the now merged #96 remedies the problem described in OP.

    For

    class Model(BaseModel):
    x: int | None = None
    y: int | None = None

    data = [
    {"z": 1},
    {"z": 2, "x": 3},
    ]

    the planner now produces

    bindings = [
    {
    "x": null
    },
    {
    "x": 3
    }
    ]

    model_dump = [
    {
    "x": null,
    "y": null
    },
    {
    "x": 3,
    "y": null
    }
    ]

    There is an interesting aspect about this worth mentioning: Note that the data here is asymmetrical, the second row in the data has x that's missing entirely from the first row. graphty loads the data into a pl.LazyFrame with default settings applied; so for asymmetrical data, the frame will be filled with None/null.

    This behavior is perfectly reasonable and library users can pass their own pre-processed pl.LazyFrame to graphty.ModelMaterializer and graphty.LazyFramePlanner if they wish to do so. Just note that for asymmetrical data, the frame will by default have None/null values implicitly.

  7. lu-pl commented on Oct 6, 2026

    @lu-pl
    OwnerAuthor

    An better example would be

    class Model(BaseModel):
    x: int
    y: int = 42
    z: int = Field(default_factory=lambda: 0)

    data = [
    {"x": 1},
    {"x": 2},
    {"x": 3},
    ]

    Previously this also produced a polars.exceptions.ColumnNotFoundError; with #96 however, responsibiltiy for default resolution is passed to Pydantic, so the above produces

    bindings = [
    {
    "x": 1
    },
    {
    "x": 2
    },
    {
    "x": 3
    }
    ]

    model_dump = [
    {
    "x": 1,
    "y": 42,
    "z": 0
    },
    {
    "x": 2,
    "y": 42,
    "z": 0
    },
    {
    "x": 3,
    "y": 42,
    "z": 0
    }
    ]

    There should be tests for default value resolution in graphty; opened #98.

    And closing this as resolved.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions