or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

tessl/pypi-python-ulid

Universally unique lexicographically sortable identifier implementation for Python

Workspace
tessl
Visibility
Public
Created
Last updated
Describes
pypipkg:pypi/python-ulid@3.1.x

To install, run

npx @tessl/cli install tessl/pypi-python-ulid@3.1.0

0

# Python ULID

1

2

A Python implementation of ULID (Universally Unique Lexicographically Sortable Identifier). ULIDs are 128-bit identifiers that combine the benefits of UUIDs with lexicographic sorting capabilities. They contain a 48-bit timestamp component and 80 bits of randomness, encoded as 26-character strings using Crockford's base32 for efficiency and readability.

3

4

## Package Information

5

6

- **Package Name**: python-ulid

7

- **Package Type**: pypi

8

- **Language**: Python

9

- **Installation**: `pip install python-ulid`

10

11

## Core Imports

12

13

```python

14

from ulid import ULID

15

```

16

17

Import version information and other module components:

18

19

```python

20

from ulid import __version__, ValueProvider, validate_type

21

```

22

23

## Basic Usage

24

25

```python

26

from ulid import ULID

27

from datetime import datetime

28

import time

29

30

# Generate a new ULID with current timestamp

31

ulid = ULID()

32

print(str(ulid)) # '01ARZ3NDEKTSV4RRFFQ69G5FAV'

33

34

# Create ULID from various input types

35

ulid_from_str = ULID.from_str('01ARZ3NDEKTSV4RRFFQ69G5FAV')

36

ulid_from_timestamp = ULID.from_timestamp(time.time())

37

ulid_from_datetime = ULID.from_datetime(datetime.now())

38

39

# Convert ULID to different formats

40

print(ulid.hex) # '0158a4c7bb6f5b48bc9b8b8a0b4d5e8f'

41

print(int(ulid)) # 1827391827391827391827391827391827391

42

print(ulid.timestamp) # 1588257207.56

43

print(ulid.datetime) # datetime.datetime(2020, 4, 30, 14, 33, 27, 560000, tzinfo=timezone.utc)

44

45

# Convert to UUID

46

uuid_obj = ulid.to_uuid()

47

uuid4_obj = ulid.to_uuid4() # RFC 4122 compliant UUIDv4

48

```

49

50

## Architecture

51

52

The python-ulid package is built around these core components:

53

54

- **ULID Class**: Main identifier class providing creation, conversion, and comparison methods

55

- **ValueProvider**: Thread-safe timestamp and randomness generation with monotonic guarantees

56

- **Base32 Encoding**: Crockford's base32 implementation for efficient string representation

57

- **Constants**: Timing, length, and boundary constants for ULID validation

58

- **CLI Interface**: Command-line tool for ULID generation and inspection

59

- **Pydantic Integration**: Built-in support for Pydantic v2 data validation

60

61

## Capabilities

62

63

### ULID Creation

64

65

Create ULID instances from various input types and sources.

66

67

