Skip to main content

Advanced Usage

This guide covers advanced patterns and best practices for using the Toggly Python SDK.

Feature Evaluation​

Basic Evaluation​

from toggly import TogglyClient, TogglyConfig

client = TogglyClient(TogglyConfig(app_key="your-app-key"))
client.init()

# Simple check
if client.is_enabled("my-feature"):
print("Feature enabled!")

# With default value
enabled = client.is_enabled("unknown-feature", default=True)

With Context​

from toggly import EvaluationContext

context = EvaluationContext(
identity="user-123",
groups=["beta-testers", "premium"],
traits={
"plan": "enterprise",
"country": "US",
"signup_date": "2024-01-15",
}
)

if client.is_enabled("premium-feature", context):
print("Premium feature for this user!")

Feature Gates​

Evaluate multiple features at once:

from toggly import FeatureRequirement

# All features must be enabled
if client.evaluate_gate(
["feature-a", "feature-b"],
FeatureRequirement.ALL,
context
):
print("All features enabled!")

# Any feature must be enabled
if client.evaluate_gate(
["premium", "trial"],
FeatureRequirement.ANY,
context
):
print("User has access!")

# Negate the result
if client.evaluate_gate(
["maintenance-mode"],
FeatureRequirement.ALL,
context,
negate=True
):
print("Not in maintenance mode!")

Get All Flags​

# Get all feature flags as a dictionary
flags = client.feature_flags
for key, enabled in flags.items():
print(f"{key}: {'enabled' if enabled else 'disabled'}")

Async Support​

AsyncTogglyClient​

import asyncio
from toggly import AsyncTogglyClient, TogglyConfig

async def main():
config = TogglyConfig(app_key="your-app-key")
client = AsyncTogglyClient(config)

await client.init()

if await client.is_enabled("async-feature"):
print("Feature enabled!")

await client.close()

asyncio.run(main())

Context Manager​

async with AsyncTogglyClient(config) as client:
await client.init()
enabled = await client.is_enabled("my-feature")

With FastAPI​

For native middleware and gate dependencies, use the synchronous client in the FastAPI lifespan example. For direct async evaluation, own the async client's lifespan and await every check:

import os
from contextlib import asynccontextmanager
from fastapi import FastAPI, Request
from toggly import AsyncTogglyClient, TogglyConfig

@asynccontextmanager
async def lifespan(app: FastAPI):
client = AsyncTogglyClient(TogglyConfig(
app_key=os.environ.get("TOGGLY_APP_KEY"),
))
await client.init()
app.state.feature_client = client
try:
yield
finally:
await client.close()

app = FastAPI(lifespan=lifespan)

@app.get("/feature")
async def feature(request: Request):
enabled = await request.app.state.feature_client.is_enabled("async-feature")
return {"enabled": enabled}

Testing​

Mock the Client​

from unittest.mock import MagicMock, patch

def test_feature_enabled():
mock_client = MagicMock()
mock_client.is_enabled.return_value = True

with patch("your_module.get_toggly_client", return_value=mock_client):
result = your_function()
assert result == "enabled"
mock_client.is_enabled.assert_called_with("my-feature")

Feature Defaults for Testing​

# Use feature_defaults for predictable test behavior
config = TogglyConfig(
app_key="test-key",
feature_defaults={
"feature-a": True,
"feature-b": False,
},
disable_background_refresh=True, # Disable network calls in tests
)

client = TogglyClient(config)
# No need to call init() - defaults will be used

Test Fixtures​

import pytest
from toggly import TogglyClient, TogglyConfig, set_default_client

@pytest.fixture
def toggly_client():
config = TogglyConfig(
app_key="test-key",
feature_defaults={"test-feature": True},
disable_background_refresh=True,
)
client = TogglyClient(config)
set_default_client(client)
yield client
client.close()

def test_with_toggly(toggly_client):
assert toggly_client.is_enabled("test-feature") is True

Error Handling​

Graceful Degradation​

from toggly import TogglyClient, TogglyConfig, TogglyError

try:
client = TogglyClient(config)
client.init()
except TogglyError as e:
print(f"Failed to initialize Toggly: {e}")
# Use defaults or disable feature flags
client = None

def is_feature_enabled(feature_key: str, default: bool = False) -> bool:
if client is None:
return default
try:
return client.is_enabled(feature_key, default=default)
except TogglyError:
return default

Timeout Handling​

config = TogglyConfig(
app_key="your-app-key",
connect_timeout=5.0, # Connection timeout
request_timeout=10.0, # Request timeout
)

