Examples

This section points to working examples in the repository and groups them by current product areas.

Core Workflows

Structured operations and parsed results
#!/usr/bin/env python3
"""
Enhanced demo mode example showing realistic log streaming.

This example demonstrates:
1. Progressive log streaming like real Odoo
2. Error scenarios with detailed logging
3. Different module behaviors with realistic timing
4. Comparison between demo and real execution patterns
"""

from oduit.config_loader import ConfigLoader
from oduit.odoo_operations import ModuleUpdateError, OdooOperations


def test_streaming_logs():
    """Test the new streaming log functionality"""
    print("=== Testing Enhanced Demo Mode with Log Streaming ===")

    config_loader = ConfigLoader()
    env_config = config_loader.load_demo_config()
    ops = OdooOperations(env_config, verbose=True)

    # Test successful module with log streaming
    print("\n1. Testing successful module with streaming logs:")
    print("   (Watch the progressive log output)")
    result = ops.update_module("sale", suppress_output=True)
    print(f"   ✓ Success: {result['success']}")
    print(f"   Duration: {result['duration']:.2f}s")

    # Test error module with detailed error logs
    print("\n2. Testing error module with detailed error streaming:")
    result = ops.update_module("module_error", suppress_output=True)
    print(f"   ✗ Success: {result['success']}")
    print(f"   Return code: {result['return_code']}")

    # Test warning module
    print("\n3. Testing warning module with streaming:")
    result = ops.update_module("module_warning", suppress_output=True)
    print(f"   ⚠ Success: {result['success']} (with warnings)")

    # Test unknown module
    print("\n4. Testing unknown module (should show warning stream):")
    result = ops.update_module("unknown_module", suppress_output=True)
    print(f"   ✗ Success: {result['success']} (module not found)")

    # Test slow module to see extended processing
    print("\n5. Testing slow module (extended processing time):")
    result = ops.update_module("module_slow", suppress_output=True)
    print(f"   ✓ Success: {result['success']} (took longer)")


def compare_demo_vs_real():
    """Compare demo output patterns with real Odoo patterns"""
    print("\n=== Demo vs Real Execution Comparison ===")

    print("\n📊 Demo mode characteristics:")
    print("   • Progressive log streaming with realistic timestamps")
    print("   • Simulated processing delays")
    print("   • Realistic error patterns and messages")
    print("   • No actual odoo-bin process required")
    print("   • Predictable outcomes for testing")

    print("\n📊 Real mode characteristics:")
    print("   • Actual Odoo process execution")
    print("   • Real database operations")
    print("   • Unpredictable timing based on system load")
    print("   • Genuine error conditions")
    print("   • Requires working Odoo installation")


def test_error_scenarios():
    """Test various error scenarios with detailed logging"""
    print("\n=== Testing Error Scenarios ===")

    config_loader = ConfigLoader()
    env_config = config_loader.load_demo_config()
    ops = OdooOperations(env_config, verbose=True)

    # Test with exception handling
    print("\n1. Testing exception handling with streaming:")
    try:
        ops.update_module("module_error", suppress_output=True, raise_on_error=True)
        print("   This shouldn't print")
    except ModuleUpdateError as e:
        print(f"   ✓ Caught expected error: {e}")
        if e.operation_result:
            print(f"   Error details: {e.operation_result.get('error', 'No details')}")

    # Test module installation error
    print("\n2. Testing installation error:")
    result = ops.install_module("module_error", suppress_output=True)
    print(f"   Install result: {result['success']}")


def main():
    """Main demo function"""
    print("🚀 Enhanced ODUIT Demo Mode with Log Streaming")
    print("=" * 60)

    try:
        test_streaming_logs()
        compare_demo_vs_real()
        test_error_scenarios()

        print("\n✅ Enhanced demo completed successfully!")
        print("\nNew features demonstrated:")
        print("• 📡 Progressive log streaming with timestamps")
        print("• ⏱️  Realistic processing delays")
        print("• 🔍 Detailed error scenario simulation")
        print("• 🎭 Multiple module behavior patterns")
        print("• 🔄 Stream-based output like real Odoo")

        return 0

    except Exception as e:
        print(f"\n❌ Demo failed with error: {e}")
        import traceback

        traceback.print_exc()
        return 1


