Python

Scope: This document defines the mandatory standards for all Python code to ensure readability, maintainability, security, and performance.

1. Code Layout & Formatting

1.1 Indentation

  • Rule: Use 4 spaces per indentation level.
  • Prohibited: Never use tabs.

1.2 Line Length

  • Standard: Limit all lines to a maximum of 79 characters (PEP 8 standard).
  • Modern Exception: For complex configurations or type hints, 88 characters (Black formatter default) is acceptable if agreed upon by the team.
  • Handling: Break long lines using parentheses (), not backslashes \.
# Good
user_permissions = (
    "read_access",
    "write_access",
    "delete_access",
    "admin_access"
)

1.3 Imports

  • Order:
    • Standard Library (e.g., os, sys)
    • Third-Party (e.g., requests, pandas)
    • Local Application (e.g., from myapp.utils import helper)
  • Style: Use absolute imports. Avoid wildcard imports (from module import *).

1.4 Whitespace

  • Operators: Surround binary operators with a single space (x = 12).
  • Definitions: One space after the colon in type hints (age: int).
  • Functions: Two blank lines before top-level functions; one blank line before methods inside a class.

2. Naming Conventions

Follow strict PEP 8 naming to communicate intent instantly.

Entity Convention Example
Modules snake_case data_processor.py
Classes PascalCase UserProfile
Functions snake_case calculate_total()
Variables snake_case is_active, user_id
Constants SCREAMING_SNAKE MAX_RETRIES = 3
Private _leading_underscore _internal_cache

3. Modern Type Hinting (Python 3.10+)

Type hinting is no longer optional; it is mandatory for maintainability.

3.1 Syntax

  • Rule: Use the modern pipe | operator for Unions instead of importing Union or Optional.
  • Return Types: Always specify the return type, even if it is None.
# Bad (Old Style)
from typing import List, Optional
def find_user(ids: List[int]) -> Optional[str]: ...

# Good (Modern Style)
def find_user(ids: list[int]) -> str | None:
    ...

3.2 Collections

  • Rule: Use standard collections (list, dict, tuple) instead of typing.List or typing.Dict.

4. Documentation & Docstrings

4.1 Format

  • Standard: Use the Google Style docstring format. It is cleaner and more readable than reStructuredText.
  • Requirement: All public modules, classes, and functions must have a docstring.
def connect_to_db(timeout: int = 10) -> bool:
    """Establishes a connection to the primary database.

    Args:
        timeout (int): Max time in seconds to wait for connection.

    Returns:
        bool: True if connection successful, False otherwise.

    Raises:
        ConnectionError: If the network is down.
    """
    pass

4.2 Comments

  • Rule: Comments must explain "Why", not "What".
  • Maintenance: Delete commented-out code immediately. Do not commit dead code.

5. Programming Best Practices

5.1 Error Handling

  • Prohibited: Never use bare except:.
  • Best Practice: Catch specific exceptions.
  • Chaining: When re-raising an exception, use raise ... from e to preserve the stack trace.
# Good
try:
    process_data()
except ValueError as e:
    raise DataProcessingError("Invalid data format") from e

5.2 Conditionals & Truthiness

  • Explicit is better: Use if x is None: instead of if x == None:.
  • Implicit False: Use implicit boolean checks for lists/strings.
  • Yes: if my_list: (Checks if list is not empty)
  • No: if len(my_list) > 0:

5.3 Magic Numbers

  • Rule: Replace raw numbers/strings with named constants or Enums.
from enum import Enum

class Status(Enum):
    PENDING = "pending"
    ACTIVE = "active"

# Usage
if user.status == Status.ACTIVE:
    ...

6. Async & Performance (Modern Standards)

6.1 Async/Await

  • Rule: Do not use blocking code (e.g., time.sleep, requests.get) inside async functions. Use await asyncio.sleep or asynchronous libraries (e.g., httpx).
  • Safe Handling: Use TaskGroup (Python 3.11+) for managing concurrent tasks safely.

6.2 Loops

  • Rule: Use enumerate() for indexing.
  • Rule: Prefer List Comprehensions for simple transformations, but use for loops if the logic is complex.

7. Security & Privacy

  • Secrets: Never commit API keys, passwords, or tokens to Git. Use .env files and pydantic-settings.
  • Inputs: Always validate user input. Never pass user input directly to eval(), exec(), or raw SQL queries (use ORM or parameterized queries).

8. Automation & Tooling

Manual enforcement is unreliable. The project can use the following automated tools:

  • Ruff: The modern, high-speed replacement for Flake8 and isort.
  • Black: For uncompromising code formatting.
  • Mypy: For static type checking.
  • Pre-commit: To run these checks before every commit.

Configuration (pyproject.toml)

Copy this configuration to standardise your team's tooling:

[tool.ruff]
line-length = 79
select = ["E", "F", "I", "UP", "B"] # Enforce PEP8, Imports, pyupgrade, Bugbear

[tool.mypy]
strict = true
ignore_missing_imports = true

Recommended folder structure for modern Python projects)

project_root/
│
├── src/
│   └── my_package/
│       ├── __init__.py
│       ├── main.py
│       └── utils.py
├── tests/
│   ├── __init__.py
│   └── test_main.py
├── .env                # Secrets (GitIgnored)
├── .gitignore
├── pyproject.toml      # Tooling Config
└── README.md