Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

UUID with less than 8000 fails with NoneType #7355

Closed
1 task done
dazzlee69 opened this issue Sep 6, 2023 · 3 comments · Fixed by pydantic/pydantic-core#981 or #7566
Closed
1 task done

UUID with less than 8000 fails with NoneType #7355

dazzlee69 opened this issue Sep 6, 2023 · 3 comments · Fixed by pydantic/pydantic-core#981 or #7566
Assignees
Labels
bug V2 Bug related to Pydantic V2 unconfirmed Bug not yet confirmed as valid/applicable

Comments

@dazzlee69
Copy link

dazzlee69 commented Sep 6, 2023

Initial Checks

  • I confirm that I'm using Pydantic V2

Description

We had been using some 'dummy' UUID values, which are valid UUID4, as per https://www.freecodeformat.com/validate-uuid-guid.php for example, but pydantic does not seem to like them, we get a NoneType error.

I've attached an example script which passes 2 UUIDs to a test, first as str, which works, then as UUID, and this one fails:

00000000-7fff-4000-7fff-000000000000

When we run in our application, we get them from the DB layer and they come up as UUID() type, which was then failing.

It seems if the 2nd and 4th sections are less than 8000, the parsing fails, so 7fff fails but 8000 works.

The output we get is like this:

> python pydantic-test.py 
id='00000000-8000-4000-8000-000000000000' type is <class 'str'>
DemoModel(uuid_field = UUID('00000000-8000-4000-8000-000000000000'))
id='00000000-7fff-4000-7fff-000000000000' type is <class 'str'>
DemoModel(uuid_field = UUID('00000000-7fff-4000-7fff-000000000000'))
id=UUID('00000000-8000-4000-8000-000000000000') type is <class 'uuid.UUID'>
DemoModel(uuid_field = UUID('00000000-8000-4000-8000-000000000000'))
id=UUID('00000000-7fff-4000-7fff-000000000000') type is <class 'uuid.UUID'>
Traceback (most recent call last):
  File "/Users/dkeverne/Desktop/Python/oss-play/pydantic-test.py", line 25, in <module>
    test(UUID('00000000-7fff-4000-7fff-000000000000'))
  File "/Users/dkeverne/Desktop/Python/oss-play/pydantic-test.py", line 13, in test
    object = DemoModel(
  File "/Users/dkeverne/Desktop/Python/oss-play/.venv/lib/python3.10/site-packages/pydantic/main.py", line 165, in __init__
    __pydantic_self__.__pydantic_validator__.validate_python(data, self_instance=__pydantic_self__)
TypeError: 'NoneType' object cannot be interpreted as an integer

Example Code

from pydantic import BaseModel, UUID4
from uuid import UUID

class DemoModel(BaseModel):
    uuid_field: UUID4

    def __str__(self):
        return f'DemoModel(uuid_field = {self.uuid_field!r})'

def test(id: UUID4):
    print(f'{id=} type is {type(id)}')

    object = DemoModel(
        uuid_field = id
    )

    print(object)


test('00000000-8000-4000-8000-000000000000')
test('00000000-7fff-4000-7fff-000000000000')


test(UUID('00000000-8000-4000-8000-000000000000'))
test(UUID('00000000-7fff-4000-7fff-000000000000'))

Python, Pydantic & OS Version

pydantic version: 2.3.0
        pydantic-core version: 2.6.3
          pydantic-core build: profile=release pgo=false
                 install path: /Users/username/Desktop/Python/oss-play/.venv/lib/python3.10/site-packages/pydantic
               python version: 3.10.12 (main, Jun 20 2023, 19:43:52) [Clang 14.0.3 (clang-1403.0.22.14.1)]
                     platform: macOS-13.5.1-arm64-arm-64bit
     optional deps. installed: ['typing-extensions']
@dazzlee69 dazzlee69 added bug V2 Bug related to Pydantic V2 unconfirmed Bug not yet confirmed as valid/applicable labels Sep 6, 2023
@Viicos
Copy link
Contributor

Viicos commented Sep 6, 2023

I think pydantic-core uses the UUID.version property:

impl BuildValidator for UuidValidator {
    const EXPECTED_TYPE: &'static str = "uuid";

    fn build(
        schema: &PyDict,
        config: Option<&PyDict>,
        _definitions: &mut DefinitionsBuilder<CombinedValidator>,
    ) -> PyResult<CombinedValidator> {
        let py = schema.py();
        let version = schema.get_as::<u8>(intern!(py, "version"))?.map(Version::from);

which only returns a value if the variant is RFC 4122 (returns None otherwise):

    @property
    def version(self):
        # The version bits are only meaningful for RFC 4122 UUIDs.
        if self.variant == RFC_4122:
            return int((self.int >> 76) & 0xf)

So maybe depending on the uuid value, self.variant (which is also a property computed from the uuid value) doesn't evaluates to RFC_4122, hence the unhandled exception.

@samuelcolvin
Copy link
Member

Thanks for reporting, sorry for the slow reply, I'll look at this today.

@lig
Copy link
Contributor

lig commented Sep 21, 2023

Just to clarify, your model requires UUID4 but the value '00000000-7fff-4000-7fff-000000000000' is not UUID version 4.

Indeed, the uuid Pyhton module thinks the same.

>>> from uuid import UUID
>>> uid = UUID('00000000-7fff-4000-7fff-000000000000')
>>> repr(uid.version)
'None'

Modifying your example to ignore UUID version in the type check passes your tests.

from uuid import UUID

from pydantic import BaseModel


class DemoModel(BaseModel):
    uuid_field: UUID

    def __str__(self):
        return f'DemoModel(uuid_field = {self.uuid_field!r})'


def test(id: str | UUID):
    print(f'{id=} type is {type(id)}')

    object = DemoModel(uuid_field=id)

    print(object)


test('00000000-8000-4000-8000-000000000000')
test('00000000-7fff-4000-7fff-000000000000')


test(UUID('00000000-8000-4000-8000-000000000000'))
test(UUID('00000000-7fff-4000-7fff-000000000000'))
id='00000000-8000-4000-8000-000000000000' type is <class 'str'>
DemoModel(uuid_field = UUID('00000000-8000-4000-8000-000000000000'))
id='00000000-7fff-4000-7fff-000000000000' type is <class 'str'>
DemoModel(uuid_field = UUID('00000000-7fff-4000-7fff-000000000000'))
id=UUID('00000000-8000-4000-8000-000000000000') type is <class 'uuid.UUID'>
DemoModel(uuid_field = UUID('00000000-8000-4000-8000-000000000000'))
id=UUID('00000000-7fff-4000-7fff-000000000000') type is <class 'uuid.UUID'>
DemoModel(uuid_field = UUID('00000000-7fff-4000-7fff-000000000000'))

However, this doesn't mean that there is no bug in Pydantic. Pydantic should provide meaningful error message that reflects the version check correctly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug V2 Bug related to Pydantic V2 unconfirmed Bug not yet confirmed as valid/applicable
Projects
None yet
4 participants