Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cannot determine if type of field in a Pydantic model is of type List

I am trying to automatically convert a Pydantic model to a DB schema. To do that, I am recursively looping through a Pydantic model's fields to determine the type of field.

As an example, I have this simple model:

from typing import List
from pydantic import BaseModel

class TestModel(BaseModel):
    tags: List[str]

I am recursing through the model using the __fields__ property as described here: https://docs.pydantic.dev/usage/models/#model-properties

If I do type(TestModel).__fields__['tags'] I see:

ModelField(name='tags', type=List[str], required=True)

I want to programatically check if the ModelField type has a List origin. I have tried the following, and none of them work:

  • type(TestModel).__fields__['tags'].type_ is List[str]
  • type(TestModel).__fields__['tags'].type_ == List[str]
  • typing.get_origin(type(TestModel).__fields__['tags'].type_) is List
  • typing.get_origin(type(TestModel).__fields__['tags'].type_) == List

Frustratingly, this does return True:

  • type(TestModel).__fields__['tags'].type_ is str

What is the correct way for me to confirm a field is a List type?

like image 394
rbhalla Avatar asked Oct 21 '25 13:10

rbhalla


1 Answers

Pydantic has the concept of the shape of a field. These shapes are encoded as integers and available as constants in the fields module. The more-or-less standard types have been accommodated there already. If a field was annotated with list[T], then the shape attribute of the field will be SHAPE_LIST and the type_ will be T.

The type_ refers to the element type in the context of everything that is not SHAPE_SINGLETON, i.e. with container-like types. This is why you get str in your example.

Thus for something as simple as list, you can simply check the shape against that constant:

from pydantic import BaseModel
from pydantic.fields import SHAPE_LIST


class TestModel(BaseModel):
    tags: list[str]
    other: tuple[str]


tags_field = TestModel.__fields__["tags"]
other_field = TestModel.__fields__["other"]
assert tags_field.shape == SHAPE_LIST
assert other_field.shape != SHAPE_LIST

If you want more insight into the actual annotation of the field, that is stored in the annotation attribute of the field. With that you should be able to do all the typing related analyses like get_origin.

That means another way of accomplishing your check would be this:

from typing import get_origin

from pydantic import BaseModel


class TestModel(BaseModel):
    tags: list[str]
    other: tuple[str]


tags_field = TestModel.__fields__["tags"]
other_field = TestModel.__fields__["other"]

assert get_origin(tags_field.annotation) is list
assert get_origin(other_field.annotation) is tuple

Sadly, neither of those attributes are officially documented anywhere as far as I know, but the beauty of open-source is that we can just check ourselves. Neither the attributes nor the shape constants are obfuscated, protected or made private in any of the usual ways, so I'll assume these are stable (at least until Pydantic v2 drops).

like image 54
Daniil Fajnberg Avatar answered Oct 24 '25 03:10

Daniil Fajnberg



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!