Skip to content

nc9/python-sanity

 
 

Repository files navigation

python-sanity

Python client for Sanity.io CMS HTTP API. Sanity is a hosted CMS solution for content management. This project is not affiliated with Sanity.io and is a third-party package.

ℹ️ Note: This package is an active fork of the original project at OmniPro-Group/sanity-python.

Install

Available on pypi as package python-sanity

Install with uv:

uv add python-sanity

Install with pip:

pip install python-sanity

Environment Variables

You can pass parameters to the client constructor directly, but it is recommended to use environment variables.

Variable Description Required Default Value
SANITY_PROJECT_ID The project ID Yes
SANITY_DATASET The dataset to use No production
SANITY_API_TOKEN The API token No (required for mutations)
SANITY_LOG_LEVEL Level of logging No INFO

What's New in v0.2.0

  • AsyncClient: Full async/await support for all operations
  • Optional Logger: Logger parameter is now optional, uses built-in logger with SANITY_LOG_LEVEL support
  • httpx: Migrated from requests to httpx for better async support and HTTP/2
  • Automatic Retries: Configurable retry logic with exponential backoff
  • Better Error Handling: Specific exception types (SanityAuthError, SanityRateLimitError, etc.)
  • New API Parameters:
    • Query: perspective, result_source_map, tag, return_query
    • Mutation: auto_generate_array_keys, skip_cross_dataset_references_validation, transaction_id
  • Context Managers: Both Client and AsyncClient support context managers
  • Updated API Version: Default API version updated to 2025-02-19

Quick Start

Synchronous Client

from sanity import Client

# Simple initialization (logger is now optional!)
client = Client()  # Uses environment variables

# Or with explicit parameters
client = Client(
    project_id="your-project-id",
    dataset="production",
    token="your-api-token",  # Optional for read-only queries
    use_cdn=True
)

# Query with GROQ
result = client.query(
    groq="*[_type == 'post'] | order(publishedAt desc)[0...10]",
    variables={"limit": 10}
)

# Mutations
transactions = [{
    "createOrReplace": {
        "_id": "post.123",
        "_type": "post",
        "title": "Hello World",
        "publishedAt": "2025-01-15T00:00:00Z"
    }
}]

result = client.mutate(
    transactions=transactions,
    return_documents=True
)

# Upload assets
result = client.assets(
    file_path="https://example.com/image.png"
)

Async Client

from sanity import AsyncClient
import asyncio

async def main():
    # Use async context manager
    async with AsyncClient() as client:
        # Async query
        result = await client.query(
            groq="*[_type == 'post']",
            perspective="published"
        )

        # Async mutation
        result = await client.mutate(
            transactions=[{
                "create": {
                    "_type": "post",
                    "title": "Async Post"
                }
            }]
        )

        # Async asset upload
        result = await client.assets(
            file_path="/path/to/image.png"
        )

asyncio.run(main())

Advanced Configuration

from sanity import Client, TimeoutConfig, RetryConfig

# Custom timeouts and retries
client = Client(
    timeout=TimeoutConfig(
        connect=5.0,
        read=30.0,
        write=30.0,
        pool=5.0
    ),
    retry_config=RetryConfig(
        max_retries=5,
        backoff_factor=1.0
    ),
    http2=True
)

Error Handling

from sanity import (
    Client,
    SanityAuthError,
    SanityRateLimitError,
    SanityValidationError
)

client = Client()

try:
    result = client.query(groq="*[_type == 'post']")
except SanityAuthError as e:
    print(f"Authentication failed: {e.message}")
except SanityRateLimitError as e:
    print(f"Rate limited, retry after {e.retry_after}s")
except SanityValidationError as e:
    print(f"Validation error: {e.response_body}")

Migration Guide from v0.1.x

Breaking Changes

None! v0.2.0 is fully backward compatible.

Optional Improvements

  1. Logger is now optional:

    # Old way (still works)
    import logging
    client = Client(logger=logging.getLogger(__name__))
    
    # New way (simpler)
    client = Client()  # Uses built-in logger with SANITY_LOG_LEVEL
  2. Use context managers for cleanup:

    # Recommended
    with Client() as client:
        result = client.query(groq="*[_type == 'post']")
  3. Try async for better performance:

    from sanity import AsyncClient
    
    async with AsyncClient() as client:
        result = await client.query(groq="*[_type == 'post']")
  4. Use new parameters:

    # Query with perspective
    result = client.query(
        groq="*[_type == 'post']",
        perspective="published",  # drafts, published, raw
        tag="my-app"
    )
    
    # Mutations with new options
    result = client.mutate(
        transactions=[...],
        auto_generate_array_keys=True,
        transaction_id="my-custom-id"
    )

About

Python Client for Sanity CMS

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • Python 99.1%
  • Makefile 0.9%