```python { .api }

68

class ULID:

69

def __init__(self, value: bytes | None = None):

70

"""

71

Create a ULID instance.

72

73

Args:

74

value: 16-byte sequence representing an encoded ULID, or None to generate new

75

76

Raises:

77

ValueError: If value is not exactly 16 bytes

78

"""

79

80

@classmethod

81

def from_datetime(cls, value: datetime) -> Self:

82

"""

83

Create ULID from datetime object.

84

85

Args:

86

value: datetime object for timestamp component

87

88

Returns:

89

New ULID instance with timestamp from datetime, random component

90

"""

91

92

@classmethod

93

def from_timestamp(cls, value: float) -> Self:

94

"""

95

Create ULID from timestamp.

96

97

Args:

98

value: Timestamp as float (seconds) or int (milliseconds)

99

100

Returns:

101

New ULID instance with given timestamp, random component

102

"""

103

104

@classmethod

105

def from_uuid(cls, value: uuid.UUID) -> Self:

106

"""

107

Create ULID from UUID (timestamp becomes random).

108

109

Args:

110

value: UUID object to convert

111

112

Returns:

113

New ULID instance with UUID bytes, random timestamp

114

"""

115

116

@classmethod

117

def from_bytes(cls, bytes_: bytes) -> Self:

118

"""

119

Create ULID from 16-byte sequence.

120

121

Args:

122

bytes_: 16-byte sequence

123

124

Returns:

125

New ULID instance

126

"""

127

128

@classmethod

129

def from_hex(cls, value: str) -> Self:

130

"""

131

Create ULID from 32-character hex string.

132

133

Args:

134

value: 32-character hex string

135

136

Returns:

137

New ULID instance

138

"""

139

140

@classmethod

141

def from_str(cls, string: str) -> Self:

142

"""

143

Create ULID from 26-character base32 string.

144

145

Args:

146

string: 26-character base32 encoded ULID string

147

148

Returns:

149

New ULID instance

150

"""

151

152

@classmethod

153

def from_int(cls, value: int) -> Self:

154

"""

155

Create ULID from integer.

156

157

Args:

158

value: Integer representation of ULID

159

160

Returns:

161

New ULID instance

162

"""

163

164

@classmethod

165

def parse(cls, value: Any) -> Self:

166

"""

167

Create ULID from various input types with auto-detection.

168

169

Args:

170

value: Input value (ULID, UUID, str, int, float, datetime, bytes)

171

172

Returns:

173

New ULID instance

174

175

Raises:

176

ValueError: If string length is invalid

177

TypeError: If type cannot be parsed

178

"""

179

```

180

181

### ULID Properties and Conversion

182

183

Access timestamp information and convert ULIDs to different formats.

184

185

```python { .api }

186

class ULID:

187

@functools.cached_property

188

def milliseconds(self) -> int:

189

"""Timestamp component as epoch milliseconds."""

190

191

@functools.cached_property

192

def timestamp(self) -> float:

193

"""Timestamp component as epoch seconds."""

194

195

@functools.cached_property

196

def datetime(self) -> datetime:

197

"""Timestamp as timezone-aware UTC datetime."""

198

199

@functools.cached_property

200

def hex(self) -> str:

201

"""32-character hex representation."""

202

203

bytes: bytes # Raw 16-byte representation

204

205

def __str__(self) -> str:

206

"""26-character base32 encoded string."""

207

208

def __int__(self) -> int:

209

"""Integer representation."""

210

211

def __bytes__(self) -> bytes:

212

"""Raw bytes representation."""

213

214

def to_uuid(self) -> uuid.UUID:

215

"""Convert to UUID object."""

216

217

def to_uuid4(self) -> uuid.UUID:

218

"""

219

Convert to RFC 4122 compliant UUIDv4.

220

221

Note: This is a destructive conversion - the resulting UUID

222

cannot be converted back to the same ULID.

223

"""

224

```

225

226

### ULID Comparison and Hashing

227

228

Compare and hash ULID instances with various types.

229

230

```python { .api }

231

class ULID:

232

def __lt__(self, other: Any) -> bool:

233

"""

234

Less-than comparison.

235

236

Args:

237

other: ULID, int, bytes, or str to compare against

238

239

Returns:

240

True if this ULID is less than other

241

"""

242

243

def __eq__(self, other: object) -> bool:

244

"""

245

Equality comparison.

246

247

Args:

248

other: Object to compare against

249

250

Returns:

251

True if equal (supports ULID, int, bytes, str)

252

"""

253

254

def __hash__(self) -> int:

255

"""Hash based on bytes representation."""

256

```

257

258

### Pydantic Integration

259

260

Built-in support for Pydantic v2 data validation and serialization.

261

262

```python { .api }

263

class ULID:

264

@classmethod

265

def __get_pydantic_core_schema__(

266

cls,

267

source: Any,

268

handler: GetCoreSchemaHandler

269

) -> CoreSchema:

270

"""Generate Pydantic v2 core schema for ULID validation."""

271

272

@classmethod

273

def _pydantic_validate(

274

cls,

275

value: Any,

276

handler: ValidatorFunctionWrapHandler

277

) -> Any:

278

"""Pydantic validation handler for ULID instances."""

279

```

280

281

Usage with Pydantic:

282

283

