Skip to main content
This guide covers the @session.function() decorator for running Python functions in sandboxes. It’s intended for developers who want to offload individual Python computations to isolated environments without managing a sandbox lifecycle directly, and explains how to define remote functions, choose serialization modes, and decide when this API is the right fit.

Overview

The function decorator API lets you execute Python functions in isolated sandbox containers. Use it when you want to run discrete Python work remotely while keeping your calling code simple:
import cwsandbox
from cwsandbox import SandboxDefaults

with cwsandbox.Session(SandboxDefaults(container_image="python:3.11")) as session:
    @session.function()
    def compute(x: int, y: int) -> int:
        return x + y

    # Execute in sandbox
    result = compute.remote(2, 3).result()
    print(result)  # 5

Basic usage

Define functions

Decorate functions with @session.function():
@session.function()
def process_data(data: dict) -> dict:
    # Code runs inside the sandbox
    import pandas as pd
    df = pd.DataFrame(data)
    return {"mean": df["value"].mean()}

Call functions

Call .remote() on the decorated function to execute in the sandbox:
# Returns OperationRef immediately
ref = compute.remote(2, 3)

# Block for result
result = ref.result()

# One-liner
result = compute.remote(2, 3).result()

Execution methods

Run in parallel with map()

Execute across multiple inputs:
@session.function()
def square(x: int) -> int:
    return x * x

# Execute for each input
refs = square.map((x,) for x in range(10))

# Collect all results
from cwsandbox import results
all_results = results(refs)  # [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
With tuples for multiple arguments:
@session.function()
def add(x: int, y: int) -> int:
    return x + y

# Each tuple is unpacked as arguments
refs = add.map([(1, 2), (3, 4), (5, 6)])
all_results = results(refs)  # [3, 7, 11]

Run locally with local()

Run locally without a sandbox, which is useful for testing:
# No sandbox created - runs in current process
result = compute.local(2, 3)
print(result)  # 5

Serialization modes

Choose a serialization mode based on the types of arguments and return values your function uses.

JSON (default)

Safe and human-readable, but limited to JSON-serializable types:
from cwsandbox import Serialization

@session.function(serialization=Serialization.JSON)
def process(data: dict) -> dict:
    return {"result": data["value"] * 2}

Pickle

Supports complex Python objects:
import numpy as np

@session.function(serialization=Serialization.PICKLE)
def compute_numpy(arr: np.ndarray) -> np.ndarray:
    return arr * 2

arr = np.array([1, 2, 3])
result = compute_numpy.remote(arr).result()  # array([2, 4, 6])
Use pickle when you need:
  • NumPy arrays.
  • Pandas DataFrames.
  • Custom class instances.
  • Complex nested objects.

Closures and globals

Closure variables

Functions capture variables from their enclosing scope:
multiplier = 10

@session.function()
def multiply(x: int) -> int:
    return x * multiplier  # Captures 'multiplier'

result = multiply.remote(5).result()  # 50

Global variables

The decorator serializes referenced globals with the function:
CONFIG = {"threshold": 0.5}

@session.function()
def check_value(x: float) -> bool:
    return x > CONFIG["threshold"]

result = check_value.remote(0.7).result()  # True

Container image

Override the container image for specific functions:
@session.function(container_image="pytorch/pytorch:latest")
def train_model(data: dict) -> dict:
    import torch
    # GPU training code
    return {"loss": 0.01}

Error handling

Function exceptions propagate to the caller:
@session.function()
def failing_function() -> None:
    raise ValueError("Something went wrong")

try:
    failing_function.remote().result()
except Exception as e:
    print(f"Function failed: {e}")

When to use functions compared to sandboxes

Use the following table to decide whether the function decorator API or the sandbox API is a better fit for your workload.
Use caseRecommended API
Simple Python computationFunction decorator
Map/reduce over dataFunction decorator
Interactive workflow, multiple commandsSandbox
Streaming outputSandbox
File manipulationSandbox
Long-running processesSandbox

Limitations

The function API is intentionally simple. For complex workflows:
  • Retries and backoff: Implement in calling code.
  • Task dependencies and DAGs: Use a workflow orchestrator such as Airflow or Prefect.
  • Complex scheduling: Use the sandbox API directly.

Complete example

The following example combines the concepts in this guide. It shows JSON and pickle serialization, single and parallel execution, and a NumPy workflow in one session:
import cwsandbox
from cwsandbox import SandboxDefaults, Serialization, results
import numpy as np

defaults = SandboxDefaults(
    container_image="python:3.11",
    tags=("remote-functions-demo",),
)

with cwsandbox.Session(defaults=defaults) as session:
    # JSON serialization for simple types
    @session.function()
    def square(x: int) -> int:
        return x * x

    # Pickle for complex types
    @session.function(serialization=Serialization.PICKLE)
    def process_array(arr: np.ndarray) -> float:
        return float(arr.mean())

    # Single execution
    result = square.remote(7).result()
    print(f"7 squared: {result}")

    # Parallel execution
    refs = square.map((x,) for x in range(5))
    all_results = results(refs)
    print(f"Squares: {all_results}")

    # NumPy example
    arr = np.array([1, 2, 3, 4, 5])
    mean = process_array.remote(arr).result()
    print(f"Array mean: {mean}")
Last modified on May 29, 2026