Skip to content

fix/#652#668

Open
vbhavh wants to merge 6 commits intoapache:mainfrom
vbhavh:main
Open

fix/#652#668
vbhavh wants to merge 6 commits intoapache:mainfrom
vbhavh:main

Conversation

@vbhavh
Copy link

@vbhavh vbhavh commented Mar 3, 2026

fix of issue: #652

Changes:
type(model).model_fields accesses the attribute on the class, which is the correct way per Pydantic v2.11+.

@skrawcz
Copy link
Contributor

skrawcz commented Mar 6, 2026

this needs minimum pin in pyproject.toml to accompany this

@vbhavh
Copy link
Author

vbhavh commented Mar 6, 2026

@skrawcz done.

@skrawcz
Copy link
Contributor

skrawcz commented Mar 6, 2026

@vbhavh I think we want it for all pydantic dependencies in pyproject.toml.

@vbhavh
Copy link
Author

vbhavh commented Mar 8, 2026

hey @skrawcz, is it solved now ? I have done it for all pydantic deps.

@elijahbenizzy
Copy link
Contributor

Could we add a test that confirms this?

@vbhavh
Copy link
Author

vbhavh commented Mar 13, 2026

Could we add a test that confirms this?

i'll add one in test/integrations.

@vbhavh
Copy link
Author

vbhavh commented Mar 14, 2026

@elijahbenizzy done.

Comment on lines +137 to +142
def test_pydantic_version():
"""Ensure pydantic >= 2.11 is installed (required for class-level model_fields access)."""
from packaging.version import Version
assert float(float(".".join(pydantic.__version__.split(".")[:2]))) >= float("2.11"), (
f"pydantic >= 2.11 required, got {pydantic.__version__}"
)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@elijahbenizzy you didn't mean this right ?

What type of test were you looking for? I think the existing unit tests should have covered things?

Copy link
Contributor

@andreahlert andreahlert left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tested locally with pydantic 2.12.5. All 32 existing tests pass. The core fix (accessing model_fields from the class) works correctly. However, this PR has several structural issues that need addressing.


def model_to_dict(model: pydantic.BaseModel, include: Optional[List[str]] = None) -> dict:
"""Utility function to convert a pydantic model to a dictionary."""
keys = model.model_fields.keys()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This guard is unnecessary. model_to_dict has the signature model: pydantic.BaseModel - it always receives an instance, never a class. No call site in the entire codebase passes a class here.

Simpler and equivalent:

keys = type(model).model_fields.keys()

The isinstance(model, type) branch is dead code.

keys = model_cls.model_fields.keys()
keys = keys if include is None else [item for item in include if item in model_cls.model_fields]
return {key: getattr(model, key) for key in keys}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same issue. subset_model has the signature model: Type[ModelType] - it always receives a class, never an instance. The isinstance(model, type) guard will always be True, making type(model) never execute.

This is dead code that adds confusion. The original model.model_fields on a class was already correct and never triggered the deprecation warning - there's nothing to fix here.

Verified locally:

>>> TestModel.model_fields  # class access, no warning
>>> type(TestModel).model_fields  # goes to ModelMetaclass, AttributeError!

The guard accidentally prevents the type() path from executing, which would actually crash since type(SomeBaseModel) is ModelMetaclass and has no model_fields.

@@ -76,7 +77,8 @@ def subset_model(
"""
new_fields = {}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same pattern, same problem. model_from_state takes model: Type[ModelType] - always a class. The original code model.model_fields.keys() was already correct here. No deprecation warning is emitted when accessing model_fields on a class.

This change adds unnecessary indirection without fixing anything.

model_cls = model if isinstance(model, type) else type(model) # Handles the possibility that sometimes model is a class not instance
for name, field_info in model_cls.model_fields.items():
if name in fields:
# copy directly
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same pattern again. _validate_keys takes model: Type[pydantic.BaseModel] - always a class. No fix needed.

Out of the 4 functions changed in this PR, only model_to_dict actually needed the fix. The other 3 already accessed model_fields on the class and never triggered the deprecation.

"langchain_community",
"pandas",
"pydantic[email]",
"pydantic[email]>=2.11",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This version bump is a breaking change that isn't justified by the code fix.

type(model).model_fields works on all pydantic 2.x versions - it's not a new API. The deprecation warning only appears in >=2.11 when accessing model_fields on instances, and the fix removes that access pattern entirely.

Bumping the minimum to >=2.11 breaks users on pydantic 2.0-2.10 who currently work fine and would continue to work fine after this fix.

If a version pin is desired (as @skrawcz mentioned), it should be the minimum already supported pydantic 2.x version, not 2.11. Or it should be handled separately in PR #654 with proper justification.

@@ -133,6 +134,12 @@ class MyModelWithConfig(pydantic.BaseModel):
assert SubsetModel.__name__ == "MyModelWithConfigSubset"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test checks the installed pydantic version, not behavior. It will fail on any CI or user environment running pydantic 2.0-2.10, which currently work perfectly.

A better approach: test that model_to_dict does not emit DeprecationWarning:

import warnings

def test_model_to_dict_no_deprecation_warning():
    model = OriginalModel(foo=1, bar="bar", nested=NestedModel(nested_field1=1))
    with warnings.catch_warnings():
        warnings.simplefilter("error")
        result = model_to_dict(model)
    assert "foo" in result

This tests actual behavior regardless of pydantic version.

@@ -22,6 +22,7 @@
import pytest
from pydantic import BaseModel, ConfigDict, EmailStr, Field
from pydantic.fields import FieldInfo
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

import warnings is added but never used in any test.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants