Metadata-Version: 2.4
Name: pyuuid7
Version: 0.1.1
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Rust
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: Implementation :: CPython
Classifier: Topic :: Software Development :: Libraries
License-File: LICENSE
Summary: Fast UUID v4, v5, v7 generation in Rust
Keywords: uuid,uuid7,uuid5,uuid4,rust
License: MIT
Requires-Python: >=3.9
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
Project-URL: Homepage, https://github.com/corebunker/pyuuid7
Project-URL: Issues, https://github.com/corebunker/pyuuid7/issues
Project-URL: Repository, https://github.com/corebunker/pyuuid7
Project-URL: Source, https://github.com/corebunker/pyuuid7

# PyUUID7

Fast UUID v4, v5, and v7 generation for Python, implemented in Rust with PyO3.

`pyuuid7` is useful when you need standard UUID strings from Python code while keeping generation fast and dependency-light. It supports random UUIDs, deterministic name-based UUIDs, time-sortable UUIDs, validation, version detection, and canonical parsing.

## Installation

```bash
pip install pyuuid7
```

## Quick Start

```python
import pyuuid7

user_id = pyuuid7.uuid7()
request_id = pyuuid7.uuid4()
tenant_id = pyuuid7.uuid5(
    "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
    "example.com",
)

print(user_id)
print(request_id)
print(tenant_id)
```

All generated values are returned as canonical UUID strings:

```text
019389a1-2b3c-7d4e-8f5a-6b7c8d9e0f1a
```

## Features

| Function | Returns | Use it for |
| --- | --- | --- |
| `pyuuid7.uuid4()` | `str` | Random identifiers such as request IDs, trace IDs, and public tokens. |
| `pyuuid7.uuid5(namespace, name)` | `str \| None` | Deterministic IDs derived from a namespace UUID and a name. |
| `pyuuid7.uuid7()` | `str` | Time-sortable IDs for database primary keys and event records. |
| `pyuuid7.is_valid(value)` | `bool` | Checking whether a string is any valid UUID. |
| `pyuuid7.get_version(value)` | `int \| None` | Detecting the UUID version of a valid UUID string. |
| `pyuuid7.parse(value)` | `str \| None` | Normalizing UUID input to canonical lowercase format. |

## Common Use Cases

### Generate Time-Sortable IDs

UUID v7 includes a Unix timestamp and random data. The resulting strings sort naturally by creation time, which makes them a good fit for database primary keys, logs, events, and queues.

```python
import pyuuid7

order = {
    "id": pyuuid7.uuid7(),
    "status": "pending",
}

print(order["id"])
```

### Use UUID v7 in Application Models

```python
from dataclasses import dataclass, field
from typing import Optional

import pyuuid7


@dataclass
class Event:
    id: str = field(default_factory=pyuuid7.uuid7)
    name: str = ""
    payload: Optional[dict] = None


event = Event(name="user.created", payload={"user_id": "123"})
```

### Create Deterministic IDs with UUID v5

UUID v5 returns the same UUID every time you pass the same namespace and name. This is useful when you want stable IDs for external resources, slugs, tenant names, or imported records.

```python
import uuid

import pyuuid7

customer_id = pyuuid7.uuid5(str(uuid.NAMESPACE_DNS), "customer.example.com")

if customer_id is None:
    raise ValueError("Invalid namespace UUID")
```

### Validate and Normalize User Input

Use `parse()` when you want to accept UUID input in supported formats and store it in canonical lowercase form.

```python
import pyuuid7


def normalize_uuid(value: str) -> str:
    parsed = pyuuid7.parse(value)
    if parsed is None:
        raise ValueError("Invalid UUID")
    return parsed


resource_id = normalize_uuid("urn:uuid:A1B2C3D4-E5F6-4A7B-8C9D-0E1F2A3B4C5D")
print(resource_id)
# a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d
```

### Route Behavior by UUID Version

```python
import pyuuid7


def describe_uuid(value: str) -> str:
    version = pyuuid7.get_version(value)
    if version is None:
        return "invalid UUID"
    if version == 7:
        return "time-sortable UUID"
    if version == 4:
        return "random UUID"
    if version == 5:
        return "name-based UUID"
    return f"UUID version {version}"
```

## UUID Versions

| Version | Description | Typical use |
| --- | --- | --- |
| v4 | Random UUID with 122 random bits. | Request IDs, trace IDs, public identifiers. |
| v5 | Name-based UUID using SHA-1 over a namespace and name. | Stable deterministic identifiers. |
| v7 | Time-sortable UUID using Unix epoch milliseconds plus random data. | Database IDs, events, logs, sortable records. |

## Error Handling

Functions that can fail because of invalid input return `None`:

```python
import pyuuid7

assert pyuuid7.uuid5("invalid", "example.com") is None
assert pyuuid7.get_version("invalid") is None
assert pyuuid7.parse("invalid") is None
```

`is_valid()` returns `False` for invalid input:

```python
import pyuuid7

assert pyuuid7.is_valid("invalid") is False
```

## API Reference

### `uuid4() -> str`

Generate a random UUID v4 string.

### `uuid5(namespace: str, name: str) -> str | None`

Generate a deterministic UUID v5 string from a namespace UUID and a name. Returns `None` when `namespace` is not a valid UUID.

### `uuid7() -> str`

Generate a time-sortable UUID v7 string.

### `is_valid(uuid_str: str) -> bool`

Return `True` when `uuid_str` can be parsed as a UUID.

### `get_version(uuid_str: str) -> int | None`

Return the UUID version number, or `None` when the input is invalid.

### `parse(uuid_str: str) -> str | None`

Parse a UUID string and return the canonical lowercase representation, or `None` when the input is invalid.

## Development

This project uses `mise` for tool versions and `uv` for Python dependency management.

```bash
# Install pinned tools
mise install

# Setup the development environment
mise run setup

# Build and install the extension in editable mode
mise run dev

# Run Python tests
mise run test

# Run Rust tests
mise exec -- cargo test

# Build distribution artifacts
mise run build
```

## License

MIT