Retry Logic​

The SDK includes built-in retry logic for transient failures. For custom retry behavior:

import time
from toggly import TogglyClient, TogglyConfig, TogglyError

def init_with_retry(config: TogglyConfig, max_retries: int = 3):
client = TogglyClient(config)

for attempt in range(max_retries):
try:
client.init()
return client
except TogglyError as e:
if attempt == max_retries - 1:
raise
time.sleep(2 ** attempt) # Exponential backoff

return client

Background Refresh​

Automatic Refresh​

config = TogglyConfig(
app_key="your-app-key",
refresh_interval=60.0, # Refresh every 60 seconds
)

Manual Refresh​

# Force refresh from API
client.refresh()

Disable Background Refresh​

config = TogglyConfig(
app_key="your-app-key",
disable_background_refresh=True,
)

# Manually refresh when needed
client.refresh()

Usage Tracking​

Enable/Disable Tracking​

config = TogglyConfig(
app_key="your-app-key",
enable_usage_tracking=True, # Default
)

Flush Metrics​

# Manually flush usage metrics
client.flush_metrics()

Signed Definitions​

For enhanced security, use signed feature definitions:

config = TogglyConfig(
app_key="your-app-key",
use_signed_definitions=True,
)

Logging​

Enable Debug Logging​

import logging

# Configure logging
logging.basicConfig(level=logging.DEBUG)

# Or for just Toggly
logger = logging.getLogger("toggly")
logger.setLevel(logging.DEBUG)

config = TogglyConfig(
app_key="your-app-key",
debug=True,
)

Custom Logger​

import logging

custom_logger = logging.getLogger("my-app.toggly")
custom_logger.setLevel(logging.INFO)

# The SDK respects the logging configuration

Multi-Environment​

Environment-Specific Config​

import os

environment = os.getenv("ENV", "Production")

config = TogglyConfig(
app_key=os.getenv("TOGGLY_APP_KEY"),
environment=environment,
)

Multiple Clients​

# Production client
prod_config = TogglyConfig(
app_key="prod-key",
environment="Production",
)
prod_client = TogglyClient(prod_config)
prod_client.init()

# Staging client
staging_config = TogglyConfig(
app_key="staging-key",
environment="Staging",
)
staging_client = TogglyClient(staging_config)
staging_client.init()

Lifecycle Management​

Application Startup/Shutdown​

from toggly import TogglyClient, TogglyConfig, set_default_client

# Global client
_client: TogglyClient | None = None

def init_toggly():
global _client
config = TogglyConfig(app_key="your-app-key")
_client = TogglyClient(config)
_client.init()
set_default_client(_client)

def shutdown_toggly():
global _client
if _client:
_client.close()
_client = None

With atexit​

import atexit
from toggly import TogglyClient, TogglyConfig

config = TogglyConfig(app_key="your-app-key")
client = TogglyClient(config)
client.init()

# Register cleanup
atexit.register(client.close)

Performance Optimization​

Caching​

The core client evaluates definitions locally. Add a file snapshot for recovery across restarts:

from toggly import TogglyClient, TogglyConfig
from toggly.providers import FileSnapshotProvider

config = TogglyConfig(
app_key="your-app-key",
snapshot_provider=FileSnapshotProvider(directory="./cache/my-app-production"),
)
client = TogglyClient(config)
client.init() # Close once at application shutdown.

See Caching for the core snapshot interface and the separate Redis/Memcached definition-list utilities.

Batch Evaluation​

Evaluate multiple features efficiently:

# Instead of multiple calls
features = ["feature-a", "feature-b", "feature-c"]
results = {f: client.is_enabled(f, context) for f in features}

Local Evaluation​

Feature definitions are cached locally after the initial fetch:

# First call fetches from API (or cache)
client.init()

# Subsequent calls use local cache
client.is_enabled("feature") # Fast, no network call

Type Safety​

Type Hints​

The SDK is fully typed for IDE support:

from toggly import (
TogglyClient,
TogglyConfig,
EvaluationContext,
FeatureRequirement,
)

def check_feature(
client: TogglyClient,
feature: str,
context: EvaluationContext | None = None,
) -> bool:
return client.is_enabled(feature, context)

Protocol Support​

from typing import Protocol

class FeatureFlagClient(Protocol):
def is_enabled(
self,
feature_key: str,
context: EvaluationContext | None = None,
default: bool = False,
) -> bool: ...

def use_client(client: FeatureFlagClient):
if client.is_enabled("my-feature"):
print("Enabled!")