Tool Execution and Progress

This chapter documents execution styles and progress handling.

Execution style affects both correctness and maintainability. Object-first calls are concise for in-memory workflows, while path-first calls can be clearer in batch pipelines and audit logs. Progress callbacks are operational tools, not just UI niceties: they support observability, timeout policies, and better failure triage in long geoprocessing runs.

Object-First Execution

Use object-first execution when your script is primarily in-memory and you want concise dataflow between steps.

import whitebox_workflows as wb

wbe = wb.WbEnvironment()
dem = wbe.read_raster('dem.tif')

filled = wbe.hydrology.depressions_storage.fill_depressions(dem)
accum = wbe.hydrology.flow_routing.d8_flow_accum(filled)
wbe.write_raster(accum, 'accum.tif')

Path-First Execution

Use path-first execution when you need explicit artifact paths for auditability, handoffs to external tools, or checkpointed batch runs.

import whitebox_workflows as wb

wbe = wb.WbEnvironment()

result = wbe.hydrology.depressions_storage.fill_depressions(
	dem='dem.tif',
	output='filled.tif',
)
print(result)

Basic Progress Callback

Use a simple callback for interactive runs where immediate feedback is enough.

import whitebox_workflows as wb

wbe = wb.WbEnvironment()

filled = wbe.hydrology.depressions_storage.fill_depressions(
	dem='dem.tif',
	output='filled.tif',
	callback=wb.print_progress,
)

Custom Progress Callback

Use a custom callback when integrating with logs, dashboards, job schedulers, or failure retry logic.

import json
import re

PERCENT_RE = re.compile(r'(-?\d+(?:\.\d+)?)\s*%')

def on_progress(event):
	payload = event
	if isinstance(event, str):
		try:
			payload = json.loads(event)
		except json.JSONDecodeError:
			payload = {'message': event}

	if isinstance(payload, dict):
		pct = payload.get('percent')
		msg = payload.get('message', '')
		if pct is None and msg:
			m = PERCENT_RE.search(str(msg))
			pct = float(m.group(1)) if m else None
		if pct is not None:
			if pct <= 1.0:
				pct *= 100.0
			print(f'[{int(max(0, min(100, pct))):3d}%] {msg}')

# Use: callback=on_progress

Treat this as the default operational template for robust scripts.

  1. Validate required tools.
  2. Run tool chain memory-first.
  3. Emit progress for long operations.
  4. Persist only key outputs.