Groundfloor Docs

Python handler reference

Handler contract for Coderunner Python functions, background jobs, and scheduled jobs.

Applies to the function, job, and schedule workload types — code you upload as a ZIP and Code Runner executes against a Python bootstrap image. For a service (any container, your own Dockerfile), see Deploy a container image instead — there's no handler contract there, just a container listening on a port.

Project structure

my-coderunner/
├── handler.py
└── requirements.txt

The entry point must be a function named handler inside handler.py — Code Runner invokes handler(context, payload) directly.

Handler contract

def handler(context, payload):
    # ...
    pass
TypeSignatureReturn value
functiondef handler(context, payload){"statusCode": ..., "body": ..., "headers": ...} — required. A function is a request/response HTTP handler; whatever you return becomes the response.
job / scheduledef handler(context, payload)None required — the process runs to completion and exits. Log via print(); there's no response to return.

payload is the JSON dict supplied by the caller (function) or trigger (job/schedule). context is runtime-provided; no properties are documented yet beyond its presence in the signature — don't depend on undocumented context fields.

Minimal example (function)

def handler(context, payload):
    try:
        if not payload or "name" not in payload:
            return {
                "statusCode": 400,
                "body": {"error": "Name is required"},
                "headers": {"content-type": "application/json"},
            }
        return {
            "statusCode": 200,
            "body": {"message": f"Hello {payload['name']}"},
            "headers": {"content-type": "application/json"},
        }
    except Exception as err:
        print(f"Function failed: {err}")
        return {
            "statusCode": 500,
            "body": {"error": "Internal server error"},
            "headers": {"content-type": "application/json"},
        }

A job/schedule handler looks the same minus the return — do the work, print, and let the function end:

def handler(context, payload):
    try:
        print("Background job started")
        do_something(payload)
        print("Background job completed successfully")
    except Exception as err:
        print(f"Background job failed: {err}")

External packages

Declare PyPI dependencies in requirements.txt:

requests

Environment variables and secrets

os.environ.get("NAME") reads deploy-time env vars (gf deploy -e KEY=value) and injected secrets (gf deploy --secret KEY, see Secrets). Never hard-code API keys, passwords, or connection strings — and never zip a .env file.

import os
api_key = os.environ.get("API_KEY")

Common status codes (function)

CodeUsage
200Successful execution
201Resource created
400Invalid or missing input
401 / 403Auth required / denied
404Not found
500Unexpected error — don't leak exception internals in the body

Best practices

  1. Expose the entry point as exactly handler(context, payload).
  2. Access payload safely with .get() rather than direct indexing.
  3. Wrap risky operations in try/except; don't let an unhandled exception crash-loop the container.
  4. Use print() for logs — stdout/stderr are captured as Process Log output.
  5. Keep secrets out of the ZIP — inject them at deploy time.

On this page