How to use two variable types in a pydantic.BaseModel with typing.Union?

Question:

I need my model to accept either a bytes type variable or a string type variable and to raise an exception if any other type was passed.

from typing import Union

from pydantic import BaseModel


class MyModel(BaseModel):
    a: Union[bytes, str]


m1 = MyModel(a='123')
m2 = MyModel(a=b'123')

print(type(m1.a))
print(type(m2.a))

In my case the model interprets both bytes and string as bytes.

Output:

<class 'bytes'>
<class 'bytes'>

Desired output:

<class 'str'>
<class 'bytes'>

The desired output above can be achieved if I re-assign member a:

m1 = MyModel(a='123')
m1.a = '123'

Is it possible to get it in one go?

Asked By: Mirrah

||

Answers:

The problem you are facing is that the str type does some automatic conversions (here in the docs):

strings are accepted as-is, int float and Decimal are coerced using str(v), bytes and bytearray are converted using v.decode(), enums inheriting from str are converted using v.value, and all other types cause an error

bytes are accepted as-is, bytearray is converted using bytes(v), str are converted using v.encode(), and int, float, and Decimal are coerced using str(v).encode()

You can use StrictTypes to avoid automatic conversion between compatible types (e.g.: str and bytes):

from typing import Union

from pydantic import BaseModel, StrictStr, StrictBytes


class MyModel(BaseModel):
    a: Union[StrictStr, StrictBytes]


m1 = MyModel(a='123')
m2 = MyModel(a=b'123')

print(type(m1.a))
print(type(m2.a))

Output will be as expected:

<class 'str'>
<class 'bytes'>
Answered By: Matteo Zanoni
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.