Skip to main content
rule

pyramid — Cursor rule: Pyramid best practices

A Cursor rule with coding conventions for the Pyramid Python framework: project layout, configuration, transactions and view predicates.

Install into Cursor · Manually: put the files in place All methods and platforms ↓
.cursor/rules/pyramid.mdc
Open on GitHub

What pyramid does

The file .cursor/rules/pyramid.mdc plugs into Cursor and applies to **/*.py. It lays out how to write Pyramid code: split the app into modules instead of one __init__.py, compose configuration with config.include() and config.scan(), and move request conditions into @view_config predicates rather than if/else inside a view.

There is a section on transactions too. The rule tells you to let pyramid_tm handle commit and rollback, keeping only flush in the view. It also warns against mutable global state and singletons, since Pyramid is built to run several application instances in one process. For style, it suggests wiring black and flake8 through pre-commit.

Worth adding if your project already runs on Pyramid and you want the agent to stop piling logic into a single file. Keep in mind this is a set of conventions, not a ready config — you still install and hook up pre-commit and pyramid_tm yourself.

What it helps with

  • split a Pyramid app into modules instead of one __init__.py
  • move request method handling into @view_config predicates
  • drop manual commit and rollback from views
  • set up formatting and linting through pre-commit
  • keep mutable global state out of the codebase

Example prompts for the agent

  • Split routes and views into separate modules and wire them with config.include()
  • Rewrite this view using @view_config predicates instead of if/else on request.method
  • Remove the manual commit from this view and let pyramid_tm handle the transaction

How to install pyramid

Manually: put the files in place

.cursor/rules/pyramid.mdc

Frontmatter: description, globs, alwaysApply

Step by step, with commands for each platform: Cursor rules: .mdc files, four modes and what replaced .cursorrules

Questions about pyramid

How do I install the pyramid rule in Cursor?

Create .cursor/rules/pyramid.mdc in your project root and paste the rule contents into it. Cursor picks it up for **/*.py files.

What does the pyramid rule do?

It gives the agent conventions for Pyramid code: modular configuration, view predicates, transactions via pyramid_tm, and no global state.

Do I need extra packages for this rule?

The rule references black, flake8 and pyramid_tm, so install and configure them in your project or the linting and transaction advice won't apply.

Source text

pyramid.mdc · CC0-1.0
---
description: This guide provides opinionated, actionable best practices for building modern, maintainable, and performant applications with the Pyramid web framework.
globs: **/*.py
---
# 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"}
```

© sanjeed5 · CC0-1.0 · sanjeed5/awesome-cursor-rules-mdc

Similar IDE rules

All IDE rules for Backend and APIs →

From the same set69

Project sponsors

Companies that keep this analytics open
Gold sponsor

Impressions, clicks and code copies — open statistics. We keep the numbers public for the same reason NeBlask itself is public. Become a sponsor