OperationResult
The OperationResult class provides enhanced result processing for Odoo operations with automatic parsing, structured data extraction, and intelligent semantic success detection. It transforms raw command output into structured, programmatically accessible results.
- class oduit.operation_result.OperationResult(operation: str | None = None, module: str | None = None, database: str | None = None)[source]
Bases:
objectBuilder class for creating standardized operation results
- ANSI_ESCAPE_PATTERN = re.compile('\\x1b\\[[0-9;]*m')
- TIMESTAMP_PREFIX_PATTERN = re.compile('^\\d{4}-\\d{2}-\\d{2}\\s+\\d{2}:\\d{2}:\\d{2},\\d{3}\\s+\\d+\\s+')
- FAILURE_LOG_PREFIX_PATTERN = re.compile('^(?:INFO|WARNING|ERROR|DEBUG)\\s+\\S+\\s+[\\w]+\\.[\\w.]+:\\s*')
- FAILURE_PATTERNS = {'generic': [re.compile('ERROR.*FATAL'), re.compile('CRITICAL'), re.compile('Exception.*Traceback')], 'install': [re.compile('odoo\\.modules\\.loading: invalid module names, ignored:'), re.compile('odoo\\.modules\\.loading: Some modules are not loaded, some dependencies or manifest may be missing:'), re.compile('ModuleNotFoundError'), re.compile('No module named'), re.compile('ImportError')], 'test': [re.compile('odoo\\.modules\\.loading: invalid module names, ignored:'), re.compile('odoo\\.modules\\.loading: Some modules are not loaded, some dependencies or manifest may be missing:'), re.compile('ModuleNotFoundError'), re.compile('No module named'), re.compile('ImportError')], 'update': [re.compile('odoo\\.modules\\.loading: invalid module names, ignored:'), re.compile('odoo\\.modules\\.loading: Some modules are not loaded, some dependencies or manifest may be missing:'), re.compile('ModuleNotFoundError'), re.compile('No module named'), re.compile('ImportError')]}
Builder class for creating standardized operation results
- __init__(operation: str | None = None, module: str | None = None, database: str | None = None)[source]
- classmethod from_operation(command_operation: CommandOperation) OperationResult[source]
Factory method to create OperationResult from CommandOperation.
- set_success(success: bool, return_code: int = 0) OperationResult[source]
- set_new_operation(operation: str) OperationResult[source]
- set_operation(operation: str) OperationResult[source]
- set_command(command: list[str]) OperationResult[source]
- set_output(stdout: str = '', stderr: str = '') OperationResult[source]
- set_module(module: str) OperationResult[source]
- set_database(database: str) OperationResult[source]
- set_addon_name(addon_name: str) OperationResult[source]
- set_addons(addons: list[str]) OperationResult[source]
- set_custom_data(**kwargs: Any) OperationResult[source]
Add operation-specific data
- parse_and_merge_install_results(output: str, **additional_data: Any) OperationResult[source]
Parse install output and merge with existing custom data
- parse_and_merge_test_results(output: str, **additional_data: Any) OperationResult[source]
Parse test output and merge with existing custom data
- process_with_parsers(output: str, **additional_data: Any) OperationResult[source]
Automatically select and apply appropriate parsers based on operation metadata.
Class Reference
- class oduit.OperationResult(operation: str | None = None, module: str | None = None, database: str | None = None)[source]
Bases:
objectBuilder class for creating standardized operation results
- ANSI_ESCAPE_PATTERN = re.compile('\\x1b\\[[0-9;]*m')
- TIMESTAMP_PREFIX_PATTERN = re.compile('^\\d{4}-\\d{2}-\\d{2}\\s+\\d{2}:\\d{2}:\\d{2},\\d{3}\\s+\\d+\\s+')
- FAILURE_LOG_PREFIX_PATTERN = re.compile('^(?:INFO|WARNING|ERROR|DEBUG)\\s+\\S+\\s+[\\w]+\\.[\\w.]+:\\s*')
- FAILURE_PATTERNS = {'generic': [re.compile('ERROR.*FATAL'), re.compile('CRITICAL'), re.compile('Exception.*Traceback')], 'install': [re.compile('odoo\\.modules\\.loading: invalid module names, ignored:'), re.compile('odoo\\.modules\\.loading: Some modules are not loaded, some dependencies or manifest may be missing:'), re.compile('ModuleNotFoundError'), re.compile('No module named'), re.compile('ImportError')], 'test': [re.compile('odoo\\.modules\\.loading: invalid module names, ignored:'), re.compile('odoo\\.modules\\.loading: Some modules are not loaded, some dependencies or manifest may be missing:'), re.compile('ModuleNotFoundError'), re.compile('No module named'), re.compile('ImportError')], 'update': [re.compile('odoo\\.modules\\.loading: invalid module names, ignored:'), re.compile('odoo\\.modules\\.loading: Some modules are not loaded, some dependencies or manifest may be missing:'), re.compile('ModuleNotFoundError'), re.compile('No module named'), re.compile('ImportError')]}
Builder class for creating standardized operation results
- __init__(operation: str | None = None, module: str | None = None, database: str | None = None)[source]
- classmethod from_operation(command_operation: CommandOperation) OperationResult[source]
Factory method to create OperationResult from CommandOperation.
- set_success(success: bool, return_code: int = 0) OperationResult[source]
- set_new_operation(operation: str) OperationResult[source]
- set_operation(operation: str) OperationResult[source]
- set_command(command: list[str]) OperationResult[source]
- set_output(stdout: str = '', stderr: str = '') OperationResult[source]
- set_module(module: str) OperationResult[source]
- set_database(database: str) OperationResult[source]
- set_addon_name(addon_name: str) OperationResult[source]
- set_addons(addons: list[str]) OperationResult[source]
- set_custom_data(**kwargs: Any) OperationResult[source]
Add operation-specific data
- parse_and_merge_install_results(output: str, **additional_data: Any) OperationResult[source]
Parse install output and merge with existing custom data
- parse_and_merge_test_results(output: str, **additional_data: Any) OperationResult[source]
Parse test output and merge with existing custom data
- process_with_parsers(output: str, **additional_data: Any) OperationResult[source]
Automatically select and apply appropriate parsers based on operation metadata.
Enhanced Result Processing
Factory Method from CommandOperation
The from_operation() factory method creates structured results from CommandOperation objects:
@classmethod
def from_operation(cls, command_operation: CommandOperation) -> OperationResult:
"""Factory method to create OperationResult from CommandOperation."""
This method automatically: - Sets up operation metadata (type, modules, database) - Configures result parsing based on operation type - Prepares structured result containers
Automatic Result Parsing
The process_with_parsers() method provides intelligent parsing based on operation metadata:
def process_with_parsers(self, output: str, **additional_data: Any) -> OperationResult:
"""Automatically select and apply appropriate parsers based on operation metadata."""
Supported parsers: - Install Parser: Extracts module dependencies, loading statistics, and installation errors - Test Parser: Parses test statistics, failure details, and error tracebacks - Coverage Parser: Extracts coverage metrics and reporting data
Usage Examples
Creating Results from Operations
from oduit.builders import InstallCommandBuilder
from oduit.operation_result import OperationResult
from oduit.config_loader import ConfigProvider
# Build operation
config = ConfigProvider()
builder = InstallCommandBuilder(config, "sale")
operation = builder.build_operation()
# Create result from operation
result_builder = OperationResult.from_operation(operation)
# Process execution results with automatic parsing
output = "Some command output..."
result_builder.set_success(True, 0)
result_builder.set_output(stdout=output)
result_builder.process_with_parsers(output)
# Finalize and get structured result
final_result = result_builder.finalize()
Install Operation Results
# Install operation with enhanced parsing
builder = InstallCommandBuilder(config, "purchase")
operation = builder.build_operation()
# Execute and get structured results
result = process_manager.run_operation(operation)
# Access parsed install information
print(f"Success: {result['success']}")
print(f"Modules Loaded: {result.get('modules_loaded', 0)}")
print(f"Total Modules: {result.get('total_modules', 0)}")
if result.get('unmet_dependencies'):
print("Unmet Dependencies:")
for dep in result['unmet_dependencies']:
print(f" {dep['module']}: {dep['dependencies']}")
if result.get('dependency_errors'):
print("Dependency Errors:")
for error in result['dependency_errors']:
print(f" - {error}")
Test Operation Results
# Test operation with enhanced parsing
builder = OdooTestCommandBuilder(config, "/my_module")
operation = builder.build_operation()
# Execute and get structured results
result = process_manager.run_operation(operation)
# Access parsed test information
print(f"Test Success: {result['success']}")
print(f"Total Tests: {result.get('total_tests', 0)}")
print(f"Passed Tests: {result.get('passed_tests', 0)}")
print(f"Failed Tests: {result.get('failed_tests', 0)}")
print(f"Error Tests: {result.get('error_tests', 0)}")
# Access failure details
if result.get('failures'):
print("Test Failures:")
for failure in result['failures']:
print(f" Test: {failure['test_name']}")
if failure['file']:
print(f" File: {failure['file']}:{failure['line']}")
if failure['error_message']:
print(f" Error: {failure['error_message']}")
Manual Result Building
from oduit.operation_result import OperationResult
# Create result builder manually
result_builder = OperationResult(
operation="install",
module="sale",
database="demo_db"
)
# Chain builder methods
result = (result_builder
.set_success(True, 0)
.set_command(['odoo-bin', '-i', 'sale'])
.set_output(stdout="Installation completed successfully")
.set_custom_data(
installation_time=45.2,
modules_installed=['sale', 'base']
)
.finalize())
print(f"Duration: {result['duration']}")
print(f"Installation Time: {result['installation_time']}")
Error Handling and Semantic Success
# The OperationResult provides semantic success detection
# Example: Install operation that exits with code 0 but has dependency errors
output = """
loading 1 modules...
module sale: Unmet dependencies: account, stock
Some modules are not loaded: ['sale']
"""
result_builder = OperationResult.from_operation(install_operation)
result_builder.set_success(True, 0) # Command exited with 0
result_builder.process_with_parsers(output)
final_result = result_builder.finalize()
# Semantic analysis detects this as failure despite exit code 0
print(f"Success: {final_result['success']}") # False
print(f"Error: {final_result['error']}") # "Module installation failed: ..."
Custom Data and Metadata
# Add custom operation-specific data
result_builder.set_custom_data(
benchmark_time=123.45,
memory_usage="2.1GB",
custom_metrics={
"queries_executed": 1247,
"cache_hits": 892
}
)
# Custom data is preserved alongside standard fields
final_result = result_builder.finalize()
print(f"Benchmark: {final_result['benchmark_time']}")
print(f"Metrics: {final_result['custom_metrics']}")
Result Structure
Standard Result Fields
All OperationResult instances include these standard fields:
{
# Execution results
'success': bool, # Overall operation success
'return_code': int, # Process exit code
'stdout': str, # Standard output
'stderr': str, # Error output
'command': list[str], # Executed command
# Operation metadata
'operation': str, # Operation type
'module': str | None, # Target module
'database': str | None, # Target database
'addon_name': str | None, # Addon name
'addons': list[str] | None, # Multiple addons
# Error information
'error': str | None, # Error message
'error_type': str | None, # Error classification
# Timing
'duration': float, # Operation duration in seconds
'timestamp': str, # ISO timestamp
}
Install Operation Results
Install operations provide additional parsed fields:
{
# Standard fields...
# Install-specific parsed results
'modules_loaded': int, # Successfully loaded modules
'total_modules': int, # Total modules processed
'unmet_dependencies': list[dict], # Dependency issues
'failed_modules': list[str], # Modules that failed to load
'dependency_errors': list[str], # Human-readable dependency errors
'error_messages': list[str], # Installation error messages
}
Example unmet_dependencies structure:
'unmet_dependencies': [
{
'module': 'sale_extended',
'dependencies': ['account', 'stock', 'sale']
}
]
Test Operation Results
Test operations provide detailed test statistics and failure information:
{
# Standard fields...
# Test-specific parsed results
'total_tests': int, # Total number of tests run
'passed_tests': int, # Number of passed tests
'failed_tests': int, # Number of failed tests
'error_tests': int, # Number of tests with errors
'failures': list[dict], # Detailed failure information
'warnings': list[str], # Parser diagnostics when excerpts are preserved
}
Example failures structure:
'failures': [
{
'test_name': 'FastAPIDemoCase.test_no_key',
'file': '/path/to/test_file.py',
'line': 42,
'error_message': 'AssertionError: Expected value not found',
'traceback': ['Full traceback lines...'],
'raw_failure_excerpt': [
'FAIL: FastAPIDemoCase.test_no_key',
'Traceback (most recent call last):',
'AssertionError: Expected value not found',
],
}
]
Semantic Success Logic
Install Operations
Exit Code Success but Dependency Errors: Marked as failure if unmet dependencies exist
Module Loading Failures: Marked as failure if modules fail to load
Error Messages: Marked as failure if installation-related ERROR lines are detected
Test Operations
Test Failures: Marked as failure if any tests fail, regardless of exit code
Test Errors: Marked as failure if any tests have errors
Combines Exit Code and Test Results: Provides comprehensive success assessment
Custom Operations
Extensible: Custom parsers can implement domain-specific success logic
Override Capability: Manual success setting takes precedence when needed
Metadata-Driven: Success logic adapts based on operation metadata
This semantic approach ensures that operations are correctly classified as successful or failed based on their actual outcomes, not just process exit codes.