"""UUID and ID generation utilities with optional acceleration.
Provides wrapper functions for uuid3, uuid4, uuid5, uuid6, uuid7, and nanoid generation.
Uses uuid-utils and fastnanoid packages for performance when available,
falling back to standard library.
When uuid-utils is installed:
- uuid3, uuid4, uuid5, uuid6, uuid7 use the faster Rust implementation
- uuid6 and uuid7 provide proper time-ordered UUIDs per RFC 9562
When uuid-utils is NOT installed:
- uuid3, uuid4, uuid5 fall back silently to stdlib (equivalent output)
- uuid6, uuid7 fall back to uuid4 with a warning (different UUID version)
When fastnanoid is installed:
- nanoid() uses the Rust implementation for 21-char URL-safe IDs
When fastnanoid is NOT installed:
- nanoid() falls back to uuid4().hex with a warning (different format)
"""
import uuid as _uuid_mod
import warnings
from typing import Any, cast
from uuid import NAMESPACE_DNS, NAMESPACE_OID, NAMESPACE_URL, NAMESPACE_X500, UUID
from uuid import uuid3 as _stdlib_uuid3
from uuid import uuid4 as _stdlib_uuid4
from uuid import uuid5 as _stdlib_uuid5
from sqlspec.typing import NANOID_INSTALLED, UUID_UTILS_INSTALLED
from sqlspec.utils.module_loader import import_optional
__all__ = (
"NAMESPACE_DNS",
"NAMESPACE_OID",
"NAMESPACE_URL",
"NAMESPACE_X500",
"NANOID_INSTALLED",
"UUID_UTILS_INSTALLED",
"nanoid",
"uuid3",
"uuid4",
"uuid5",
"uuid6",
"uuid7",
)
_uuid_utils_mod: Any | None = import_optional("uuid_utils.compat")
_fastnanoid_mod: Any | None = import_optional("fastnanoid")
[docs]
def uuid3(name: str, namespace: "UUID | None" = None) -> "UUID":
"""Generate a deterministic UUID (version 3) using MD5 hash.
Uses uuid-utils for performance when available, falls back to
standard library uuid.uuid3() silently (equivalent output).
Args:
name: The name to hash within the namespace.
namespace: The namespace UUID. Defaults to NAMESPACE_DNS if not provided.
Returns:
A deterministic UUID based on namespace and name.
"""
module = _uuid_utils_mod
namespace_value = NAMESPACE_DNS if namespace is None else namespace
if module is None:
return _stdlib_uuid3(namespace_value, name)
return cast("UUID", module.uuid3(namespace_value, name))
[docs]
def uuid4() -> "UUID":
"""Generate a random UUID (version 4).
Uses uuid-utils for performance when available, falls back to
standard library uuid.uuid4() silently (equivalent output).
Returns:
A randomly generated UUID.
"""
module = _uuid_utils_mod
if module is None:
return _stdlib_uuid4()
return cast("UUID", module.uuid4())
[docs]
def uuid5(name: str, namespace: "UUID | None" = None) -> "UUID":
"""Generate a deterministic UUID (version 5) using SHA-1 hash.
Uses uuid-utils for performance when available, falls back to
standard library uuid.uuid5() silently (equivalent output).
Args:
name: The name to hash within the namespace.
namespace: The namespace UUID. Defaults to NAMESPACE_DNS if not provided.
Returns:
A deterministic UUID based on namespace and name.
"""
module = _uuid_utils_mod
namespace_value = NAMESPACE_DNS if namespace is None else namespace
if module is None:
return _stdlib_uuid5(namespace_value, name)
return cast("UUID", module.uuid5(namespace_value, name))
[docs]
def uuid6() -> "UUID":
"""Generate a time-ordered UUID (version 6).
Uses uuid-utils when available, falls back to Python 3.14+ native
uuid.uuid6() or uuid4() with a warning.
UUIDv6 is lexicographically sortable by timestamp, making it
suitable for database primary keys. It is a reordering of UUIDv1
fields to improve database performance.
Returns:
A time-ordered UUID, or a random UUID if time-ordered generation unavailable.
"""
module = _uuid_utils_mod
if module is not None:
return cast("UUID", module.uuid6())
native_uuid6 = getattr(_uuid_mod, "uuid6", None)
if native_uuid6 is not None:
return cast("UUID", native_uuid6())
warnings.warn(
"uuid-utils not installed and Python < 3.14, falling back to uuid4 for UUID v6 generation. "
"Install with: pip install sqlspec[uuid]",
UserWarning,
stacklevel=2,
)
return _stdlib_uuid4()
[docs]
def uuid7() -> "UUID":
"""Generate a time-ordered UUID (version 7).
Uses uuid-utils when available, falls back to Python 3.14+ native
uuid.uuid7() or uuid4() with a warning.
UUIDv7 is the recommended time-ordered UUID format per RFC 9562,
providing millisecond precision timestamps. It is designed for
modern distributed systems and database primary keys.
Returns:
A time-ordered UUID, or a random UUID if time-ordered generation unavailable.
"""
module = _uuid_utils_mod
if module is not None:
return cast("UUID", module.uuid7())
native_uuid7 = getattr(_uuid_mod, "uuid7", None)
if native_uuid7 is not None:
return cast("UUID", native_uuid7())
warnings.warn(
"uuid-utils not installed and Python < 3.14, falling back to uuid4 for UUID v7 generation. "
"Install with: pip install sqlspec[uuid]",
UserWarning,
stacklevel=2,
)
return _stdlib_uuid4()
[docs]
def nanoid() -> str:
"""Generate a Nano ID.
Uses fastnanoid for performance when available. When fastnanoid is
not installed, falls back to uuid4().hex with a warning.
Nano IDs are URL-safe, compact 21-character identifiers suitable
for use as primary keys or short identifiers. The default alphabet
uses A-Za-z0-9_- characters.
Returns:
A 21-character Nano ID string, or 32-character UUID hex if
fastnanoid unavailable.
"""
module = _fastnanoid_mod
if module is None:
warnings.warn(
"fastnanoid not installed, falling back to uuid4.hex for Nano ID generation. "
"Install with: pip install sqlspec[nanoid]",
UserWarning,
stacklevel=2,
)
return _nanoid_impl()
return cast("str", module.generate())
def _nanoid_impl() -> str:
return _stdlib_uuid4().hex