Context
The Context class is the primary data container that flows through your workflow, allowing tasks to share information. It provides a dictionary-like interface with additional features for workflow data management.
Constructor
Section titled “Constructor”Context(data=None)
Section titled “Context(data=None)”Create a new Context instance.
Parameters:
data(dict, optional): Initial data dictionary. Defaults to empty dict.
Example:
import cloaca
# Empty contextcontext = cloaca.Context()
# Context with initial datacontext = cloaca.Context({ "user_id": 123, "email": "user@example.com"})Core Methods
Section titled “Core Methods”get(key, default=None)
Section titled “get(key, default=None)”Get a value by key with optional default.
Parameters:
key(str): The key to look updefault(Any, optional): Value to return if key doesn’t exist
Returns: The value associated with the key, or default if not found
Example:
context = cloaca.Context({"name": "Alice", "age": 30})
name = context.get("name") # "Alice"city = context.get("city", "Unknown") # "Unknown" (default)missing = context.get("missing") # Noneset(key, value)
Section titled “set(key, value)”Set a value (insert new or update existing).
Parameters:
key(str): The key to setvalue(Any): The value to store (must be JSON-serializable)
Example:
context = cloaca.Context()
context.set("user_id", 123)context.set("preferences", {"theme": "dark", "notifications": True})context.set("processed_at", "2025-01-07T10:00:00Z")insert(key, value)
Section titled “insert(key, value)”Insert a new key-value pair. Raises ValueError if key already exists.
Parameters:
key(str): The key to insertvalue(Any): The value to store
Raises: ValueError if key already exists
Example:
context = cloaca.Context()
context.insert("user_id", 123) # OK# context.insert("user_id", 456) # ValueError: Key already existsupdate(key, value)
Section titled “update(key, value)”Update an existing key. Raises KeyError if key doesn’t exist.
Parameters:
key(str): The key to updatevalue(Any): The new value
Raises: KeyError if key doesn’t exist
Example:
context = cloaca.Context({"count": 0})
context.update("count", 1) # OK# context.update("missing", 1) # KeyError: Key not foundremove(key)
Section titled “remove(key)”Remove a key and return its value.
Parameters:
key(str): The key to remove
Returns: The value that was removed, or None if key didn’t exist
Example:
context = cloaca.Context({"temp": "data", "keep": "this"})
removed = context.remove("temp") # Returns "data"missing = context.remove("missing") # Returns NoneDictionary-Style Operations
Section titled “Dictionary-Style Operations”Context supports Python dictionary-style operations:
context[key]
Section titled “context[key]”Get a value by key. Raises KeyError if key doesn’t exist.
Example:
context = cloaca.Context({"name": "Alice"})
name = context["name"] # "Alice"# missing = context["missing"] # KeyErrorcontext[key] = value
Section titled “context[key] = value”Set a value by key.
Example:
context = cloaca.Context()
context["user_id"] = 123context["data"] = {"key": "value"}del context[key]
Section titled “del context[key]”Delete a key. Raises KeyError if key doesn’t exist.
Example:
context = cloaca.Context({"temp": "data"})
del context["temp"] # OK# del context["missing"] # KeyErrorkey in context
Section titled “key in context”Check if a key exists.
Example:
context = cloaca.Context({"name": "Alice"})
has_name = "name" in context # Truehas_age = "age" in context # Falselen(context)
Section titled “len(context)”Get the number of key-value pairs.
Example:
context = cloaca.Context({"a": 1, "b": 2})
count = len(context) # 2Serialization Methods
Section titled “Serialization Methods”to_dict()
Section titled “to_dict()”Convert the context to a Python dictionary.
Returns: dict containing all key-value pairs
Example:
context = cloaca.Context({"name": "Alice", "age": 30})
data = context.to_dict()# {"name": "Alice", "age": 30}update_from_dict(data)
Section titled “update_from_dict(data)”Update the context from a dictionary.
Parameters:
data(dict): Dictionary of key-value pairs to add/update
Example:
context = cloaca.Context({"name": "Alice"})
context.update_from_dict({ "age": 30, "city": "New York"})to_json()
Section titled “to_json()”Serialize the context to a JSON string.
Returns: JSON string representation
Example:
context = cloaca.Context({"name": "Alice", "age": 30})
json_str = context.to_json()# '{"name": "Alice", "age": 30}'Context.from_json(json_str)
Section titled “Context.from_json(json_str)”Create a Context from a JSON string.
Parameters:
json_str(str): Valid JSON string
Returns: New Context instance
Example:
json_data = '{"name": "Alice", "age": 30}'context = cloaca.Context.from_json(json_data)
name = context.get("name") # "Alice"Secret Access
Section titled “Secret Access”Inside a workflow whose package declares secrets
(@cloaca.workflow_secrets(...)), tasks read them through the context. The
resolved value is returned to the caller only — it is never written into the
context’s serialized data, so it cannot leak into schedules, fire logs, or
execution history.
secret(name)
Section titled “secret(name)”Read all fields of a named secret.
Parameters:
name(str): The declared local binding name or the concrete secret name (alias-aware)
Returns: dict mapping field names to values
Raises: RuntimeError if no secret resolver is configured
(CLOACINA_SECRET_KEK unset) or the backend fails; KeyError if the secret
does not exist; PermissionError if the secret is not granted to this
workflow.
secret_field(name, field)
Section titled “secret_field(name, field)”Read a single field of a named secret.
Returns: str
Raises: As secret(), plus KeyError when the secret exists but has no
field of that name.
@cloaca.task()def use_db(context): creds = context.secret("db_prod") # {"username": ..., "password": ...} password = context.secret_field("db_prod", "password") return contextData Types
Section titled “Data Types”Context can store any JSON-serializable Python data:
context = cloaca.Context()
# Strings, numbers, booleanscontext.set("name", "Alice")context.set("age", 30)context.set("active", True)context.set("score", 95.5)context = cloaca.Context()
# Lists and dictionariescontext.set("tags", ["user", "premium", "active"])context.set("preferences", { "theme": "dark", "notifications": True, "language": "en"})
# Nested structurescontext.set("user_profile", { "personal": {"name": "Alice", "age": 30}, "settings": {"theme": "dark"}, "activity": [ {"action": "login", "timestamp": "2025-01-07T10:00:00Z"}, {"action": "view_page", "timestamp": "2025-01-07T10:01:00Z"} ]})# Timestamps (store as ISO strings)context.set("created_at", "2025-01-07T10:00:00Z")context.set("processed_at", datetime.now().isoformat())
# Identifierscontext.set("user_id", 123)context.set("session_id", "sess_abc123")context.set("request_id", str(uuid.uuid4()))
# Status trackingcontext.set("status", "processing")context.set("progress", 0.75)context.set("errors", [])
# Counts and metricscontext.set("items_processed", 100)context.set("total_items", 150)context.set("error_count", 2)Usage Patterns
Section titled “Usage Patterns”Safe Access Pattern
Section titled “Safe Access Pattern”@cloaca.task()def safe_task(context): # Safe access with defaults user_id = context.get("user_id", 0) preferences = context.get("preferences", {})
# Validate required data if not user_id: raise ValueError("user_id is required")
# Process data context.set("processed", True) return contextData Transformation Pattern
Section titled “Data Transformation Pattern”@cloaca.task()def transform_data(context): # Get input data raw_data = context.get("raw_data", [])
# Transform processed_data = [] for item in raw_data: processed_item = { "id": item["id"], "value": item["value"] * 2, "processed_at": datetime.now().isoformat() } processed_data.append(processed_item)
# Store results context.set("processed_data", processed_data) context.set("transformation_complete", True)
return contextAccumulator Pattern
Section titled “Accumulator Pattern”@cloaca.task()def accumulate_results(context): # Get existing results all_results = context.get("all_results", [])
# Add new results new_results = context.get("batch_results", []) all_results.extend(new_results)
# Update context context.set("all_results", all_results) context.set("total_count", len(all_results))
return contextThread Safety
Section titled “Thread Safety”Access to a Context is serialized by the Python interpreter, but the class
provides no atomicity across compound operations (read-modify-write). If you
share one Context object across threads outside the engine, guard mutation
with your own lock:
import threading
context = cloaca.Context()lock = threading.Lock()
def safe_update(key, value): with lock: context.set(key, value)Within a workflow, tasks receive the context as an argument and return it — the engine manages that flow, so task bodies do not need locking.
Performance Considerations
Section titled “Performance Considerations”- Serialization: Context data is serialized for database persistence
- Memory usage: Large objects should be avoided; consider external storage
- JSON compatibility: All data must be JSON-serializable
- Deep copying: Context operations may create deep copies for safety
Best Practices
Section titled “Best Practices”# Good: descriptive, consistent namingcontext.set("user_profile", user_data)context.set("processing_start_time", start_time)context.set("validation_errors", errors)
# Avoid: unclear or inconsistent namescontext.set("data", user_data) # Too genericcontext.set("start", start_time) # Unclearcontext.set("errs", errors) # Abbreviated# Good: organized structurecontext.set("user", { "id": 123, "name": "Alice", "email": "alice@example.com"})context.set("processing", { "status": "active", "progress": 0.5, "started_at": start_time})
# Avoid: flat structure with prefixescontext.set("user_id", 123)context.set("user_name", "Alice")context.set("user_email", "alice@example.com")context.set("processing_status", "active")@cloaca.task()def robust_task(context): try: # Validate required data required_fields = ["user_id", "operation_type"] for field in required_fields: if field not in context: raise ValueError(f"Missing required field: {field}")
# Process data result = process_operation(context) context.set("result", result) context.set("success", True)
except Exception as e: # Store error information context.set("success", False) context.set("error_message", str(e)) context.set("error_type", type(e).__name__) raise # Re-raise to mark task as failed
return contextRelated Classes
Section titled “Related Classes”- DefaultRunner - Executes workflows with Context
- WorkflowBuilder - Builds workflows that use Context
- Task Decorator - Defines tasks that receive Context