ZooStub User Guide¶
The ZooStub class provides a mock implementation of ZOO-Project's runtime functions for local testing and development.
Overview¶
When developing ZOO services locally, you don't have access to the actual ZOO-Project runtime functions like status updates and logging. ZooStub provides compatible implementations that allow you to test your services outside of the ZOO environment.
Features¶
- Status Constants: Service execution status codes
- Status Updates: Mock
update_status()for testing - Logging: Integration with
logurufor local logging - Translation: Mock
_()for internationalization
Status Constants¶
from zoostub import ZooStub
zoo = ZooStub()
# Available status codes
zoo.SERVICE_ACCEPTED # 0 - Service request accepted
zoo.SERVICE_STARTED # 1 - Service execution started
zoo.SERVICE_PAUSED # 2 - Service paused (deprecated)
zoo.SERVICE_SUCCEEDED # 3 - Service completed successfully
zoo.SERVICE_FAILED # 4 - Service failed
zoo.SERVICE_DEPLOYED # 6 - Service deployed
zoo.SERVICE_UNDEPLOYED # 7 - Service undeployed
Basic Usage¶
Local Testing Setup¶
from zoostub import ZooStub
# Create stub instance
zoo = ZooStub()
def my_service(conf, inputs, outputs):
"""ZOO Service for local testing"""
# Update status
zoo.update_status(conf, 20)
# Log progress
zoo.info("Processing started")
# ... processing ...
zoo.info("Processing completed")
zoo.update_status(conf, 100)
return zoo.SERVICE_SUCCEEDED
Status Updates¶
from zoostub import ZooStub
zoo = ZooStub()
# Simulate progress updates
zoo.update_status(conf, 10) # 10% complete
zoo.update_status(conf, 50) # 50% complete
zoo.update_status(conf, 100) # 100% complete
# Output:
# Status 10
# Status 50
# Status 100
In production, this calls the actual ZOO-Project C function to update the execution status.
Logging Functions¶
ZooStub integrates with loguru for comprehensive logging:
Available Log Levels¶
from zoostub import ZooStub
zoo = ZooStub()
# Trace level (most verbose)
zoo.trace("Detailed execution trace")
# Debug level
zoo.debug("Debug information")
# Info level
zoo.info("General information")
# Success level
zoo.success("Operation completed successfully")
# Warning level
zoo.warning("Potential issue detected")
# Error level
zoo.error("An error occurred")
# Critical level (most severe)
zoo.critical("Critical system error")
Example with Real Service¶
from zoostub import ZooStub
from base_runner import BaseRunner
zoo = ZooStub()
def process_data(conf, inputs, outputs):
"""Process data with comprehensive logging"""
zoo.info("Service started")
zoo.update_status(conf, 10)
try:
# Validate inputs
zoo.debug(f"Inputs: {inputs}")
if "data_url" not in inputs:
zoo.error("Missing required input: data_url")
return zoo.SERVICE_FAILED
zoo.info("Creating runner")
zoo.update_status(conf, 20)
runner = BaseRunner(
cwl_path="/path/to/workflow.cwl",
input_params=inputs,
conf=conf
)
zoo.info("Executing workflow")
zoo.update_status(conf, 50)
result = runner.execute()
zoo.success("Workflow completed successfully")
zoo.update_status(conf, 90)
# Set outputs
outputs["result"]["value"] = result["stac_catalog"]
zoo.info("Service completed")
zoo.update_status(conf, 100)
return zoo.SERVICE_SUCCEEDED
except FileNotFoundError as e:
zoo.error(f"File not found: {e}")
return zoo.SERVICE_FAILED
except Exception as e:
zoo.critical(f"Unexpected error: {e}")
return zoo.SERVICE_FAILED
Translation Function¶
The _() function provides a mock for internationalization:
from zoostub import ZooStub
zoo = ZooStub()
# Mock translation (just prints in stub)
message = zoo._("Processing started")
# Output: invoked _ with Processing started
# In production, this would use ZOO's translation system
Using in Development vs Production¶
Development (with ZooStub)¶
from zoostub import ZooStub
zoo = ZooStub()
def my_service(conf, inputs, outputs):
zoo.info("Running locally")
zoo.update_status(conf, 50)
# ... processing ...
return zoo.SERVICE_SUCCEEDED
# Test locally
if __name__ == "__main__":
conf = {"lenv": {"message": ""}}
inputs = {"data": {"value": "test.tif"}}
outputs = {}
status = my_service(conf, inputs, outputs)
print(f"Service returned: {status}")
Production (with real ZOO)¶
try:
import zoo
except ImportError:
from zoostub import ZooStub
zoo = ZooStub()
def my_service(conf, inputs, outputs):
"""Works in both development and production"""
zoo.info("Service started")
zoo.update_status(conf, 50)
# ... processing ...
return zoo.SERVICE_SUCCEEDED
Integration with BaseRunner¶
ZooStub works seamlessly with BaseRunner:
from zoostub import ZooStub
from base_runner import BaseRunner
zoo = ZooStub()
def test_runner_locally():
"""Test a CWL workflow locally"""
conf = {
"lenv": {
"Identifier": "test-workflow",
"message": ""
},
"main": {
"tmpPath": "/tmp/zoo-test"
}
}
inputs = {
"input_file": {
"cache_file": "/path/to/test.tif",
"mimeType": "image/tiff"
},
"threshold": {
"dataType": "float",
"value": "0.5"
}
}
outputs = {"stac": {}}
zoo.info("Starting local test")
runner = BaseRunner(
cwl_path="/path/to/app-package.cwl",
input_params=inputs,
conf=conf
)
zoo.info("Executing workflow")
result = runner.execute()
zoo.success(f"Result: {result}")
return zoo.SERVICE_SUCCEEDED
if __name__ == "__main__":
status = test_runner_locally()
print(f"Test completed with status: {status}")
Logging Configuration¶
Customize loguru logging when using ZooStub:
from loguru import logger
from zoostub import ZooStub
# Configure loguru
logger.remove() # Remove default handler
logger.add(
"service.log",
rotation="10 MB",
level="DEBUG",
format="{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {message}"
)
zoo = ZooStub()
# Now all zoo.info(), zoo.debug(), etc. use custom config
zoo.info("Service started")
zoo.debug("Debug message")
Best Practices¶
1. Import Pattern for Development/Production¶
# ✅ DO: Support both environments
try:
import zoo
except ImportError:
from zoostub import ZooStub
zoo = ZooStub()
2. Consistent Status Updates¶
# ✅ DO: Regular status updates
zoo.update_status(conf, 0) # Started
zoo.update_status(conf, 25) # 1/4 complete
zoo.update_status(conf, 50) # 1/2 complete
zoo.update_status(conf, 75) # 3/4 complete
zoo.update_status(conf, 100) # Complete
3. Appropriate Log Levels¶
# ✅ DO: Use appropriate log levels
zoo.debug("Variable value: {value}") # Development info
zoo.info("Processing step completed") # Normal operation
zoo.warning("Using default value") # Potential issue
zoo.error("Failed to connect to API") # Error occurred
zoo.critical("System out of memory") # Critical failure
# ❌ DON'T: Use info for everything
4. Error Handling with Logging¶
# ✅ DO: Log errors with context
try:
result = process_data()
except ValueError as e:
zoo.error(f"Invalid data format: {e}")
return zoo.SERVICE_FAILED
except Exception as e:
zoo.critical(f"Unexpected error: {e}")
return zoo.SERVICE_FAILED
Testing Example¶
Create a complete test with ZooStub:
from zoostub import ZooStub
from zoo_conf import ZooInputs, ZooOutputs
zoo = ZooStub()
def test_service_locally():
"""Complete local service test"""
# Setup test data
conf = {
"lenv": {"Identifier": "test", "message": ""},
"main": {"tmpPath": "/tmp/test"}
}
inputs = {
"threshold": {
"dataType": "float",
"value": "0.75"
},
"input_file": {
"cache_file": "/tmp/test.tif",
"mimeType": "image/tiff"
}
}
outputs = {"result": {}}
# Run service
zoo.info("=== Starting Service Test ===")
zoo.update_status(conf, 0)
# Parse inputs
zoo_inputs = ZooInputs(inputs)
params = zoo_inputs.get_processing_parameters()
zoo.debug(f"Parameters: {params}")
# Simulate processing
zoo.info("Processing data...")
zoo.update_status(conf, 50)
# Set results
zoo_outputs = ZooOutputs(outputs)
zoo_outputs.set_output("/tmp/result.json")
zoo.success("Service completed successfully")
zoo.update_status(conf, 100)
return zoo.SERVICE_SUCCEEDED
if __name__ == "__main__":
status = test_service_locally()
if status == 3:
print("✓ Test PASSED")
else:
print("✗ Test FAILED")
Limitations¶
ZooStub is for local testing only. It provides:
- ✅ Status code constants
- ✅ Mock status updates (prints to console)
- ✅ Logging via loguru
- ✅ Mock translation function
It does NOT provide:
- ❌ Actual ZOO-Project runtime integration
- ❌ Real status updates to ZOO server
- ❌ ZOO-Project's full API
- ❌ Multi-language translation