if __name__ == "__main__":
    exit(main())
Streaming structured process output
#!/usr/bin/env python3
"""
Example demonstrating the run_command_yielding method in ProcessManager.

The run_command_yielding method is a generator that yields output lines as they arrive,
allowing for real-time processing of command output. This is useful for:
- Real-time log processing and filtering
- Building interactive applications that need to react to command output
- Creating custom progress indicators based on command output
- Processing large amounts of output without storing it all in memory
"""

import time

from oduit.process_manager import ProcessManager


def main():
    # Initialize the process manager
    pm = ProcessManager()

    print("=== ProcessManager run_command_yielding Examples ===\n")

    # Example 1: Basic usage with simple command
    print("1. Basic yielding with echo command:")
    print("   Running: echo -e 'Line 1\\nLine 2\\nLine 3'")

    line_count = 0
    for item in pm.run_command_yielding(
        ["echo", "-e", "Line 1\nLine 2\nLine 3"],
        verbose=True,
        suppress_output=True,  # Suppress direct output to see yielded lines
    ):
        if "line" in item:
            line_count += 1
            line_content = item["line"].strip()
            if line_content:  # Skip empty lines
                print(f"   📝 Yielded line {line_count}: '{line_content}'")
                print(
                    f"      Should show: {item['should_show']}, "
                    f"Is error: {item['is_error']}"
                )
        else:
            # Final result
            result = item["result"]
            print(
                f"   ✅ Final result: Success={result['success']}, "
                f"Return code={result['return_code']}"
            )
    print()

    # Example 2: Error detection with yielding
    print("2. Error detection with yielding:")
    print(
        "   Running: echo -e 'Normal line\\nERROR: Something went wrong\\nAnother line'"
    )

    error_count = 0
    normal_count = 0

    for item in pm.run_command_yielding(
        ["echo", "-e", "Normal line\nERROR: Something went wrong\nAnother line"],
        verbose=True,
        suppress_output=True,
    ):
        if "line" in item:
            line_content = item["line"].strip()
            if line_content:  # Skip empty lines
                if item["is_error"]:
                    error_count += 1
                    print(f"   🚨 ERROR detected: '{line_content}'")
                else:
                    normal_count += 1
                    print(f"   ℹ️  Normal line: '{line_content}'")
        else:
            result = item["result"]
            print(
                f"   📊 Stats: {normal_count} normal lines, {error_count} error lines"
            )
            print(f"   ✅ Command completed: Success={result['success']}")
    print()

    # Example 3: Real-time processing simulation
    print("3. Real-time processing with longer command:")
    print("   Running: python -c with multiple print statements")

    python_code = """
import time
for i in range(5):
    print(f"Processing step {i+1}/5...")
    time.sleep(0.1)
print("Processing complete!")
"""

    start_time = time.time()
    for item in pm.run_command_yielding(
        ["python", "-c", python_code], verbose=False, suppress_output=True
    ):
        if "line" in item:
            line_content = item["line"].strip()
            if line_content:
                elapsed = time.time() - start_time
                print(f"   [{elapsed:.1f}s] {line_content}")
        else:
            result = item["result"]
            total_time = time.time() - start_time
            print(f"   ⏱️  Total execution time: {total_time:.1f}s")
            print(f"   ✅ Success: {result['success']}")
    print()

    # Example 4: Custom output filtering
    print("4. Custom output filtering:")
    print("   Running command and filtering for specific patterns")

    filter_cmd = [
        "echo",
        "-e",
        "INFO: Starting process\nDEBUG: Detailed info\nWARNING: Minor issue\n"
        "ERROR: Critical problem\nINFO: Process complete",
    ]

    important_lines = []
    for item in pm.run_command_yielding(filter_cmd, suppress_output=True):
        if "line" in item:
            line = item["line"].strip()
            # Filter for INFO, WARNING, and ERROR lines only (skip DEBUG)
            if any(level in line for level in ["INFO:", "WARNING:", "ERROR:"]):
                important_lines.append(line)
                level = (
                    "ERROR"
                    if "ERROR:" in line
                    else "WARNING"
                    if "WARNING:" in line
                    else "INFO"
                )
                emoji = "🚨" if level == "ERROR" else "⚠️" if level == "WARNING" else "ℹ️"
                print(f"   {emoji} [{level}] {line}")
        else:
            result = item["result"]
            print(f"   📋 Filtered {len(important_lines)} important lines from output")
    print()

    # Example 5: Memory-efficient processing of large output
    print("5. Memory-efficient processing (simulated large output):")
    print("   Demonstrating how yielding prevents memory buildup")

    # Simulate processing a command that produces lots of output
    large_output_cmd = [
        "python",
        "-c",
        "for i in range(50): print(f'Data line {i:03d}: Some content here')",
    ]

    processed_lines = 0
    memory_friendly_summary = []

    for item in pm.run_command_yielding(large_output_cmd, suppress_output=True):
        if "line" in item:
            processed_lines += 1
            line = item["line"].strip()

            # Process line immediately without storing full content
            if line and "Data line" in line:
                # Extract just the line number for summary
                try:
                    line_num = line.split("Data line ")[1].split(":")[0]
                    if int(line_num) % 10 == 0:  # Every 10th line
                        memory_friendly_summary.append(f"Checkpoint at line {line_num}")
                        print(f"   📍 Processed line {line_num}")
                except (IndexError, ValueError):
                    pass
        else:
            result = item["result"]
            print(f"   ✅ Processed {processed_lines} lines efficiently")
            print(
                f"   💾 Memory summary: {len(memory_friendly_summary)} "
                f"checkpoints stored"
            )

    print("\n=== Summary ===")
    print("✅ run_command_yielding provides:")
    print("   • Real-time line-by-line output processing")
    print("   • Memory-efficient handling of large outputs")
    print("   • Custom filtering and analysis capabilities")
    print("   • Error detection and immediate response")
    print("   • Same error handling and cleanup as run_command")


