pyramid
This guide provides opinionated, actionable best practices for building modern, maintainable, and performant applications with the Pyramid web framework.
The skill
# 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**
```python
# Inconsistent spacing, line length, and missing imports
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**
```python
# Ensure your pre-commit setup handles this automatically
# Example .pre-commit-config.yaml:
# - repo: https://github.com/psf/black
# rev: 23.1.0
# hooks: [- id: black]
# - repo: https://github.com/PyCQA/flake8
# rev: 6.0.0
# hooks: [- id: flake8]
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`**
```python
# myapp/__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'}
# ... hundreds of lines of routes and views ...
return config.make_wsgi_app()
```
**✅ GOOD - Modular Configuration with `include()`**
```python
# myapp/__init__.py
from pyramid.config import Configurator
def main(global_config, **settings):
with Configurator(settings=settings) as config:
config.include("pyramid_jinja2") # Example external package
config.include(".routes") # Local routes
config.include(".views") # Local views (for decorator-based views)
config.include(".security") # Local security policies
config.scan(".") # Scan for decorator-based views and subscribers
return config.make_wsgi_app()
# myapp/routes.py
def includeme(config):
config.add_route("home", "/")
config.add_route("users", "/users")
config.add_route("user_detail", "/users/{id}")
# myapp/views.py (or specific modules like myapp/views/user.py)
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[str, str]:
return {"project": "myapp"}
@view_config(route_name="users", request_method="GET", renderer="json")
def list_users_view(request) -> Dict[str, Any]:
# ... logic to list users ...
return {"users": []}
```
## 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**
```python
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":
# ... create item logic ...
return Response(json_body={"message": "Item created"}, status=201)
else:
return Response(status=405) # Method Not Allowed
```
**✅ GOOD - Predicates for Clarity**
```python
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."""
# ... create item logic ...
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**
```python
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() # Manual flush
DBSession.commit() # Manual commit
return {"message": "User registered", "id": user.id}
except Exception:
DBSession.rollback() # Manual rollback
raise
```
**✅ GOOD - Automatic Transaction Handling (with `pyramid_tm`)**
```python
# Ensure pyramid_tm is configured in your __init__.py
# config.include('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() # Flush is fine, commit/rollback are handled by pyramid_tm
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**
```python
# myapp/settings.py (BAD practice)
GLOBAL_CONFIG = {} # Mutable global dictionary
def initialize_config(app_settings):
GLOBAL_CONFIG.update(app_settings)
# myapp/views.py
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`**
```python
# myapp/__init__.py
from pyramid.config import Configurator
def main(global_config, **settings):
with Configurator(settings=settings) as config:
# settings are automatically available via request.registry.settings
pass
return config.make_wsgi_app()
# myapp/views.py
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**
```python
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**
```python
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**
```python
# Sometimes returns HTML, sometimes JSON, inconsistent error structure
@view_config(route_name="data", renderer="json")
def get_data_view(request):
if not request.authenticated_userid:
return Response("Unauthorized", status=401) # Returns plain text
# ...
return {"data": "some_data"}
```
**✅ GOOD - Consistent JSON Responses**
```python
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,
)
# ... logic to fetch data ...
return {"data": "some_data"}
```
How to install it
1. Open the tool and start a new conversation, project or template. 2. Paste the text below wherever the tool keeps standing instructions — usually called system prompt, instructions, or persona. 3. Save it so it applies to every run rather than a single message.