Coding Conventions

These conventions are enforced across all integrations. They exist to ensure consistency, security, and maintainability.


Python style

General

Imports

Naming

Functions

Error handling

Good — catch specific exceptions and emit a structured error:

except urllib.error.HTTPError as e:
    emit_error("module_name", f"HTTP {e.code}: {e.reason}")
    return cursor  # return unchanged cursor so next run retries

Bad — silently ignores all errors:

except Exception:
    pass

String formatting


HTTP patterns

Request construction

def http_get(url, headers, timeout=30):
    req = urllib.request.Request(url, headers=headers, method="GET")
    try:
        with urllib.request.urlopen(req, timeout=timeout) as resp:
            return json.loads(resp.read().decode("utf-8"))
    except urllib.error.HTTPError as e:
        body = e.read().decode("utf-8", errors="replace")
        raise RuntimeError(f"HTTP {e.code}: {body[:200]}")

POST with JSON body

def http_post(url, headers, body, timeout=30):
    data = json.dumps(body).encode("utf-8")
    headers["Content-Type"] = "application/json"
    req = urllib.request.Request(url, data=data, headers=headers, method="POST")
    try:
        with urllib.request.urlopen(req, timeout=timeout) as resp:
            return json.loads(resp.read().decode("utf-8"))
    except urllib.error.HTTPError as e:
        body_text = e.read().decode("utf-8", errors="replace")
        raise RuntimeError(f"HTTP {e.code}: {body_text[:200]}")

Rate limit retry

def http_with_retry(request_fn, *args, max_wait=60):
    try:
        return request_fn(*args)
    except urllib.error.HTTPError as e:
        if e.code == 429:
            retry_after = int(e.headers.get("Retry-After", "30"))
            wait = min(retry_after, max_wait)
            log(1, "Rate limited. Waiting {} seconds", wait)
            time.sleep(wait)
            return request_fn(*args)  # one retry
        raise

State management patterns

Load

def load_state(path):
    try:
        with open(path, "r") as f:
            return json.load(f)
    except (FileNotFoundError, json.JSONDecodeError):
        return {}

Save (atomic)

def save_state(path, state):
    dir_name = os.path.dirname(path) or "."
    with tempfile.NamedTemporaryFile("w", dir=dir_name, delete=False, suffix=".tmp") as tmp:
        json.dump(state, tmp, indent=2)
        tmp.flush()
        os.fsync(tmp.fileno())
        tmp_path = tmp.name
    os.replace(tmp_path, path)

Emission patterns

Standard event

def emit(event):
    sys.stdout.write(json.dumps(event, separators=(",", ":")) + "\n")
    sys.stdout.flush()

Error event

def emit_error(source, message, code=None):
    event = {
        "integration": INTEGRATION_NAME,
        NAMESPACE: {
            "event_type": "error",
            "error_source": source,
            "error_message": message
        }
    }
    if code is not None:
        event[NAMESPACE]["error_code"] = code
    emit(event)

Logging patterns

DEBUG_LEVEL = 0  # set from env/CLI

def log(level, msg, *args):
    if level <= DEBUG_LEVEL:
        text = msg.format(*args) if args else msg
        sys.stderr.write(f"[{INTEGRATION_NAME}] {text}\n")
        sys.stderr.flush()

Usage:

log(1, "Fetched {} events from {}", count, endpoint)    # info
log(2, "Request headers: {}", sanitize_headers(headers)) # verbose
log(3, "Raw response: {}", response_text[:500])           # trace

Never log credentials at any level. If logging headers, sanitize the Authorization value:

def sanitize_headers(headers):
    safe = dict(headers)
    if "Authorization" in safe:
        safe["Authorization"] = "***"
    return safe

Shell script conventions

run.sh

#!/usr/bin/env bash
set -euo pipefail

# Configuration — environment variables only
export VN_SETTING="value"

# Execute — replace shell with Python
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
exec "$SCRIPT_DIR/vendorname.py" "$@"

Documentation conventions

Configuration reference tables

| Variable | Default | Description |
|---|---|---|
| `VN_SETTING` | `value` | One-line description ending in period. |

Code examples in docs

README flow diagram