if __name__ == "__main__":
    main()

Addon and Manifest Workflows

Manifest inspection
#!/usr/bin/env python3
"""
Example script showing how to parse Odoo module manifests and build dependency
graphs using oduit.

This example demonstrates:
1. Parsing module manifest files
2. Getting module dependencies
3. Building complete dependency graphs
4. Creating hierarchical dependency trees
5. Getting proper module installation order
6. Finding missing dependencies
7. Finding reverse dependencies (modules that depend on a given module)
8. Error handling for missing or invalid manifests
"""

from oduit.config_loader import ConfigLoader
from oduit.module_manager import ModuleManager


def print_dependency_graph(
    graph: dict[str, list[str]], title: str = "Dependency Graph"
):
    """Helper function to pretty print dependency graphs."""
    print(f"\n{title}:")
    print("-" * len(title))
    for module, deps in graph.items():
        if deps:
            print(f"  {module}{', '.join(deps)}")
        else:
            print(f"  {module} (no dependencies)")


def print_dependency_tree(tree: dict, indent: int = 0):
    """Helper function to pretty print dependency trees."""
    for module, subtree in tree.items():
        print("  " * indent + f"├─ {module}")
        if subtree:
            print_dependency_tree(subtree, indent + 1)


def main():  # noqa: C901
    # Load configuration using ConfigLoader
    config_loader = ConfigLoader()
    try:
        env_config = config_loader.load_local_config()
        addons_path = env_config.get(
            "addons_path", "/opt/odoo/addons,/opt/custom/addons"
        )
        print("Loaded configuration from local .oduit.toml")
        print(f"Using addons_path: {addons_path}")
    except Exception as e:
        print(f"Could not load config: {e}")
        print("Using default addons_path")
        addons_path = "/opt/odoo/addons,/opt/custom/addons"

    # Initialize the module manager
    module_manager = ModuleManager(addons_path)

    try:
        # Example 1: Parse a module manifest
        module_name = "base"  # Base module should exist in most Odoo installations

        print(f"Parsing manifest for module: {module_name}")
        manifest = module_manager.parse_manifest(module_name)

        if manifest:
            print(f"✓ Successfully parsed manifest for {module_name}")
            print(f"  Name: {manifest.get('name', 'Unknown')}")
            print(f"  Version: {manifest.get('version', 'Unknown')}")
            print(f"  Installable: {manifest.get('installable', False)}")
            print(f"  Auto-install: {manifest.get('auto_install', False)}")
        else:
            print(f"✗ Module {module_name} not found or has no manifest")

        # Example 2: Get direct manifest dependencies
        print(f"\nGetting direct manifest dependencies for module: {module_name}")
        codependencies = module_manager.get_module_codependencies(module_name)

        if codependencies:
            print(
                f"✓ Module {module_name} has {len(codependencies)} direct dependencies:"
            )
            for dep in codependencies:
                print(f"  - {dep}")
        else:
            print(f"✓ Module {module_name} has no direct manifest dependencies")

        # Example 3: Build complete dependency graph
        print(f"\nBuilding dependency graph for module: {module_name}")
        try:
            graph = module_manager.build_dependency_graph(module_name)
            print(f"✓ Successfully built dependency graph with {len(graph)} modules")
            print_dependency_graph(
                graph, f"Complete dependency graph for {module_name}"
            )

        except ValueError as e:
            print(f"✗ Error building dependency graph: {e}")

        # Example 4: Get hierarchical dependency tree
        print(f"\nBuilding dependency tree for module: {module_name}")
        try:
            tree = module_manager.get_dependency_tree(module_name)
            print("✓ Successfully built dependency tree")
            print(f"\nHierarchical dependency tree for {module_name}:")
            print("-" * 40)
            print_dependency_tree(tree)

        except ValueError as e:
            print(f"✗ Error building dependency tree: {e}")

        # Example 5: Test with a common module that has dependencies
        common_modules = ["web", "sale", "account", "stock"]

        print("\nChecking direct manifest dependencies for common modules...")
        for mod in common_modules:
            deps = module_manager.get_module_codependencies(mod)
            if deps:  # Only print if module exists and has dependencies
                print(f"  {mod}: {', '.join(deps)}")

        # Example 6: Demonstrate graph building for a module with complex dependencies
        complex_module = "sale"  # Sale module typically has multiple dependencies
        print(f"\nBuilding dependency graph for complex module: {complex_module}")
        try:
            complex_graph = module_manager.build_dependency_graph(complex_module)
            print(f"✓ Built graph with {len(complex_graph)} total modules")

            # Show only modules with dependencies
            modules_with_deps = {k: v for k, v in complex_graph.items() if v}
            if modules_with_deps:
                print_dependency_graph(
                    modules_with_deps,
                    f"Modules with dependencies in {complex_module} graph",
                )

        except ValueError as e:
            print(f"✗ Could not build dependency graph for {complex_module}: {e}")

        # Example 7: Get installation order for modules
        print("\n" + "=" * 60)
        print("Installation Planning Features")
        print("=" * 60)

        test_modules = ["base", "web"]
        print(f"\nGetting installation order for modules: {', '.join(test_modules)}")
        try:
            install_order = module_manager.get_install_order(*test_modules)
            print(f"✓ Installation order: {' → '.join(install_order)}")
            print("  (Install modules in this order to satisfy dependencies)")
        except Exception as e:
            print(f"✗ Could not determine install order: {type(e).__name__}: {e}")
            import traceback

            traceback.print_exc()

        # Example 8: Find missing dependencies
        print(f"\nChecking for missing dependencies in module: {complex_module}")
        try:
            missing_deps = module_manager.find_missing_dependencies(complex_module)
            if missing_deps:
                print(f"✗ Missing dependencies found: {', '.join(missing_deps)}")
                print("  These modules are required but not found in addons_path")
            else:
                print(f"✓ All dependencies for {complex_module} are available")
        except ValueError as e:
            print(f"✗ Error checking dependencies: {e}")

        # Example 9: Find reverse dependencies
        base_module = "base"
        print(f"\nFinding modules that depend on: {base_module}")
        try:
            reverse_deps = module_manager.get_reverse_dependencies(base_module)
            if reverse_deps:
                print(
                    f"✓ Found {len(reverse_deps)} modules that depend on {base_module}:"
                )
                # Show first 10 to avoid overwhelming output
                for dep in reverse_deps[:10]:
                    print(f"  - {dep}")
                if len(reverse_deps) > 10:
                    print(f"  ... and {len(reverse_deps) - 10} more modules")
            else:
                print(f"✓ No modules depend on {base_module}")
        except Exception as e:
            print(f"✗ Error finding reverse dependencies: {e}")

        # Example 10: Demonstrate installation planning
        planning_modules = ["account", "sale"]
        print(
            f"\nDemonstrating installation planning for: {', '.join(planning_modules)}"
        )
        try:
            # Check for missing dependencies first
            all_missing = set()
            for mod in planning_modules:
                try:
                    missing = module_manager.find_missing_dependencies(mod)
                    all_missing.update(missing)
                except ValueError:
                    pass

            if all_missing:
                missing_list = ", ".join(sorted(all_missing))
                print(f"⚠  Warning: Missing dependencies: {missing_list}")

            # Get installation order
            install_order = module_manager.get_install_order(*planning_modules)
            print("✓ Recommended installation order:")
            for i, module in enumerate(install_order, 1):
                print(f"  {i}. {module}")

        except ValueError as e:
            print(f"✗ Installation planning failed: {e}")

    except ValueError as e:
        print(f"✗ Error parsing manifest: {e}")
        return 1
    except Exception as e:
        print(f"✗ Unexpected error: {e}")
        return 1

    return 0


