# **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 `\`.

```Python
# 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`.
```Python

# 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.

```Python
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.
```Python
# 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.

```Python
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:
```Python
[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
```
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9