@mytec: pushed back before 1.1
This commit is contained in:
258
backend/venv/lib/python3.12/site-packages/bson/objectid.py
Normal file
258
backend/venv/lib/python3.12/site-packages/bson/objectid.py
Normal file
@@ -0,0 +1,258 @@
|
||||
# Copyright 2009-2015 MongoDB, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""Tools for working with MongoDB ObjectIds."""
|
||||
from __future__ import annotations
|
||||
|
||||
import datetime
|
||||
import os
|
||||
import struct
|
||||
import threading
|
||||
import time
|
||||
from random import SystemRandom
|
||||
from typing import Any, NoReturn, Optional, Type, Union
|
||||
|
||||
from bson.datetime_ms import _datetime_to_millis
|
||||
from bson.errors import InvalidId
|
||||
from bson.tz_util import utc
|
||||
|
||||
_MAX_COUNTER_VALUE = 0xFFFFFF
|
||||
_PACK_INT = struct.Struct(">I").pack
|
||||
_PACK_INT_RANDOM = struct.Struct(">I5s").pack
|
||||
_UNPACK_INT = struct.Struct(">I").unpack
|
||||
|
||||
|
||||
def _raise_invalid_id(oid: str) -> NoReturn:
|
||||
raise InvalidId(
|
||||
"%r is not a valid ObjectId, it must be a 12-byte input"
|
||||
" or a 24-character hex string" % oid
|
||||
)
|
||||
|
||||
|
||||
def _random_bytes() -> bytes:
|
||||
"""Get the 5-byte random field of an ObjectId."""
|
||||
return os.urandom(5)
|
||||
|
||||
|
||||
class ObjectId:
|
||||
"""A MongoDB ObjectId."""
|
||||
|
||||
_pid = os.getpid()
|
||||
|
||||
_inc = SystemRandom().randint(0, _MAX_COUNTER_VALUE)
|
||||
_inc_lock = threading.Lock()
|
||||
|
||||
__random = _random_bytes()
|
||||
|
||||
__slots__ = ("__id",)
|
||||
|
||||
_type_marker = 7
|
||||
|
||||
def __init__(self, oid: Optional[Union[str, ObjectId, bytes]] = None) -> None:
|
||||
"""Initialize a new ObjectId.
|
||||
|
||||
An ObjectId is a 12-byte unique identifier consisting of:
|
||||
|
||||
- a 4-byte value representing the seconds since the Unix epoch,
|
||||
- a 5-byte random value,
|
||||
- a 3-byte counter, starting with a random value.
|
||||
|
||||
By default, ``ObjectId()`` creates a new unique identifier. The
|
||||
optional parameter `oid` can be an :class:`ObjectId`, or any 12
|
||||
:class:`bytes`.
|
||||
|
||||
For example, the 12 bytes b'foo-bar-quux' do not follow the ObjectId
|
||||
specification but they are acceptable input::
|
||||
|
||||
>>> ObjectId(b'foo-bar-quux')
|
||||
ObjectId('666f6f2d6261722d71757578')
|
||||
|
||||
`oid` can also be a :class:`str` of 24 hex digits::
|
||||
|
||||
>>> ObjectId('0123456789ab0123456789ab')
|
||||
ObjectId('0123456789ab0123456789ab')
|
||||
|
||||
Raises :class:`~bson.errors.InvalidId` if `oid` is not 12 bytes nor
|
||||
24 hex digits, or :class:`TypeError` if `oid` is not an accepted type.
|
||||
|
||||
:param oid: a valid ObjectId.
|
||||
|
||||
.. seealso:: The MongoDB documentation on `ObjectIds <http://dochub.mongodb.org/core/objectids>`_.
|
||||
|
||||
.. versionchanged:: 3.8
|
||||
:class:`~bson.objectid.ObjectId` now implements the `ObjectID
|
||||
specification version 0.2
|
||||
<https://github.com/mongodb/specifications/blob/master/source/
|
||||
objectid.rst>`_.
|
||||
"""
|
||||
if oid is None:
|
||||
# Generate a new value for this ObjectId.
|
||||
with ObjectId._inc_lock:
|
||||
inc = ObjectId._inc
|
||||
ObjectId._inc = (inc + 1) % (_MAX_COUNTER_VALUE + 1)
|
||||
|
||||
# 4 bytes current time, 5 bytes random, 3 bytes inc.
|
||||
self.__id = _PACK_INT_RANDOM(int(time.time()), ObjectId._random()) + _PACK_INT(inc)[1:4]
|
||||
elif isinstance(oid, bytes) and len(oid) == 12:
|
||||
self.__id = oid
|
||||
elif isinstance(oid, str):
|
||||
if len(oid) == 24:
|
||||
try:
|
||||
self.__id = bytes.fromhex(oid)
|
||||
except (TypeError, ValueError):
|
||||
_raise_invalid_id(oid)
|
||||
else:
|
||||
_raise_invalid_id(oid)
|
||||
elif isinstance(oid, ObjectId):
|
||||
self.__id = oid.binary
|
||||
else:
|
||||
raise TypeError(f"id must be an instance of (bytes, str, ObjectId), not {type(oid)}")
|
||||
|
||||
@classmethod
|
||||
def from_datetime(cls: Type[ObjectId], generation_time: datetime.datetime) -> ObjectId:
|
||||
"""Create a dummy ObjectId instance with a specific generation time.
|
||||
|
||||
This method is useful for doing range queries on a field
|
||||
containing :class:`ObjectId` instances.
|
||||
|
||||
.. warning::
|
||||
It is not safe to insert a document containing an ObjectId
|
||||
generated using this method. This method deliberately
|
||||
eliminates the uniqueness guarantee that ObjectIds
|
||||
generally provide. ObjectIds generated with this method
|
||||
should be used exclusively in queries.
|
||||
|
||||
`generation_time` will be converted to UTC. Naive datetime
|
||||
instances will be treated as though they already contain UTC.
|
||||
|
||||
An example using this helper to get documents where ``"_id"``
|
||||
was generated before January 1, 2010 would be:
|
||||
|
||||
>>> gen_time = datetime.datetime(2010, 1, 1)
|
||||
>>> dummy_id = ObjectId.from_datetime(gen_time)
|
||||
>>> result = collection.find({"_id": {"$lt": dummy_id}})
|
||||
|
||||
:param generation_time: :class:`~datetime.datetime` to be used
|
||||
as the generation time for the resulting ObjectId.
|
||||
"""
|
||||
oid = (
|
||||
_PACK_INT(_datetime_to_millis(generation_time) // 1000)
|
||||
+ b"\x00\x00\x00\x00\x00\x00\x00\x00"
|
||||
)
|
||||
return cls(oid)
|
||||
|
||||
@classmethod
|
||||
def is_valid(cls: Type[ObjectId], oid: Any) -> bool:
|
||||
"""Checks if a `oid` string is valid or not.
|
||||
|
||||
:param oid: the object id to validate
|
||||
|
||||
.. versionadded:: 2.3
|
||||
"""
|
||||
if not oid:
|
||||
return False
|
||||
|
||||
try:
|
||||
ObjectId(oid)
|
||||
return True
|
||||
except (InvalidId, TypeError):
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
def _random(cls) -> bytes:
|
||||
"""Generate a 5-byte random number once per process."""
|
||||
pid = os.getpid()
|
||||
if pid != cls._pid:
|
||||
cls._pid = pid
|
||||
cls.__random = _random_bytes()
|
||||
return cls.__random
|
||||
|
||||
@property
|
||||
def binary(self) -> bytes:
|
||||
"""12-byte binary representation of this ObjectId."""
|
||||
return self.__id
|
||||
|
||||
@property
|
||||
def generation_time(self) -> datetime.datetime:
|
||||
"""A :class:`datetime.datetime` instance representing the time of
|
||||
generation for this :class:`ObjectId`.
|
||||
|
||||
The :class:`datetime.datetime` is timezone aware, and
|
||||
represents the generation time in UTC. It is precise to the
|
||||
second.
|
||||
"""
|
||||
timestamp = _UNPACK_INT(self.__id[0:4])[0]
|
||||
return datetime.datetime.fromtimestamp(timestamp, utc)
|
||||
|
||||
def __getstate__(self) -> bytes:
|
||||
"""Return value of object for pickling.
|
||||
needed explicitly because __slots__() defined.
|
||||
"""
|
||||
return self.__id
|
||||
|
||||
def __setstate__(self, value: Any) -> None:
|
||||
"""Explicit state set from pickling"""
|
||||
# Provide backwards compatibility with OIDs
|
||||
# pickled with pymongo-1.9 or older.
|
||||
if isinstance(value, dict):
|
||||
oid = value["_ObjectId__id"]
|
||||
else:
|
||||
oid = value
|
||||
# ObjectIds pickled in python 2.x used `str` for __id.
|
||||
# In python 3.x this has to be converted to `bytes`
|
||||
# by encoding latin-1.
|
||||
if isinstance(oid, str):
|
||||
self.__id = oid.encode("latin-1")
|
||||
else:
|
||||
self.__id = oid
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self.__id.hex()
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"ObjectId('{self!s}')"
|
||||
|
||||
def __eq__(self, other: Any) -> bool:
|
||||
if isinstance(other, ObjectId):
|
||||
return self.__id == other.binary
|
||||
return NotImplemented
|
||||
|
||||
def __ne__(self, other: Any) -> bool:
|
||||
if isinstance(other, ObjectId):
|
||||
return self.__id != other.binary
|
||||
return NotImplemented
|
||||
|
||||
def __lt__(self, other: Any) -> bool:
|
||||
if isinstance(other, ObjectId):
|
||||
return self.__id < other.binary
|
||||
return NotImplemented
|
||||
|
||||
def __le__(self, other: Any) -> bool:
|
||||
if isinstance(other, ObjectId):
|
||||
return self.__id <= other.binary
|
||||
return NotImplemented
|
||||
|
||||
def __gt__(self, other: Any) -> bool:
|
||||
if isinstance(other, ObjectId):
|
||||
return self.__id > other.binary
|
||||
return NotImplemented
|
||||
|
||||
def __ge__(self, other: Any) -> bool:
|
||||
if isinstance(other, ObjectId):
|
||||
return self.__id >= other.binary
|
||||
return NotImplemented
|
||||
|
||||
def __hash__(self) -> int:
|
||||
"""Get a hash value for this :class:`ObjectId`."""
|
||||
return hash(self.__id)
|
||||
Reference in New Issue
Block a user