if __name__ == "__main__":
    exit(main())

Trusted Code Execution

Executing trusted Python through the Odoo shell interface
#!/usr/bin/env python3
"""
Example script showing the new execute_python_code function in OdooOperations.

This example demonstrates:
1. Using the simplified execute_python_code method with an explicit
   shell_interface
2. Executing single Python commands in Odoo environment
3. Executing multiple Python commands
4. Error handling with the new function
5. Comparison with the manual approach
"""

from oduit.config_loader import ConfigLoader
from oduit.odoo_operations import OdooOperations


def main():
    # Initialize configuration and operations
    config_loader = ConfigLoader()
    env_config = config_loader.load_local_config()
    odoo_ops = OdooOperations(env_config, verbose=True)
    odoo_ops_quiet = OdooOperations(env_config, verbose=False)

    print("=== New execute_python_code Examples ===\n")
    print("Using shell_interface='python' for all execute_python_code() calls.\n")

    try:
        # Run examples
        run_simple_example(odoo_ops)
        run_environment_info_example(odoo_ops_quiet)
        run_query_data_example(odoo_ops_quiet)
        run_error_handling_example(odoo_ops_quiet)
        run_statistics_example(odoo_ops_quiet)

    except FileNotFoundError as e:
        print(f"Configuration file not found: {e}")
        print("Available environments:", config_loader.get_available_environments())
        return 1
    except Exception as e:
        print(f"Error: {e}")
        return 1

    print_summary()
    return 0


