Skip to main content

Type-safe UUID management with prefix identification for Python

Project description

TypedUUID

CI PyPI version Python 3.10+ License: MIT

A robust Python library for type-safe UUID management with prefix identification. TypedUUID enhances standard UUIDs by adding a type prefix, making it easy to identify what kind of entity a UUID represents at a glance.

Features

  • Type-safe UUIDs: Prefix UUIDs with a type identifier (e.g., user-550e8400-e29b-41d4-a716-446655440000)
  • Human-readable: Instantly identify what type of entity a UUID belongs to
  • Thread-safe: Safe for use in multi-threaded applications
  • Framework integrations: Built-in support for SQLAlchemy, Pydantic, and FastAPI
  • Zero hard dependencies: Core library works standalone; adapters activate when frameworks are installed
  • Full validation: Comprehensive validation of type IDs and UUID formats
  • Comparison support: Full support for equality, ordering, and hashing

Installation

pip install typed-uuid

Optional dependencies

# For SQLAlchemy support
pip install typed-uuid[sqlalchemy]

# For Pydantic support
pip install typed-uuid[pydantic]

# For FastAPI support (includes Pydantic)
pip install typed-uuid[fastapi]

# For all integrations
pip install typed-uuid[all]

Quick Start

from typed_uuid import create_typed_uuid_class

# Create a typed UUID class for users
UserUUID = create_typed_uuid_class('User', 'user')

# Generate a new UUID
user_id = UserUUID()
print(user_id)  # user-550e8400-e29b-41d4-a716-446655440000

# Parse from string
user_id = UserUUID.from_string('user-550e8400-e29b-41d4-a716-446655440000')

# Create from existing UUID
from uuid import UUID
user_id = UserUUID(uuid_value=UUID('550e8400-e29b-41d4-a716-446655440000'))

# Get the raw UUID without prefix
raw_uuid = user_id.get_uuid()  # '550e8400-e29b-41d4-a716-446655440000'

Type ID Rules

  • Must be alphanumeric only (a-z, A-Z, 0-9)
  • Case-sensitive (user and User are different types)
  • Cannot be empty
# Valid type IDs
UserUUID = create_typed_uuid_class('User', 'user')
OrderUUID = create_typed_uuid_class('Order', 'order')
ProductUUID = create_typed_uuid_class('Product', 'prod')
OrgUUID = create_typed_uuid_class('Organization', 'organization')  # Long type IDs are fine

# Invalid type IDs (will raise InvalidTypeIDError)
create_typed_uuid_class('Invalid', 'user-id')      # Contains hyphen
create_typed_uuid_class('Invalid', 'user@id')      # Contains special character
create_typed_uuid_class('Invalid', '')             # Empty

API Reference

TypedUUID Class

Class Methods

Method Description
from_string(value) Parse a TypedUUID from a string
generate() Generate a new instance with a random UUID
validate(value) Validate and convert a value to this TypedUUID type
is_type_registered(type_id) Check if a type_id is registered
list_registered_types() List all registered type IDs
get_class_by_type_id(type_id) Get the class for a type_id
format_pattern() Get the regex pattern for validation

Instance Properties

Property Description
type_id The type identifier prefix
uuid The underlying UUID object

Instance Methods

Method Description
get_uuid() Get the UUID string without the type prefix
__str__() Returns the full typed UUID string
__hash__() Enables use in sets and dict keys

Factory Functions

create_typed_uuid_class(class_name, type_id)

Creates a new TypedUUID subclass with the specified type identifier.

UserUUID = create_typed_uuid_class('User', 'user')

create_typed_uuid_classes(name, type_id)

Creates both a TypedUUID class and its corresponding SQLAlchemy type (if SQLAlchemy is available).

# With SQLAlchemy installed
UserUUID, UserUUIDType = create_typed_uuid_classes('User', 'user')

# Without SQLAlchemy
UserUUID = create_typed_uuid_classes('User', 'user')

Framework Integrations

SQLAlchemy

TypedUUID integrates seamlessly with SQLAlchemy for database storage.

