Skip to main content
rule

python · sanjeed5 — Cursor rule: what it does, how to install

This Cursor rule sets conventions for Python files, from formatting and imports to typing, packaging, and tests. Use it when a project needs one consistent code style.

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

What python does

The rule targets **/*.py files and builds its guidance on PEP 8. It defines four-space indentation, an 88-character line limit, spacing between declarations, and naming patterns for modules, classes, functions, variables, and constants. Black or Ruff can enforce formatting, while isort keeps imports grouped and ordered.

Public modules, classes, and functions should carry reStructuredText-style docstrings. Function signatures receive type annotations, with mypy available for static checks. The suggested package layout uses src/, pyproject.toml, README.md, LICENSE, and tests/.

The rule requires Poetry or Pipenv for dependency management and isolated environments. Tests use pytest, with pytest-cov for coverage and unittest.mock or pytest-mock for isolation. A project using neither Poetry nor Pipenv conflicts with this rule’s environment requirement.

What it helps with

  • format Python code around PEP 8
  • sort imports into clear groups
  • standardize names across a codebase
  • add docstrings and type annotations
  • organize tests with pytest

Example prompts for the agent

  • Apply the Python rules to app.py without changing its behavior.
  • Review every file under src/ for imports, naming, and type annotations.
  • Suggest a package layout with src/, pyproject.toml, and tests/.

How to install python

Manually: put the files in place

.cursor/rules/python.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 python

How do I install python in Cursor?

Add the rule file at .cursor/rules/python.mdc inside the Cursor project.

What does the python rule do?

It guides formatting, imports, naming, documentation, type hints, project layout, environment management, and Python tests.

Which tools does the rule expect?

It points to Black or Ruff, isort, mypy, Poetry or Pipenv, plus pytest and pytest-cov.

Source text

python.mdc · CC0-1.0
---
description: This guide defines the definitive Python best practices for our team, focusing on readability, maintainability, and modern development standards. Adhere to these rules for consistent, high-quality Python code.
globs: **/*.py
---
# python Best Practices

This document outlines the definitive Python best practices for our team. Adherence ensures consistent, readable, and maintainable code across all projects. We prioritize **PEP 8** as the foundation, augmented with modern tooling and patterns.

## 1. Code Layout & Formatting

Always adhere to PEP 8. Use an auto-formatter like `Black` or `Ruff` to enforce consistency.

*   **Indentation**: Use 4 spaces. Never tabs.
*   **Line Length**: Limit lines to 88 characters. Docstrings and comments should ideally wrap at 72 characters.
*   **Blank Lines**:
    *   Two blank lines between top-level functions and classes.
    *   One blank line between methods within a class.
    *   One blank line to separate logical sections within functions/methods.

❌ BAD:
```python
def my_func():
    x = 1
    y = 2
    return x + y
class MyClass:
    def __init__(self, name):
        self.name = name
    def greet(self):
        print(f"Hello, {self.name}!")
```

✅ GOOD:
```python
def my_function():
    x = 1
    y = 2

    # Separate logical steps
    result = x + y
    return result


class MyClass:
    def __init__(self, name):
        self.name = name

    def greet(self):
        print(f"Hello, {self.name}!")
```

## 2. Imports

Organize imports for clarity and to prevent circular dependencies. Use `isort` to automate this.

*   **Grouping**:
    1.  Standard library imports.
    2.  Third-party library imports.
    3.  Local application/project-specific imports.
*   **Alphabetical Order**: Sort imports alphabetically within each group.
*   **Absolute Imports**: Prefer absolute imports over relative imports.

❌ BAD:
```python
import os, sys
from my_package.sub_module import some_function
import requests
from .another_module import another_function
```

✅ GOOD:
```python
import os
import sys

import requests

from my_package.sub_module import some_function
from my_package.another_module import another_function
```

## 3. Naming Conventions

Follow PEP 8 naming conventions strictly.