def run_simple_example(odoo_ops):
    """Run simple Python code execution example"""
    print("1. Simple Python code execution:")
    python_code = "print('Partners found:', len(env['res.partner'].search([])))"
    print(f"   Code: {python_code}")

    result = odoo_ops.execute_python_code(
        python_code=python_code,
        capture_output=True,
        shell_interface="python",
    )

    if result["success"]:
        print("   ✓ Output:")
        for line in result["stdout"].strip().split("\n"):
            if line.strip() and "Partners found:" in line:
                print(f"     {line}")
    else:
        print(f"   ✗ Error: {result.get('error', 'Unknown error')}")
    print()


def run_environment_info_example(odoo_ops):
    """Run environment information query example"""
    print("2. Environment information query:")
    env_info_code = """
print('=== Odoo Environment Info ===')
print('Database:', env.cr.dbname)
print('User:', env.user.name)
print('Company:', env.company.name)
partner_count = len(env['res.partner'].search([]))
print('Total partners:', partner_count)
print('=== Done ===')
"""
    result = odoo_ops.execute_python_code(
        python_code=env_info_code,
        capture_output=True,
        shell_interface="python",
    )

    if result["success"]:
        print("   ✓ Output:")
        for line in result["stdout"].strip().split("\n"):
            if line.strip() and any(
                keyword in line
                for keyword in [
                    "Database:",
                    "User:",
                    "Company:",
                    "Total partners:",
                    "===",
                ]
            ):
                print(f"     {line}")
    else:
        print(f"   ✗ Error: {result.get('error', 'Unknown error')}")
    print()