from sqlalchemy import Column, String
from sqlalchemy.orm import declarative_base
from typed_uuid import create_typed_uuid_classes

Base = declarative_base()

# Create both UUID class and SQLAlchemy type
UserUUID, UserUUIDType = create_typed_uuid_classes('User', 'user')

class User(Base):
    __tablename__ = 'users'

    id = Column(UserUUIDType(), primary_key=True, default=UserUUID)
    name = Column(String(100))

# Usage
user = User(id=UserUUID(), name="Alice")
session.add(user)
session.commit()

# The ID is stored as 'user-550e8400-e29b-41d4-a716-446655440000' in the database

Pydantic

TypedUUID works with Pydantic v2 for validation and serialization.

from pydantic import BaseModel
from typed_uuid import create_typed_uuid_class

UserUUID = create_typed_uuid_class('User', 'user')

class UserModel(BaseModel):
    id: UserUUID
    name: str

# Validation from string
user = UserModel(id='user-550e8400-e29b-41d4-a716-446655440000', name='Alice')

# Validation from UUID instance
user = UserModel(id=UserUUID(), name='Bob')

# Serialization
print(user.model_dump_json())
# {"id": "user-550e8400-e29b-41d4-a716-446655440000", "name": "Bob"}

FastAPI

TypedUUID provides FastAPI path parameter support with automatic OpenAPI documentation.

from fastapi import FastAPI
from typed_uuid import create_typed_uuid_class

app = FastAPI()

UserUUID = create_typed_uuid_class('User', 'user')

@app.get("/users/{user_id}")
async def get_user(user_id: UserUUID.path_param(description="The user's ID")):
    return {"user_id": str(user_id)}

# OpenAPI docs will show the parameter with:
# - Example: user-550e8400-e29b-41d4-a716-446655440000
# - Pattern validation
# - Description

Comparison and Hashing

TypedUUID instances support full comparison operations:

from typed_uuid import create_typed_uuid_class

UserUUID = create_typed_uuid_class('User', 'user')

id1 = UserUUID()
id2 = UserUUID()

# Equality
id1 == id2  # False (different UUIDs)
id1 == id1  # True

# String comparison
id1 == 'user-550e8400-e29b-41d4-a716-446655440000'  # True if UUIDs match

# Ordering (for sorting)
sorted([id2, id1])  # Sorts by (type_id, uuid)

# Hashing (for sets and dicts)
user_set = {id1, id2}
user_dict = {id1: "Alice", id2: "Bob"}

JSON Serialization

TypedUUID supports multiple JSON serialization methods:

import json
from typed_uuid import create_typed_uuid_class, TypedUUID

UserUUID = create_typed_uuid_class('User', 'user')
user_id = UserUUID()

# Using default encoder
json.dumps({'id': user_id}, default=TypedUUID.json_default)

# Using __json__ method (supported by simplejson, FastAPI)
user_id.__json__()  # 'user-550e8400-e29b-41d4-a716-446655440000'

# Direct string conversion
str(user_id)  # 'user-550e8400-e29b-41d4-a716-446655440000'

Short Encoding

TypedUUID supports compact base62 encoding for URL-friendly identifiers:

from typed_uuid import create_typed_uuid_class

UserUUID = create_typed_uuid_class('User', 'user')
user_id = UserUUID()

# Get short representation
print(user_id.short)  # user_7n42DGM5Tflk9n8mt7Fhc7

# Decode from short format
decoded = UserUUID.from_short('user_7n42DGM5Tflk9n8mt7Fhc7')
assert decoded.uuid == user_id.uuid

The short format uses underscore (_) as separator to distinguish from the standard hyphen-separated format.

Auto-Parsing

Parse typed UUIDs without knowing the type in advance:

from typed_uuid import TypedUUID, create_typed_uuid_class

UserUUID = create_typed_uuid_class('User', 'user')
OrderUUID = create_typed_uuid_class('Order', 'order')

# Auto-detect type from string (standard format)
entity = TypedUUID.parse('user-550e8400-e29b-41d4-a716-446655440000')
assert isinstance(entity, UserUUID)

