Skip to content

Your First Job

Let's start with the simplest possible Job: running a function once.

The Basics

A Job wraps your function to provide execution tracking, logging, and output storage.

from examples.common.functions import hello
from runnable import PythonJob

def main():
    job = PythonJob(function=hello)
    job.execute()
    return job  # REQUIRED: Always return the job object

if __name__ == "__main__":
    main()
See complete runnable code
examples/11-jobs/python_tasks.py
"""
You can execute this pipeline by:

    python examples/01-tasks/python_tasks.py

The stdout of "Hello World!" would be captured as execution
log and stored in the catalog.

An example of the catalog structure:

.catalog
└── baked-heyrovsky-0602
    └── hello.execution.log

2 directories, 1 file


The hello.execution.log has the captured stdout of "Hello World!".
"""

from examples.common.functions import hello
from runnable import PythonJob


def main():
    job = PythonJob(function=hello)

    job.execute()

    return job


if __name__ == "__main__":
    main()

Try it now:

uv run examples/11-jobs/python_tasks.py

Always Return the Job Object

Your main() function must return the job object. This is required for:

  • Execution tracking - Runnable needs the job object to track execution status
  • Metadata access - The returned object contains run IDs, execution logs, and results
  • Integration compatibility - External tools expect the job object for further processing
  • Debugging support - Access to execution context and error details

❌ Without return:

def main():
    job = PythonJob(function=hello)
    job.execute()
    # Missing return - breaks Runnable's execution model!

✅ Correct pattern:

def main():
    job = PythonJob(function=hello)
    job.execute()
    return job  # Required for proper execution tracking

What Happens When You Run It

1. Rich Context Display

🏃 Lets go!!
Working with context:
JobContext(
    run_id='null-panini-0628',
    catalog=FileSystemCatalog(catalog_location='.catalog'),
    job_executor=LocalJobExecutor(),
    ...
)

2. Function Execution

Hello World!
WARNING:This is a warning message.

3. Automatic Storage

  • Execution logs captured in catalog
  • Unique run ID generated for tracking
  • All output preserved automatically

4. Summary Report

{
    "Available parameters": [],
    "Output parameters": [],
    "status": "SUCCESS"
}

Generated Run IDs

Each execution gets a unique, memorable run ID: - null-panini-0628 - minty-brattain-0628 - feasible-booth-0628

These organize your catalog and make runs easy to find.

Custom Run IDs

Control execution tracking with custom identifiers:

# Set custom run ID for tracking and debugging
export RUNNABLE_RUN_ID="experiment-learning-rate-comparison-v1"
uv run training_job.py

Benefits:

  • Easy identification in logs and run history
  • Consistent naming across related executions
  • Better debugging when tracking specific experiments
  • Integration with external systems using predictable IDs
# Example: A/B testing with clear run IDs
export RUNNABLE_RUN_ID="model-comparison-baseline-v1"
uv run baseline_job.py

export RUNNABLE_RUN_ID="model-comparison-experimental-v1"
uv run experimental_job.py

Default vs Custom Run IDs

Without RUNNABLE_RUN_ID: Auto-generated names like null-panini-0628

With RUNNABLE_RUN_ID: Your custom identifier experiment-learning-rate-comparison-v1

Job Types at a Glance

Job Type Purpose Example Use
PythonJob Execute Python functions Data analysis, calculations
ShellJob Run shell commands File processing, system ops
NotebookJob Execute Jupyter notebooks Interactive analysis, reports

What's Next?

Now that you can run basic Jobs:

Ready to return data from your Jobs? Continue to Working with Data!