4

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?

Mirrah
  • 125
  • 2
  • 9
  • Just a slight inconsistency in your code. If you define `Foo.a` as `Union[bytes, str]` your output should be `` for both. Instead to get `` for both you would need `Union[str, bytes]`. – Matteo Zanoni Nov 16 '22 at 13:38

1 Answers1

4

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'>
Matteo Zanoni
  • 3,429
  • 9
  • 27