```python

284

from pydantic import BaseModel

285

from ulid import ULID

286

287

class User(BaseModel):

288

id: ULID

289

name: str

290

291

# Automatically validates and converts various ULID formats

292

user = User(id="01ARZ3NDEKTSV4RRFFQ69G5FAV", name="Alice")

293

user = User(id=ULID(), name="Bob")

294

```

295

296

### Command Line Interface

297

298

Generate and inspect ULIDs from the command line using the `ulid` command.

299

300

```bash { .api }

301

# Show version information

302

ulid --version # or ulid -V

303

304

# Generate new ULID

305

ulid build

306

307

# Generate ULID from different sources

308

ulid build --from-timestamp 1588257207.56

309

ulid build --from-datetime "2020-04-30T14:33:27.560000+00:00"

310

ulid build --from-hex "0158a4c7bb6f5b48bc9b8b8a0b4d5e8f"

311

ulid build --from-str "01ARZ3NDEKTSV4RRFFQ69G5FAV"

312

ulid build --from-int 1827391827391827391827391827391827391

313

ulid build --from-uuid "01234567-89ab-cdef-0123-456789abcdef"

314

315

# Inspect ULID properties

316

ulid show 01ARZ3NDEKTSV4RRFFQ69G5FAV

317

ulid show 01ARZ3NDEKTSV4RRFFQ69G5FAV --timestamp # or --ts

318

ulid show 01ARZ3NDEKTSV4RRFFQ69G5FAV --datetime # or --dt

319

ulid show 01ARZ3NDEKTSV4RRFFQ69G5FAV --hex

320

ulid show 01ARZ3NDEKTSV4RRFFQ69G5FAV --int

321

ulid show 01ARZ3NDEKTSV4RRFFQ69G5FAV --uuid

322

ulid show 01ARZ3NDEKTSV4RRFFQ69G5FAV --uuid4

323

324

# Read ULID from stdin

325

echo "01ARZ3NDEKTSV4RRFFQ69G5FAV" | ulid show -

326

```

327

328

### Base32 Encoding Utilities

329

330

Low-level base32 encoding and decoding functions using Crockford's base32 alphabet.

331

332

```python { .api }

333

from ulid.base32 import (

334

encode, decode, encode_timestamp, encode_randomness,

335

decode_timestamp, decode_randomness, ENCODE, DECODE

336

)

337

338

def encode(binary: bytes) -> str:

339

"""

340

Encode 16-byte ULID to 26-character string.

341

342

Args:

343

binary: 16-byte ULID representation

344

345

Returns:

346

26-character base32 encoded string

347

348

Raises:

349

ValueError: If input is not exactly 16 bytes

350

"""

351

352

def decode(encoded: str) -> bytes:

353

"""

354

Decode 26-character string to 16-byte ULID.

355

356

Args:

357

encoded: 26-character base32 encoded string

358

359

Returns:

360

16-byte ULID representation

361

362

Raises:

363

ValueError: If string is wrong length or contains invalid characters

364

"""

365

366

def encode_timestamp(binary: bytes) -> str:

367

"""

368

Encode 6-byte timestamp to 10-character string.

369

370

Args:

371

binary: 6-byte timestamp representation

372

373

Returns:

374

10-character base32 encoded timestamp string

375

376

Raises:

377

ValueError: If input is not exactly 6 bytes

378

"""

379

380

def encode_randomness(binary: bytes) -> str:

381

"""

382

Encode 10-byte randomness to 16-character string.

383

384

Args:

385

binary: 10-byte randomness representation

386

387

Returns:

388

16-character base32 encoded randomness string

389

390

Raises:

391

ValueError: If input is not exactly 10 bytes

392

"""

393

394

def decode_timestamp(encoded: str) -> bytes:

395

"""

396

Decode 10-character timestamp string to 6 bytes.

397

398

Args:

399

encoded: 10-character base32 encoded timestamp string

400

401

Returns:

402

6-byte timestamp representation

403

404

Raises:

405

ValueError: If string is wrong length or timestamp value would overflow

406

"""

407

408

def decode_randomness(encoded: str) -> bytes:

409

"""

410

Decode 16-character randomness string to 10 bytes.

411

412

Args:

413

encoded: 16-character base32 encoded randomness string

414

415

Returns:

416

10-byte randomness representation

417

418

Raises:

419

ValueError: If string is wrong length

420

"""

421

422

ENCODE: str # "0123456789ABCDEFGHJKMNPQRSTVWXYZ" - Crockford's base32 alphabet

423

DECODE: Sequence[int] # 256-element lookup table for decoding base32 characters

424

```

