| name | pyramid |
| description | [Applies to: **/*.py] This guide provides opinionated, actionable best practices for building modern, maintainable, and performant applications with the Pyramid web framework. |
| source | cursor_mdc |
pyramid Best Practices
Pyramid is a flexible, "pay-only-what-you-use" Python web framework. To build robust and scalable Pyramid applications, adhere to these modern best practices.
1. Code Organization and Structure
Organize your application for clarity, testability, and scalability. Avoid monolithic __init__.py files.
1.1. Enforce Code Style with black and flake8
Always format your code automatically and lint it. Integrate black and flake8 with pre-commit hooks.
❌ BAD - Inconsistent Formatting & Linting
from pyramid.view import view_config
import os
def my_view(request):
data = request.json_body
if data and 'name' in data:
return {'status': 'ok', 'message': f"Hello, {data['name']}!"}
return {'status': 'error', 'message': 'Name is required.'}
✅ GOOD - Automated Formatting & Linting
from pyramid.view import view_config
from pyramid.response import Response
from typing import Dict, Any
@view_config(route_name="hello", renderer="json")
def my_view(request) -> Dict[str, str]:
"""Handles a greeting request."""
data: Dict[str, Any] = request.json_body
if data and "name" in data:
return {"status": "ok", "message": f"Hello, {data['name']}!"}
return Response(json_body={"status": "error", "message": "Name is required."}, status=400)
1.2. Modularize Configuration
Separate your application's configuration (routes, views, security) into distinct modules. Use config.include() to compose your application.
❌ BAD - Monolithic __init__.py
from pyramid.config import Configurator
from pyramid.view import view_config
def main(global_config, **settings):
with Configurator(settings=settings) as config:
config.add_route('home', '/')
config.add_route('users', '/users')
config.add_route('user_detail', '/users/{id}')
@view_config(route_name='home', renderer='json')
def home_view(request):
return {'project': 'myapp'}
return config.make_wsgi_app()
✅ GOOD - Modular Configuration with include()
from pyramid.config import Configurator
def main(global_config, **settings):
with Configurator(settings=settings) as config:
config.include("pyramid_jinja2")
config.include(".routes")
config.include(".views")
config.include(".security")
config.scan(".")
return config.make_wsgi_app()
def includeme(config):
config.add_route("home", "/")
config.add_route("users", "/users")
config.add_route("user_detail", "/users/{id}")
from pyramid.view import view_config
from pyramid.response import Response
from typing import Dict, Any
@view_config(route_name="home", renderer="json")
def home_view(request) -> Dict[, ]:
{: }
() -> [, ]:
{: []}
2. Common Patterns and Anti-patterns
Leverage Pyramid's design principles to write clean, maintainable code.
2.1. Use View Predicates for Conditional Logic
Avoid if/else statements within views to handle different request methods, authentication states, or other request properties. Use @view_config predicates instead.
❌ BAD - Logic in View
from pyramid.view import view_config
from pyramid.response import Response
@view_config(route_name="items", renderer="json")
def items_view(request):
if request.method == "GET":
return {"items": ["item1", "item2"]}
elif request.method == "POST":
return Response(json_body={"message": "Item created"}, status=201)
else:
return Response(status=405)
✅ GOOD - Predicates for Clarity
from pyramid.view import view_config
from pyramid.response import Response
from typing import Dict, Any
@view_config(route_name="items", request_method="GET", renderer="json")
def get_items_view(request) -> Dict[str, Any]:
"""Retrieves a list of items."""
return {"items": ["item1", "item2"]}
@view_config(route_name="items", request_method="POST", renderer="json")
def create_item_view(request) -> Response:
"""Creates a new item."""
return Response(json_body={"message": "Item created"}, status=201)
2.2. Embrace Pyramid's Transaction Management
Utilize the transaction management system provided by Pyramid's cookiecutters (e.g., pyramid_tm). This ensures atomicity for database operations without manual commit/rollback in your view logic.
❌ BAD - Manual Transaction Handling
from pyramid.view import view_config
from myapp.models import DBSession, User
@view_config(route_name="register", request_method="POST", renderer="json")
def register_user_view(request):
try:
user = User(name=request.json_body["name"])
DBSession.add(user)
DBSession.flush()
DBSession.commit()
return {"message": "User registered", "id": user.id}
except Exception:
DBSession.rollback()
raise
✅ GOOD - Automatic Transaction Handling (with pyramid_tm)
from pyramid.view import view_config
from myapp.models import DBSession, User
from typing import Dict, Any
@view_config(route_name="register", request_method="POST", renderer="json")
def register_user_view(request) -> Dict[str, Any]:
"""Registers a new user, transaction handled automatically."""
user = User(name=request.json_body["name"])
DBSession.add(user)
DBSession.flush()
return {"message": "User registered", "id": user.id}
2.3. Avoid Mutable Global State and Singletons
Pyramid is designed to run multiple application instances in a single process. Avoid patterns that introduce mutable global state, which can lead to hard-to-debug side effects.
❌ BAD - Global State
GLOBAL_CONFIG = {}
def initialize_config(app_settings):
GLOBAL_CONFIG.update(app_settings)
from myapp.settings import GLOBAL_CONFIG
@view_config(route_name="config_info", renderer="json")
def config_info_view(request):
return {"global_setting": GLOBAL_CONFIG.get("some_setting")}
✅ GOOD - Pass Configuration via request.registry.settings
from pyramid.config import Configurator
def main(global_config, **settings):
with Configurator(settings=settings) as config:
pass
return config.make_wsgi_app()
from pyramid.view import view_config
from typing import Dict, Any
@view_config(route_name="config_info", renderer="json")
def config_info_view(request) -> Dict[str, Any]:
"""Retrieves configuration information."""
return {"some_setting": request.registry.settings.get("some_setting")}
3. Type Hints
Always use type hints for functions, methods, and variables. This improves code readability, enables static analysis, and helps prevent runtime errors.
❌ BAD - Untyped Code
def calculate_total(price, quantity):
return price * quantity
class Product:
def __init__(self, name, price):
self.name = name
self.price = price
✅ GOOD - Fully Typed Code
from typing import Union
def calculate_total(price: Union[int, float], quantity: int) -> Union[int, float]:
"""Calculates the total price for a given quantity."""
return price * quantity
class Product:
def __init__(self, name: str, price: float):
self.name = name
self.price = price
4. API Design
Design RESTful APIs with clear resources, HTTP verbs, and consistent response formats.
4.1. Consistent JSON Responses
Always return JSON for API endpoints, including error responses. Use pyramid.response.Response for custom status codes.
❌ BAD - Inconsistent Error Responses
@view_config(route_name="data", renderer="json")
def get_data_view(request):
if not request.authenticated_userid:
return Response("Unauthorized", status=401)
return {"data": "some_data"}
✅ GOOD - Consistent JSON Responses
from pyramid.view import view_config
from pyramid.response import Response
from typing import Dict, Any
@view_config(route_name="data", request_method="GET", renderer="json")
def get_data_view(request) -> Union[Dict[str, Any], Response]:
"""Retrieves data, returns consistent JSON."""
if not request.authenticated_userid:
return Response(
json_body={"error": "Unauthorized", "code": "AUTH_001"},
status=401,
)
return {"data": "some_data"}