*   **Modules**: `lowercase_with_underscores`
*   **Packages**: `lowercase_with_underscores`
*   **Classes**: `CamelCase`
*   **Functions/Methods**: `lowercase_with_underscores`
*   **Variables**: `lowercase_with_underscores`
*   **Constants**: `UPPERCASE_WITH_UNDERSCORES`
*   **Protected Members**: `_single_leading_underscore` (internal use)
*   **Private Members**: `__double_leading_underscore` (name mangling, avoid unless necessary for mixins)

❌ BAD:
```python
class myClass: # Class name not CamelCase
    def Get_Data(self): # Method name not lowercase_with_underscores
        MY_VAR = 10 # Variable name not lowercase_with_underscores
        return MY_VAR
```

✅ GOOD:
```python
class MyClass:
    def get_data(self):
        my_var = 10
        return my_var

GLOBAL_CONSTANT = 100
```

## 4. Docstrings & Comments

Document all public modules, classes, and functions using PEP 257 docstring conventions. Use reStructuredText format for Sphinx compatibility.

*   **Module Docstrings**: Top of the file, after `__future__` imports.
*   **Class Docstrings**: First line after the class definition.
*   **Function/Method Docstrings**: First line after the `def` statement.
*   **Comments**: Use sparingly for *why* code exists, not *what* it does.

❌ BAD:
```python
def calculate_sum(a, b): # This function adds two numbers
    return a + b
```

✅ GOOD:
```python
def calculate_sum(a: int, b: int) -> int:
    """Calculate the sum of two integers.

    :param a: The first integer.
    :param b: The second integer.
    :return: The sum of a and b.
    :raises TypeError: If a or b are not integers.
    """
    if not isinstance(a, int) or not isinstance(b, int):
        raise TypeError("Inputs must be integers.")
    return a + b
```

## 5. Type Hints

**Always use type hints.** They improve readability, enable static analysis with `mypy`, and catch errors early.

*   **All Function Signatures**: Annotate parameters and return types.
*   **Variables**: Annotate complex or ambiguous variable types.
*   **`typing` module**: Use `List`, `Dict`, `Optional`, `Union`, `Callable`, `Any`, etc.
*   **`TypeAlias`**: For complex type signatures.

❌ BAD:
```python
def process_data(data):
    # ...
    return len(data)

def get_user(user_id):
    # ...
    return {"id": user_id, "name": "Test"}
```

✅ GOOD:
```python
from typing import Dict, Any, List, Optional, Union, TypeAlias

UserId: TypeAlias = Union[int, str]

def process_data(data: List[str]) -> int:
    """Processes a list of strings and returns its length."""
    return len(data)

def get_user(user_id: UserId) -> Optional[Dict[str, Any]]:
    """Retrieves user data by ID."""
    if user_id == 1:
        return {"id": 1, "name": "Alice"}
    return None
```

## 6. Virtual Environments

**Mandatory for all projects.** Use `Poetry` or `Pipenv` for dependency management and environment isolation.

*   **Poetry**: Recommended for new projects due to superior dependency resolution and packaging features.
*   **Pipenv**: Acceptable for existing projects already using it.
*   **Never commit `venv/` directories.**

❌ BAD:
```bash
# Installing directly into global Python environment
pip install requests black
```

✅ GOOD:
```bash
# Using Poetry
poetry new my_project
cd my_project
poetry add requests black --group dev
poetry run python my_script.py

# Using Pipenv
mkdir my_project && cd my_project
pipenv install requests
pipenv install black --dev
pipenv run python my_script.py
```

## 7. Packaging

Structure projects for easy distribution and installation.

*   **`src/` Layout**: Place all package code inside a `src/` directory.
*   **`pyproject.toml`**: Use this for project metadata and build configuration (PEP 621).
*   **`README.md`**: Comprehensive project description.
*   **`LICENSE`**: Clearly state the project's license.

❌ BAD:
```
my_project/
├── my_module.py
├── setup.py # Old style
└── requirements.txt
```

✅ GOOD:
```
my_project/
├── src/
│   └── my_package/
│       ├── __init__.py
│       └── main.py
├── pyproject.toml
├── README.md
├── LICENSE
└── tests/
    └── test_main.py
```

## 8. Testing Approaches

