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: object

Builder 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]
result: dict[str, Any]
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_error(error: str, error_type: str | None = None) 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.

handle_process_result(process_result: dict | None, check_module_warnings: bool = False, module: str | None = None) OperationResult[source]

Convert ProcessManager result to our standard format

finalize() dict[str, Any][source]

Complete the result and return the dictionary

Class Reference

class oduit.OperationResult(operation: str | None = None, module: str | None = None, database: str | None = None)[source]

Bases: object

Builder 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]
result: dict[str, Any]
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_error(error: str, error_type: str | None = None) 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.

handle_process_result(process_result: dict | None, check_module_warnings: bool = False, module: str | None = None) OperationResult[source]

Convert ProcessManager result to our standard format

finalize() dict[str, Any][source]

Complete the result and return the dictionary

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.