# Python

> Python coding standards for Scenescape — imports, indentation, patterns, and conventions.

- Skill: `open-edge-platform/python` (Agent Skill)
- Install (CLI): `npx skillmds@latest add open-edge-platform/python`
- Raw SKILL.md: https://api.skillmd.com/api/skills/open-edge-platform/python/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: open-edge-platform (https://skillmd.com/u/open-edge-platform)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/open-edge-platform/python

---


# Python Coding Standards for Scenescape

## Import Organization

Organize imports in this order:

1. Standard library imports
2. Third-party imports (external packages)
3. Local application imports (scene_common, manager, controller, etc.)

```python
# Standard library
import json
import os
from pathlib import Path

# Third-party
import numpy as np
from django.http import HttpResponse

# Local
from scene_common import log
from scene_common.geometry import Point
from manager.models import Scene
```

## Naming Conventions

- **Classes**: `PascalCase` (e.g., `SceneController`, `RESTClient`)
- **Functions/Methods**: `snake_case` (e.g., `get_scene_data`, `validate_message`)
- **Constants**: `UPPER_SNAKE_CASE` (e.g., `TOPIC_BASE`, `CHUNK_SIZE`)
- **Private members**: Prefix with single underscore `_` (e.g., `_process_data`, `_internal_state`)

## Code Style

### Linting & Formatting

- **Linters**: pylint and flake8 (both must pass)
- **Formatter**: autopep8
- **Commands**:
  ```bash
  make lint-python        # Run both pylint and flake8
  make format-python      # Auto-format with autopep8
  ```

### Indentation

- Use **2 spaces** (never tabs)
- Checked via `make indent-check`

### Line Length

- Target: 100 characters (soft limit)
- Hard limit: 120 characters

### Docstrings

Use docstrings for classes and public methods:

```python
def process_detection(self, detection_data):
    """Process detection data from sensor.

    Args:
        detection_data: Dictionary containing detection information

    Returns:
        Processed detection object or None on failure
    """
```

## Django Patterns

### Models

- Import from `scene_common` for shared geometry/camera classes
- Use `ListField` (from `manager.fields`) for list/array storage - provides database portability (PostgreSQL and non-PostgreSQL) and robust handling of edge cases
- Implement `__str__` for admin interface readability

```python
from django.db import models
from scene_common.geometry import Region as ScenescapeRegion
from manager.fields import ListField

class Scene(models.Model):
    name = models.CharField(max_length=255)
    map = models.FileField(upload_to='maps/')
    coordinates = ListField(default=list)  # Works with PostgreSQL and SQLite

    def __str__(self):
        return self.name
```

### Views

- Use class-based views for CRUD operations (`CreateView`, `UpdateView`, `DeleteView`)
- Use `@login_required` decorator for protected views
- Return `JsonResponse` for AJAX endpoints

### API Endpoints (Django REST Framework)

- Use serializers from `manager.serializers`
- Implement custom permissions (e.g., `IsAdminOrReadOnly`)
- Return proper HTTP status codes

```python
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status

class SceneAPI(APIView):
    def get(self, request, scene_id):
        # Implementation
        return Response(data, status=status.HTTP_200_OK)
```

## Scene Common Library Patterns

### MQTT/PubSub

```python
from scene_common.mqtt import PubSub

pubsub = PubSub(mqtt_auth, client_cert, root_cert, mqtt_broker, keepalive=60)
pubsub.onMessage = self.handle_message
pubsub.connect()
```

### Schema Validation

```python
from scene_common.schema import SchemaValidation

schema_val = SchemaValidation(schema_file)
if not schema_val.validateMessage("detector", message_data):
    log.error("Validation failed")
```

### REST Client

```python
from scene_common.rest_client import RESTClient

client = RESTClient(rest_url, rest_auth, root_cert)
result = client.getScene(scene_id)
```

### Logging

- Use f-strings for interpolated log messages.
- Do not use printf-style placeholders (for example `%s`, `%d`) in log statements.

```python
from scene_common import log

log.info("Processing started")
log.error(f"Failed to process scene {scene_id}")
log.debug(f"Debug information: {debug_payload}")
```

## Type Hints

Use type hints for function signatures:

```python
from typing import Optional, List, Dict, Union
from pathlib import Path

def process_images(
    image_paths: List[Path],
    config: Optional[Dict] = None
) -> Union[np.ndarray, None]:
    pass
```

## Error Handling

Use specific exceptions and log appropriately:

```python
try:
    result = dangerous_operation()
except FileNotFoundError as e:
    log.error(f"File not found: {e}")
    return None
except Exception as e:
    log.error(f"Unexpected error: {e}")
    raise
```

## Performance Patterns

### Avoid Deep Copies When Possible

```python
# Good - shallow copy
data = original_data.copy()

# Use only when needed
data = copy.deepcopy(original_data)
```

### Use List Comprehensions

```python
# Preferred
results = [process(item) for item in items if item.valid]

# Over
results = []
for item in items:
    if item.valid:
        results.append(process(item))
```

## Common Anti-Patterns to Avoid

❌ **Don't use mutable default arguments**:

```python
# Bad
def process(data, cache={}):
    pass

# Good
def process(data, cache=None):
    if cache is None:
        cache = {}
```

❌ **Don't catch bare exceptions unless re-raising**:

```python
# Bad
try:
    risky_operation()
except:
    pass

# Good
try:
    risky_operation()
except SpecificException as e:
    log.error(f"Failed: {e}")
```

❌ **Don't use `from module import *`**:

```python
# Bad
from scene_common.geometry import *

# Good
from scene_common.geometry import Point, Region
```

## Virtual Environment Setup

Development uses `.venv` in project root:

```bash
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements-runtime.txt
```

VS Code configuration should point to `.venv/bin/python`.

## Dependencies

- Add to `requirements-runtime.txt` for runtime dependencies
- Add to `requirements-build.txt` for build-time dependencies
- Rebuild Docker image after dependency changes

