Configuration
Tet provides enhanced configuration capabilities that extend Pyramid’s configuration system with additional directives and conveniences.
Basic Configuration
Tet modules are configured through Pyramid’s Configurator using the include directive.
Application Factory Pattern
Tet provides the tet.config.application_factory() decorator, which wraps
a configuration function so it becomes a standard Pyramid/Paster application
entry point accepting (global_config, **settings). The wrapped function
receives a single argument – the Configurator – and
by default the wrapper returns a WSGI application built from it:
from tet.config import application_factory, ALL_FEATURES, MINIMAL_FEATURES
@application_factory(included_features=ALL_FEATURES)
def main(config):
"""Tet application factory."""
# Your application configuration
config.add_route("home", "/")
config.scan()
# The decorator can also be applied without arguments. In that case the
# default is MINIMAL_FEATURES (no features are auto-included), so you add
# what you need manually.
@application_factory
def minimal_main(config):
"""Minimal Tet application."""
config.include("tet.renderers.json") # Add features manually
config.add_route("api", "/api")
The decorator accepts the following keyword arguments:
included_features– iterable of feature names to include automatically (defaults toMINIMAL_FEATURES, i.e. an empty list). Nested iterables are flattened.excluded_features– iterable of feature names to skip even if present inincluded_features.configure_only– ifTrue, the wrapper returns whatever the wrapped function returns (typically the configurator) instead of building a WSGI application. Defaults toFalse.package– the package passed to theConfigurator; defaults to the caller’s package.
Any extra keyword arguments are passed through to
create_configurator().
If the wrapped function returns a Configurator, that returned configurator
is used to build the WSGI application; otherwise the configurator created by
the decorator is used.
Available Features
Tet provides two predefined feature sets in tet.config:
ALL_FEATURES– all standard Tet features (see the list below)MINIMAL_FEATURES– no features (an empty list)
Individual features are named with the part of the dotted module path that
follows tet.; create_configurator includes each as tet.<feature>.
The available feature names are:
"services"- Dependency injection via pyramid_di"i18n"- Internationalization support"renderers.json"- JSON rendering with custom type adapters"renderers.tonnikala"- Tonnikala template engine integration"renderers.tonnikala.i18n"- Tonnikala with i18n support"security.authorization"- Custom authorization policy"security.csrf"- CSRF token protection
Manual Configuration
For fine-grained control, create the configurator manually with
tet.config.create_configurator(). All of its parameters are
keyword-only:
from tet.config import create_configurator
def main(global_config, **settings):
config = create_configurator(
global_config=global_config,
settings=settings,
included_features=["renderers.json", "security.csrf"],
excluded_features=["i18n"],
)
# Your configuration
config.add_route("home", "/")
config.scan()
return config.make_wsgi_app()
The most commonly used keyword arguments are:
global_config– the global configuration mapping (from PasteDeploy).settings– the application settings mapping.merge_global_config– whenTrue(the default) andglobal_configis a mapping, it is merged intosettingsvia aChainMap.included_features/excluded_features– iterables of feature names; both are flattened, and the effective set isincluded_features - excluded_features. The resulting set is stored onconfig.registry.tet_features.configurator_class– theConfiguratorsubclass to instantiate (defaults topyramid.config.Configurator).package– the package for the configurator; defaults to the caller’s package. The package name is also used as the default i18n domain.
Any remaining keyword arguments are forwarded to the Configurator
constructor (the default_i18n_domain setting, if given, is extracted and
applied via add_settings).
Configuration Directives
Tet adds several custom configuration directives to enhance Pyramid’s functionality.
JSON Renderer Directives
- add_json_renderer
Register a custom JSON renderer:
from pyramid.renderers import JSON
# Create custom renderer
api_renderer = JSON()
# Register with custom name
config.add_json_renderer(renderer=api_renderer, name="api_json")
- add_json_adapter
Register a type adapter for JSON serialization:
from decimal import Decimal
def decimal_adapter(obj, request):
return str(obj)
config.add_json_adapter(
for_=Decimal,
adapter=decimal_adapter,
renderer="json", # Optional, defaults to 'json'
)
Module Configuration
Individual Tet modules can be configured with specific options.
CSRF Configuration
The CSRF module sets secure defaults but can be customized:
def main(global_config, **settings):
with Configurator(settings=settings) as config:
# Include CSRF with custom options
config.include("tet.security.csrf")
# Override CSRF settings if needed
config.set_default_csrf_options(
require_csrf=True, token="csrf_token", header="X-CSRF-Token"
)
return config.make_wsgi_app()
JSON Renderer Configuration
Customize the JSON renderer behavior:
from pyramid.renderers import JSON
def main(global_config, **settings):
with Configurator(settings=settings) as config:
# Include JSON renderer
config.include("tet.renderers.json")
# Add custom adapters
config.add_json_adapter(for_=MyModel, adapter=lambda obj, req: obj.to_dict())
# Create specialized renderer
api_renderer = JSON(sort_keys=True, indent=2)
config.add_json_renderer(renderer=api_renderer, name="pretty_json")
return config.make_wsgi_app()
Settings Integration
Tet modules respect Pyramid’s settings system for configuration.
Database Settings
Configure SQLAlchemy integration through settings:
# development.ini
[app:main]
use = egg:myapp
# Database configuration
sqlalchemy.url = postgresql://user:pass@localhost/myapp
sqlalchemy.pool_size = 10
sqlalchemy.max_overflow = 20
# Session configuration
session.secret = your-secret-key-here
session.cookie_httponly = true
session.cookie_secure = true
Security Settings
Configure security-related settings:
# production.ini
[app:main]
# CSRF settings
csrf.secret = different-secret-for-csrf
csrf.timeout = 7200
# Authorization settings
auth.policy = myapp.security.AuthPolicy
auth.secret = auth-signing-secret
Application Settings
Access settings in your application code:
def my_view(request):
settings = request.registry.settings
# Access configuration values
database_url = settings.get("sqlalchemy.url")
debug_mode = settings.get("debug", False)
return {"debug": debug_mode}
Environment Configuration
Tet applications can be configured for different environments.
Development Configuration
# development.py
def main(global_config, **settings):
# Development-specific settings
settings.update(
{
"debug": True,
"reload_templates": True,
"sqlalchemy.echo": True,
}
)
with Configurator(settings=settings) as config:
config.include("tet.security.csrf")
config.include("tet.renderers.json")
# Development-only includes
if settings.get("debug"):
config.include("pyramid_debugtoolbar")
return config.make_wsgi_app()
Production Configuration
# production.py
def main(global_config, **settings):
# Production-specific settings
settings.update(
{
"debug": False,
"reload_templates": False,
"session.secure": True,
"session.httponly": True,
}
)
with Configurator(settings=settings) as config:
config.include("tet.security.csrf")
config.include("tet.security.authorization")
config.include("tet.renderers.json")
# Production-only security
config.set_default_csrf_options(require_csrf=True)
return config.make_wsgi_app()
Testing Configuration
# testing.py
def main(global_config, **settings):
# Testing-specific settings
settings.update(
{
"debug": True,
"sqlalchemy.url": "sqlite:///:memory:",
"csrf.disable": True, # Disable CSRF for easier testing
}
)
with Configurator(settings=settings) as config:
config.include("tet.renderers.json")
# Conditional CSRF inclusion
if not settings.get("csrf.disable"):
config.include("tet.security.csrf")
return config.make_wsgi_app()
Advanced Configuration
Complex configuration scenarios and patterns.
Factory Configuration
Configure root factories and other components:
from tet.sqlalchemy.factory import SQLARootFactory
class MyRootFactory(SQLARootFactory):
def supplier(self, item):
# Implementation here
pass
def main(global_config, **settings):
with Configurator(settings=settings) as config:
# Set custom root factory
config.set_root_factory(MyRootFactory)
# Configure based on settings
if settings.get("use_traversal"):
config.add_route("api", "/api/*traverse")
else:
config.add_route("api", "/api/{action}")
return config.make_wsgi_app()
Service Configuration
Configure services with pyramid_di:
from pyramid_di import service
@service(name="mailer", scope="singleton")
def create_mailer(settings):
smtp_host = settings.get("mail.smtp.host", "localhost")
return MailerService(smtp_host)
def main(global_config, **settings):
with Configurator(settings=settings) as config:
config.include("pyramid_di")
config.include("tet.renderers.json")
# Services are automatically available
return config.make_wsgi_app()
Configuration Validation
Validate configuration to catch errors early.
Settings Validation
def validate_settings(settings):
"""Validate required settings."""
required = [
"sqlalchemy.url",
"session.secret",
]
missing = [key for key in required if not settings.get(key)]
if missing:
raise ValueError(f"Missing required settings: {missing}")
# Validate specific values
if len(settings.get("session.secret", "")) < 32:
raise ValueError("session.secret must be at least 32 characters")
def main(global_config, **settings):
# Validate configuration early
validate_settings(settings)
with Configurator(settings=settings) as config:
# Configuration continues...
return config.make_wsgi_app()
Configuration Utilities
Helper functions for configuration management.
Settings Helper
def get_settings_helper(settings):
"""Create a helper for accessing settings."""
class SettingsHelper:
def __init__(self, settings):
self.settings = settings
def get_bool(self, key, default=False):
value = self.settings.get(key, default)
if isinstance(value, str):
return value.lower() in ("true", "1", "yes", "on")
return bool(value)
def get_int(self, key, default=0):
return int(self.settings.get(key, default))
def get_list(self, key, separator=",", default=None):
value = self.settings.get(key, default or [])
if isinstance(value, str):
return [item.strip() for item in value.split(separator)]
return value
return SettingsHelper(settings)
Configuration Profiles
Managing different configuration profiles.
Profile System
PROFILES = {
"development": {
"debug": True,
"sqlalchemy.echo": True,
"reload_templates": True,
},
"testing": {
"debug": True,
"sqlalchemy.url": "sqlite:///:memory:",
"csrf.disable": True,
},
"production": {
"debug": False,
"session.secure": True,
"session.httponly": True,
},
}
def main(global_config, **settings):
# Load profile-specific settings
profile = settings.get("profile", "development")
profile_settings = PROFILES.get(profile, {})
# Merge settings with profile taking precedence
final_settings = {**settings, **profile_settings}
with Configurator(settings=final_settings) as config:
# Configure based on merged settings
return config.make_wsgi_app()
Best Practices
- Validate Early
Validate configuration at application startup to catch errors early.
- Use Environment Variables
Use environment variables for sensitive configuration like secrets and API keys.
- Profile-Based Configuration
Use configuration profiles for different environments.
- Document Settings
Document all configuration options and their effects.
- Secure Defaults
Use secure defaults and require explicit configuration for less secure options.
- Modular Configuration
Keep configuration modular and only include what you need.
- Settings Helpers
Create helper functions for common configuration patterns like boolean conversion.
- Configuration Testing
Test your configuration validation and different configuration scenarios.