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.
- 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
pip install typed-uuid# 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]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'- Must be alphanumeric only (a-z, A-Z, 0-9)
- Case-sensitive (
userandUserare 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| 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 |
| Property | Description |
|---|---|
type_id |
The type identifier prefix |
uuid |
The underlying UUID object |
| 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 |
Creates a new TypedUUID subclass with the specified type identifier.
UserUUID = create_typed_uuid_class('User', 'user')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')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 databaseTypedUUID 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"}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
# - DescriptionTypedUUID 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"}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'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.uuidThe short format uses underscore (_) as separator to distinguish from the standard hyphen-separated format.
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)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)| 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}")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]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 classMIT License
Contributions are welcome! Please feel free to submit a Pull Request.