def run_query_data_example(odoo_ops):
    """Run query specific data example"""
    print("3. Query specific data:")
    query_code = """
partners = env['res.partner'].search([('is_company', '=', True)], limit=3)
print('=== Company Partners ===')
for partner in partners:
    print('-', partner.name, '(ID:', str(partner.id) + ')')
companies_total = len(env['res.partner'].search([('is_company', '=', True)]))
print('Total companies:', companies_total)
"""

    result = odoo_ops.execute_python_code(
        python_code=query_code,
        capture_output=True,
        shell_interface="python",
    )

    if result["success"]:
        print("   ✓ Output:")
        for line in result["stdout"].strip().split("\n"):
            if line.strip() and (
                "Company Partners" in line
                or line.strip().startswith("-")
                or "Total companies:" in line
            ):
                print(f"     {line}")
    else:
        print(f"   ✗ Error: {result.get('error', 'Unknown error')}")
    print()


def run_error_handling_example(odoo_ops):
    """Run error handling example"""
    print("4. Error handling example:")
    error_code = "print(nonexistent_variable)"

    result = odoo_ops.execute_python_code(
        python_code=error_code,
        capture_output=True,
        shell_interface="python",
    )

    if result["success"]:
        print("   ✓ Unexpected success")
    else:
        print("   ✓ Expected error caught")
        if "NameError" in result.get("stdout", "") or "NameError" in result.get(
            "stderr", ""
        ):
            print("     Error type: NameError (variable not defined)")
    print()


def run_statistics_example(odoo_ops):
    """Run complex calculation example"""
    print("5. Complex calculation example:")
    calc_code = """
# Calculate some statistics
user_count = len(env['res.users'].search([]))
partner_count = len(env['res.partner'].search([]))
company_count = len(env['res.partner'].search([('is_company', '=', True)]))

print('=== Odoo Statistics ===')
print(f'Users: {user_count}')
print(f'Partners: {partner_count}')
print(f'Companies: {company_count}')
print(f'Individual contacts: {partner_count - company_count}')
print('=== End Statistics ===')
"""

    result = odoo_ops.execute_python_code(
        python_code=calc_code,
        capture_output=True,
        shell_interface="python",
    )

    if result["success"]:
        print("   ✓ Statistics:")
        for line in result["stdout"].strip().split("\n"):
            if line.strip() and any(
                keyword in line
                for keyword in [
                    "Users:",
                    "Partners:",
                    "Companies:",
                    "Individual contacts:",
                    "===",
                ]
            ):
                print(f"     {line}")
    else:
        print(f"   ✗ Error: {result.get('error', 'Unknown error')}")
    print()


def print_summary():
    """Print summary of the examples"""
    print("=== Summary ===")
    print("✓ execute_python_code() simplifies Odoo shell interaction")
    print("✓ A shell_interface must be passed or configured")
    print("✓ No need to manually build commands or handle pipes")
    print("✓ Built-in error handling and result parsing")
    print("✓ Supports both simple and complex Python code execution")
    print("✓ Consistent return format with other oduit operations")


if __name__ == "__main__":
    exit(main())

Inspection and Database Workflows

oduit --env dev inspect ref base.action_partner_form
oduit --env dev inspect model res.partner
oduit --env dev inspect field res.partner email --with-db
oduit --env dev db table res_partner
oduit --env dev performance slow-queries --limit 10
oduit --env dev manifest check sale

Demo Mode

Demo-mode operations
#!/usr/bin/env python3
"""
Example script demonstrating the demo mode functionality in oduit.

This example shows:
1. Loading demo configuration without requiring a real Odoo installation
2. Testing different module behaviors (success, error, warning)
3. Using all operations in demo mode for development and testing
"""

from oduit.config_loader import ConfigLoader
from oduit.odoo_operations import ModuleInstallError, OdooOperations


