OdooCodeExecutor
The OdooCodeExecutor class provides a way to execute trusted Python code within an Odoo environment and capture results directly as Python objects, without printing to console.
For common read-only data access, prefer OdooQuery. OdooCodeExecutor is
the lower-level escape hatch for trusted arbitrary code and still requires
allow_unsafe=True.
Odoo code execution for programmatic use.
This module provides a class that allows executing Python code within an Odoo environment and capturing the results directly, without printing to console. It’s designed for programmatic use where you want to execute Odoo operations and get the results back as Python objects.
- class oduit.odoo_code_executor.OdooCodeExecutor(config_provider: ConfigProvider)[source]
Bases:
objectExecute Python code within an Odoo environment and capture results.
This class provides a way to execute arbitrary Python code within an Odoo environment and capture the results directly as Python objects, without printing to console. It’s perfect for programmatic use cases where you want to query data, perform operations, and get results back.
Features: - Execute code within proper Odoo environment with ‘env’ variable - Capture return values and exceptions - Support for both single expressions and multi-line code blocks - Automatic database connection and cleanup - Thread-safe execution - Proper transaction handling (read-only by default)
Example
executor = OdooCodeExecutor(config_provider) result = executor.execute_code(“env[‘res.partner’].search([],limit=1).name”) print(f”Partner name: {result[‘value’]}”)
- __init__(config_provider: ConfigProvider)[source]
Initialize the code executor.
- Parameters:
config_provider – ConfigProvider instance with Odoo configuration
- execute_code(code: str, database: str | None = None, commit: bool = False, timeout: float = 30.0, allow_unsafe: bool = False) dict[str, Any][source]
Execute trusted Python code within an Odoo environment.
- Parameters:
code – Python code to execute (can be expression or statements)
database – Database name to connect to (uses config default if None)
commit – Whether to commit changes (default: False for safety)
timeout – Maximum execution time in seconds
allow_unsafe – Must be True to allow arbitrary code execution
- Returns:
success (bool): True if execution succeeded
value (Any): Return value if code was an expression
output (str): Any stdout output from the code
error (str): Error message if execution failed
traceback (str): Full traceback if an exception occurred
- Return type:
Dictionary with execution results
- execute_multiple(code_blocks: list[str], database: str | None = None, commit: bool = False, stop_on_error: bool = True, timeout: float = 30.0, allow_unsafe: bool = False) dict[str, Any][source]
Execute multiple trusted code blocks within the same transaction.
- Parameters:
code_blocks – List of Python code strings to execute
database – Database name to connect to
commit – Whether to commit changes after all blocks succeed
stop_on_error – Whether to stop execution if any block fails
timeout – Maximum execution time per block in seconds
allow_unsafe – Must be True to allow arbitrary code execution
- Returns:
success (bool): True if all blocks executed successfully
results (list): List of individual execution results
failed_at (int): Index of failed block (if stop_on_error=True)
error (str): Overall error message
- Return type:
Dictionary with execution results
Features
Direct Result Capture: Execute code within proper Odoo environment with ‘env’ variable
Return Value Handling: Capture return values and exceptions as Python objects
Smart Compilation: Support for both single expressions and multi-line code blocks
Automatic Database Connection: Handles database connection and cleanup automatically
Thread-Safe Execution: Proper threading context setup for Odoo
Transaction Management: Read-only by default with optional commit functionality
Explicit Trust Gate:
allow_unsafe=Trueis required to execute arbitrary code
Basic Usage
Simple Expression Execution
Execute a single expression and get the result:
from oduit.config_provider import ConfigProvider
from oduit.odoo_code_executor import OdooCodeExecutor
config_provider = ConfigProvider(config_dict)
executor = OdooCodeExecutor(config_provider)
# Get partner name
result = executor.execute_code(
"env['res.partner'].search([],limit=1).name",
allow_unsafe=True,
)
if result["success"]:
partner_name = result["value"] # Returns actual string
print(f"Partner: {partner_name}")
Multi-line Code with Return Value
Execute complex code blocks that end with an expression:
code = """
partner_count = len(env['res.partner'].search([]))
customer_count = len(env['res.partner'].search([('is_company', '=', False)]))
company_count = len(env['res.partner'].search([('is_company', '=', True)]))
{
'total_partners': partner_count,
'customers': customer_count,
'companies': company_count,
'ratio': f"{customer_count}/{company_count}" if company_count > 0 else "N/A"
}
"""
result = executor.execute_code(code, allow_unsafe=True)
if result["success"]:
stats = result["value"] # Returns the dictionary
print(f"Statistics: {stats}")
Data Modification with Transaction Control
Create or modify data with explicit commit control:
create_code = """
partner = env['res.partner'].create({
'name': 'Test Partner',
'email': 'test@example.com',
'is_company': False
})
{'id': partner.id, 'name': partner.name, 'email': partner.email}
"""
# Execute with commit=True to persist changes
result = executor.execute_code(create_code, commit=True, allow_unsafe=True)
if result["success"]:
partner_data = result["value"]
print(f"Created partner: {partner_data}")
Multiple Code Blocks
Execute multiple related code blocks in sequence within the same transaction:
code_blocks = [
"company_partners = env['res.partner'].search([('is_company', '=', True)], limit=3)",
"partner_names = [p.name for p in company_partners]",
"{'count': len(partner_names), 'names': partner_names}"
]
result = executor.execute_multiple(code_blocks, allow_unsafe=True)
if result["success"]:
# Get result from the last block
final_result = result["results"][-1]["value"]
print(f"Companies: {final_result}")
Error Handling
The OdooCodeExecutor provides comprehensive error handling with detailed information:
result = executor.execute_code("nonexistent_variable + 42", allow_unsafe=True)
if not result["success"]:
print(f"Error: {result['error']}")
print(f"Traceback: {result['traceback']}")
Return Value Structure
All execution methods return a dictionary with the following structure:
Single Code Execution (execute_code)
{
"success": bool, # True if execution succeeded
"value": Any, # Return value if code was an expression
"output": str, # Any stdout output from the code
"error": str, # Error message if execution failed
"traceback": str # Full traceback if an exception occurred
}
Multiple Code Execution (execute_multiple)
{
"success": bool, # True if all blocks executed successfully
"results": list, # List of individual execution results
"failed_at": int, # Index of failed block (if stop_on_error=True)
"total_blocks": int, # Total number of code blocks
"executed_blocks": int, # Number of blocks that were executed
"error": str # Overall error message (if applicable)
}
Execution Context
When code is executed, the following variables are available in the execution context:
Standard Odoo Variables
env: Odoo Environment object with all modelsodoo: The odoo moduleregistry: Database registrycr: Database cursoruid: User ID (SUPERUSER_ID)context: User context dictionary
Common Python Modules
datetime: Python datetime modulejson: JSON handling moduleos: Operating system interfacesys: System-specific parameters
Configuration Requirements
The OdooCodeExecutor requires a ConfigProvider with the following configuration:
Required Configuration
config = {
"db_name": "your_database",
"db_user": "odoo_user",
"db_password": "password",
"addons_path": "/path/to/addons",
}
Optional Configuration
config = {
"db_host": "localhost", # Default: localhost
"db_port": 5432, # Default: 5432
"data_dir": "~/data", # Default: ~/.local/share/Odoo
}
Thread Safety and Performance
The OdooCodeExecutor is designed to be thread-safe and handles:
Proper threading context setup with
setattr(threading.current_thread(), "dbname", db_name)Database connection pooling through Odoo’s registry system
Automatic transaction management (rollback by default)
Output stream redirection and restoration
Security Considerations
When using OdooCodeExecutor:
Code is executed with SUPERUSER_ID privileges
All database operations are performed within transactions
By default, all changes are rolled back unless
commit=Trueis specifiedallow_unsafe=Trueis required to execute codeInput code must be trusted as it has full access to the Odoo environment
Consider implementing additional access controls in production environments
Timeout enforcement uses
signal.setitimerand requires Unix + main thread
Best Practices
Always check the success flag before using result values
Prefer ``OdooQuery`` for common read-only access
Use explicit commit control; only commit when necessary
Handle errors gracefully with proper error checking
Keep code blocks focused and break complex operations into smaller pieces
Test expressions first before using them in production code
Use timeouts for long-running operations to prevent hanging
Pass ``allow_unsafe=True`` only for trusted, reviewed code snippets