**Automated testing is non-negotiable.** Use `pytest` for all tests.

*   **`pytest`**: The standard test runner.
*   **Coverage**: Integrate `pytest-cov` to ensure adequate test coverage. Aim for >90%.
*   **Fixtures**: Use `pytest` fixtures for setup and teardown.
*   **Parametrization**: Use `pytest.mark.parametrize` for testing multiple inputs.
*   **Mocks**: Use `unittest.mock` (or `pytest-mock`) for isolating units under test.
*   **Test-Driven Development (TDD)**: Strongly encouraged. Write tests before code.

❌ BAD:
```python
# No tests, or using unittest.TestCase directly without pytest
def add(a, b):
    return a + b

# Manual testing
print(add(1, 2))
```

✅ GOOD:
```python
# src/my_package/math.py
def add(a: int, b: int) -> int:
    return a + b

# tests/test_math.py
import pytest
from src.my_package.math import add

@pytest.mark.parametrize("a, b, expected", [
    (1, 2, 3),
    (0, 0, 0),
    (-1, 1, 0),
])
def test_add(a: int, b: int, expected: int):
    assert add(a, b) == expected

def test_add_raises_type_error():
    with pytest.raises(TypeError):
        add("1", 2) # type: ignore
```

## 9. Common Patterns & Anti-patterns

*   **Context Managers**: Use `with` statements for resource management.

    ❌ BAD:
    ```python
    f = open("file.txt", "r")
    data = f.read()
    f.close() # Easy to forget or miss on error
    ```

    ✅ GOOD:
    ```python
    with open("file.txt", "r") as f:
        data = f.read()
    # File is automatically closed
    ```

*   **List Comprehensions/Generator Expressions**: For concise data transformations.

    ❌ BAD:
    ```python
    squares = []
    for i in range(10):
        squares.append(i * i)
    ```

    ✅ GOOD:
    ```python
    squares = [i * i for i in range(10)]
    ```

*   **F-strings**: Prefer `f-strings` for string formatting.

    ❌ BAD:
    ```python
    name = "Alice"
    age = 30
    print("Hello, %s. You are %d years old." % (name, age))
    print("Hello, {}. You are {} years old.".format(name, age))
    ```

    ✅ GOOD:
    ```python
    name = "Alice"
    age = 30
    print(f"Hello, {name}. You are {age} years old.")
    ```
    *   **Caveat**: Avoid complex expressions or function calls inside f-strings. Assign to a variable first.

*   **Enums**: Use `enum.Enum` for symbolic constants.

    ❌ BAD:
    ```python
    STATUS_PENDING = "pending"
    STATUS_COMPLETED = "completed"
    ```

    ✅ GOOD:
    ```python
    from enum import Enum

    class Status(Enum):
        PENDING = "pending"
        COMPLETED = "completed"

    current_status = Status.PENDING
    ```

*   **Avoid Mutable Default Arguments**: This is a common pitfall.

    ❌ BAD:
    ```python
    def add_item(item, item_list=[]): # item_list is created once
        item_list.append(item)
        return item_list

    print(add_item(1)) # [1]
    print(add_item(2)) # [1, 2] - unexpected!
    ```

    ✅ GOOD:
    ```python
    from typing import List, Optional

    def add_item(item: Any, item_list: Optional[List[Any]] = None) -> List[Any]:
        if item_list is None:
            item_list = []
        item_list.append(item)
        return item_list

    print(add_item(1)) # [1]
    print(add_item(2)) # [2]
    ```

*   **Catch Specific Exceptions**: Never use bare `except:`.

    ❌ BAD:
    ```python
    try:
        # risky operation
    except: # Catches ALL exceptions, including SystemExit, KeyboardInterrupt
        print("An error occurred.")
    ```

    ✅ GOOD:
    ```python
    try:
        result = 1 / 0
    except ZeroDivisionError:
        print("Cannot divide by zero.")
    except Exception as e: # Catching a broader base for unexpected errors
        print(f"An unexpected error occurred: {e}")
    ```

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

Similar IDE rules

All IDE rules for Programming languages →

python by other authors comes first.

From the same set240

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