From 888809d1eb18b7bfb78d606f1e57080dfef3689a Mon Sep 17 00:00:00 2001 From: Amin Alaee Date: Fri, 27 Oct 2023 12:18:24 +0200 Subject: [PATCH] Add std library compatible UUID generation functions (#38) --- python/uuid_utils/compat/__init__.py | 46 +++++++++++++++++++++++++++ python/uuid_utils/compat/__init__.pyi | 41 ++++++++++++++++++++++++ tests/test_compat/__init__.py | 0 tests/test_compat/test_compat.py | 23 ++++++++++++++ 4 files changed, 110 insertions(+) create mode 100644 python/uuid_utils/compat/__init__.py create mode 100644 python/uuid_utils/compat/__init__.pyi create mode 100644 tests/test_compat/__init__.py create mode 100644 tests/test_compat/test_compat.py diff --git a/python/uuid_utils/compat/__init__.py b/python/uuid_utils/compat/__init__.py new file mode 100644 index 0000000..eea3f4d --- /dev/null +++ b/python/uuid_utils/compat/__init__.py @@ -0,0 +1,46 @@ +import uuid + +import uuid_utils + + +def uuid1(node=None, clock_seq=None): + """Generate a UUID from a host ID, sequence number, and the current time. + If 'node' is not given, getnode() is used to obtain the hardware + address. If 'clock_seq' is given, it is used as the sequence number; + otherwise a random 14-bit sequence number is chosen.""" + return uuid.UUID(int=uuid_utils.uuid1(node, clock_seq).int) + + +def uuid3(namespace, name): + """Generate a UUID from the MD5 hash of a namespace UUID and a name.""" + namespace = uuid_utils.UUID(namespace.hex) if namespace else namespace + return uuid.UUID(int=uuid_utils.uuid3(namespace, name).int) + + +def uuid4(): + """Generate a random UUID.""" + return uuid.UUID(int=uuid_utils.uuid4().int) + + +def uuid5(namespace, name): + """Generate a UUID from the SHA-1 hash of a namespace UUID and a name.""" + namespace = uuid_utils.UUID(namespace.hex) if namespace else namespace + return uuid.UUID(int=uuid_utils.uuid5(namespace, name).int) + + +def uuid6(node=None, timestamp=None): + """Generate a version 6 UUID using the given timestamp and a host ID. + This is similar to version 1 UUIDs, + except that it is lexicographically sortable by timestamp. + """ + return uuid.UUID(int=uuid_utils.uuid6(node, timestamp).int) + + +def uuid7(timestamp=None): + """Generate a version 7 UUID using a time value and random bytes.""" + return uuid.UUID(int=uuid_utils.uuid7(timestamp).int) + + +def uuid8(bytes): + """Generate a custom UUID comprised almost entirely of user-supplied bytes..""" + return uuid.UUID(bytes=uuid_utils.uuid8(bytes).bytes) diff --git a/python/uuid_utils/compat/__init__.pyi b/python/uuid_utils/compat/__init__.pyi new file mode 100644 index 0000000..de42132 --- /dev/null +++ b/python/uuid_utils/compat/__init__.pyi @@ -0,0 +1,41 @@ +from uuid import UUID + +from typing_extensions import TypeAlias + +# Because UUID has properties called int and bytes we need to rename these temporarily. +_Int: TypeAlias = int +_Bytes: TypeAlias = bytes + +def uuid1(node: _Int | None = None, clock_seq: _Int | None = None) -> UUID: + """Generate a UUID from a host ID, sequence number, and the current time. + If 'node' is not given, getnode() is used to obtain the hardware + address. If 'clock_seq' is given, it is used as the sequence number; + otherwise a random 14-bit sequence number is chosen.""" + ... + +def uuid3(namespace: UUID, name: str) -> UUID: + """Generate a UUID from the MD5 hash of a namespace UUID and a name.""" + ... + +def uuid4() -> UUID: + """Generate a random UUID.""" + ... + +def uuid5(namespace: UUID, name: str) -> UUID: + """Generate a UUID from the SHA-1 hash of a namespace UUID and a name.""" + ... + +def uuid6(node: _Int | None = None, timestamp: _Int | None = None) -> UUID: + """Generate a version 6 UUID using the given timestamp and a host ID. + This is similar to version 1 UUIDs, + except that it is lexicographically sortable by timestamp. + """ + ... + +def uuid7(timestamp: _Int | None = None) -> UUID: + """Generate a version 7 UUID using a time value and random bytes.""" + ... + +def uuid8(bytes: _Bytes) -> UUID: + """Generate a custom UUID comprised almost entirely of user-supplied bytes..""" + ... diff --git a/tests/test_compat/__init__.py b/tests/test_compat/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/test_compat/test_compat.py b/tests/test_compat/test_compat.py new file mode 100644 index 0000000..76969d4 --- /dev/null +++ b/tests/test_compat/test_compat.py @@ -0,0 +1,23 @@ +import uuid +from typing import Callable + +import pytest + +from uuid_utils.compat import uuid1, uuid3, uuid4, uuid5, uuid6, uuid7, uuid8 + + +@pytest.mark.parametrize("generator", [uuid1, uuid4, uuid6, uuid7]) +def test_uuid(generator: Callable[..., uuid.UUID]) -> None: + assert isinstance(generator(), uuid.UUID) + + +def test_uuid3() -> None: + assert isinstance(uuid3(uuid.NAMESPACE_DNS, "python.org"), uuid.UUID) + + +def test_uuid5() -> None: + assert isinstance(uuid5(uuid.NAMESPACE_DNS, "python.org"), uuid.UUID) + + +def test_uuid8() -> None: + assert isinstance(uuid8(b"1234567812345678"), uuid.UUID)