mongomock icon indicating copy to clipboard operation
mongomock copied to clipboard

Support UUID representation

Open pcorpet opened this issue 4 years ago • 5 comments

As of pymongo v4+, it should error if used with default representation (Unspecified).

We could implement a first feature that accepts custom representation, and only fails (NotImplementedError) if writing/reading/comparing in different uuid representations for the same server. Otherwise if it's not Unspecified, we could handle UUID the same way we did before pymongo v4.

pcorpet avatar Jan 18 '22 23:01 pcorpet

Hello, I'm bumping this as I believe not being able to specify UUID representation w/ mongomock is the reason for my tests to be breaking with pymongo v4.

I tried sanity checking this by making some DB queries, updates, and additions, and was successful. I am currently using mongoengine v0.24.0 which falls back to PYTHON_LEGACY, which is presumably why it works. I am only failing my unit tests.

Here is a stack trace from a breaking test, which leads me to believe that mongomock is the culprit:

lib/mongo/mongo_nodes.py:188: in save
    return super().save(*args, **kwargs)
../../../../.local/share/virtualenvs/services-5Tycw3tD/lib/python3.9/site-packages/mongoengine/document.py:416: in save
    object_id = self._save_create(doc, force_insert, write_concern)
../../../../.local/share/virtualenvs/services-5Tycw3tD/lib/python3.9/site-packages/mongoengine/document.py:481: in _save_create
    object_id = wc_collection.insert_one(doc).inserted_id
../../../../.local/share/virtualenvs/services-5Tycw3tD/lib/python3.9/site-packages/mongomock/collection.py:463: in insert_one
    return InsertOneResult(self._insert(document, session), acknowledged=True)
../../../../.local/share/virtualenvs/services-5Tycw3tD/lib/python3.9/site-packages/mongomock/collection.py:514: in _insert
    BSON.encode(data, check_keys=True)
../../../../.local/share/virtualenvs/services-5Tycw3tD/lib/python3.9/site-packages/bson/__init__.py:1150: in encode
    return cls(encode(document, check_keys, codec_options))
../../../../.local/share/virtualenvs/services-5Tycw3tD/lib/python3.9/site-packages/bson/__init__.py:871: in encode
    return _dict_to_bson(document, check_keys, codec_options)

Can someone confirm?

Kalbi avatar Mar 09 '22 16:03 Kalbi

pymongo 4.0+ requires you to set uuidRepresentation along with other options on client initialization. This is not supported in mongomock and breaks unittests that once relied on the legacy behavior of uuidRepresentation 3 (pythonLegacy).

This should be added along side other kwarg options such as 'tzinfo' and 'tz_aware'.

Poking around I was able to make it work by updating collection.py to pass codec_options when it calls bson.encode. Also had to update the code in the chain of calls that initialize the codec_options for the connection.

elanou avatar May 19 '22 17:05 elanou

@elanou Is this similar to the hack you did?

https://github.com/joshuashaffer/mongomock-uuid-pymongo4/commit/64a5551629bbf2506004eac47ce27daf54d094fc

joshuashaffer avatar Jul 12 '22 23:07 joshuashaffer

https://github.com/joshuashaffer/mongomock-uuid-pymongo4/commit/5944358bdc3577f5515dcbfd689728ca9a4834b4

Need to propigate codec_options in a few more places...

joshuashaffer avatar Jul 14 '22 14:07 joshuashaffer

Seeing this open for some time and not knowing when the fixes will be merged, here is another suggestion I use, so I can continue using the released version.

import unittest.mock as mock
from uuid import UUID
from bson.binary import Binary, UuidRepresentation, UUID_SUBTYPE

def side_effect_mongo_mock_from_uuid(uuid: UUID, uuid_representation = UuidRepresentation.STANDARD):
    """Override (Mock) the bson.binary.Binary.from_uuid function to work for mongomock

    Code is copy pasted from the original function,
    but `uuid_representation` is ignored and only code parts as if
    uuid_representation == UuidRepresentation.STANDARD are used
    """
    if not isinstance(uuid, UUID):
        raise TypeError("uuid must be an instance of uuid.UUID")

    subtype = UUID_SUBTYPE
    payload = uuid.bytes

    return Binary(payload, subtype)

@pytest.fixture()
def mongo_uuid(monkeypatch):
    with mock.patch.object(Binary, 'from_uuid', side_effect=side_effect_mongo_mock_from_uuid):
        yield

Probably the same is requried for the as_uuid function

If using this, be careful as other errors may not be noticed by the test. e.g. uuid_representation having an invalid value is never checked here.

till-goodbytz avatar Jan 30 '23 13:01 till-goodbytz