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 case | Recommended API |
|---|
| Simple Python computation | Function decorator |
| Map/reduce over data | Function decorator |
| Interactive workflow, multiple commands | Sandbox |
| Streaming output | Sandbox |
| File manipulation | Sandbox |
| Long-running processes | Sandbox |
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