# Also works with short format
entity = TypedUUID.parse('order_7n42DGM5Tflk9n8mt7Fhc7')
assert isinstance(entity, OrderUUID)

Pickle Support

TypedUUID instances can be pickled and unpickled:

import pickle
from typed_uuid import create_typed_uuid_class

UserUUID = create_typed_uuid_class('User', 'user')
user_id = UserUUID()

# Pickle and restore
data = pickle.dumps(user_id)
restored = pickle.loads(data)

assert restored.uuid == user_id.uuid
assert isinstance(restored, UserUUID)

Exceptions

Exception Description
TypedUUIDError Base exception for all TypedUUID errors
InvalidTypeIDError Raised when a type_id is invalid
InvalidUUIDError Raised when a UUID value is invalid
from typed_uuid import create_typed_uuid_class, InvalidTypeIDError, InvalidUUIDError

try:
    create_typed_uuid_class('Invalid', 'too-long-type-id')
except InvalidTypeIDError as e:
    print(f"Invalid type ID: {e}")

UserUUID = create_typed_uuid_class('User', 'user')

try:
    UserUUID.from_string('not-a-valid-uuid')
except InvalidUUIDError as e:
    print(f"Invalid UUID: {e}")

Thread Safety

TypedUUID is thread-safe. The class registry uses a lock to prevent race conditions when creating new TypedUUID classes from multiple threads.

from concurrent.futures import ThreadPoolExecutor
from typed_uuid import create_typed_uuid_class

def create_user_uuid():
    # Safe to call from multiple threads
    UserUUID = create_typed_uuid_class('User', 'user')
    return UserUUID()

with ThreadPoolExecutor(max_workers=10) as executor:
    futures = [executor.submit(create_user_uuid) for _ in range(100)]
    results = [f.result() for f in futures]

Registry

TypedUUID maintains a registry of all created classes, preventing duplicate type IDs:

from typed_uuid import create_typed_uuid_class, TypedUUID

# Create a class
UserUUID = create_typed_uuid_class('User', 'user')

# Calling again with the same type_id returns the existing class
UserUUID2 = create_typed_uuid_class('User', 'user')
assert UserUUID is UserUUID2  # Same class

# Check registered types
TypedUUID.list_registered_types()  # ['user']
TypedUUID.is_type_registered('user')  # True
TypedUUID.get_class_by_type_id('user')  # UserUUID class

License

MIT License

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

typed_uuid-1.1.0.tar.gz (13.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

typed_uuid-1.1.0-py3-none-any.whl (16.2 kB view details)

Uploaded Python 3

File details

Details for the file typed_uuid-1.1.0.tar.gz.

File metadata

  • Download URL: typed_uuid-1.1.0.tar.gz
  • Upload date:
  • Size: 13.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for typed_uuid-1.1.0.tar.gz
Algorithm Hash digest
SHA256 62b7a7564a9f6784131de64c673a0de7cac25ea12ec951197960f0677ba5c4a5
MD5 399e2c6f689302938ce36938ebe36b8f
BLAKE2b-256 dd2ed12f2136169f2c57703e4ce746abb9798b8e7ef4016f3228a3cfa95f34ac

See more details on using hashes here.

Provenance

The following attestation bundles were made for typed_uuid-1.1.0.tar.gz:

Publisher: publish.yml on boxcake/TypedUUID

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file typed_uuid-1.1.0-py3-none-any.whl.

File metadata

  • Download URL: typed_uuid-1.1.0-py3-none-any.whl
  • Upload date:
  • Size: 16.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for typed_uuid-1.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 777579cdf138261fed7fdb38e5c0121b22485873af88914740793429ac9f13cf
MD5 b2f77bbb34dd0ca0f7d2de8b9d26e315
BLAKE2b-256 7085cbcec2dec83d47288e1453e0a90f1e2e42cc42afd450e75c5ba15a7f6b04

See more details on using hashes here.

Provenance

The following attestation bundles were made for typed_uuid-1.1.0-py3-none-any.whl:

Publisher: publish.yml on boxcake/TypedUUID

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page