def test_module_operations():
    """Test module install/update operations in demo mode"""
    print("=== Testing Module Operations in Demo Mode ===")

    config_loader = ConfigLoader()
    env_config = config_loader.load_demo_config()
    ops = OdooOperations(env_config, verbose=True)

    # Test successful module update
    print("\n1. Testing successful module update:")
    result = ops.update_module("module_ok", suppress_output=True)
    print(f"   Success: {result['success']}")
    print(f"   Duration: {result['duration']:.2f}s")
    print(f"   Output: {result['stdout'][:100]}...")

    # Test module with warnings
    print("\n2. Testing module with warnings:")
    result = ops.update_module("module_warning", suppress_output=True)
    print(f"   Success: {result['success']}")
    print(f"   Output: {result['stdout'][:100]}...")

    # Test failing module
    print("\n3. Testing failing module:")
    result = ops.update_module("module_error", suppress_output=True)
    print(f"   Success: {result['success']}")
    print(f"   Error: {result.get('error', 'No error message')}")

    # Test unknown module
    print("\n4. Testing unknown module:")
    result = ops.update_module("unknown_module", suppress_output=True)
    print(f"   Success: {result['success']}")
    print(f"   Output: {result['stdout']}")

    # Test with exception handling
    print("\n5. Testing with exception handling:")
    try:
        ops.install_module("module_error", suppress_output=True, raise_on_error=True)
        print("   This shouldn't print")
    except ModuleInstallError as e:
        print(f"   ✓ Caught expected error: {e}")


def test_other_operations():
    """Test other operations in demo mode"""
    print("\n=== Testing Other Operations in Demo Mode ===")

    config_loader = ConfigLoader()
    env_config = config_loader.load_demo_config()
    ops = OdooOperations(env_config, verbose=True)

    # Test database creation
    print("\n1. Testing database creation:")
    result = ops.create_db(suppress_output=True)
    print(f"   Success: {result['success']}")
    print(f"   Database: {result['database']}")

    # Test addon creation
    print("\n2. Testing addon creation:")
    ops.create_addon("test_addon", template="default")

    # Test language export
    print("\n3. Testing language export:")
    ops.export_module_language(module="sale", filename="test.po", language="en_US")

    # Test module tests
    print("\n4. Testing module tests:")
    result = ops.run_tests(module="sale", compact=True)
    print(f"   Test success: {result['success']}")


def test_demo_vs_real_mode():
    """Demonstrate difference between demo and real mode"""
    print("\n=== Demo Mode vs Real Mode Comparison ===")

    config_loader = ConfigLoader()
    demo_config = config_loader.load_demo_config()
    print(f"Demo mode enabled: {demo_config.get('demo_mode', False)}")
    print(f"Available demo modules: {demo_config['available_modules']}")

    # Regular config would look like this (commented out since we don't have
    # real config)
    # try:
    #     real_config = load_config("development")  # Would load real config
    #     print(f"Real mode: {real_config.get('demo_mode', False)}")
    # except:
    #     print("No real config available - that's fine for this demo")

    ops = OdooOperations(demo_config, verbose=True)

    # Show that the same operation behaves differently
    print("\n1. Same operation, different behaviors:")

    # In demo mode - will succeed
    result1 = ops.update_module("sale", suppress_output=True)
    print(f"   Demo mode result: {result1['success']} (simulated)")

    # In real mode - would try to execute actual odoo-bin command
    # result2 = ops.update_module(
    #     real_config, "sale", verbose=True, suppress_output=True
    # )
    # print(f"   Real mode result: {result2['success']} (would run real command)")


def main():
    """Main demo function"""
    print("🚀 ODUIT Demo Mode Example")
    print("=" * 50)

    try:
        test_module_operations()
        test_other_operations()
        test_demo_vs_real_mode()

        print("\n✅ All demo tests completed successfully!")
        print("\nDemo mode allows you to:")
        print("• Test oduit functionality without Odoo installation")
        print("• Simulate different module behaviors (success/error/warning)")
        print("• Develop and test scripts safely")
        print("• Run CI/CD tests without database dependencies")

        return 0

    except Exception as e:
        print(f"\n❌ Demo failed with error: {e}")
        import traceback

        traceback.print_exc()
        return 1


if __name__ == "__main__":
    exit(main())

Configuration Examples

Preferred local config:

[binaries]
python_bin = "./venv/bin/python"
odoo_bin = "./odoo/odoo-bin"

[odoo_params]
addons_path = "./addons"
db_name = "project_dev"

Compatibility support still exists for YAML and flat config files, but new examples should prefer sectioned TOML.