Core API Reference¶
This page documents the core UUID generation functionality provided by uuid_forge.core.
Overview¶
The core module provides three main approaches to generating deterministic UUIDs:
- Functional API (recommended): Pure functions for UUID generation
- Object-Oriented API: Class-based wrapper for convenience
- Utility Functions: Helper functions for salt generation and UUID extraction
Configuration¶
uuid_forge.core.IDConfig
dataclass
¶
Configuration for deterministic UUID generation.
This configuration ensures consistent UUID generation across different services and deployments. The namespace provides logical separation between different entity types or applications, while the salt adds security by preventing UUID prediction attacks.
Attributes:
| Name | Type | Description |
|---|---|---|
namespace |
UUID | Namespace
|
UUID namespace for generation. Can be a UUID object or a Namespace. Defaults to DNS-based namespace. For custom namespaces, use Namespace("domain.com"). |
salt |
str
|
Random salt for security. CRITICAL: Keep this secret! Generate once per deployment and store securely in environment variables. Without a salt, UUIDs are predictable, which may be a security risk. |
Examples:
from uuid_forge.core import IDConfig, Namespace
# Simple domain-based namespace
config = IDConfig(
namespace=Namespace("mycompany.com"),
salt="xvW9Kz_kRzPmNqYvTaWcXdYeFgZhAiB"
)
# Service-specific namespace
config = IDConfig(
namespace=Namespace("users.mycompany.com"),
salt="xvW9Kz_kRzPmNqYvTaWcXdYeFgZhAiB"
)
Examples:
>>> from uuid_forge.core import IDConfig
>>> import uuid
>>> config = IDConfig(namespace=uuid.NAMESPACE_DNS, salt="test-salt")
>>> config.namespace == uuid.NAMESPACE_DNS
True
>>> config.salt == "test-salt"
True
>>> # Test immutability - this should raise FrozenInstanceError
>>> config.salt = "new-salt"
Traceback (most recent call last):
...
FrozenInstanceError: cannot assign to field 'salt'
Methods:
| Name | Description |
|---|---|
__post_init__ |
Validate configuration after initialization. |
Primary Functions¶
generate_uuid_only¶
uuid_forge.core.generate_uuid_only
¶
Generate a deterministic UUID from entity type and business data.
This is the core function for generating idempotent UUIDs. Given the same inputs and configuration, it will always produce the same UUID. This enables zero-coordination ID generation across multiple services and storage systems.
The function uses UUIDv5 (name-based, SHA-1) for deterministic generation, with an optional salt for security. The entity_type provides logical separation between different kinds of entities (e.g., "invoice", "order", "user").
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
entity_type
|
str
|
Type of entity being identified (e.g., "invoice", "order"). This provides namespace separation between different entity types. |
required |
*args
|
Any
|
Positional arguments contributing to the UUID. Can be any serializable data that uniquely identifies the entity. |
()
|
config
|
IDConfig | None
|
Configuration for UUID generation. If None, uses default configuration (DNS namespace, no salt). IMPORTANT: In production, always provide a config with a salt for security. |
None
|
**kwargs
|
Any
|
Keyword arguments contributing to the UUID. Keys are sorted alphabetically to ensure consistency regardless of argument order. |
{}
|
Returns:
| Type | Description |
|---|---|
UUID
|
A deterministic UUID that will be identical for the same inputs and config. |
Raises:
| Type | Description |
|---|---|
TypeError
|
If config is provided but is not an IDConfig instance. |
Example
from uuid_forge.core import generate_uuid_only, IDConfig
import uuid
import os
# Production usage with config
config = IDConfig(
namespace=uuid.uuid5(uuid.NAMESPACE_DNS, "mycompany.com"),
salt=os.getenv("UUID_SALT", "")
)
# Generate UUID for an invoice
invoice_uuid = generate_uuid_only(
"invoice",
region="EUR",
invoice_number=12345,
config=config
)
# Later, regenerate the same UUID from business data
regenerated = generate_uuid_only(
"invoice",
region="EUR",
invoice_number=12345,
config=config
)
assert invoice_uuid == regenerated # Always the same!
Examples:
>>> from uuid_forge.core import generate_uuid_only, IDConfig
>>> import uuid
>>> # Test basic generation
>>> uuid1 = generate_uuid_only("test", key="value")
>>> isinstance(uuid1, uuid.UUID)
True
>>> # Test idempotency
>>> uuid2 = generate_uuid_only("test", key="value")
>>> uuid1 == uuid2 # Same inputs should produce same UUID
True
>>> # Test with different inputs
>>> uuid3 = generate_uuid_only("test", key="other")
>>> uuid1 != uuid3 # Different inputs should produce different UUIDs
True
>>> # Test with config
>>> config = IDConfig(salt="test-salt")
>>> uuid4 = generate_uuid_only("test", key="value", config=config)
>>> uuid5 = generate_uuid_only("test", key="value", config=config)
>>> uuid4 == uuid5 # Same config should produce same UUID
True
>>> # Test that different config produces different UUID
>>> uuid6 = generate_uuid_only("test", key="value")
>>> uuid4 != uuid6 # Different config should produce different UUID
True
>>> # Test kwargs order doesn't matter
>>> uuid7 = generate_uuid_only("test", a=1, b=2)
>>> uuid8 = generate_uuid_only("test", b=2, a=1)
>>> uuid7 == uuid8 # Kwargs order shouldn't matter
True
Source code in src/uuid_forge/core.py
264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 | |
generate_uuid_with_prefix¶
uuid_forge.core.generate_uuid_with_prefix
¶
Generate a deterministic UUID with an optional human-readable prefix.
This function extends generate_uuid_only by adding a human-readable prefix to the UUID. The prefix can be useful for: - Quick visual identification of entity types (e.g., "INV-" for invoices) - Including business context (e.g., "EUR-2024-" for European 2024 invoices) - Making logs and debugging more human-friendly
The prefix does NOT affect the UUID generation - it's purely cosmetic. The same business data will always produce the same UUID, regardless of prefix used.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
entity_type
|
str
|
Type of entity being identified (e.g., "invoice", "order"). |
required |
*args
|
Any
|
Positional arguments contributing to the UUID. |
()
|
prefix
|
str | None
|
Human-readable prefix to prepend to the UUID. If None, only the UUID is returned (as a string). Can include business context like region codes, year, or entity type abbreviations. |
None
|
separator
|
str
|
Character(s) to use between prefix and UUID. Default is "-". |
'-'
|
config
|
IDConfig | None
|
Configuration for UUID generation. If None, uses default config. |
None
|
**kwargs
|
Any
|
Keyword arguments contributing to the UUID. |
{}
|
Returns:
| Type | Description |
|---|---|
str
|
A string in the format "{prefix}{separator}{uuid}" if prefix is provided, |
str
|
or just "{uuid}" if prefix is None. |
Raises:
| Type | Description |
|---|---|
TypeError
|
If config is provided but is not an IDConfig instance. |
Example
from uuid_forge.core import generate_uuid_with_prefix, IDConfig
import os
config = IDConfig(salt=os.getenv("UUID_SALT", ""))
# With prefix
invoice_id = generate_uuid_with_prefix(
"invoice",
prefix="INV-EUR",
region="EUR",
number=12345,
config=config
)
# Result: "INV-EUR-550e8400-e29b-41d4-a716-446655440000"
# Without prefix (just UUID as string)
invoice_id = generate_uuid_with_prefix(
"invoice",
region="EUR",
number=12345,
config=config
)
# Result: "550e8400-e29b-41d4-a716-446655440000"
# Custom separator
invoice_id = generate_uuid_with_prefix(
"invoice",
prefix="INV",
separator="_",
region="EUR",
number=12345,
config=config
)
# Result: "INV_550e8400-e29b-41d4-a716-446655440000"
Examples:
>>> from uuid_forge.core import generate_uuid_with_prefix, IDConfig
>>> # Test without prefix
>>> id1 = generate_uuid_with_prefix("test", key="value")
>>> "-" in id1 # Should be a UUID string
True
>>> not id1.startswith("test") # No prefix
True
>>> # Test with prefix
>>> id2 = generate_uuid_with_prefix("test", prefix="TST", key="value")
>>> id2.startswith("TST-")
True
>>> # Test idempotency with prefix
>>> id3 = generate_uuid_with_prefix("test", prefix="TST", key="value")
>>> id2 == id3 # Same inputs should produce same result
True
>>> # Test that different prefixes don't change UUID part
>>> id4 = generate_uuid_with_prefix("test", prefix="OTHER", key="value")
>>> uuid_part_2 = id2.split("-", 1)[1]
>>> uuid_part_4 = id4.split("-", 1)[1]
>>> uuid_part_2 == uuid_part_4 # UUID should be same regardless of prefix
True
>>> # Test custom separator
>>> id5 = generate_uuid_with_prefix("test", prefix="TST", separator="_", key="value")
>>> id5.startswith("TST_") # Should use custom separator
True
>>> # Test with config
>>> config = IDConfig(salt="test-salt")
>>> id6 = generate_uuid_with_prefix("test", prefix="TST", config=config, key="value")
>>> id7 = generate_uuid_with_prefix("test", prefix="TST", config=config, key="value")
>>> id6 == id7 # Config should be applied consistently
True
Source code in src/uuid_forge/core.py
377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 | |
extract_uuid_from_prefixed¶
uuid_forge.core.extract_uuid_from_prefixed
¶
Extract the UUID from a prefixed identifier.
This function parses a prefixed identifier string (created with generate_uuid_with_prefix) and extracts just the UUID portion. It intelligently handles both prefixed and non-prefixed UUIDs.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
prefixed_id
|
str
|
The prefixed identifier string. Can be either: - A prefixed UUID: "INV-EUR-550e8400-e29b-41d4-a716-446655440000" - A plain UUID: "550e8400-e29b-41d4-a716-446655440000" |
required |
separator
|
str
|
The separator used between prefix and UUID. Default is "-". |
'-'
|
Returns:
| Type | Description |
|---|---|
UUID
|
The extracted UUID object. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If no valid UUID can be found in the input string. |
Example
from uuid_forge.core import (
generate_uuid_with_prefix,
extract_uuid_from_prefixed,
IDConfig
)
config = IDConfig(salt="my-secret-salt")
# Generate a prefixed ID
prefixed = generate_uuid_with_prefix(
"invoice",
prefix="INV-EUR",
region="EUR",
number=12345,
config=config
)
# Result: "INV-EUR-550e8400-e29b-41d4-a716-446655440000"
# Extract the UUID
extracted_uuid = extract_uuid_from_prefixed(prefixed)
# Regenerate from business data
regenerated_uuid = generate_uuid_only(
"invoice",
region="EUR",
number=12345,
config=config
)
assert extracted_uuid == regenerated_uuid
Examples:
>>> from uuid_forge.core import (
... generate_uuid_with_prefix,
... extract_uuid_from_prefixed,
... generate_uuid_only
... )
>>> import uuid
>>> # Test with prefixed UUID
>>> prefixed = generate_uuid_with_prefix("test", prefix="TST", key="value")
>>> extracted = extract_uuid_from_prefixed(prefixed)
>>> isinstance(extracted, uuid.UUID)
True
>>> # Verify extracted UUID matches original
>>> original = generate_uuid_only("test", key="value")
>>> extracted == original
True
>>> # Test with plain UUID string
>>> plain_uuid = str(generate_uuid_only("test", key="other"))
>>> extracted2 = extract_uuid_from_prefixed(plain_uuid)
>>> isinstance(extracted2, uuid.UUID)
True
>>> str(extracted2) == plain_uuid
True
>>> # Test with custom separator
>>> prefixed_custom = generate_uuid_with_prefix("test", prefix="TST", separator="_", key="value")
>>> extracted3 = extract_uuid_from_prefixed(prefixed_custom, separator="_")
extracted3 == original True
Test with invalid input¶
extract_uuid_from_prefixed("not-a-uuid") # doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ... ValueError: No valid UUID found in 'not-a-uuid'
Source code in src/uuid_forge/core.py
493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 | |
Utility Functions¶
generate_salt¶
uuid_forge.core.generate_salt
¶
Generate a cryptographically secure random salt for UUID generation.
This function creates a URL-safe, base64-encoded random string suitable for use as a salt in IDConfig. The salt should be generated once per deployment and stored securely in environment variables.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
length
|
int
|
Length of the generated salt in bytes. Default is 32 bytes, which produces a 43-character base64 string. |
32
|
Returns:
| Type | Description |
|---|---|
str
|
A URL-safe base64-encoded string suitable for use as a salt. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If length is less than 16 bytes (minimum recommended). |
Example
Examples:
>>> from uuid_forge.core import generate_salt
>>> salt = generate_salt()
>>> isinstance(salt, str)
True
>>> len(salt) > 0
True
>>> # Test different lengths
>>> salt_16 = generate_salt(16)
>>> salt_64 = generate_salt(64)
>>> len(salt_16) < len(salt_64)
True
>>> # Test minimum length validation
>>> generate_salt(15)
Traceback (most recent call last):
...
ValueError: Salt length must be at least 16 bytes for security, got 15
Source code in src/uuid_forge/core.py
Object-Oriented API¶
UUIDGenerator¶
uuid_forge.core.UUIDGenerator
¶
Object-oriented convenience wrapper for UUID generation.
This class provides a stateful interface for UUID generation, holding a configuration that's applied to all generated UUIDs. This is useful when you're generating many UUIDs with the same configuration, as it reduces boilerplate and ensures consistency.
The functional API (generate_uuid_only, generate_uuid_with_prefix) is recommended for most use cases. Use this class when you need: - Multiple UUIDs with the same configuration - Encapsulation of configuration in a service/repository - Dependency injection patterns
Attributes:
| Name | Type | Description |
|---|---|---|
config |
The IDConfig used for all UUID generation operations. |
Example
from uuid_forge.core import UUIDGenerator, IDConfig
import uuid
import os
# Create generator with production config
generator = UUIDGenerator(
config=IDConfig(
namespace=uuid.uuid5(uuid.NAMESPACE_DNS, "mycompany.com"),
salt=os.getenv("UUID_SALT", "")
)
)
# Generate multiple UUIDs with same config
invoice_uuid = generator.generate("invoice", region="EUR", number=123)
order_uuid = generator.generate("order", customer_id=456)
# With prefixes
prefixed_invoice = generator.generate_with_prefix(
"invoice",
prefix="INV",
region="EUR",
number=123
)
Examples:
>>> from uuid_forge.core import UUIDGenerator, IDConfig
>>> import uuid
>>> # Test with default config
>>> gen = UUIDGenerator()
>>> uuid1 = gen.generate("test", key="value")
>>> isinstance(uuid1, uuid.UUID)
True
>>> # Test idempotency
>>> uuid2 = gen.generate("test", key="value")
>>> uuid1 == uuid2
True
>>> # Test with custom config
>>> config = IDConfig(salt="test-salt")
>>> gen_custom = UUIDGenerator(config=config)
>>> uuid3 = gen_custom.generate("test", key="value")
>>> uuid4 = gen_custom.generate("test", key="value")
>>> uuid3 == uuid4
True
>>> # Different config produces different UUID
>>> uuid1 != uuid3
True
>>> # Test generate_with_prefix
>>> prefixed = gen.generate_with_prefix("test", prefix="TST", key="value")
>>> prefixed.startswith("TST-")
True
>>> # Verify UUID part matches
>>> from uuid_forge.core import extract_uuid_from_prefixed
>>> extracted = extract_uuid_from_prefixed(prefixed)
>>> extracted == uuid1
True
Initialize the UUID generator with a configuration.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
config
|
IDConfig | None
|
Configuration for UUID generation. If None, uses default configuration (DNS namespace, no salt). |
None
|
Methods:
| Name | Description |
|---|---|
generate |
Generate a deterministic UUID using this generator's configuration. |
generate_with_prefix |
Generate a deterministic UUID with prefix using this generator's configuration. |
Source code in src/uuid_forge/core.py
Functions¶
generate
¶
Generate a deterministic UUID using this generator's configuration.
This is a convenience method that calls generate_uuid_only with the generator's stored configuration.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
entity_type
|
str
|
Type of entity being identified. |
required |
*args
|
Any
|
Positional arguments contributing to the UUID. |
()
|
**kwargs
|
Any
|
Keyword arguments contributing to the UUID. |
{}
|
Returns:
| Type | Description |
|---|---|
UUID
|
A deterministic UUID. |
Source code in src/uuid_forge/core.py
generate_with_prefix
¶
Generate a deterministic UUID with prefix using this generator's configuration.
This is a convenience method that calls generate_uuid_with_prefix with the generator's stored configuration.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
entity_type
|
str
|
Type of entity being identified. |
required |
*args
|
Any
|
Positional arguments contributing to the UUID. |
()
|
prefix
|
str | None
|
Human-readable prefix to prepend to the UUID. |
None
|
separator
|
str
|
Character(s) to use between prefix and UUID. |
'-'
|
**kwargs
|
Any
|
Keyword arguments contributing to the UUID. |
{}
|
Returns:
| Type | Description |
|---|---|
str
|
A string with optional prefix and UUID. |
Source code in src/uuid_forge/core.py
Protocols¶
Representable¶
uuid_forge.core.Representable
¶
Bases: Protocol
Protocol for objects that can be represented as strings.
Any object implementing repr satisfies this protocol and can be used directly with UUID generation functions.
Methods:
| Name | Description |
|---|---|
__repr__ |
Return string representation of object. |
Usage Examples¶
Basic UUID Generation¶
from uuid_forge.core import generate_uuid_only, IDConfig
# Create configuration
config = IDConfig(salt="production-secret-salt")
# Generate UUID
user_uuid = generate_uuid_only(
"user",
config=config,
email="alice@example.com"
)
print(user_uuid) # UUID object
With Human-Readable Prefix¶
from uuid_forge.core import generate_uuid_with_prefix
# Generate prefixed ID
invoice_id = generate_uuid_with_prefix(
"invoice",
prefix="INV-EUR",
config=config,
region="EUR",
number=12345
)
print(invoice_id) # "INV-EUR-550e8400-e29b-41d4-a716-446655440000"
Using the OO API¶
from uuid_forge.core import UUIDGenerator, IDConfig
# Create generator with configuration
generator = UUIDGenerator(
config=IDConfig(salt="production-secret-salt")
)
# Generate multiple UUIDs with same config
order_uuid = generator.generate("order", order_number=123)
invoice_uuid = generator.generate("invoice", order_id=str(order_uuid))
Extracting UUIDs¶
from uuid_forge.core import extract_uuid_from_prefixed
# Extract UUID from prefixed string
prefixed = "INV-EUR-550e8400-e29b-41d4-a716-446655440000"
uuid = extract_uuid_from_prefixed(prefixed)
print(uuid) # UUID('550e8400-e29b-41d4-a716-446655440000')
Type Information¶
All functions in the core module are fully typed. Import types for type hints:
from uuid import UUID
from uuid_forge.core import IDConfig, Representable
from typing import Optional
def process_entity(
entity_type: str,
config: Optional[IDConfig] = None,
**kwargs: Any
) -> UUID:
return generate_uuid_only(entity_type, config=config, **kwargs)
Thread Safety¶
All functions in the core module are thread-safe. They have no shared mutable state and can be called concurrently from multiple threads.
Performance Considerations¶
- UUID generation is fast: ~10 microseconds per call
- No I/O operations are performed
- Memory usage is minimal (< 1KB per call)
- Consider caching UUIDs if generating millions per second
Security Notes¶
Always Use a Salt in Production
Without a salt, UUIDs are predictable and may pose a security risk. Always configure a cryptographic salt using generate_salt().
Keep Your Salt Secret
The salt is effectively a secret key. Anyone with your salt can predict your UUIDs. Store it securely:
- Environment variables
- Secret management systems (AWS Secrets Manager, Vault, etc.)
- Never commit to version control
See Also¶
- Configuration API - Loading configuration from environment
- CLI Reference - Command-line interface
- Best Practices - Production guidelines