Complete API Reference
This page contains the complete API reference for all oduit modules.
Oduit - Odoo development utilities.
- class oduit.ConfigLoader(config_dir: str | None = None)[source]
Bases:
objectHandles loading and managing Odoo environment configurations.
- __init__(config_dir: str | None = None)[source]
Initialize ConfigLoader with optional custom config directory.
- get_config_path(env_name: str, format_type: str = 'yaml') str[source]
Returns full path to environment-specific config.
- resolve_config_path(env_name: str) tuple[str, str][source]
Resolve the config path and format for an environment name.
- load_local_config_details() LoadedConfigDetails[source]
Load local config plus canonical normalized metadata.
- inspect_odoo_conf_import(conf_path: str, sectioned: bool = False) ImportedOdooConfDetails[source]
Import Odoo configuration and return migration-friendly metadata.
- import_odoo_conf(conf_path: str, sectioned: bool = False) dict[str, Any][source]
Import Odoo configuration from .conf file and convert to oduit format.
- Parameters:
conf_path – Path to the Odoo .conf file
sectioned – If True, return configuration in sectioned format
- Returns:
Dictionary with oduit-compatible configuration
- Raises:
FileNotFoundError – If conf file doesn’t exist
ValueError – If conf file format is invalid
- load_config(env_name: str) dict[str, Any][source]
Load config from ~/.config/oduit/<env>.(yaml|toml|conf) or from a direct file path
- load_config_details(env_name: str) LoadedConfigDetails[source]
Load config plus canonical normalized metadata.
- class oduit.Manifest(module_path: str)[source]
Bases:
objectRepresents an Odoo module manifest (__manifest__.py).
This is a wrapper around manifestoo-core’s Manifest class that provides backward compatibility with the original oduit API.
- __init__(module_path: str)[source]
Initialize Manifest from a module directory path.
- Parameters:
module_path – Absolute path to the module directory
- Raises:
ManifestNotFoundError – If __manifest__.py is not found
InvalidManifestError – If manifest contains invalid syntax or structure
- classmethod from_dict(data: dict[str, Any], module_name: str = 'test_module') Manifest[source]
Create a Manifest instance from a dictionary (primarily for testing).
- Parameters:
data – Dictionary containing manifest data
module_name – Name of the module (for testing purposes)
- Returns:
Manifest instance
- property name: str
Get the module name from manifest or use directory name as fallback.
- property version: str
Get the module version.
- property codependencies: list[str]
Get codependencies from manifest ‘depends’ field.
Codependencies are modules that this module depends on, meaning changes to those modules may impact this module.
- Returns:
List of codependency module names, empty list if no codependencies
- property installable: bool
Check if the module is installable.
- property auto_install: bool
Check if the module is auto-installable.
- property summary: str
Get the module summary/description.
- property description: str
Get the module description.
- property author: str
Get the module author.
- property website: str
Get the module website.
- property license: str
Get the module license.
- property external_dependencies: dict[str, list[str]]
Get external dependencies (python packages, system binaries).
- has_dependency(dependency_name: str) bool[source]
Check if the module has a specific codependency.
- Parameters:
dependency_name – Name of the codependency to check for
- Returns:
True if the codependency exists, False otherwise
- exception oduit.InvalidManifestError[source]
Bases:
ManifestErrorRaised when manifest contains invalid syntax or structure.
- exception oduit.ManifestNotFoundError[source]
Bases:
ManifestErrorRaised when manifest file is not found.
- class oduit.ManifestCollection[source]
Bases:
objectRepresents a collection of Odoo module manifests.
- add(addon_name: str, manifest: Manifest) None[source]
Add a manifest to the collection.
- Parameters:
addon_name – Name of the addon
manifest – Manifest instance to add
- remove(addon_name: str) None[source]
Remove a manifest from the collection.
- Parameters:
addon_name – Name of the addon to remove
- Raises:
KeyError – If addon_name is not in the collection
- get(addon_name: str) Manifest | None[source]
Get a manifest by addon name.
- Parameters:
addon_name – Name of the addon
- Returns:
Manifest instance or None if not found
- __getitem__(addon_name: str) Manifest[source]
Get a manifest by addon name using dict-like access.
- Parameters:
addon_name – Name of the addon
- Returns:
Manifest instance
- Raises:
KeyError – If addon_name is not in the collection
- __contains__(addon_name: str) bool[source]
Check if an addon is in the collection.
- Parameters:
addon_name – Name of the addon to check
- Returns:
True if addon exists in collection, False otherwise
- __iter__() Iterator[str][source]
Iterate over addon names in the collection.
- Returns:
Iterator over addon names
- items() Iterator[tuple[str, Manifest]][source]
Get iterator over (addon_name, manifest) pairs.
- Returns:
Iterator of tuples containing addon name and Manifest
- values() Iterator[Manifest][source]
Get iterator over manifests.
- Returns:
Iterator over Manifest instances
- get_all_dependencies() set[str][source]
Get all unique codependencies across all manifests in the collection.
- Returns:
Set of all codependency names
- get_installable_addons() list[str][source]
Get list of all installable addon names.
- Returns:
List of addon names that are installable
- get_auto_install_addons() list[str][source]
Get list of all auto-install addon names.
- Returns:
List of addon names that are auto-installable
- filter_by_dependency(dependency_name: str) ManifestCollection[source]
Create a new collection with only addons that depend on a specific module.
- Parameters:
dependency_name – Name of the dependency to filter by
- Returns:
New ManifestCollection containing only matching addons
- class oduit.AddonsPathManager(addons_path: str)[source]
Bases:
objectManages discovery and loading of Odoo modules from addons paths.
- __init__(addons_path: str)[source]
Initialize AddonsPathManager with comma-separated addons paths.
- Parameters:
addons_path – Comma-separated string of addon directory paths
- get_all_paths() list[str][source]
Get all addon paths (configured + base Odoo paths).
- Returns:
List of all addon paths
- get_configured_paths() list[str][source]
Get only configured addon paths (excluding base Odoo paths).
- Returns:
List of configured addon paths
- find_duplicate_module_names() dict[str, list[str]][source]
Return module names that appear in more than one addons path.
- get_collection_from_path(path: str, skip_invalid: bool = False) ManifestCollection[source]
Get ManifestCollection from a specific addon path.
- Parameters:
path – Path to addon directory
skip_invalid – If True, skip modules with invalid manifests
- Returns:
ManifestCollection containing modules from the specified path
- Raises:
ManifestError – If manifest is invalid and skip_invalid is False
- get_collection_from_paths(paths: list[str], skip_invalid: bool = False) ManifestCollection[source]
Get ManifestCollection from multiple specific addon paths.
- Parameters:
paths – List of addon directory paths
skip_invalid – If True, skip modules with invalid manifests
- Returns:
ManifestCollection containing modules from all specified paths (duplicates are excluded)
- Raises:
ManifestError – If manifest is invalid and skip_invalid is False
- get_all_collections(skip_invalid: bool = False) ManifestCollection[source]
Get ManifestCollection from all configured and base addon paths.
- Parameters:
skip_invalid – If True, skip modules with invalid manifests
- Returns:
ManifestCollection containing all modules from all paths (duplicates are excluded)
- Raises:
ManifestError – If manifest is invalid and skip_invalid is False
- get_collection_by_filter(filter_dir: str, skip_invalid: bool = False) ManifestCollection[source]
Get ManifestCollection filtered by directory basename.
- Parameters:
filter_dir – Directory basename to filter by
skip_invalid – If True, skip modules with invalid manifests
- Returns:
ManifestCollection containing modules from paths matching filter
- Raises:
ManifestError – If manifest is invalid and skip_invalid is False
- find_module_path(module_name: str) str | None[source]
Find the absolute path to a module.
- Parameters:
module_name – Name of the module to find
- Returns:
Absolute path to module directory or None if not found
- class oduit.EnvironmentSource(name: str | None = None, source: str | None = None, config_path: str | None = None)[source]
Bases:
DictModelSource metadata for an inspected environment.
- class oduit.BinaryProbe(value: str | None = None, resolved_path: str | None = None, exists: bool = False, executable: bool = False, configured: bool = False, auto_detected: bool = False)[source]
Bases:
DictModelResolved binary metadata for an environment.
- exists: bool = False
- executable: bool = False
- configured: bool = False
- auto_detected: bool = False
- class oduit.AddonsPathStatus(configured: list[str] = <factory>, base: list[str] = <factory>, all: list[str] = <factory>, valid: list[str] = <factory>, invalid: list[str] = <factory>)[source]
Bases:
DictModelResolved addon path information.
- class oduit.OdooVersionInfo(version: str | None = None, series: str | None = None)[source]
Bases:
DictModelResolved Odoo version and series information.
- class oduit.DatabaseSummary(db_name: str | None = None, db_host: str | None = None, db_user: str | None = None)[source]
Bases:
DictModelSafe database configuration summary.
- class oduit.EnvironmentContext(environment: ~oduit.api_models.EnvironmentSource, resolved_binaries: dict[str, ~oduit.api_models.BinaryProbe], addons_paths: ~oduit.api_models.AddonsPathStatus, odoo: ~oduit.api_models.OdooVersionInfo, database: ~oduit.api_models.DatabaseSummary, duplicate_modules: dict[str, list[str]] = <factory>, available_module_count: int = 0, invalid_addon_paths: list[str] = <factory>, missing_critical_config: list[str] = <factory>, doctor_summary: dict[str, int] = <factory>, doctor_checks: list[dict[str, ~typing.Any]] = <factory>, warnings: list[str] = <factory>, remediation: list[str] = <factory>)[source]
Bases:
DictModelTyped environment snapshot for planning and inspection.
- environment: EnvironmentSource
- resolved_binaries: dict[str, BinaryProbe]
- addons_paths: AddonsPathStatus
- odoo: OdooVersionInfo
- database: DatabaseSummary
- available_module_count: int = 0
- __init__(environment: ~oduit.api_models.EnvironmentSource, resolved_binaries: dict[str, ~oduit.api_models.BinaryProbe], addons_paths: ~oduit.api_models.AddonsPathStatus, odoo: ~oduit.api_models.OdooVersionInfo, database: ~oduit.api_models.DatabaseSummary, duplicate_modules: dict[str, list[str]] = <factory>, available_module_count: int = 0, invalid_addon_paths: list[str] = <factory>, missing_critical_config: list[str] = <factory>, doctor_summary: dict[str, int] = <factory>, doctor_checks: list[dict[str, ~typing.Any]] = <factory>, warnings: list[str] = <factory>, remediation: list[str] = <factory>) None
- class oduit.DocumentationTrackedFile(path: str, category: str, size_bytes: int | None = None, sha256: str | None = None)[source]
Bases:
DictModelTracked addon source file entry used for documentation freshness checks.
- path: str
- category: str
- class oduit.DocumentationSourceSnapshot(algorithm: str = 'sha256', fingerprint: str = '', file_count: int = 0, files: list[DocumentationTrackedFile] = <factory>)[source]
Bases:
DictModelDeterministic fingerprint of addon source inputs.
- algorithm: str = 'sha256'
- fingerprint: str = ''
- file_count: int = 0
- files: list[DocumentationTrackedFile]
- class oduit.DocumentationDocumentSnapshot(algorithm: str = 'sha256', fingerprint: str = '', size_bytes: int | None = None, line_count: int | None = None)[source]
Bases:
DictModelFingerprint of the generated Markdown document.
- algorithm: str = 'sha256'
- fingerprint: str = ''
- class oduit.DocumentationDiagram(kind: str, title: str, format: str, content: str)[source]
Bases:
DictModelRendered documentation diagram artifact.
- kind: str
- title: str
- format: str
- content: str
- class oduit.DocumentSection(title: str, markdown: str, summary: str = '', order: int = 0)[source]
Bases:
DictModelRendered documentation section.
- title: str
- markdown: str
- summary: str = ''
- order: int = 0
- class oduit.AddonDocTarget(module: str, addon_root: str, target_kind: str, manifest_path: str, inside_configured_addons_path: bool = False, warnings: list[str] = <factory>, candidate_addon_roots: list[str] = <factory>, ambiguous: bool = False)[source]
Bases:
DictModelResolved addon target for technical documentation workflows.
- module: str
- addon_root: str
- target_kind: str
- manifest_path: str
- inside_configured_addons_path: bool = False
- ambiguous: bool = False
- class oduit.AddonContributionSummary(model: str, module: str, relation_kinds: list[str] = <factory>, class_names: list[str] = <factory>, added_fields: list[str] = <factory>, added_methods: list[str] = <factory>, source_paths: list[str] = <factory>, line_hints: list[int] = <factory>, shared_model_doc_path: str | None = None, shared_model_doc_anchor: str | None = None)[source]
Bases:
DictModelCompact per-addon summary for one shared model.
- model: str
- module: str
- __init__(model: str, module: str, relation_kinds: list[str] = <factory>, class_names: list[str] = <factory>, added_fields: list[str] = <factory>, added_methods: list[str] = <factory>, source_paths: list[str] = <factory>, line_hints: list[int] = <factory>, shared_model_doc_path: str | None = None, shared_model_doc_anchor: str | None = None) None
- class oduit.AddonTechnicalFile(path: str, category: str, size_bytes: int | None = None)[source]
Bases:
DictModelCompact file entry used for technical documentation inventories.
- path: str
- category: str
- class oduit.AddonXmlRecord(path: str, record_id: str | None = None, model: str | None = None, name: str | None = None, xml_tag: str = 'record', attributes: dict[str, str]=<factory>)[source]
Bases:
DictModelCompact XML inventory record for addon-local architecture evidence.
- path: str
- xml_tag: str = 'record'
- class oduit.AddonHttpRoute(path: str, class_name: str, method_name: str, route: str | list[str], auth: str | None = None, route_type: str | None = None, methods: list[str] = <factory>, csrf: bool | None = None, website: bool | None = None, line_hint: int | None = None)[source]
Bases:
DictModelStatic controller route discovered from addon Python sources.
- path: str
- class_name: str
- method_name: str
- __init__(path: str, class_name: str, method_name: str, route: str | list[str], auth: str | None = None, route_type: str | None = None, methods: list[str] = <factory>, csrf: bool | None = None, website: bool | None = None, line_hint: int | None = None) None
- class oduit.AddonTechnicalInventory(module: str, addon_root: str, files: list[AddonTechnicalFile] = <factory>, xml_records: list[AddonXmlRecord] = <factory>, http_routes: list[AddonHttpRoute] = <factory>, security_files: list[str] = <factory>, migration_files: list[str] = <factory>, todo_markers: list[SourceEvidence] = <factory>, warnings: list[str] = <factory>, remediation: list[str] = <factory>)[source]
Bases:
DictModelRead-only technical inventory for addon-local architecture docs.
- module: str
- addon_root: str
- files: list[AddonTechnicalFile]
- xml_records: list[AddonXmlRecord]
- http_routes: list[AddonHttpRoute]
- todo_markers: list[SourceEvidence]
- __init__(module: str, addon_root: str, files: list[AddonTechnicalFile] = <factory>, xml_records: list[AddonXmlRecord] = <factory>, http_routes: list[AddonHttpRoute] = <factory>, security_files: list[str] = <factory>, migration_files: list[str] = <factory>, todo_markers: list[SourceEvidence] = <factory>, warnings: list[str] = <factory>, remediation: list[str] = <factory>) None
- class oduit.AddonInfo(module: str, module_path: str | None, addon_type: str, version_display: str, name: str = '', summary: str = '', description: str = '', category: str = '', author: str = '', website: str = '', license: str = '', external_dependencies: dict[str, list[str]]=<factory>, depends: list[str] = <factory>, reverse_dependencies: list[str] = <factory>, reverse_dependency_count: int = 0, missing_dependencies: list[str] = <factory>, installable: bool = False, auto_install: bool = False, models: list[str] = <factory>, inherit_models: list[str] = <factory>, model_count: int = 0, test_cases: list[AddonTestFile] = <factory>, test_count: int = 0, languages: list[str] = <factory>, installed_state: AddonInstallState | None = None, warnings: list[str] = <factory>, remediation: list[str] = <factory>)[source]
Bases:
DictModelCombined static and runtime addon summary for onboarding workflows.
- module: str
- addon_type: str
- version_display: str
- name: str = ''
- summary: str = ''
- description: str = ''
- category: str = ''
- author: str = ''
- website: str = ''
- license: str = ''
- reverse_dependency_count: int = 0
- installable: bool = False
- auto_install: bool = False
- model_count: int = 0
- test_cases: list[AddonTestFile]
- test_count: int = 0
- installed_state: AddonInstallState | None = None
- __init__(module: str, module_path: str | None, addon_type: str, version_display: str, name: str = '', summary: str = '', description: str = '', category: str = '', author: str = '', website: str = '', license: str = '', external_dependencies: dict[str, list[str]]=<factory>, depends: list[str] = <factory>, reverse_dependencies: list[str] = <factory>, reverse_dependency_count: int = 0, missing_dependencies: list[str] = <factory>, installable: bool = False, auto_install: bool = False, models: list[str] = <factory>, inherit_models: list[str] = <factory>, model_count: int = 0, test_cases: list[AddonTestFile] = <factory>, test_count: int = 0, languages: list[str] = <factory>, installed_state: AddonInstallState | None = None, warnings: list[str] = <factory>, remediation: list[str] = <factory>) None
- class oduit.AddonInstallState(success: bool, operation: str, module: str, record_found: bool = False, state: str = 'uninstalled', installed: bool = False, database: str | None = None, error: str | None = None, error_type: str | None = None)[source]
Bases:
DictModelTyped runtime install-state lookup result for one addon.
- success: bool
- operation: str
- module: str
- record_found: bool = False
- state: str = 'uninstalled'
- installed: bool = False
- class oduit.AddonInspection(module: str, exists: bool, module_path: str | None, addon_type: str, version_display: str, manifest: dict[str, ~typing.Any], manifest_fields: list[str], direct_dependencies: list[str] = <factory>, reverse_dependencies: list[str] = <factory>, reverse_dependency_count: int = 0, install_order_slice: list[str] = <factory>, install_order_available: bool = False, dependency_cycle: list[str] = <factory>, missing_dependencies: list[str] = <factory>, impacted_modules: list[str] = <factory>, series: str | None = None, python_dependencies: list[str] = <factory>, binary_dependencies: list[str] = <factory>, warnings: list[str] = <factory>, remediation: list[str] = <factory>)[source]
Bases:
DictModelTyped addon inspection payload.
- module: str
- exists: bool
- addon_type: str
- version_display: str
- reverse_dependency_count: int = 0
- install_order_available: bool = False
- __init__(module: str, exists: bool, module_path: str | None, addon_type: str, version_display: str, manifest: dict[str, ~typing.Any], manifest_fields: list[str], direct_dependencies: list[str] = <factory>, reverse_dependencies: list[str] = <factory>, reverse_dependency_count: int = 0, install_order_slice: list[str] = <factory>, install_order_available: bool = False, dependency_cycle: list[str] = <factory>, missing_dependencies: list[str] = <factory>, impacted_modules: list[str] = <factory>, series: str | None = None, python_dependencies: list[str] = <factory>, binary_dependencies: list[str] = <factory>, warnings: list[str] = <factory>, remediation: list[str] = <factory>) None
- class oduit.AddonDocumentationModel(model: str, relation_kinds: list[str] = <factory>, source_entries: list[AddonModelEntry] = <factory>, documentation: ModelDocumentation | None = None)[source]
Bases:
DictModelPer-model documentation detail inside one addon bundle.
- model: str
- source_entries: list[AddonModelEntry]
- documentation: ModelDocumentation | None = None
- __init__(model: str, relation_kinds: list[str] = <factory>, source_entries: list[AddonModelEntry] = <factory>, documentation: ModelDocumentation | None = None) None
- class oduit.AddonDocumentation(module: str, database: str | None = None, source_only: bool = False, addon_info: AddonInfo | None = None, dependency_graph: dict[str, ~typing.Any]=<factory>, model_inventory: AddonModelInventory | None = None, models: list[AddonDocumentationModel] = <factory>, shared_model_contributions: list[AddonContributionSummary] = <factory>, recommended_tests: dict[str, ~typing.Any]=<factory>, diagrams: list[DocumentationDiagram] = <factory>, sections: list[DocumentSection] = <factory>, output_path: str | None = None, markdown: str = '', warnings: list[str] = <factory>, remediation: list[str] = <factory>)[source]
Bases:
DictModelDocumentation bundle for one addon.
- module: str
- source_only: bool = False
- model_inventory: AddonModelInventory | None = None
- models: list[AddonDocumentationModel]
- shared_model_contributions: list[AddonContributionSummary]
- diagrams: list[DocumentationDiagram]
- sections: list[DocumentSection]
- markdown: str = ''
- __init__(module: str, database: str | None = None, source_only: bool = False, addon_info: AddonInfo | None = None, dependency_graph: dict[str, ~typing.Any]=<factory>, model_inventory: AddonModelInventory | None = None, models: list[AddonDocumentationModel] = <factory>, shared_model_contributions: list[AddonContributionSummary] = <factory>, recommended_tests: dict[str, ~typing.Any]=<factory>, diagrams: list[DocumentationDiagram] = <factory>, sections: list[DocumentSection] = <factory>, output_path: str | None = None, markdown: str = '', warnings: list[str] = <factory>, remediation: list[str] = <factory>) None
- class oduit.ModelDocumentation(model: str, database: str | None = None, source_only: bool = False, field_attributes: list[str] = <factory>, requested_view_types: list[str] = <factory>, extension_inventory: ModelExtensionInventory | None = None, field_metadata: ModelFieldsResult | None = None, view_inventory: ModelViewInventory | None = None, diagrams: list[DocumentationDiagram] = <factory>, sections: list[DocumentSection] = <factory>, markdown: str = '', warnings: list[str] = <factory>, remediation: list[str] = <factory>)[source]
Bases:
DictModelDocumentation bundle for one model.
- model: str
- source_only: bool = False
- extension_inventory: ModelExtensionInventory | None = None
- field_metadata: ModelFieldsResult | None = None
- view_inventory: ModelViewInventory | None = None
- diagrams: list[DocumentationDiagram]
- sections: list[DocumentSection]
- markdown: str = ''
- __init__(model: str, database: str | None = None, source_only: bool = False, field_attributes: list[str] = <factory>, requested_view_types: list[str] = <factory>, extension_inventory: ModelExtensionInventory | None = None, field_metadata: ModelFieldsResult | None = None, view_inventory: ModelViewInventory | None = None, diagrams: list[DocumentationDiagram] = <factory>, sections: list[DocumentSection] = <factory>, markdown: str = '', warnings: list[str] = <factory>, remediation: list[str] = <factory>) None
- class oduit.SharedModelDocumentation(model: str, owning_modules: list[str] = <factory>, contributing_modules: list[str] = <factory>, documentation: ModelDocumentation | None = None, output_path: str | None = None, markdown: str = '')[source]
Bases:
DictModelFull shared-model documentation within a multi-addon bundle.
- model: str
- documentation: ModelDocumentation | None = None
- markdown: str = ''
- class oduit.MultiAddonDocumentation(modules: list[str] = <factory>, database: str | None = None, source_only: bool = False, addon_docs: list[AddonDocumentation] = <factory>, shared_models: list[SharedModelDocumentation] = <factory>, index_markdown: str = '', warnings: list[str] = <factory>, remediation: list[str] = <factory>)[source]
Bases:
DictModelDocumentation bundle for multiple addons in one selected scope.
- source_only: bool = False
- addon_docs: list[AddonDocumentation]
- shared_models: list[SharedModelDocumentation]
- index_markdown: str = ''
- __init__(modules: list[str] = <factory>, database: str | None = None, source_only: bool = False, addon_docs: list[AddonDocumentation] = <factory>, shared_models: list[SharedModelDocumentation] = <factory>, index_markdown: str = '', warnings: list[str] = <factory>, remediation: list[str] = <factory>) None
- class oduit.DependencyGraphDocumentation(modules: list[str] = <factory>, database: str | None = None, source_only: bool = False, installed_only: bool = False, transitive: bool = True, dependency_graph: dict[str, ~typing.Any]=<factory>, installed_addons: InstalledAddonInventory | None = None, diagrams: list[DocumentationDiagram] = <factory>, sections: list[DocumentSection] = <factory>, markdown: str = '', warnings: list[str] = <factory>, remediation: list[str] = <factory>)[source]
Bases:
DictModelDocumentation bundle for addon dependency graphs.
- source_only: bool = False
- installed_only: bool = False
- transitive: bool = True
- installed_addons: InstalledAddonInventory | None = None
- diagrams: list[DocumentationDiagram]
- sections: list[DocumentSection]
- markdown: str = ''
- __init__(modules: list[str] = <factory>, database: str | None = None, source_only: bool = False, installed_only: bool = False, transitive: bool = True, dependency_graph: dict[str, ~typing.Any]=<factory>, installed_addons: InstalledAddonInventory | None = None, diagrams: list[DocumentationDiagram] = <factory>, sections: list[DocumentSection] = <factory>, markdown: str = '', warnings: list[str] = <factory>, remediation: list[str] = <factory>) None
- class oduit.TechnicalDocumentation(module: str, addon_root: str, source_addon_root: str | None = None, template: str = 'arc42', target: AddonDocTarget | None = None, output_path: str | None = None, metadata_path: str | None = None, generated_at: str | None = None, source_only: bool = False, addon_documentation: AddonDocumentation | None = None, technical_inventory: AddonTechnicalInventory | None = None, sections: list[DocumentSection] = <factory>, markdown: str = '', warnings: list[str] = <factory>, remediation: list[str] = <factory>, used_existing_evidence: bool = False)[source]
Bases:
DictModelTyped architecture-documentation bundle for one addon.
- module: str
- addon_root: str
- template: str = 'arc42'
- target: AddonDocTarget | None = None
- source_only: bool = False
- addon_documentation: AddonDocumentation | None = None
- technical_inventory: AddonTechnicalInventory | None = None
- sections: list[DocumentSection]
- markdown: str = ''
- used_existing_evidence: bool = False
- __init__(module: str, addon_root: str, source_addon_root: str | None = None, template: str = 'arc42', target: AddonDocTarget | None = None, output_path: str | None = None, metadata_path: str | None = None, generated_at: str | None = None, source_only: bool = False, addon_documentation: AddonDocumentation | None = None, technical_inventory: AddonTechnicalInventory | None = None, sections: list[DocumentSection] = <factory>, markdown: str = '', warnings: list[str] = <factory>, remediation: list[str] = <factory>, used_existing_evidence: bool = False) None
- class oduit.TechnicalDocumentationMetadata(schema_version: str = 'oduit.technical_documentation.v1', module: str = '', addon_root: str = '', doc_path: str = 'docs/architecture.md', metadata_path: str = 'docs/architecture.oduit.json', template: str = 'arc42-addon-v1', created_at: str | None = None, last_generated_at: str | None = None, generation_count: int = 0, generator: dict[str, str]=<factory>, generation_options: dict[str, ~typing.Any]=<factory>, evidence_counts: dict[str, int]=<factory>, source_snapshot: DocumentationSourceSnapshot | None = None, document_snapshot: DocumentationDocumentSnapshot | None = None, generated_blocks: list[TechnicalDocumentationGeneratedBlock] = <factory>, refresh_count: int = 0, last_refreshed_at: str | None = None, reviewed_at: str | None = None, reviewed_by: str | None = None, review_note: str | None = None, warnings: list[str] = <factory>)[source]
Bases:
DictModelDurable addon-local metadata persisted next to generated docs.
- schema_version: str = 'oduit.technical_documentation.v1'
- module: str = ''
- addon_root: str = ''
- doc_path: str = 'docs/architecture.md'
- metadata_path: str = 'docs/architecture.oduit.json'
- template: str = 'arc42-addon-v1'
- generation_count: int = 0
- source_snapshot: DocumentationSourceSnapshot | None = None
- document_snapshot: DocumentationDocumentSnapshot | None = None
- generated_blocks: list[TechnicalDocumentationGeneratedBlock]
- refresh_count: int = 0
- __init__(schema_version: str = 'oduit.technical_documentation.v1', module: str = '', addon_root: str = '', doc_path: str = 'docs/architecture.md', metadata_path: str = 'docs/architecture.oduit.json', template: str = 'arc42-addon-v1', created_at: str | None = None, last_generated_at: str | None = None, generation_count: int = 0, generator: dict[str, str]=<factory>, generation_options: dict[str, ~typing.Any]=<factory>, evidence_counts: dict[str, int]=<factory>, source_snapshot: DocumentationSourceSnapshot | None = None, document_snapshot: DocumentationDocumentSnapshot | None = None, generated_blocks: list[TechnicalDocumentationGeneratedBlock] = <factory>, refresh_count: int = 0, last_refreshed_at: str | None = None, reviewed_at: str | None = None, reviewed_by: str | None = None, review_note: str | None = None, warnings: list[str] = <factory>) None
- class oduit.TechnicalDocumentationStatus(module: str, addon_root: str, doc_path: str, metadata_path: str, status: str, has_document: bool = False, has_metadata: bool = False, generated_by_oduit: bool = False, up_to_date: bool = False, document_edited_since_last_generation: bool = False, source_changed_since_last_generation: bool = False, generated_block_count: int = 0, stale_generated_blocks: list[str] = <factory>, edited_generated_blocks: list[str] = <factory>, missing_generated_blocks: list[str] = <factory>, unknown_generated_blocks: list[str] = <factory>, generated_blocks_up_to_date: bool = False, created_at: str | None = None, last_generated_at: str | None = None, generation_count: int | None = None, generation_options: dict[str, ~typing.Any]=<factory>, evidence_counts: dict[str, int]=<factory>, template: str | None = None, changed_files: list[str] = <factory>, added_files: list[str] = <factory>, removed_files: list[str] = <factory>, warnings: list[str] = <factory>, remediation: list[str] = <factory>)[source]
Bases:
DictModelCurrent tracking status for one addon-local technical document.
- module: str
- addon_root: str
- doc_path: str
- metadata_path: str
- status: str
- has_document: bool = False
- has_metadata: bool = False
- generated_by_oduit: bool = False
- up_to_date: bool = False
- document_edited_since_last_generation: bool = False
- source_changed_since_last_generation: bool = False
- generated_block_count: int = 0
- generated_blocks_up_to_date: bool = False
- __init__(module: str, addon_root: str, doc_path: str, metadata_path: str, status: str, has_document: bool = False, has_metadata: bool = False, generated_by_oduit: bool = False, up_to_date: bool = False, document_edited_since_last_generation: bool = False, source_changed_since_last_generation: bool = False, generated_block_count: int = 0, stale_generated_blocks: list[str] = <factory>, edited_generated_blocks: list[str] = <factory>, missing_generated_blocks: list[str] = <factory>, unknown_generated_blocks: list[str] = <factory>, generated_blocks_up_to_date: bool = False, created_at: str | None = None, last_generated_at: str | None = None, generation_count: int | None = None, generation_options: dict[str, ~typing.Any]=<factory>, evidence_counts: dict[str, int]=<factory>, template: str | None = None, changed_files: list[str] = <factory>, added_files: list[str] = <factory>, removed_files: list[str] = <factory>, warnings: list[str] = <factory>, remediation: list[str] = <factory>) None
- class oduit.InstalledAddonRecord(module: str, state: str, installed: bool, shortdesc: str | None = None, application: bool | None = None, auto_install: bool | None = None)[source]
Bases:
DictModelOne runtime addon record read from
ir.module.module.- module: str
- state: str
- installed: bool
- class oduit.InstalledAddonInventory(success: bool, operation: str, addons: list[InstalledAddonRecord] = <factory>, total: int = 0, states: list[str] = <factory>, modules_filter: list[str] = <factory>, database: str | None = None, error: str | None = None, error_type: str | None = None, warnings: list[str] = <factory>, remediation: list[str] = <factory>)[source]
Bases:
DictModelTyped runtime addon inventory for one environment.
- success: bool
- operation: str
- addons: list[InstalledAddonRecord]
- total: int = 0
- __init__(success: bool, operation: str, addons: list[InstalledAddonRecord] = <factory>, total: int = 0, states: list[str] = <factory>, modules_filter: list[str] = <factory>, database: str | None = None, error: str | None = None, error_type: str | None = None, warnings: list[str] = <factory>, remediation: list[str] = <factory>) None
- class oduit.UpdatePlan(module: str, exists: bool, impact_set: list[str] = <factory>, impact_count: int = 0, missing_dependencies: list[str] = <factory>, duplicate_name_risk: bool = False, duplicate_module_locations: list[str] = <factory>, dependency_cycle: list[str] = <factory>, cycle_risk: bool = False, ordering_constraints: list[str] = <factory>, recommended_sequence: list[str] = <factory>, backup_advised: bool = False, write_protect_db: bool = False, agent_write_protect_db: bool = False, needs_mutation_flag: bool = False, agent_needs_mutation_flag: bool = False, human_runtime_db_mutation_policy: str = 'allow', human_runtime_db_mutation_allowed: bool = True, human_runtime_db_mutation_requires_flag: bool = False, agent_runtime_db_mutation_policy: str = 'allow', agent_runtime_db_mutation_allowed: bool = True, agent_runtime_db_mutation_requires_flag: bool = False, risk_score: int = 0, risk_level: str = 'low', risk_factors: list[str] = <factory>, verification_steps: list[str] = <factory>, inspection: AddonInspection | None = None, warnings: list[str] = <factory>, remediation: list[str] = <factory>)[source]
Bases:
DictModelTyped read-only update planning payload.
- module: str
- exists: bool
- impact_count: int = 0
- duplicate_name_risk: bool = False
- cycle_risk: bool = False
- backup_advised: bool = False
- write_protect_db: bool = False
- agent_write_protect_db: bool = False
- needs_mutation_flag: bool = False
- agent_needs_mutation_flag: bool = False
- human_runtime_db_mutation_policy: str = 'allow'
- human_runtime_db_mutation_allowed: bool = True
- human_runtime_db_mutation_requires_flag: bool = False
- agent_runtime_db_mutation_policy: str = 'allow'
- agent_runtime_db_mutation_allowed: bool = True
- agent_runtime_db_mutation_requires_flag: bool = False
- risk_score: int = 0
- risk_level: str = 'low'
- inspection: AddonInspection | None = None
- __init__(module: str, exists: bool, impact_set: list[str] = <factory>, impact_count: int = 0, missing_dependencies: list[str] = <factory>, duplicate_name_risk: bool = False, duplicate_module_locations: list[str] = <factory>, dependency_cycle: list[str] = <factory>, cycle_risk: bool = False, ordering_constraints: list[str] = <factory>, recommended_sequence: list[str] = <factory>, backup_advised: bool = False, write_protect_db: bool = False, agent_write_protect_db: bool = False, needs_mutation_flag: bool = False, agent_needs_mutation_flag: bool = False, human_runtime_db_mutation_policy: str = 'allow', human_runtime_db_mutation_allowed: bool = True, human_runtime_db_mutation_requires_flag: bool = False, agent_runtime_db_mutation_policy: str = 'allow', agent_runtime_db_mutation_allowed: bool = True, agent_runtime_db_mutation_requires_flag: bool = False, risk_score: int = 0, risk_level: str = 'low', risk_factors: list[str] = <factory>, verification_steps: list[str] = <factory>, inspection: AddonInspection | None = None, warnings: list[str] = <factory>, remediation: list[str] = <factory>) None
- class oduit.ModelSourceCandidate(path: str, class_name: str, match_kind: str, declared_model: str, confidence: float, match_strength: str = 'confirmed', evidence: list[SourceEvidence] = <factory>, line_hint: int | None = None, reason: str | None = None)[source]
Bases:
DictModelRanked model source candidate for static source localization.
- path: str
- class_name: str
- match_kind: str
- declared_model: str
- confidence: float
- match_strength: str = 'confirmed'
- evidence: list[SourceEvidence]
- class oduit.ModelSourceLocation(model: str, module: str, addon_root: str, resolution: str = 'not_found', ambiguous: bool = False, ambiguity_reason: str | None = None, candidates: list[ModelSourceCandidate] = <factory>, scanned_python_files: list[str] = <factory>, warnings: list[str] = <factory>, remediation: list[str] = <factory>)[source]
Bases:
DictModelStatic source localization result for one addon/model pair.
- model: str
- module: str
- addon_root: str
- resolution: str = 'not_found'
- ambiguous: bool = False
- candidates: list[ModelSourceCandidate]
- __init__(model: str, module: str, addon_root: str, resolution: str = 'not_found', ambiguous: bool = False, ambiguity_reason: str | None = None, candidates: list[ModelSourceCandidate] = <factory>, scanned_python_files: list[str] = <factory>, warnings: list[str] = <factory>, remediation: list[str] = <factory>) None
- class oduit.FieldSourceCandidate(path: str, class_name: str, field_name: str, match_kind: str, declared_model: str, confidence: float, match_strength: str = 'confirmed', evidence: list[SourceEvidence] = <factory>, line_hint: int | None = None, reason: str | None = None)[source]
Bases:
DictModelRanked field source candidate for static source localization.
- path: str
- class_name: str
- field_name: str
- match_kind: str
- declared_model: str
- confidence: float
- match_strength: str = 'confirmed'
- evidence: list[SourceEvidence]
- class oduit.FieldSourceLocation(model: str, field: str, module: str, addon_root: str, exists: bool, resolution: str = 'not_found', ambiguous: bool = False, ambiguity_reason: str | None = None, source_exists: bool = False, runtime_exists: bool | None = None, runtime_only: bool = False, runtime_source_modules: list[str] = <factory>, candidates: list[FieldSourceCandidate] = <factory>, insertion_candidate: ModelSourceCandidate | None = None, insertion_line_range: list[int] | None = None, insertion_reason: str | None = None, insertion_confidence: float | None = None, related_files: list[str] = <factory>, scanned_python_files: list[str] = <factory>, rationale: str | None = None, warnings: list[str] = <factory>, remediation: list[str] = <factory>)[source]
Bases:
DictModelStatic source localization result for one addon/model/field target.
- model: str
- field: str
- module: str
- addon_root: str
- exists: bool
- resolution: str = 'not_found'
- ambiguous: bool = False
- source_exists: bool = False
- runtime_only: bool = False
- candidates: list[FieldSourceCandidate]
- insertion_candidate: ModelSourceCandidate | None = None
- __init__(model: str, field: str, module: str, addon_root: str, exists: bool, resolution: str = 'not_found', ambiguous: bool = False, ambiguity_reason: str | None = None, source_exists: bool = False, runtime_exists: bool | None = None, runtime_only: bool = False, runtime_source_modules: list[str] = <factory>, candidates: list[FieldSourceCandidate] = <factory>, insertion_candidate: ModelSourceCandidate | None = None, insertion_line_range: list[int] | None = None, insertion_reason: str | None = None, insertion_confidence: float | None = None, related_files: list[str] = <factory>, scanned_python_files: list[str] = <factory>, rationale: str | None = None, warnings: list[str] = <factory>, remediation: list[str] = <factory>) None
- class oduit.AddonTestFile(path: str, test_type: str, references_model: bool = False, references_field: bool = False, confidence: float = 0.0, ranking_signals: list[str] = <factory>, related_paths: list[str] = <factory>)[source]
Bases:
DictModelRanked addon test file entry.
- path: str
- test_type: str
- references_model: bool = False
- references_field: bool = False
- confidence: float = 0.0
- class oduit.AddonTestInventory(module: str, addon_root: str, model: str | None = None, field: str | None = None, tests: list[AddonTestFile] = <factory>, warnings: list[str] = <factory>, remediation: list[str] = <factory>)[source]
Bases:
DictModelStatic addon test inventory for coding-agent test selection.
- module: str
- addon_root: str
- tests: list[AddonTestFile]
- class oduit.QueryModelResult(success: bool, operation: str, model: str, domain: list[Any] = <factory>, fields: list[str] | None = None, limit: int = 0, count: int = 0, total_count: int | None = None, limited: bool = False, ids: list[int] = <factory>, records: list[dict[str, ~typing.Any]]=<factory>, database: str | None = None, error: str | None = None, error_type: str | None = None)[source]
Bases:
DictModelTyped wrapper for
OdooQuery.query_model()results.- success: bool
- operation: str
- model: str
- limit: int = 0
- count: int = 0
- limited: bool = False
- classmethod from_dict(result: dict[str, Any]) QueryModelResult[source]
Create a typed result from a raw
OdooQuerydictionary.
- __init__(success: bool, operation: str, model: str, domain: list[Any] = <factory>, fields: list[str] | None = None, limit: int = 0, count: int = 0, total_count: int | None = None, limited: bool = False, ids: list[int] = <factory>, records: list[dict[str, ~typing.Any]]=<factory>, database: str | None = None, error: str | None = None, error_type: str | None = None) None
- class oduit.RecordReadResult(success: bool, operation: str, model: str, record_id: int, found: bool, record: dict[str, Any] | None, fields: list[str] | None = None, database: str | None = None, error: str | None = None, error_type: str | None = None)[source]
Bases:
DictModelTyped wrapper for
OdooQuery.read_record()results.- success: bool
- operation: str
- model: str
- record_id: int
- found: bool
- classmethod from_dict(result: dict[str, Any]) RecordReadResult[source]
Create a typed result from a raw
OdooQuerydictionary.
- class oduit.SearchCountResult(success: bool, operation: str, model: str, domain: list[Any] = <factory>, count: int = 0, database: str | None = None, error: str | None = None, error_type: str | None = None)[source]
Bases:
DictModelTyped wrapper for
OdooQuery.search_count()results.- success: bool
- operation: str
- model: str
- count: int = 0
- classmethod from_dict(result: dict[str, Any]) SearchCountResult[source]
Create a typed result from a raw
OdooQuerydictionary.
- class oduit.ModelFieldsResult(success: bool, operation: str, model: str, attributes: list[str] | None = None, module: str | None = None, field_names: list[str] = <factory>, field_definitions: dict[str, dict[str, ~typing.Any]]=<factory>, database: str | None = None, error: str | None = None, error_type: str | None = None)[source]
Bases:
DictModelTyped wrapper for
OdooQuery.get_model_fields()results.- success: bool
- operation: str
- model: str
- classmethod from_dict(result: dict[str, Any]) ModelFieldsResult[source]
Create a typed result from a raw
OdooQuerydictionary.
- __init__(success: bool, operation: str, model: str, attributes: list[str] | None = None, module: str | None = None, field_names: list[str] = <factory>, field_definitions: dict[str, dict[str, ~typing.Any]]=<factory>, database: str | None = None, error: str | None = None, error_type: str | None = None) None
- class oduit.ModuleManager(addons_path: str)[source]
Bases:
objectManages Odoo module operations and dependency resolution.
- __init__(addons_path: str)[source]
Initialize ModuleManager.
- Parameters:
addons_path – Comma-separated string of addon directory paths
- static parse_cycle_error(message: str) list[str][source]
Extract the module cycle path from an error message.
- find_module_dirs(filter_dir: str | None = None) list[str][source]
Return all module directories with __manifest__.py in configured paths
- Parameters:
filter_dir – Optional directory name to filter results. Only modules in directories with exact basename match will be returned.
- Returns:
Sorted list of module directory names
- find_modules(filter_dir: str | None = None, skip_invalid: bool = False) ManifestCollection[source]
Return all modules with manifests in configured paths as a collection
- Parameters:
filter_dir – Optional directory name to filter results. Only modules in directories with exact basename match will be returned.
skip_invalid – If True, skip modules with invalid manifests instead of raising an exception
- Returns:
ManifestCollection containing all found modules
- Raises:
ManifestError – If a manifest is invalid and skip_invalid is False
- find_module_path(module_name: str) str | None[source]
Find the absolute path to a module within addons_path and Odoo base addons
- Parameters:
module_name – Name of the module to find
- Returns:
Absolute path to module directory or None if not found
- get_manifest(module_name: str) Manifest | None[source]
Get the manifest for a module.
- Parameters:
module_name – Name of the module to get manifest for
- Returns:
Manifest instance or None if module not found
- parse_manifest(module_name: str) dict[str, Any] | None[source]
Parse and return module’s __manifest__.py content.
- Parameters:
module_name – Name of the module to parse manifest for
- Returns:
Dictionary containing manifest data or None if not found
- Raises:
ValueError – If manifest exists but contains invalid Python syntax
Note
This method is maintained for backward compatibility. Consider using get_manifest() for new code.
- get_module_codependencies(module_name: str) list[str][source]
Get codependencies from module’s manifest ‘depends’ field.
Codependencies are modules that this module depends on, meaning changes to those modules may impact this module.
- Parameters:
module_name – Name of the module to get codependencies for
- Returns:
List of codependency module names, empty list if no codependencies or module not found
- get_direct_dependencies(*module_names: str) list[str][source]
Get direct dependencies needed to install a set of modules.
Direct dependencies are the minimal set of external modules (not in the provided set) needed to install the specified modules.
- Parameters:
*module_names – One or more module names to get direct dependencies for
- Returns:
Sorted list of module names that are direct dependencies (external to the provided set) needed for installation
Example
For modules a, b, c where: - a depends on [‘b’, ‘c’] - b depends on [‘crm’] - c depends on [‘mail’] get_direct_dependencies(‘a’, ‘b’, ‘c’) returns [‘crm’, ‘mail’]
- build_dependency_graph(module_name: str) dict[str, list[str]][source]
Build complete dependency graph for a module and all its codependencies.
- Parameters:
module_name – Name of the root module to build graph for
- Returns:
Dictionary mapping each module to its direct codependencies. Format: {module_name: [list_of_codependencies]}
- Raises:
ValueError – If circular dependency is detected
- get_dependency_tree(module_name: str, max_depth: int | None = None) dict[str, Any][source]
Get hierarchical dependency tree for a module.
- Parameters:
module_name – Name of the module to get dependency tree for
max_depth – Maximum depth to traverse (None for unlimited)
- Returns:
Nested dictionary representing the dependency tree. Format: {module_name: {codependency1: {subdeps…}, codependency2: {}}}
- Raises:
ValueError – If circular dependency is detected
- get_dependencies_at_depth(module_names: list[str], max_depth: int | None = None) list[str][source]
Get all dependencies up to a specified depth for a list of modules.
- Parameters:
module_names – List of module names to get dependencies for
max_depth – Maximum depth to traverse (None for unlimited)
- Returns:
Sorted list of unique dependency names (excluding input modules)
- get_install_order(*module_names: str) list[str][source]
Get the proper installation order for one or more modules and their codependencies.
Uses topological sorting to determine the correct order for installing modules such that all codependencies are installed before the modules that depend on them.
- Parameters:
*module_names – One or more module names to get install order for
- Returns:
List of module names in the order they should be installed. Codependencies come first, then modules that depend on them.
- Raises:
ValueError – If circular dependency is detected
- analyze_dependency_cycle(*module_names: str) dict[str, Any][source]
Return structured diagnostics for the first detected dependency cycle.
- find_missing_dependencies(module_name: str) list[str][source]
Find codependencies that are not available in the addons_path.
- Parameters:
module_name – Name of the module to check codependencies for
- Returns:
List of codependency names that could not be found in addons_path. Empty list if all codependencies are available.
- Raises:
ValueError – If circular dependency is detected during graph traversal
- get_reverse_dependencies(target_module: str) list[str][source]
Get all modules that directly or indirectly depend on the target module.
This method searches through all available modules to find which ones have the target module in their codependency chain.
- Parameters:
target_module – Name of the module to find reverse dependencies for
- Returns:
List of module names that depend on the target module. Empty list if no modules depend on the target.
- detect_odoo_series() OdooSeries | None[source]
Detect the Odoo series from available modules.
Scans all available modules and attempts to detect the Odoo series from their version strings.
- Returns:
OdooSeries if detected, None if unable to detect
- get_module_version_display(module_name: str, odoo_series: OdooSeries | None = None) str[source]
Get formatted version string for display in dependency trees.
- Parameters:
module_name – Name of the module
odoo_series – Detected Odoo series (if None, will try to detect)
- Returns:
“16.0+ce” for core CE addons
”16.0+ee” for core EE addons
”1.0.2” for custom addons (actual version)
”✘ not installed” for missing addons
- Return type:
Formatted version string
- get_formatted_dependency_tree(module_name: str, max_depth: int | None = None) list[str][source]
Get formatted dependency tree for display.
- Parameters:
module_name – Name of the module to get dependency tree for
max_depth – Maximum depth to traverse (None for unlimited)
- Returns:
List of formatted lines representing the dependency tree
- sort_modules(module_names: list[str], sorting: SortingChoice | str = SortingChoice.ALPHABETICAL) list[str][source]
Sort module names according to the specified sorting method.
- Parameters:
module_names – List of module names to sort
sorting – Sorting method - either ‘alphabetical’ or ‘topological’
- Returns:
Sorted list of module names
- Raises:
ValueError – If circular dependency is detected in topological sort
- exception oduit.OdooOperationError(message: str, operation_result: dict | None = None)[source]
Bases:
ExceptionBase exception for Odoo operations
- exception oduit.ModuleOperationError(message: str, operation_result: dict | None = None)[source]
Bases:
OdooOperationErrorBase exception for module operations
- exception oduit.ModuleUpdateError(message: str, operation_result: dict | None = None)[source]
Bases:
ModuleOperationErrorRaised when module update fails
- exception oduit.ModuleInstallError(message: str, operation_result: dict | None = None)[source]
Bases:
ModuleOperationErrorRaised when module installation fails
- exception oduit.ModuleUninstallError(message: str, operation_result: dict | None = None)[source]
Bases:
ModuleOperationErrorRaised when module uninstall fails
- exception oduit.ModuleNotFoundError(message: str, operation_result: dict | None = None)[source]
Bases:
ModuleOperationErrorRaised when module doesn’t exist
- exception oduit.DatabaseOperationError(message: str, operation_result: dict | None = None)[source]
Bases:
OdooOperationErrorRaised when database operations fail
- class oduit.DemoProcessManager(available_modules: list[str] | None = None)[source]
Bases:
BaseProcessManagerMock process manager for demo mode that simulates Odoo operations
- __init__(available_modules: list[str] | None = None)[source]
Initialize with list of available modules
- run_command(cmd: list[str], stop_on_error: bool = False, compact: bool = False, verbose: bool = False, suppress_output: bool = False) dict[str, Any][source]
Execute a command in demo mode with simulated behavior
- run_command_yielding(cmd: list[str], stop_on_error: bool = False, compact: bool = False, verbose: bool = False, suppress_output: bool = False) Generator[dict[str, Any], None, None][source]
Generator version that yields lines as they arrive for demo operations
- run_operation(command_operation: CommandOperation, verbose: bool = False, suppress_output: bool = False) dict[str, Any][source]
Execute a CommandOperation directly in demo mode.
This provides enhanced result processing with parsing for demo operations.
- Parameters:
command_operation – Structured command operation with metadata
verbose – Enable verbose output
suppress_output – Suppress output to console
- Returns:
Dict containing execution results
- class oduit.OdooOperations(env_config: dict, verbose: bool = False)[source]
Bases:
objectCompatibility facade over smaller internal Odoo operation services.
- run_odoo(no_http: bool = False, dev: str | None = None, log_level: str | None = None, stop_after_init: bool = False) None[source]
Start the Odoo server with the specified configuration.
- run_shell(shell_interface: str | None = 'python', no_http: bool = True, compact: bool = False, log_level: str | None = None) dict[source]
Start an interactive Odoo shell or execute piped commands.
- update_module(module: str, no_http: bool = False, suppress_output: bool = False, raise_on_error: bool = False, compact: bool = False, log_level: str | None = None, max_cron_threads: int | None = None, without_demo: str | bool = False, stop_after_init: bool = True, i18n_overwrite: bool = False, language: str | None = None) dict[source]
Update a module and return operation result
- install_module(module: str, verbose: bool = False, no_http: bool = False, suppress_output: bool = False, raise_on_error: bool = False, compact: bool = False, max_cron_threads: int | None = None, log_level: str | None = None, without_demo: str | bool = False, language: str | None = None, with_demo: bool = False, stop_after_init: bool = True) dict[source]
Install a module and return operation result
- export_module_language(module: str, filename: str, language: str, no_http: bool = False, log_level: str | None = None, suppress_output: bool = False) dict[source]
Export language translations for a specific module to a file.
- run_tests(module: str | None = None, stop_on_error: bool = False, install: str | None = None, update: str | None = None, coverage: str | None = None, test_file: str | None = None, test_tags: str | None = None, compact: bool = False, suppress_output: bool = False, raise_on_error: bool = False, log_level: str | None = None) dict[source]
Run tests for a module
- db_exists(with_sudo: bool = True, suppress_output: bool = False, raise_on_error: bool = False, db_user: str | None = None) dict[source]
Check if database exists and return operation result
- drop_db(with_sudo: bool = True, suppress_output: bool = False, raise_on_error: bool = False) dict[source]
Drop database and return operation result
- create_db(with_sudo: bool = True, suppress_output: bool = False, create_role: bool = False, alter_role: bool = False, extension: str | None = None, raise_on_error: bool = False, db_user: str | None = None, with_demo: bool = False, without_demo: bool = False, country: str | None = None, language: str | None = None, username: str = 'admin', password: str = 'admin', odoo_series: OdooSeries | None = None) dict[source]
Create database and return operation result
- list_db(with_sudo: bool = True, suppress_output: bool = False, raise_on_error: bool = False, db_user: str | None = None) dict[source]
List all databases and return operation result
- create_addon(addon_name: str, destination: str | None = None, template: str | None = None, suppress_output: bool = False) dict[source]
Create a new Odoo addon using the scaffold command.
- get_odoo_version(suppress_output: bool = False, raise_on_error: bool = False) dict[source]
Get the Odoo version from odoo-bin
- get_environment_context(env_name: str | None = None, config_source: str | None = None, config_path: str | None = None, odoo_series: OdooSeries | None = None) EnvironmentContext[source]
Return a typed environment snapshot for planning and inspection.
- inspect_addon(module_name: str, odoo_series: OdooSeries | None = None) AddonInspection[source]
Return a typed inspection payload for one addon.
- addon_info(module_name: str, *, odoo_series: OdooSeries | None = None, database: str | None = None, timeout: float = 30.0) AddonInfo[source]
Return a combined addon summary for onboarding and planning.
- plan_update(module_name: str, odoo_series: OdooSeries | None = None) UpdatePlan[source]
Return a typed, read-only update plan for one addon.
- inspect_addons(module_names: list[str], odoo_series: OdooSeries | None = None) list[AddonInspection][source]
Return typed inspection payloads for multiple addons.
- locate_model(module_name: str, model: str) ModelSourceLocation[source]
Return static source candidates for a model extension.
- locate_field(module_name: str, model: str, field_name: str) FieldSourceLocation[source]
Return static field source candidates inside one addon.
- list_addon_tests(module_name: str, model: str | None = None, field_name: str | None = None) AddonTestInventory[source]
Return likely addon test files for one addon.
- list_addon_models(module_name: str) AddonModelInventory[source]
Return a static model inventory for one addon.
- recommend_tests(module_name: str, paths: list[str]) dict[str, Any][source]
Return changed-file to test recommendations for one addon.
- find_model_extensions(model: str, database: str | None = None, timeout: float = 30.0, source_roots: list[tuple[str, str]] | None = None, scan_cache: SourceScanCache | None = None) ModelExtensionInventory[source]
Return combined source and installed metadata for one model.
- list_duplicates() dict[str, list[str]][source]
Return duplicate module names across configured addon paths.
- list_addons_inventory(module_names: list[str], odoo_series: OdooSeries | None = None) list[dict[str, Any]][source]
Return structured addon inventory records.
- get_addon_install_state(module: str, *, database: str | None = None, timeout: float = 30.0) AddonInstallState[source]
Return the runtime install state for one addon.
- list_installed_dependents(module: str, *, database: str | None = None, timeout: float = 30.0) InstalledAddonInventory[source]
Return installed addons that depend on the target module.
- uninstall_module(module: str, *, suppress_output: bool = False, raise_on_error: bool = False, compact: bool = False, log_level: str | None = None, allow_uninstall: bool = False, check_dependents: bool = True) dict[str, Any][source]
Uninstall a module through a trusted runtime action.
- list_installed_addons(*, modules: list[str] | None = None, states: list[str] | None = None, database: str | None = None, timeout: float = 30.0) InstalledAddonInventory[source]
Return runtime addon inventory from
ir.module.module.
- dependency_graph(module_names: list[str]) dict[str, Any][source]
Return dependency graph data for one or more addons.
- build_addon_documentation(module_name: str, *, odoo_series: OdooSeries | None = None, database: str | None = None, timeout: float = 30.0, source_only: bool = False, include_arch: bool = False, field_attributes: list[str] | tuple[str, ...] | None = None, view_types: list[str] | tuple[str, ...] | None = None, max_models: int | None = None, max_fields_per_model: int | None = None, path_prefix: str | None = None, progress: Callable[[str, dict[str, Any]], None] | None = None, progress_level: str = 'compact') AddonDocumentation[source]
Build one addon documentation bundle.
- build_model_documentation(model: str, *, database: str | None = None, timeout: float = 30.0, source_only: bool = False, include_arch: bool = False, field_attributes: list[str] | tuple[str, ...] | None = None, view_types: list[str] | tuple[str, ...] | None = None, max_fields: int | None = None, source_modules: list[str] | tuple[str, ...] | None = None, path_prefix: str | None = None) ModelDocumentation[source]
Build one model documentation bundle.
- build_addons_documentation(module_names: list[str], *, odoo_series: OdooSeries | None = None, database: str | None = None, timeout: float = 30.0, source_only: bool = False, include_arch: bool = False, field_attributes: list[str] | tuple[str, ...] | None = None, view_types: list[str] | tuple[str, ...] | None = None, max_models: int | None = None, max_fields_per_model: int | None = None, path_prefix: str | None = None) MultiAddonDocumentation[source]
Build one documentation bundle spanning multiple addons.
- build_technical_documentation(target: str, *, template: str = 'arc42', odoo_series: OdooSeries | None = None, database: str | None = None, timeout: float = 30.0, source_only: bool = False, include_arch: bool = False, field_attributes: list[str] | tuple[str, ...] | None = None, view_types: list[str] | tuple[str, ...] | None = None, max_models: int | None = None, max_fields_per_model: int | None = None, path_prefix: str | None = None, path_base_dir: str | None = None, documentation_policy: DocumentationDirectoryPolicy | None = None, progress: Callable[[str, dict[str, Any]], None] | None = None, progress_level: str = 'compact', render_markdown: bool = True) TechnicalDocumentation[source]
Build one technical-documentation bundle for an addon target.
- refresh_technical_documentation(target: str, *, odoo_series: OdooSeries | None = None, database: str | None = None, timeout: float = 30.0, source_only: bool | None = None, include_arch: bool | None = None, field_attributes: list[str] | tuple[str, ...] | None = None, view_types: list[str] | tuple[str, ...] | None = None, max_models: int | None = None, max_fields_per_model: int | None = None, path_prefix: str | None = None, path_base_dir: str | None = None, documentation_policy: DocumentationDirectoryPolicy | None = None, overwrite_edited: bool = False, add_missing: bool = False, write: bool = False) dict[str, Any][source]
Refresh managed generated blocks in addon-local architecture docs.
- build_technical_evidence(target: str, *, template: str = 'arc42', odoo_series: OdooSeries | None = None, database: str | None = None, timeout: float = 30.0, source_only: bool = False, include_arch: bool = False, field_attributes: list[str] | tuple[str, ...] | None = None, view_types: list[str] | tuple[str, ...] | None = None, max_models: int | None = None, max_fields_per_model: int | None = None, path_prefix: str | None = None, path_base_dir: str | None = None, documentation_policy: DocumentationDirectoryPolicy | None = None, progress: Callable[[str, dict[str, Any]], None] | None = None, progress_level: str = 'compact', render_markdown: bool = True, evidence_version: int | None = None) TechnicalDocumentation[source]
Build split deterministic technical evidence bundle.
- write_technical_evidence(target: str, *, force: bool = False, template: str = 'arc42', odoo_series: OdooSeries | None = None, database: str | None = None, timeout: float = 30.0, source_only: bool = False, include_arch: bool = False, field_attributes: list[str] | tuple[str, ...] | None = None, view_types: list[str] | tuple[str, ...] | None = None, max_models: int | None = None, max_fields_per_model: int | None = None, path_base_dir: str | None = None, documentation_policy: DocumentationDirectoryPolicy | None = None, progress: Callable[[str, dict[str, Any]], None] | None = None, progress_level: str = 'compact') dict[str, Any][source]
Write split deterministic evidence markdown and sidecar.
- build_technical_report_seed(target: str, *, evidence_metadata: Any | None = None, generate_evidence_if_missing: bool = True, template: str = 'arc42', odoo_series: OdooSeries | None = None, database: str | None = None, timeout: float = 30.0, source_only: bool = False, include_arch: bool = False, field_attributes: list[str] | tuple[str, ...] | None = None, view_types: list[str] | tuple[str, ...] | None = None, max_models: int | None = None, max_fields_per_model: int | None = None, path_base_dir: str | None = None, documentation_policy: DocumentationDirectoryPolicy | None = None, progress: Callable[[str, dict[str, Any]], None] | None = None, progress_level: str = 'compact') TechnicalDocumentation[source]
Build split LLM/human report seed bundle.
- diff_technical_report_evidence(target: str, *, include_diff: bool = False, significant_only: bool = False, path_base_dir: str | None = None, documentation_policy: DocumentationDirectoryPolicy | None = None) dict[str, Any][source]
Diff report snapshots against current evidence document.
- build_dependency_graph_documentation(module_names: list[str], *, database: str | None = None, timeout: float = 30.0, source_only: bool = False, installed_only: bool = False, transitive: bool = True, path_prefix: str | None = None) DependencyGraphDocumentation[source]
Build dependency graph documentation for one or more addons.
- query_model(model: str, domain: list[Any] | tuple[Any, ...] | None = None, fields: list[str] | tuple[str, ...] | None = None, limit: int = 80, include_total_count: bool = False, database: str | None = None, timeout: float = 30.0) QueryModelResult[source]
Delegate typed read-only model queries to
OdooQuery.
- read_record(model: str, record_id: int, fields: list[str] | tuple[str, ...] | None = None, database: str | None = None, timeout: float = 30.0) RecordReadResult[source]
Delegate typed single-record reads to
OdooQuery.
- search_count(model: str, domain: list[Any] | tuple[Any, ...] | None = None, database: str | None = None, timeout: float = 30.0) SearchCountResult[source]
Delegate typed count queries to
OdooQuery.
- get_model_fields(model: str, attributes: list[str] | tuple[str, ...] | None = None, module: str | None = None, database: str | None = None, timeout: float = 30.0) ModelFieldsResult[source]
Delegate typed field metadata queries to
OdooQuery.
- get_model_views(model: str, view_types: list[str] | tuple[str, ...] | None = None, database: str | None = None, timeout: float = 30.0, include_arch: bool = True) ModelViewInventory[source]
Return primary and extension DB views for one model.
- get_models_documentation_runtime(models: list[str], *, module_name: str | None = None, attributes: list[str] | tuple[str, ...] | None = None, view_types: list[str] | tuple[str, ...] | None = None, include_arch: bool = False, progress: Callable[[str, dict[str, Any]], None] | None = None, database: str | None = None, timeout: float = 60.0) DocumentationRuntimeInventory[source]
Return batched runtime metadata for documentation generation.
- execute_python_code(python_code: str, no_http: bool = True, capture_output: bool = True, suppress_output: bool = False, raise_on_error: bool = False, shell_interface: str | None = None, log_level: str | None = None) dict[source]
Execute Python code in the Odoo shell environment
- execute_code(code: str, *, database: str | None = None, commit: bool = False, timeout: float = 30.0) dict[str, Any][source]
Execute trusted arbitrary Python through the embedded executor.
- inspect_ref(xmlid: str, *, database: str | None = None, timeout: float = 30.0) dict[str, Any][source]
Resolve one XMLID through the embedded Odoo runtime.
- inspect_cron(xmlid: str, *, trigger: bool = False, database: str | None = None, timeout: float = 30.0) dict[str, Any][source]
Inspect or explicitly trigger one cron job by XMLID.
- inspect_modules(*, state: str | None = None, names_only: bool = False, database: str | None = None, timeout: float = 30.0) dict[str, Any][source]
Return runtime addon inventory with inspect-command semantics.
- inspect_subtypes(model: str, *, database: str | None = None, timeout: float = 30.0) dict[str, Any][source]
List message subtypes registered for one model.
- inspect_model(model: str, *, database: str | None = None, timeout: float = 30.0) dict[str, Any][source]
Inspect runtime model registration metadata.
- inspect_field(model: str, field: str, *, with_db: bool = False, database: str | None = None, timeout: float = 30.0) dict[str, Any][source]
Inspect runtime field metadata.
- inspect_recordset(expression: str, *, database: str | None = None, timeout: float = 30.0) dict[str, Any][source]
Execute a trusted recordset expression as an inspection escape hatch.
- describe_table(table_name: str, *, database: str | None = None, timeout: float = 30.0) dict[str, Any][source]
Describe one PostgreSQL table through the live Odoo connection.
- describe_column(table_name: str, column_name: str, *, database: str | None = None, timeout: float = 30.0) dict[str, Any][source]
Describe one PostgreSQL column through the live Odoo connection.
- list_constraints(table_name: str, *, database: str | None = None, timeout: float = 30.0) dict[str, Any][source]
List PostgreSQL constraints for one table.
- list_tables(pattern: str | None = None, *, database: str | None = None, timeout: float = 30.0) dict[str, Any][source]
List PostgreSQL tables through the live Odoo connection.
- inspect_m2m(model: str, field: str, *, database: str | None = None, timeout: float = 30.0) dict[str, Any][source]
Inspect Many2many relation-table metadata.
- performance_table_scans(*, limit: int = 20, database: str | None = None, timeout: float = 30.0) dict[str, Any][source]
Return sequential scan metrics for PostgreSQL tables.
- class oduit.OdooEmbeddedManager(config_provider: ConfigProvider)[source]
Bases:
objectEmbedded Odoo server manager for direct in-process execution.
This class runs Odoo directly by importing and calling odoo.service.server.start() instead of spawning external odoo-bin processes. It provides better integration and control over the Odoo instance while maintaining compatibility with the existing configuration system.
Features: - Direct Odoo execution without subprocess overhead - Proper PID file handling based on Odoo’s server.py implementation - Integration with ConfigProvider for parameter management - Thread-safe server management - Graceful shutdown handling
- config_provider
ConfigProvider instance for accessing configuration
- _server_thread
Thread running the Odoo server
- _server_pid
PID of the server process
- _is_running
Flag indicating if server is active
- __init__(config_provider: ConfigProvider)[source]
Initialize the embedded Odoo manager.
- Parameters:
config_provider – ConfigProvider instance with Odoo configuration
- start_server(stop_after_init: bool = False, run_in_thread: bool = False) dict[str, Any][source]
Start the embedded Odoo server.
- Parameters:
stop_after_init – Stop server after initialization
run_in_thread – Run server in separate thread (for non-blocking execution)
- Returns:
success (bool): True if server started successfully
thread (Thread): Server thread if run_in_thread=True
pid (int): Process ID
error (str): Error message if startup failed
- Return type:
Dictionary with execution results
- stop_server(timeout: float = 30.0) dict[str, Any][source]
Stop the running Odoo server.
- Parameters:
timeout – Maximum time to wait for server shutdown
- Returns:
success (bool): True if server stopped successfully
error (str): Error message if shutdown failed
- Return type:
Dictionary with shutdown results
- is_running() bool[source]
Check if the server is currently running.
- Returns:
True if server is running, False otherwise
- get_server_info() dict[str, Any][source]
Get information about the server state.
- Returns:
running (bool): Whether server is running
pid (int): Process ID if available
thread_alive (bool): Whether server thread is alive (if threaded)
- Return type:
Dictionary with server information
- start_shell(database: str | None = None, extra_args: list[str] | None = None) dict[str, Any][source]
Start an embedded Odoo shell session.
- Parameters:
database – Database name for the shell session
extra_args – Additional command line arguments
- Returns:
success (bool): True if shell started successfully
output (str): Shell output or success message
error (str): Error message if startup failed
- Return type:
Dictionary with execution results
- class oduit.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
- class oduit.OdooInspector(config: ConfigProvider | dict[str, Any])[source]
Bases:
objectExpose structured Odoo inspection workflows for CLI and Python callers.
- execute_code(code: str, *, database: str | None = None, commit: bool = False, timeout: float = 30.0) dict[str, Any][source]
Execute trusted arbitrary Python within Odoo via the embedded runtime.
- inspect_ref(xmlid: str, *, database: str | None = None, timeout: float = 30.0) dict[str, Any][source]
Resolve one XMLID and return stable record metadata.
- inspect_cron(xmlid: str, *, trigger: bool = False, database: str | None = None, timeout: float = 30.0) dict[str, Any][source]
Inspect one cron job and optionally trigger it.
- inspect_modules(*, state: str | None = None, names_only: bool = False, database: str | None = None, timeout: float = 30.0) dict[str, Any][source]
List runtime addon inventory from
ir.module.module.
- inspect_subtypes(model: str, *, database: str | None = None, timeout: float = 30.0) dict[str, Any][source]
List
mail.message.subtyperecords for one model.
- inspect_model(model: str, *, database: str | None = None, timeout: float = 30.0) dict[str, Any][source]
Inspect model registration metadata.
- inspect_field(model: str, field: str, *, with_db: bool = False, database: str | None = None, timeout: float = 30.0) dict[str, Any][source]
Inspect one ORM field, optionally including DB-level metadata.
- inspect_recordset(expression: str, *, database: str | None = None, timeout: float = 30.0) dict[str, Any][source]
Execute a trusted recordset expression as an escape hatch.
- describe_table(table_name: str, *, database: str | None = None, timeout: float = 30.0) dict[str, Any][source]
Describe columns for one PostgreSQL table.
- describe_column(table_name: str, column_name: str, *, database: str | None = None, timeout: float = 30.0) dict[str, Any][source]
Describe one PostgreSQL column.
- list_constraints(table_name: str, *, database: str | None = None, timeout: float = 30.0) dict[str, Any][source]
List PostgreSQL constraints for one table.
- list_tables(pattern: str | None = None, *, database: str | None = None, timeout: float = 30.0) dict[str, Any][source]
List PostgreSQL tables, optionally filtered by an ILIKE pattern.
- inspect_m2m(model: str, field: str, *, database: str | None = None, timeout: float = 30.0) dict[str, Any][source]
Inspect Many2many relation-table metadata for one ORM field.
- performance_table_scans(*, limit: int = 20, database: str | None = None, timeout: float = 30.0) dict[str, Any][source]
Return tables with the highest sequential scan counts.
- class oduit.OdooQuery(config: ConfigProvider | dict[str, Any])[source]
Bases:
objectRun structured read-only queries against an Odoo environment.
This API is intended for common read-heavy tasks where callers should not need to pass arbitrary Python code strings. It validates structured input, generates minimal trusted code internally, and relies on the executor’s default rollback behavior.
- query_model(model: str, domain: list[Any] | tuple[Any, ...] | None = None, fields: list[str] | tuple[str, ...] | None = None, limit: int = 80, include_total_count: bool = False, database: str | None = None, timeout: float = 30.0) dict[str, Any][source]
Search records and return read data for the matched result set.
- read_record(model: str, record_id: int, fields: list[str] | tuple[str, ...] | None = None, database: str | None = None, timeout: float = 30.0) dict[str, Any][source]
Read one record by id.
- class oduit.ProcessManager[source]
Bases:
BaseProcessManagerCross-platform process execution manager for Odoo operations.
Provides comprehensive process management functionality including: - Command execution with output streaming and error handling - Cross-platform sudo authentication via stdin - Interactive shell support with pseudo-terminal (PTY) handling - Real-time output colorization and formatting - JSON-structured logging for programmatic consumption - Signal-safe process termination and cleanup
This class handles the complexities of running Odoo commands across different operating systems while providing consistent interfaces for both interactive and automated usage scenarios.
- _sudo_password
Cached sudo password for repeated operations
- Type:
str | None
Example
>>> pm = ProcessManager() >>> result = pm.run_command(['echo', 'hello world']) >>> print(result['success']) # True >>> print(result['output']) # 'hello world\n'
>>> # Interactive shell >>> pm.run_interactive_shell(['bash'])
- run_operation(command_operation: CommandOperation, verbose: bool = False, suppress_output: bool = False) dict[str, Any][source]
Execute a CommandOperation directly.
For regular ProcessManager, this builds the command from the operation and executes it normally with enhanced result processing.
- Parameters:
command_operation – Structured command operation with metadata
verbose – Enable verbose output
suppress_output – Suppress output to console
- Returns:
Dict containing execution results
- run_command(cmd: list[str], stop_on_error: bool = False, compact: bool = False, verbose: bool = False, suppress_output: bool = False) dict[str, Any][source]
Execute a command with comprehensive output handling and error management.
This method provides a unified interface for running system commands with proper error handling, output streaming, and optional sudo support.
- Parameters:
cmd – Command to execute as list of strings (e.g., [‘ls’, ‘-la’])
stop_on_error – If True, terminate on first error encountered
compact – If True, use compact output formatting
verbose – If True, print the command being executed
suppress_output – If True, suppress all output to console
- Returns:
success (bool): True if command executed successfully
return_code (int): Process exit code (0 for success)
output (str): Combined stdout/stderr output
command (str): The executed command as string
error (str): Error message if execution failed
- Return type:
Dict containing execution results with keys
- Raises:
KeyboardInterrupt – Re-raised after proper process cleanup
Examples
>>> pm = ProcessManager() >>> result = pm.run_command(['echo', 'hello']) >>> print(result['success']) # True >>> print(result['output']) # 'hello\n'
>>> result = pm.run_command(['false']) # Command that fails >>> print(result['success']) # False >>> print(result['return_code']) # 1
- run_command_yielding(cmd: list[str], stop_on_error: bool = False, compact: bool = False, verbose: bool = False, suppress_output: bool = False) Generator[dict[str, Any], None, None][source]
Generator version of run_command that yields lines as they arrive
- Parameters:
cmd – Command to execute
stop_on_error – Stop execution on first error pattern
compact – Only show relevant lines (dots, errors, warnings)
verbose – Print command before execution
suppress_output – Don’t print lines to console (only yield them)
- Yields:
For each line –
dict: {‘line’: str, ‘formatted’: str, ‘should_show’: bool, ‘is_error’: bool, ‘process_running’: bool}
Final yield:
dict: {‘result’: dict, ‘process_running’: False}
- run_shell_command(cmd: list[str] | str, verbose: bool = False, capture_output: bool = False, allow_shell: bool = False, input_data: str | None = None) dict[str, Any][source]
Run a shell command that may receive piped input
- Parameters:
cmd – Either a list of command arguments or a string to be evaluated by shell
verbose – Print command before execution
capture_output – Capture stdout/stderr instead of inheriting
allow_shell – Allow shell string evaluation when cmd is a string
input_data – Optional data to send to stdin
- 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_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.
- class oduit.OutputFormatter(format_type: str = 'text', non_interactive: bool = False)[source]
Bases:
objectHandles different output formats and modes.
- output(message: str, level: str = 'info', data: dict[str, Any] | None = None) None[source]
Output a message in the configured format.
- oduit.configure_output(format_type: str = 'text', non_interactive: bool = False) None[source]
Configure the global output formatter.
- oduit.print_result(data: dict[str, Any], message: str = 'Operation completed') None[source]
Print operation result with data.
- oduit.print_error_result(error_msg: str, error_code: int = 1) None[source]
Print error result and exit with code.
Core Classes
ProcessManager
- class oduit.ProcessManager[source]
Bases:
BaseProcessManagerCross-platform process execution manager for Odoo operations.
Provides comprehensive process management functionality including: - Command execution with output streaming and error handling - Cross-platform sudo authentication via stdin - Interactive shell support with pseudo-terminal (PTY) handling - Real-time output colorization and formatting - JSON-structured logging for programmatic consumption - Signal-safe process termination and cleanup
This class handles the complexities of running Odoo commands across different operating systems while providing consistent interfaces for both interactive and automated usage scenarios.
- _sudo_password
Cached sudo password for repeated operations
- Type:
str | None
Example
>>> pm = ProcessManager() >>> result = pm.run_command(['echo', 'hello world']) >>> print(result['success']) # True >>> print(result['output']) # 'hello world\n'
>>> # Interactive shell >>> pm.run_interactive_shell(['bash'])
- run_operation(command_operation: CommandOperation, verbose: bool = False, suppress_output: bool = False) dict[str, Any][source]
Execute a CommandOperation directly.
For regular ProcessManager, this builds the command from the operation and executes it normally with enhanced result processing.
- Parameters:
command_operation – Structured command operation with metadata
verbose – Enable verbose output
suppress_output – Suppress output to console
- Returns:
Dict containing execution results
- run_command(cmd: list[str], stop_on_error: bool = False, compact: bool = False, verbose: bool = False, suppress_output: bool = False) dict[str, Any][source]
Execute a command with comprehensive output handling and error management.
This method provides a unified interface for running system commands with proper error handling, output streaming, and optional sudo support.
- Parameters:
cmd – Command to execute as list of strings (e.g., [‘ls’, ‘-la’])
stop_on_error – If True, terminate on first error encountered
compact – If True, use compact output formatting
verbose – If True, print the command being executed
suppress_output – If True, suppress all output to console
- Returns:
success (bool): True if command executed successfully
return_code (int): Process exit code (0 for success)
output (str): Combined stdout/stderr output
command (str): The executed command as string
error (str): Error message if execution failed
- Return type:
Dict containing execution results with keys
- Raises:
KeyboardInterrupt – Re-raised after proper process cleanup
Examples
>>> pm = ProcessManager() >>> result = pm.run_command(['echo', 'hello']) >>> print(result['success']) # True >>> print(result['output']) # 'hello\n'
>>> result = pm.run_command(['false']) # Command that fails >>> print(result['success']) # False >>> print(result['return_code']) # 1
- run_command_yielding(cmd: list[str], stop_on_error: bool = False, compact: bool = False, verbose: bool = False, suppress_output: bool = False) Generator[dict[str, Any], None, None][source]
Generator version of run_command that yields lines as they arrive
- Parameters:
cmd – Command to execute
stop_on_error – Stop execution on first error pattern
compact – Only show relevant lines (dots, errors, warnings)
verbose – Print command before execution
suppress_output – Don’t print lines to console (only yield them)
- Yields:
For each line –
dict: {‘line’: str, ‘formatted’: str, ‘should_show’: bool, ‘is_error’: bool, ‘process_running’: bool}
Final yield:
dict: {‘result’: dict, ‘process_running’: False}
- run_shell_command(cmd: list[str] | str, verbose: bool = False, capture_output: bool = False, allow_shell: bool = False, input_data: str | None = None) dict[str, Any][source]
Run a shell command that may receive piped input
- Parameters:
cmd – Either a list of command arguments or a string to be evaluated by shell
verbose – Print command before execution
capture_output – Capture stdout/stderr instead of inheriting
allow_shell – Allow shell string evaluation when cmd is a string
input_data – Optional data to send to stdin
ConfigLoader
- class oduit.ConfigLoader(config_dir: str | None = None)[source]
Bases:
objectHandles loading and managing Odoo environment configurations.
- __init__(config_dir: str | None = None)[source]
Initialize ConfigLoader with optional custom config directory.
- get_config_path(env_name: str, format_type: str = 'yaml') str[source]
Returns full path to environment-specific config.
- resolve_config_path(env_name: str) tuple[str, str][source]
Resolve the config path and format for an environment name.
- load_local_config_details() LoadedConfigDetails[source]
Load local config plus canonical normalized metadata.
- inspect_odoo_conf_import(conf_path: str, sectioned: bool = False) ImportedOdooConfDetails[source]
Import Odoo configuration and return migration-friendly metadata.
- import_odoo_conf(conf_path: str, sectioned: bool = False) dict[str, Any][source]
Import Odoo configuration from .conf file and convert to oduit format.
- Parameters:
conf_path – Path to the Odoo .conf file
sectioned – If True, return configuration in sectioned format
- Returns:
Dictionary with oduit-compatible configuration
- Raises:
FileNotFoundError – If conf file doesn’t exist
ValueError – If conf file format is invalid
- load_config(env_name: str) dict[str, Any][source]
Load config from ~/.config/oduit/<env>.(yaml|toml|conf) or from a direct file path
- load_config_details(env_name: str) LoadedConfigDetails[source]
Load config plus canonical normalized metadata.
OdooOperations
- class oduit.OdooOperations(env_config: dict, verbose: bool = False)[source]
Bases:
objectCompatibility facade over smaller internal Odoo operation services.
- run_odoo(no_http: bool = False, dev: str | None = None, log_level: str | None = None, stop_after_init: bool = False) None[source]
Start the Odoo server with the specified configuration.
- run_shell(shell_interface: str | None = 'python', no_http: bool = True, compact: bool = False, log_level: str | None = None) dict[source]
Start an interactive Odoo shell or execute piped commands.
- update_module(module: str, no_http: bool = False, suppress_output: bool = False, raise_on_error: bool = False, compact: bool = False, log_level: str | None = None, max_cron_threads: int | None = None, without_demo: str | bool = False, stop_after_init: bool = True, i18n_overwrite: bool = False, language: str | None = None) dict[source]
Update a module and return operation result
- install_module(module: str, verbose: bool = False, no_http: bool = False, suppress_output: bool = False, raise_on_error: bool = False, compact: bool = False, max_cron_threads: int | None = None, log_level: str | None = None, without_demo: str | bool = False, language: str | None = None, with_demo: bool = False, stop_after_init: bool = True) dict[source]
Install a module and return operation result
- export_module_language(module: str, filename: str, language: str, no_http: bool = False, log_level: str | None = None, suppress_output: bool = False) dict[source]
Export language translations for a specific module to a file.
- run_tests(module: str | None = None, stop_on_error: bool = False, install: str | None = None, update: str | None = None, coverage: str | None = None, test_file: str | None = None, test_tags: str | None = None, compact: bool = False, suppress_output: bool = False, raise_on_error: bool = False, log_level: str | None = None) dict[source]
Run tests for a module
- db_exists(with_sudo: bool = True, suppress_output: bool = False, raise_on_error: bool = False, db_user: str | None = None) dict[source]
Check if database exists and return operation result
- drop_db(with_sudo: bool = True, suppress_output: bool = False, raise_on_error: bool = False) dict[source]
Drop database and return operation result
- create_db(with_sudo: bool = True, suppress_output: bool = False, create_role: bool = False, alter_role: bool = False, extension: str | None = None, raise_on_error: bool = False, db_user: str | None = None, with_demo: bool = False, without_demo: bool = False, country: str | None = None, language: str | None = None, username: str = 'admin', password: str = 'admin', odoo_series: OdooSeries | None = None) dict[source]
Create database and return operation result
- list_db(with_sudo: bool = True, suppress_output: bool = False, raise_on_error: bool = False, db_user: str | None = None) dict[source]
List all databases and return operation result
- create_addon(addon_name: str, destination: str | None = None, template: str | None = None, suppress_output: bool = False) dict[source]
Create a new Odoo addon using the scaffold command.
- get_odoo_version(suppress_output: bool = False, raise_on_error: bool = False) dict[source]
Get the Odoo version from odoo-bin
- get_environment_context(env_name: str | None = None, config_source: str | None = None, config_path: str | None = None, odoo_series: OdooSeries | None = None) EnvironmentContext[source]
Return a typed environment snapshot for planning and inspection.
- inspect_addon(module_name: str, odoo_series: OdooSeries | None = None) AddonInspection[source]
Return a typed inspection payload for one addon.
- addon_info(module_name: str, *, odoo_series: OdooSeries | None = None, database: str | None = None, timeout: float = 30.0) AddonInfo[source]
Return a combined addon summary for onboarding and planning.
- plan_update(module_name: str, odoo_series: OdooSeries | None = None) UpdatePlan[source]
Return a typed, read-only update plan for one addon.
- inspect_addons(module_names: list[str], odoo_series: OdooSeries | None = None) list[AddonInspection][source]
Return typed inspection payloads for multiple addons.
- locate_model(module_name: str, model: str) ModelSourceLocation[source]
Return static source candidates for a model extension.
- locate_field(module_name: str, model: str, field_name: str) FieldSourceLocation[source]
Return static field source candidates inside one addon.
- list_addon_tests(module_name: str, model: str | None = None, field_name: str | None = None) AddonTestInventory[source]
Return likely addon test files for one addon.
- list_addon_models(module_name: str) AddonModelInventory[source]
Return a static model inventory for one addon.
- recommend_tests(module_name: str, paths: list[str]) dict[str, Any][source]
Return changed-file to test recommendations for one addon.
- find_model_extensions(model: str, database: str | None = None, timeout: float = 30.0, source_roots: list[tuple[str, str]] | None = None, scan_cache: SourceScanCache | None = None) ModelExtensionInventory[source]
Return combined source and installed metadata for one model.
- list_duplicates() dict[str, list[str]][source]
Return duplicate module names across configured addon paths.
- list_addons_inventory(module_names: list[str], odoo_series: OdooSeries | None = None) list[dict[str, Any]][source]
Return structured addon inventory records.
- get_addon_install_state(module: str, *, database: str | None = None, timeout: float = 30.0) AddonInstallState[source]
Return the runtime install state for one addon.
- list_installed_dependents(module: str, *, database: str | None = None, timeout: float = 30.0) InstalledAddonInventory[source]
Return installed addons that depend on the target module.
- uninstall_module(module: str, *, suppress_output: bool = False, raise_on_error: bool = False, compact: bool = False, log_level: str | None = None, allow_uninstall: bool = False, check_dependents: bool = True) dict[str, Any][source]
Uninstall a module through a trusted runtime action.
- list_installed_addons(*, modules: list[str] | None = None, states: list[str] | None = None, database: str | None = None, timeout: float = 30.0) InstalledAddonInventory[source]
Return runtime addon inventory from
ir.module.module.
- dependency_graph(module_names: list[str]) dict[str, Any][source]
Return dependency graph data for one or more addons.
- build_addon_documentation(module_name: str, *, odoo_series: OdooSeries | None = None, database: str | None = None, timeout: float = 30.0, source_only: bool = False, include_arch: bool = False, field_attributes: list[str] | tuple[str, ...] | None = None, view_types: list[str] | tuple[str, ...] | None = None, max_models: int | None = None, max_fields_per_model: int | None = None, path_prefix: str | None = None, progress: Callable[[str, dict[str, Any]], None] | None = None, progress_level: str = 'compact') AddonDocumentation[source]
Build one addon documentation bundle.
- build_model_documentation(model: str, *, database: str | None = None, timeout: float = 30.0, source_only: bool = False, include_arch: bool = False, field_attributes: list[str] | tuple[str, ...] | None = None, view_types: list[str] | tuple[str, ...] | None = None, max_fields: int | None = None, source_modules: list[str] | tuple[str, ...] | None = None, path_prefix: str | None = None) ModelDocumentation[source]
Build one model documentation bundle.
- build_addons_documentation(module_names: list[str], *, odoo_series: OdooSeries | None = None, database: str | None = None, timeout: float = 30.0, source_only: bool = False, include_arch: bool = False, field_attributes: list[str] | tuple[str, ...] | None = None, view_types: list[str] | tuple[str, ...] | None = None, max_models: int | None = None, max_fields_per_model: int | None = None, path_prefix: str | None = None) MultiAddonDocumentation[source]
Build one documentation bundle spanning multiple addons.
- build_technical_documentation(target: str, *, template: str = 'arc42', odoo_series: OdooSeries | None = None, database: str | None = None, timeout: float = 30.0, source_only: bool = False, include_arch: bool = False, field_attributes: list[str] | tuple[str, ...] | None = None, view_types: list[str] | tuple[str, ...] | None = None, max_models: int | None = None, max_fields_per_model: int | None = None, path_prefix: str | None = None, path_base_dir: str | None = None, documentation_policy: DocumentationDirectoryPolicy | None = None, progress: Callable[[str, dict[str, Any]], None] | None = None, progress_level: str = 'compact', render_markdown: bool = True) TechnicalDocumentation[source]
Build one technical-documentation bundle for an addon target.
- refresh_technical_documentation(target: str, *, odoo_series: OdooSeries | None = None, database: str | None = None, timeout: float = 30.0, source_only: bool | None = None, include_arch: bool | None = None, field_attributes: list[str] | tuple[str, ...] | None = None, view_types: list[str] | tuple[str, ...] | None = None, max_models: int | None = None, max_fields_per_model: int | None = None, path_prefix: str | None = None, path_base_dir: str | None = None, documentation_policy: DocumentationDirectoryPolicy | None = None, overwrite_edited: bool = False, add_missing: bool = False, write: bool = False) dict[str, Any][source]
Refresh managed generated blocks in addon-local architecture docs.
- build_technical_evidence(target: str, *, template: str = 'arc42', odoo_series: OdooSeries | None = None, database: str | None = None, timeout: float = 30.0, source_only: bool = False, include_arch: bool = False, field_attributes: list[str] | tuple[str, ...] | None = None, view_types: list[str] | tuple[str, ...] | None = None, max_models: int | None = None, max_fields_per_model: int | None = None, path_prefix: str | None = None, path_base_dir: str | None = None, documentation_policy: DocumentationDirectoryPolicy | None = None, progress: Callable[[str, dict[str, Any]], None] | None = None, progress_level: str = 'compact', render_markdown: bool = True, evidence_version: int | None = None) TechnicalDocumentation[source]
Build split deterministic technical evidence bundle.
- write_technical_evidence(target: str, *, force: bool = False, template: str = 'arc42', odoo_series: OdooSeries | None = None, database: str | None = None, timeout: float = 30.0, source_only: bool = False, include_arch: bool = False, field_attributes: list[str] | tuple[str, ...] | None = None, view_types: list[str] | tuple[str, ...] | None = None, max_models: int | None = None, max_fields_per_model: int | None = None, path_base_dir: str | None = None, documentation_policy: DocumentationDirectoryPolicy | None = None, progress: Callable[[str, dict[str, Any]], None] | None = None, progress_level: str = 'compact') dict[str, Any][source]
Write split deterministic evidence markdown and sidecar.
- build_technical_report_seed(target: str, *, evidence_metadata: Any | None = None, generate_evidence_if_missing: bool = True, template: str = 'arc42', odoo_series: OdooSeries | None = None, database: str | None = None, timeout: float = 30.0, source_only: bool = False, include_arch: bool = False, field_attributes: list[str] | tuple[str, ...] | None = None, view_types: list[str] | tuple[str, ...] | None = None, max_models: int | None = None, max_fields_per_model: int | None = None, path_base_dir: str | None = None, documentation_policy: DocumentationDirectoryPolicy | None = None, progress: Callable[[str, dict[str, Any]], None] | None = None, progress_level: str = 'compact') TechnicalDocumentation[source]
Build split LLM/human report seed bundle.
- diff_technical_report_evidence(target: str, *, include_diff: bool = False, significant_only: bool = False, path_base_dir: str | None = None, documentation_policy: DocumentationDirectoryPolicy | None = None) dict[str, Any][source]
Diff report snapshots against current evidence document.
- build_dependency_graph_documentation(module_names: list[str], *, database: str | None = None, timeout: float = 30.0, source_only: bool = False, installed_only: bool = False, transitive: bool = True, path_prefix: str | None = None) DependencyGraphDocumentation[source]
Build dependency graph documentation for one or more addons.
- query_model(model: str, domain: list[Any] | tuple[Any, ...] | None = None, fields: list[str] | tuple[str, ...] | None = None, limit: int = 80, include_total_count: bool = False, database: str | None = None, timeout: float = 30.0) QueryModelResult[source]
Delegate typed read-only model queries to
OdooQuery.
- read_record(model: str, record_id: int, fields: list[str] | tuple[str, ...] | None = None, database: str | None = None, timeout: float = 30.0) RecordReadResult[source]
Delegate typed single-record reads to
OdooQuery.
- search_count(model: str, domain: list[Any] | tuple[Any, ...] | None = None, database: str | None = None, timeout: float = 30.0) SearchCountResult[source]
Delegate typed count queries to
OdooQuery.
- get_model_fields(model: str, attributes: list[str] | tuple[str, ...] | None = None, module: str | None = None, database: str | None = None, timeout: float = 30.0) ModelFieldsResult[source]
Delegate typed field metadata queries to
OdooQuery.
- get_model_views(model: str, view_types: list[str] | tuple[str, ...] | None = None, database: str | None = None, timeout: float = 30.0, include_arch: bool = True) ModelViewInventory[source]
Return primary and extension DB views for one model.
- get_models_documentation_runtime(models: list[str], *, module_name: str | None = None, attributes: list[str] | tuple[str, ...] | None = None, view_types: list[str] | tuple[str, ...] | None = None, include_arch: bool = False, progress: Callable[[str, dict[str, Any]], None] | None = None, database: str | None = None, timeout: float = 60.0) DocumentationRuntimeInventory[source]
Return batched runtime metadata for documentation generation.
- execute_python_code(python_code: str, no_http: bool = True, capture_output: bool = True, suppress_output: bool = False, raise_on_error: bool = False, shell_interface: str | None = None, log_level: str | None = None) dict[source]
Execute Python code in the Odoo shell environment
- execute_code(code: str, *, database: str | None = None, commit: bool = False, timeout: float = 30.0) dict[str, Any][source]
Execute trusted arbitrary Python through the embedded executor.
- inspect_ref(xmlid: str, *, database: str | None = None, timeout: float = 30.0) dict[str, Any][source]
Resolve one XMLID through the embedded Odoo runtime.
- inspect_cron(xmlid: str, *, trigger: bool = False, database: str | None = None, timeout: float = 30.0) dict[str, Any][source]
Inspect or explicitly trigger one cron job by XMLID.
- inspect_modules(*, state: str | None = None, names_only: bool = False, database: str | None = None, timeout: float = 30.0) dict[str, Any][source]
Return runtime addon inventory with inspect-command semantics.
- inspect_subtypes(model: str, *, database: str | None = None, timeout: float = 30.0) dict[str, Any][source]
List message subtypes registered for one model.
- inspect_model(model: str, *, database: str | None = None, timeout: float = 30.0) dict[str, Any][source]
Inspect runtime model registration metadata.
- inspect_field(model: str, field: str, *, with_db: bool = False, database: str | None = None, timeout: float = 30.0) dict[str, Any][source]
Inspect runtime field metadata.
- inspect_recordset(expression: str, *, database: str | None = None, timeout: float = 30.0) dict[str, Any][source]
Execute a trusted recordset expression as an inspection escape hatch.
- describe_table(table_name: str, *, database: str | None = None, timeout: float = 30.0) dict[str, Any][source]
Describe one PostgreSQL table through the live Odoo connection.
- describe_column(table_name: str, column_name: str, *, database: str | None = None, timeout: float = 30.0) dict[str, Any][source]
Describe one PostgreSQL column through the live Odoo connection.
- list_constraints(table_name: str, *, database: str | None = None, timeout: float = 30.0) dict[str, Any][source]
List PostgreSQL constraints for one table.
- list_tables(pattern: str | None = None, *, database: str | None = None, timeout: float = 30.0) dict[str, Any][source]
List PostgreSQL tables through the live Odoo connection.
- inspect_m2m(model: str, field: str, *, database: str | None = None, timeout: float = 30.0) dict[str, Any][source]
Inspect Many2many relation-table metadata.
- performance_table_scans(*, limit: int = 20, database: str | None = None, timeout: float = 30.0) dict[str, Any][source]
Return sequential scan metrics for PostgreSQL tables.
OdooQuery
- class oduit.OdooQuery(config: ConfigProvider | dict[str, Any])[source]
Bases:
objectRun structured read-only queries against an Odoo environment.
This API is intended for common read-heavy tasks where callers should not need to pass arbitrary Python code strings. It validates structured input, generates minimal trusted code internally, and relies on the executor’s default rollback behavior.
- query_model(model: str, domain: list[Any] | tuple[Any, ...] | None = None, fields: list[str] | tuple[str, ...] | None = None, limit: int = 80, include_total_count: bool = False, database: str | None = None, timeout: float = 30.0) dict[str, Any][source]
Search records and return read data for the matched result set.
- read_record(model: str, record_id: int, fields: list[str] | tuple[str, ...] | None = None, database: str | None = None, timeout: float = 30.0) dict[str, Any][source]
Read one record by id.
OdooCodeExecutor
- class oduit.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
ModuleManager
- class oduit.ModuleManager(addons_path: str)[source]
Bases:
objectManages Odoo module operations and dependency resolution.
- __init__(addons_path: str)[source]
Initialize ModuleManager.
- Parameters:
addons_path – Comma-separated string of addon directory paths
- static parse_cycle_error(message: str) list[str][source]
Extract the module cycle path from an error message.
- find_module_dirs(filter_dir: str | None = None) list[str][source]
Return all module directories with __manifest__.py in configured paths
- Parameters:
filter_dir – Optional directory name to filter results. Only modules in directories with exact basename match will be returned.
- Returns:
Sorted list of module directory names
- find_modules(filter_dir: str | None = None, skip_invalid: bool = False) ManifestCollection[source]
Return all modules with manifests in configured paths as a collection
- Parameters:
filter_dir – Optional directory name to filter results. Only modules in directories with exact basename match will be returned.
skip_invalid – If True, skip modules with invalid manifests instead of raising an exception
- Returns:
ManifestCollection containing all found modules
- Raises:
ManifestError – If a manifest is invalid and skip_invalid is False
- find_module_path(module_name: str) str | None[source]
Find the absolute path to a module within addons_path and Odoo base addons
- Parameters:
module_name – Name of the module to find
- Returns:
Absolute path to module directory or None if not found
- get_manifest(module_name: str) Manifest | None[source]
Get the manifest for a module.
- Parameters:
module_name – Name of the module to get manifest for
- Returns:
Manifest instance or None if module not found
- parse_manifest(module_name: str) dict[str, Any] | None[source]
Parse and return module’s __manifest__.py content.
- Parameters:
module_name – Name of the module to parse manifest for
- Returns:
Dictionary containing manifest data or None if not found
- Raises:
ValueError – If manifest exists but contains invalid Python syntax
Note
This method is maintained for backward compatibility. Consider using get_manifest() for new code.
- get_module_codependencies(module_name: str) list[str][source]
Get codependencies from module’s manifest ‘depends’ field.
Codependencies are modules that this module depends on, meaning changes to those modules may impact this module.
- Parameters:
module_name – Name of the module to get codependencies for
- Returns:
List of codependency module names, empty list if no codependencies or module not found
- get_direct_dependencies(*module_names: str) list[str][source]
Get direct dependencies needed to install a set of modules.
Direct dependencies are the minimal set of external modules (not in the provided set) needed to install the specified modules.
- Parameters:
*module_names – One or more module names to get direct dependencies for
- Returns:
Sorted list of module names that are direct dependencies (external to the provided set) needed for installation
Example
For modules a, b, c where: - a depends on [‘b’, ‘c’] - b depends on [‘crm’] - c depends on [‘mail’] get_direct_dependencies(‘a’, ‘b’, ‘c’) returns [‘crm’, ‘mail’]
- build_dependency_graph(module_name: str) dict[str, list[str]][source]
Build complete dependency graph for a module and all its codependencies.
- Parameters:
module_name – Name of the root module to build graph for
- Returns:
Dictionary mapping each module to its direct codependencies. Format: {module_name: [list_of_codependencies]}
- Raises:
ValueError – If circular dependency is detected
- get_dependency_tree(module_name: str, max_depth: int | None = None) dict[str, Any][source]
Get hierarchical dependency tree for a module.
- Parameters:
module_name – Name of the module to get dependency tree for
max_depth – Maximum depth to traverse (None for unlimited)
- Returns:
Nested dictionary representing the dependency tree. Format: {module_name: {codependency1: {subdeps…}, codependency2: {}}}
- Raises:
ValueError – If circular dependency is detected
- get_dependencies_at_depth(module_names: list[str], max_depth: int | None = None) list[str][source]
Get all dependencies up to a specified depth for a list of modules.
- Parameters:
module_names – List of module names to get dependencies for
max_depth – Maximum depth to traverse (None for unlimited)
- Returns:
Sorted list of unique dependency names (excluding input modules)
- get_install_order(*module_names: str) list[str][source]
Get the proper installation order for one or more modules and their codependencies.
Uses topological sorting to determine the correct order for installing modules such that all codependencies are installed before the modules that depend on them.
- Parameters:
*module_names – One or more module names to get install order for
- Returns:
List of module names in the order they should be installed. Codependencies come first, then modules that depend on them.
- Raises:
ValueError – If circular dependency is detected
- analyze_dependency_cycle(*module_names: str) dict[str, Any][source]
Return structured diagnostics for the first detected dependency cycle.
- find_missing_dependencies(module_name: str) list[str][source]
Find codependencies that are not available in the addons_path.
- Parameters:
module_name – Name of the module to check codependencies for
- Returns:
List of codependency names that could not be found in addons_path. Empty list if all codependencies are available.
- Raises:
ValueError – If circular dependency is detected during graph traversal
- get_reverse_dependencies(target_module: str) list[str][source]
Get all modules that directly or indirectly depend on the target module.
This method searches through all available modules to find which ones have the target module in their codependency chain.
- Parameters:
target_module – Name of the module to find reverse dependencies for
- Returns:
List of module names that depend on the target module. Empty list if no modules depend on the target.
- detect_odoo_series() OdooSeries | None[source]
Detect the Odoo series from available modules.
Scans all available modules and attempts to detect the Odoo series from their version strings.
- Returns:
OdooSeries if detected, None if unable to detect
- get_module_version_display(module_name: str, odoo_series: OdooSeries | None = None) str[source]
Get formatted version string for display in dependency trees.
- Parameters:
module_name – Name of the module
odoo_series – Detected Odoo series (if None, will try to detect)
- Returns:
“16.0+ce” for core CE addons
”16.0+ee” for core EE addons
”1.0.2” for custom addons (actual version)
”✘ not installed” for missing addons
- Return type:
Formatted version string
- get_formatted_dependency_tree(module_name: str, max_depth: int | None = None) list[str][source]
Get formatted dependency tree for display.
- Parameters:
module_name – Name of the module to get dependency tree for
max_depth – Maximum depth to traverse (None for unlimited)
- Returns:
List of formatted lines representing the dependency tree
- sort_modules(module_names: list[str], sorting: SortingChoice | str = SortingChoice.ALPHABETICAL) list[str][source]
Sort module names according to the specified sorting method.
- Parameters:
module_names – List of module names to sort
sorting – Sorting method - either ‘alphabetical’ or ‘topological’
- Returns:
Sorted list of module names
- Raises:
ValueError – If circular dependency is detected in topological sort
DemoProcessManager
- class oduit.DemoProcessManager(available_modules: list[str] | None = None)[source]
Bases:
BaseProcessManagerMock process manager for demo mode that simulates Odoo operations
- __init__(available_modules: list[str] | None = None)[source]
Initialize with list of available modules
- run_command(cmd: list[str], stop_on_error: bool = False, compact: bool = False, verbose: bool = False, suppress_output: bool = False) dict[str, Any][source]
Execute a command in demo mode with simulated behavior
- run_command_yielding(cmd: list[str], stop_on_error: bool = False, compact: bool = False, verbose: bool = False, suppress_output: bool = False) Generator[dict[str, Any], None, None][source]
Generator version that yields lines as they arrive for demo operations
- run_operation(command_operation: CommandOperation, verbose: bool = False, suppress_output: bool = False) dict[str, Any][source]
Execute a CommandOperation directly in demo mode.
This provides enhanced result processing with parsing for demo operations.
- Parameters:
command_operation – Structured command operation with metadata
verbose – Enable verbose output
suppress_output – Suppress output to console
- Returns:
Dict containing execution results
Utility Classes
OperationResult
- 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_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.
OutputFormatter
- class oduit.OutputFormatter(format_type: str = 'text', non_interactive: bool = False)[source]
Bases:
objectHandles different output formats and modes.
- output(message: str, level: str = 'info', data: dict[str, Any] | None = None) None[source]
Output a message in the configured format.
Exceptions
- exception oduit.exceptions.ConfigError[source]
Bases:
ExceptionError for missing configuration values
- exception oduit.exceptions.OdooOperationError(message: str, operation_result: dict | None = None)[source]
Bases:
ExceptionBase exception for Odoo operations
- exception oduit.exceptions.ModuleOperationError(message: str, operation_result: dict | None = None)[source]
Bases:
OdooOperationErrorBase exception for module operations
- exception oduit.exceptions.ModuleUpdateError(message: str, operation_result: dict | None = None)[source]
Bases:
ModuleOperationErrorRaised when module update fails
- exception oduit.exceptions.ModuleInstallError(message: str, operation_result: dict | None = None)[source]
Bases:
ModuleOperationErrorRaised when module installation fails
- exception oduit.exceptions.ModuleUninstallError(message: str, operation_result: dict | None = None)[source]
Bases:
ModuleOperationErrorRaised when module uninstall fails
- exception oduit.exceptions.ModuleNotFoundError(message: str, operation_result: dict | None = None)[source]
Bases:
ModuleOperationErrorRaised when module doesn’t exist
- exception oduit.exceptions.DatabaseOperationError(message: str, operation_result: dict | None = None)[source]
Bases:
OdooOperationErrorRaised when database operations fail