425

426

### Constants and Configuration

427

428

Package constants for ULID validation and configuration.

429

430

```python { .api }

431

from ulid.constants import (

432

MILLISECS_IN_SECS, NANOSECS_IN_MILLISECS,

433

MIN_TIMESTAMP, MAX_TIMESTAMP,

434

MIN_RANDOMNESS, MAX_RANDOMNESS,

435

TIMESTAMP_LEN, RANDOMNESS_LEN, BYTES_LEN,

436

REPR_LEN, HEX_REPR_LEN, UUID_REPR_LEN, INT_REPR_LEN

437

)

438

439

# Time conversion constants

440

MILLISECS_IN_SECS: int = 1000

441

NANOSECS_IN_MILLISECS: int = 1000000

442

443

# Value boundaries

444

MIN_TIMESTAMP: int = 0

445

MAX_TIMESTAMP: int = 281474976710655 # 2**48 - 1

446

MIN_RANDOMNESS: bytes # 10 zero bytes

447

MAX_RANDOMNESS: bytes # 10 0xFF bytes

448

449

# Length constants

450

TIMESTAMP_LEN: int = 6 # Timestamp byte length

451

RANDOMNESS_LEN: int = 10 # Randomness byte length

452

BYTES_LEN: int = 16 # Total ULID byte length

453

454

# String representation lengths

455

TIMESTAMP_REPR_LEN: int = 10 # Timestamp string representation length

456

RANDOMNESS_REPR_LEN: int = 16 # Randomness string representation length

457

REPR_LEN: int = 26 # Base32 string length

458

HEX_REPR_LEN: int = 32 # Hex string length

459

UUID_REPR_LEN: int = 36 # UUID string length (with dashes)

460

INT_REPR_LEN: int = 37 # Integer string length

461

```

462

463

## Types

464

465

```python { .api }

466

from typing import Any, Generic, TypeVar

467

from datetime import datetime

468

import uuid

469

470

T = TypeVar("T", bound=type)

471

R = TypeVar("R")

472

473

class validate_type(Generic[T]):

474

"""Type validation decorator for methods."""

475

def __init__(self, *types: T) -> None: ...

476

def __call__(self, func: Callable[..., R]) -> Callable[..., R]: ...

477

478

class ValueProvider:

479

"""Thread-safe provider for timestamp and randomness values."""

480

lock: Lock # Threading lock for synchronization

481

prev_timestamp: int # Previous timestamp value for monotonic guarantees

482

prev_randomness: bytes # Previous randomness value for incremental generation

483

484

def __init__(self) -> None: ...

485

def timestamp(self, value: float | None = None) -> int: ...

486

def randomness(self) -> bytes: ...

487

def increment_bytes(self, value: bytes) -> bytes: ...

488

```

489

490

## Error Handling

491

492

The package raises standard Python exceptions for error conditions:

493

494

- **ValueError**: Invalid input values, format errors, overflow conditions, exhausted randomness

495

- **TypeError**: Type validation failures, unsupported conversion types

496

- **PydanticCustomError**: Pydantic validation errors (when using Pydantic integration)

497

498

Common error scenarios:

499

500

```python

501

from ulid import ULID

502

503

# ValueError examples

504

try:

505

ULID(b"too_short") # Not 16 bytes

506

except ValueError as e:

507

print(f"Invalid length: {e}")

508

509

try:

510

ULID.from_str("INVALID_LENGTH") # Not 26 characters

511

except ValueError as e:

512

print(f"Invalid string: {e}")

513

514

try:

515

ULID.from_timestamp(2**48) # Exceeds maximum timestamp

516

except ValueError as e:

517

print(f"Timestamp overflow: {e}")

518

519

# TypeError examples

520

try:

521

ULID.parse(object()) # Unsupported type

522

except TypeError as e:

523

print(f"Cannot parse: {e}")

524

```