python-ulid
python-ulid is a Python library that provides Universally Unique Lexicographically Sortable Identifiers (ULIDs). ULIDs are designed to be compatible with UUIDs but offer better performance for database indexing due to their time-based sort order. The library is actively maintained with regular updates, currently at version 3.1.0, and often introduces new features and stricter validation.
Warnings
- breaking Calling the `ULID` constructor with an invalid type argument will now raise a `TypeError` instead of a `ValueError`.
- breaking Parsing ULIDs from string or other inputs (`ULID.from_str`, `ULID.parse`) has become significantly stricter across versions 2.6.0, 2.7.0, and 3.0.0. Inputs that were previously accepted or silently coerced might now raise `ValueError` or `TypeError` due to invalid characters, out-of-range timestamps, or incorrect types.
- gotcha As of version 3.1.0, ULID generation within the same millisecond ensures monotonic sort order by incrementing the randomness component. While spec-compliant, this means the randomness component is not entirely 'random' when multiple ULIDs are generated in rapid succession within the same millisecond.
- gotcha Pydantic v2 protocol support for direct type annotation was introduced in version 2.3.0. If you are using `python-ulid` with Pydantic v2 and a version prior to 2.3.0, you might encounter issues with serialization or validation.
Install
-
pip install python-ulid
Imports
- ULID
from ulid import ULID
Quickstart
from ulid import ULID
import time
# Generate a new ULID
ulid_obj = ULID()
print(f"Generated ULID: {ulid_obj}")
print(f"As string: {str(ulid_obj)}")
print(f"As bytes: {bytes(ulid_obj)}") # requires >=2.3.0
print(f"Timestamp (float): {ulid_obj.timestamp()}")
print(f"Datetime: {ulid_obj.datetime()}")
# Generate multiple ULIDs quickly to demonstrate monotonicity (requires 3.1.0+)
print("\nMonotonicity check (same millisecond - may not trigger in all environments):")
# Simulate a very fast loop within a single millisecond
ulids_in_ms = []
for _ in range(5):
ulids_in_ms.append(ULID())
# Introduce a tiny delay if needed to help simulate
# pass # time.sleep(0.000001)
for u in ulids_in_ms:
print(f" {u}")
# Note: Asserting strict inequality `ulids_in_ms[i] < ulids_in_ms[i+1]` is generally true
# due to monotonicity, but for simplicity, we just print here.
# The library ensures ULIDs are unique and sorted within the same millisecond.
# Parse a ULID from a string (ULID.parse() introduced in 3.0.0)
ulid_str = "01HASJFZZ862S826DA2NJK4WMT" # Example ULID
parsed_ulid = ULID.parse(ulid_str)
print(f"\nParsed ULID: {parsed_ulid}")
print(f"Parsed ULID equals original string: {str(parsed_ulid) == ulid_str}")
# Convert to UUID (ULID.to_uuid4() introduced in 2.1.0)
uuid_obj = ulid_obj.to_uuid4()
print(f"Converted to UUID: {uuid_obj}")