1

So we're using pydantic and python-jsonschema to validate user input. Pydantic for internal validation, and python-jsonschema for validation on the portal. And I've come across an interesting issue, and I can't wrap my head around it.

Example code:

from pydantic import BaseModel
import typing as tp
import jsonschema

class Pet(BaseModel):
    pet_type: tp.Optional[tp.Literal['cat', 'dog']]

params = {'pet_type': None}
myPet = Pet(**params)
print(myPet.pet_type)

try:
    jsonschema.validate(instance=params, schema=Pet.schema())
    print('Passed validaton on None')
except Exception as e:
    print('Failed validation on None')

So, according to Pydantic documentation, tp.Optional is a shorthand for tp.Union. Which means that the code above should be read as tp.Union[tp.Literal['cat', 'dog'], None]. Meaning that animal type can be 'cat', 'dog' or None, right?

Well, schema seems to confirm that, with Pet.schema() output being the following, and no 'required' as pet_type:

{
  'title': 'Pet',
  'type': 'object',
  'properties': {
    'pet_type': {
      'title': 'Pet Type',
      'enum': ['cat', 'dog'],
      'type': 'string'
    }
  }
}

However, if the code is run it fails at jsonschema validation with message:

ValidationError: None is not one of ['cat', 'dog']

Failed validating 'enum' in schema['properties']['pet_type']:
    {'enum': ['cat', 'dog'], 'title': 'Pet Type', 'type': 'string'}

On instance['pet_type']:
    None

So even though pydantic specifies that attribute can be 'cat', 'dog' or None, jsonschema still fails.

Is that a problem with pydantic code, python-jsonschema code or our use of pydantic?

EDIT: To try to further narrow down the cause, I've tried creating a schema manually and validating it through jsonschema. Manual passes, pydantic one doesn't:

schema_manual = {
    'title': 'Pet',
    'type': 'object',
    'properties': {
        'pet_type': {
            'title': 'Pet Type',
            'oneOf': [
                {'type': 'string', 'enum': ['cat', 'dog']}, 
                {'type': 'null'}
            ]
        }
    }
}

validate(instance=params, schema=schema_manual)
validate(instance=params, schema=Pet.schema())
thevoiddancer
  • 385
  • 2
  • 9
  • 1
    I think https://github.com/samuelcolvin/pydantic/issues/990 may be relevant to your question. – larsks May 23 '22 at 21:07
  • It probably isn't helpful to your problem at the end of the day, but I can confirm just by looking at that schema that indeed `python-jsonschema` is behaving correctly given what pydantic is giving it (or whatever is generating that schema). It indeed does not allow None. – Julian May 24 '22 at 18:00
  • Exactly, it seems that pydantic and python-jsonschema validate at different level of strictness. Pydantic implies that since pet_type is not specified, it can be None, while python-jsonschema requires explicit option for it to be None. – thevoiddancer May 26 '22 at 09:37
  • For further readers, this is a well known pydantic bug that will be fixed in version 2, currently, we rely on patches and below is a patch that I have recently used https://github.com/pydantic/pydantic/issues/1270#issuecomment-1209704699 – user13837279 Sep 07 '22 at 21:28

0 Answers0