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

Handles 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.

has_local_config() bool[source]

Check if a local .oduit.toml file exists in current directory.

get_local_config_path() str[source]

Return the absolute path to the local .oduit.toml file.

resolve_config_path(env_name: str) tuple[str, str][source]

Resolve the config path and format for an environment name.

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

Load config from .oduit.toml in current directory.

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

get_available_environments() list[str][source]

Return a list of available environment names based on config files.

load_demo_config(sectioned: bool = False) dict[str, Any][source]

Load a demo configuration for testing without a real Odoo server

Parameters:

sectioned – If True, return configuration in sectioned format

Returns:

Dictionary with demo configuration including demo_mode=True flag

class oduit.Manifest(module_path: str)[source]

Bases: object

Represents 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).

property python_dependencies: list[str]

Get Python package dependencies.

property binary_dependencies: list[str]

Get system binary dependencies.

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

Get the raw manifest data dictionary.

is_installable() bool[source]

Check if the module is installable (alias for installable property).

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

validate_structure() list[str][source]

Validate the manifest structure and return any warnings.

Returns:

List of validation warnings (empty if no issues)

__str__() str[source]

String representation of the manifest.

__repr__() str[source]

Developer representation of the manifest.

exception oduit.ManifestError[source]

Bases: Exception

Base exception for manifest-related errors.

exception oduit.InvalidManifestError[source]

Bases: ManifestError

Raised when manifest contains invalid syntax or structure.

exception oduit.ManifestNotFoundError[source]

Bases: ManifestError

Raised when manifest file is not found.

class oduit.ManifestCollection[source]

Bases: object

Represents a collection of Odoo module manifests.

__init__() None[source]

Initialize an empty ManifestCollection.

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

__len__() int[source]

Get the number of manifests in the collection.

Returns:

Number of manifests

__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

keys() Iterator[str][source]

Get iterator over addon names.

Returns:

Iterator over addon names

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

validate_all() dict[str, list[str]][source]

Validate all manifests in the collection.

Returns:

Dictionary mapping addon names to lists of validation warnings (only includes addons with warnings)

clear() None[source]

Remove all manifests from the collection.

__str__() str[source]

String representation of the collection.

__repr__() str[source]

Developer representation of the collection.

class oduit.AddonsPathManager(addons_path: str)[source]

Bases: object

Manages 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

get_base_addons_paths() list[str][source]

Get auto-discovered base Odoo 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

get_manifest(module_name: str) Manifest | None[source]

Get the manifest for a module.

Parameters:

module_name – Name of the module

Returns:

Manifest instance or None if module not found

get_module_names(filter_dir: str | None = None) list[str][source]

Get sorted list of all module names.

Parameters:

filter_dir – Optional directory basename to filter by

Returns:

Sorted list of module names

class oduit.EnvironmentSource(name: str | None = None, source: str | None = None, config_path: str | None = None)[source]

Bases: DictModel

Source metadata for an inspected environment.

name: str | None = None
source: str | None = None
config_path: str | None = None
__init__(name: str | None = None, source: str | None = None, config_path: str | None = None) None
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: DictModel

Resolved binary metadata for an environment.

value: str | None = None
resolved_path: str | None = None
exists: bool = False
executable: bool = False
configured: bool = False
auto_detected: bool = False
__init__(value: str | None = None, resolved_path: str | None = None, exists: bool = False, executable: bool = False, configured: bool = False, auto_detected: bool = False) None
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: DictModel

Resolved addon path information.

configured: list[str]
base: list[str]
all: list[str]
valid: list[str]
invalid: list[str]
__init__(configured: list[str] = <factory>, base: list[str] = <factory>, all: list[str] = <factory>, valid: list[str] = <factory>, invalid: list[str] = <factory>) None
class oduit.OdooVersionInfo(version: str | None = None, series: str | None = None)[source]

Bases: DictModel

Resolved Odoo version and series information.

version: str | None = None
series: str | None = None
__init__(version: str | None = None, series: str | None = None) None
class oduit.DatabaseSummary(db_name: str | None = None, db_host: str | None = None, db_user: str | None = None)[source]

Bases: DictModel

Safe database configuration summary.

db_name: str | None = None
db_host: str | None = None
db_user: str | None = None
__init__(db_name: str | None = None, db_host: str | None = None, db_user: str | None = None) None
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: DictModel

Typed environment snapshot for planning and inspection.

environment: EnvironmentSource
resolved_binaries: dict[str, BinaryProbe]
addons_paths: AddonsPathStatus
odoo: OdooVersionInfo
database: DatabaseSummary
duplicate_modules: dict[str, list[str]]
available_module_count: int = 0
invalid_addon_paths: list[str]
missing_critical_config: list[str]
doctor_summary: dict[str, int]
doctor_checks: list[dict[str, Any]]
warnings: list[str]
remediation: list[str]
__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: DictModel

Tracked addon source file entry used for documentation freshness checks.

path: str
category: str
size_bytes: int | None = None
sha256: str | None = None
__init__(path: str, category: str, size_bytes: int | None = None, sha256: str | None = None) None
class oduit.DocumentationSourceSnapshot(algorithm: str = 'sha256', fingerprint: str = '', file_count: int = 0, files: list[DocumentationTrackedFile] = <factory>)[source]

Bases: DictModel

Deterministic fingerprint of addon source inputs.

algorithm: str = 'sha256'
fingerprint: str = ''
file_count: int = 0
files: list[DocumentationTrackedFile]
__init__(algorithm: str = 'sha256', fingerprint: str = '', file_count: int = 0, files: list[DocumentationTrackedFile] = <factory>) None
class oduit.DocumentationDocumentSnapshot(algorithm: str = 'sha256', fingerprint: str = '', size_bytes: int | None = None, line_count: int | None = None)[source]

Bases: DictModel

Fingerprint of the generated Markdown document.

algorithm: str = 'sha256'
fingerprint: str = ''
size_bytes: int | None = None
line_count: int | None = None
__init__(algorithm: str = 'sha256', fingerprint: str = '', size_bytes: int | None = None, line_count: int | None = None) None
class oduit.DocumentationDiagram(kind: str, title: str, format: str, content: str)[source]

Bases: DictModel

Rendered documentation diagram artifact.

kind: str
title: str
format: str
content: str
__init__(kind: str, title: str, format: str, content: str) None
class oduit.DocumentSection(title: str, markdown: str, summary: str = '', order: int = 0)[source]

Bases: DictModel

Rendered documentation section.

title: str
markdown: str
summary: str = ''
order: int = 0
__init__(title: str, markdown: str, summary: str = '', order: int = 0) None
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: DictModel

Resolved addon target for technical documentation workflows.

module: str
addon_root: str
target_kind: str
manifest_path: str
inside_configured_addons_path: bool = False
warnings: list[str]
candidate_addon_roots: list[str]
ambiguous: bool = False
__init__(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) None
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: DictModel

Compact per-addon summary for one shared model.

model: str
module: str
relation_kinds: list[str]
class_names: list[str]
added_fields: list[str]
added_methods: list[str]
source_paths: list[str]
line_hints: list[int]
shared_model_doc_path: str | None = None
shared_model_doc_anchor: str | None = None
__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: DictModel

Compact file entry used for technical documentation inventories.

path: str
category: str
size_bytes: int | None = None
__init__(path: str, category: str, size_bytes: int | None = None) None
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: DictModel

Compact XML inventory record for addon-local architecture evidence.

path: str
record_id: str | None = None
model: str | None = None
name: str | None = None
xml_tag: str = 'record'
attributes: dict[str, str]
__init__(path: str, record_id: str | None = None, model: str | None = None, name: str | None = None, xml_tag: str = 'record', attributes: dict[str, str]=<factory>) None
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: DictModel

Static controller route discovered from addon Python sources.

path: str
class_name: str
method_name: str
route: str | list[str]
auth: str | None = None
route_type: str | None = None
methods: list[str]
csrf: bool | None = None
website: bool | None = None
line_hint: int | None = None
__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: DictModel

Read-only technical inventory for addon-local architecture docs.

module: str
addon_root: str
files: list[AddonTechnicalFile]
xml_records: list[AddonXmlRecord]
http_routes: list[AddonHttpRoute]
security_files: list[str]
migration_files: list[str]
todo_markers: list[SourceEvidence]
warnings: list[str]
remediation: list[str]
__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: DictModel

Combined static and runtime addon summary for onboarding workflows.

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]]
depends: list[str]
reverse_dependencies: list[str]
reverse_dependency_count: int = 0
missing_dependencies: list[str]
installable: bool = False
auto_install: bool = False
models: list[str]
inherit_models: list[str]
model_count: int = 0
test_cases: list[AddonTestFile]
test_count: int = 0
languages: list[str]
installed_state: AddonInstallState | None = None
warnings: list[str]
remediation: list[str]
__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: DictModel

Typed runtime install-state lookup result for one addon.

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
__init__(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) None
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: DictModel

Typed addon inspection payload.

module: str
exists: bool
module_path: str | None
addon_type: str
version_display: str
manifest: dict[str, Any]
manifest_fields: list[str]
direct_dependencies: list[str]
reverse_dependencies: list[str]
reverse_dependency_count: int = 0
install_order_slice: list[str]
install_order_available: bool = False
dependency_cycle: list[str]
missing_dependencies: list[str]
impacted_modules: list[str]
series: str | None = None
python_dependencies: list[str]
binary_dependencies: list[str]
warnings: list[str]
remediation: list[str]
__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: DictModel

Per-model documentation detail inside one addon bundle.

model: str
relation_kinds: list[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: DictModel

Documentation bundle for one addon.

module: str
database: str | None = None
source_only: bool = False
addon_info: AddonInfo | None = None
dependency_graph: dict[str, Any]
model_inventory: AddonModelInventory | None = None
models: list[AddonDocumentationModel]
shared_model_contributions: list[AddonContributionSummary]
recommended_tests: dict[str, Any]
diagrams: list[DocumentationDiagram]
sections: list[DocumentSection]
output_path: str | None = None
markdown: str = ''
warnings: list[str]
remediation: list[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: DictModel

Documentation bundle for one model.

model: str
database: str | None = None
source_only: bool = False
field_attributes: list[str]
requested_view_types: list[str]
extension_inventory: ModelExtensionInventory | None = None
field_metadata: ModelFieldsResult | None = None
view_inventory: ModelViewInventory | None = None
diagrams: list[DocumentationDiagram]
sections: list[DocumentSection]
markdown: str = ''
warnings: list[str]
remediation: list[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: DictModel

Full shared-model documentation within a multi-addon bundle.

model: str
owning_modules: list[str]
contributing_modules: list[str]
documentation: ModelDocumentation | None = None
output_path: str | None = None
markdown: str = ''
__init__(model: str, owning_modules: list[str] = <factory>, contributing_modules: list[str] = <factory>, documentation: ModelDocumentation | None = None, output_path: str | None = None, markdown: str = '') None
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: DictModel

Documentation bundle for multiple addons in one selected scope.

modules: list[str]
database: str | None = None
source_only: bool = False
addon_docs: list[AddonDocumentation]
shared_models: list[SharedModelDocumentation]
index_markdown: str = ''
warnings: list[str]
remediation: list[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: DictModel

Documentation bundle for addon dependency graphs.

modules: list[str]
database: str | None = None
source_only: bool = False
installed_only: bool = False
transitive: bool = True
dependency_graph: dict[str, Any]
installed_addons: InstalledAddonInventory | None = None
diagrams: list[DocumentationDiagram]
sections: list[DocumentSection]
markdown: str = ''
warnings: list[str]
remediation: list[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: DictModel

Typed architecture-documentation bundle for one addon.

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]
markdown: str = ''
warnings: list[str]
remediation: list[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: DictModel

Durable 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'
created_at: str | None = None
last_generated_at: str | None = None
generation_count: int = 0
generator: dict[str, str]
generation_options: dict[str, Any]
evidence_counts: dict[str, int]
source_snapshot: DocumentationSourceSnapshot | None = None
document_snapshot: DocumentationDocumentSnapshot | None = None
generated_blocks: list[TechnicalDocumentationGeneratedBlock]
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]
__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: DictModel

Current 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
stale_generated_blocks: list[str]
edited_generated_blocks: list[str]
missing_generated_blocks: list[str]
unknown_generated_blocks: list[str]
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, Any]
evidence_counts: dict[str, int]
template: str | None = None
changed_files: list[str]
added_files: list[str]
removed_files: list[str]
warnings: list[str]
remediation: list[str]
__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: DictModel

One runtime addon record read from ir.module.module.

module: str
state: str
installed: bool
shortdesc: str | None = None
application: bool | None = None
auto_install: bool | None = None
__init__(module: str, state: str, installed: bool, shortdesc: str | None = None, application: bool | None = None, auto_install: bool | None = None) None
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: DictModel

Typed runtime addon inventory for one environment.

success: bool
operation: str
addons: list[InstalledAddonRecord]
total: int = 0
states: list[str]
modules_filter: list[str]
database: str | None = None
error: str | None = None
error_type: str | None = None
warnings: list[str]
remediation: list[str]
__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: DictModel

Typed read-only update planning payload.

module: str
exists: bool
impact_set: list[str]
impact_count: int = 0
missing_dependencies: list[str]
duplicate_name_risk: bool = False
duplicate_module_locations: list[str]
dependency_cycle: list[str]
cycle_risk: bool = False
ordering_constraints: list[str]
recommended_sequence: list[str]
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]
verification_steps: list[str]
inspection: AddonInspection | None = None
warnings: list[str]
remediation: list[str]
__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: DictModel

Ranked 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]
line_hint: int | None = None
reason: str | None = None
__init__(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) None
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: DictModel

Static source localization result for one addon/model pair.

model: str
module: str
addon_root: str
resolution: str = 'not_found'
ambiguous: bool = False
ambiguity_reason: str | None = None
candidates: list[ModelSourceCandidate]
scanned_python_files: list[str]
warnings: list[str]
remediation: list[str]
__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: DictModel

Ranked 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]
line_hint: int | None = None
reason: str | None = None
__init__(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) None
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: DictModel

Static 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
ambiguity_reason: str | None = None
source_exists: bool = False
runtime_exists: bool | None = None
runtime_only: bool = False
runtime_source_modules: list[str]
candidates: list[FieldSourceCandidate]
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]
scanned_python_files: list[str]
rationale: str | None = None
warnings: list[str]
remediation: list[str]
__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: DictModel

Ranked addon test file entry.

path: str
test_type: str
references_model: bool = False
references_field: bool = False
confidence: float = 0.0
ranking_signals: list[str]
related_paths: list[str]
__init__(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>) None
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: DictModel

Static addon test inventory for coding-agent test selection.

module: str
addon_root: str
model: str | None = None
field: str | None = None
tests: list[AddonTestFile]
warnings: list[str]
remediation: list[str]
__init__(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>) None
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: DictModel

Typed wrapper for OdooQuery.query_model() results.

success: bool
operation: str
model: str
domain: list[Any]
fields: list[str] | None = None
limit: int = 0
count: int = 0
total_count: int | None = None
limited: bool = False
ids: list[int]
records: list[dict[str, Any]]
database: str | None = None
error: str | None = None
error_type: str | None = None
classmethod from_dict(result: dict[str, Any]) QueryModelResult[source]

Create a typed result from a raw OdooQuery dictionary.

__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: DictModel

Typed wrapper for OdooQuery.read_record() results.

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
classmethod from_dict(result: dict[str, Any]) RecordReadResult[source]

Create a typed result from a raw OdooQuery dictionary.

__init__(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) None
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: DictModel

Typed wrapper for OdooQuery.search_count() results.

success: bool
operation: str
model: str
domain: list[Any]
count: int = 0
database: str | None = None
error: str | None = None
error_type: str | None = None
classmethod from_dict(result: dict[str, Any]) SearchCountResult[source]

Create a typed result from a raw OdooQuery dictionary.

__init__(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) None
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: DictModel

Typed wrapper for OdooQuery.get_model_fields() results.

success: bool
operation: str
model: str
attributes: list[str] | None = None
module: str | None = None
field_names: list[str]
field_definitions: dict[str, dict[str, Any]]
database: str | None = None
error: str | None = None
error_type: str | None = None
classmethod from_dict(result: dict[str, Any]) ModelFieldsResult[source]

Create a typed result from a raw OdooQuery dictionary.

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

Manages 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.ConfigError[source]

Bases: Exception

Error for missing configuration values

exception oduit.OdooOperationError(message: str, operation_result: dict | None = None)[source]

Bases: Exception

Base exception for Odoo operations

__init__(message: str, operation_result: dict | None = None)[source]
exception oduit.ModuleOperationError(message: str, operation_result: dict | None = None)[source]

Bases: OdooOperationError

Base exception for module operations

exception oduit.ModuleUpdateError(message: str, operation_result: dict | None = None)[source]

Bases: ModuleOperationError

Raised when module update fails

exception oduit.ModuleInstallError(message: str, operation_result: dict | None = None)[source]

Bases: ModuleOperationError

Raised when module installation fails

exception oduit.ModuleUninstallError(message: str, operation_result: dict | None = None)[source]

Bases: ModuleOperationError

Raised when module uninstall fails

exception oduit.ModuleNotFoundError(message: str, operation_result: dict | None = None)[source]

Bases: ModuleOperationError

Raised when module doesn’t exist

exception oduit.DatabaseOperationError(message: str, operation_result: dict | None = None)[source]

Bases: OdooOperationError

Raised when database operations fail

class oduit.DemoProcessManager(available_modules: list[str] | None = None)[source]

Bases: BaseProcessManager

Mock 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

run_shell_command(cmd: list[str] | str, verbose: bool = False, capture_output: bool = True, allow_shell: bool = False, input_data: str | None = None) dict[str, Any][source]

Simulate shell command execution

static run_interactive_shell(cmd: list[str]) int[source]

Simulate interactive shell - just print a message

class oduit.OdooOperations(env_config: dict, verbose: bool = False)[source]

Bases: object

Compatibility facade over smaller internal Odoo operation services.

__init__(env_config: dict, verbose: bool = False)[source]
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.

performance_slow_queries(*, limit: int = 10, database: str | None = None, timeout: float = 30.0) dict[str, Any][source]

Return slow-query metrics from pg_stat_statements when available.

performance_indexes(*, limit: int = 20, database: str | None = None, timeout: float = 30.0) dict[str, Any][source]

Return basic index-usage metrics for PostgreSQL tables.

class oduit.OdooEmbeddedManager(config_provider: ConfigProvider)[source]

Bases: object

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

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

Expose structured Odoo inspection workflows for CLI and Python callers.

__init__(config: ConfigProvider | dict[str, Any])[source]
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.subtype records 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.

performance_slow_queries(*, limit: int = 10, database: str | None = None, timeout: float = 30.0) dict[str, Any][source]

Return the slowest statements from pg_stat_statements when available.

performance_indexes(*, limit: int = 20, database: str | None = None, timeout: float = 30.0) dict[str, Any][source]

Return simple table index-usage metrics.

class oduit.OdooQuery(config: ConfigProvider | dict[str, Any])[source]

Bases: object

Run 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.

__init__(config: ConfigProvider | dict[str, Any])[source]
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.

search_count(model: str, domain: list[Any] | tuple[Any, ...] | None = None, database: str | None = None, timeout: float = 30.0) dict[str, Any][source]

Return the number of records matching a domain.

get_model_fields(model: str, attributes: list[str] | tuple[str, ...] | None = None, module: str | None = None, database: str | None = None, timeout: float = 30.0) dict[str, Any][source]

Inspect field metadata for a model via fields_get.

class oduit.ProcessManager[source]

Bases: BaseProcessManager

Cross-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'])
__init__() None[source]
clear_sudo_password() None[source]

Clear cached sudo password from memory.

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

static run_interactive_shell(cmd: list[str]) int[source]

Run an interactive shell command with proper cross-platform handling

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

Bases: object

Builder class for creating standardized operation results

ANSI_ESCAPE_PATTERN = re.compile('\\x1b\\[[0-9;]*m')
TIMESTAMP_PREFIX_PATTERN = re.compile('^\\d{4}-\\d{2}-\\d{2}\\s+\\d{2}:\\d{2}:\\d{2},\\d{3}\\s+\\d+\\s+')
FAILURE_LOG_PREFIX_PATTERN = re.compile('^(?:INFO|WARNING|ERROR|DEBUG)\\s+\\S+\\s+[\\w]+\\.[\\w.]+:\\s*')
FAILURE_PATTERNS = {'generic': [re.compile('ERROR.*FATAL'), re.compile('CRITICAL'), re.compile('Exception.*Traceback')], 'install': [re.compile('odoo\\.modules\\.loading: invalid module names, ignored:'), re.compile('odoo\\.modules\\.loading: Some modules are not loaded, some dependencies or manifest may be missing:'), re.compile('ModuleNotFoundError'), re.compile('No module named'), re.compile('ImportError')], 'test': [re.compile('odoo\\.modules\\.loading: invalid module names, ignored:'), re.compile('odoo\\.modules\\.loading: Some modules are not loaded, some dependencies or manifest may be missing:'), re.compile('ModuleNotFoundError'), re.compile('No module named'), re.compile('ImportError')], 'update': [re.compile('odoo\\.modules\\.loading: invalid module names, ignored:'), re.compile('odoo\\.modules\\.loading: Some modules are not loaded, some dependencies or manifest may be missing:'), re.compile('ModuleNotFoundError'), re.compile('No module named'), re.compile('ImportError')]}

Builder class for creating standardized operation results

__init__(operation: str | None = None, module: str | None = None, database: str | None = None)[source]
classmethod from_operation(command_operation: CommandOperation) OperationResult[source]

Factory method to create OperationResult from CommandOperation.

set_success(success: bool, return_code: int = 0) OperationResult[source]
set_new_operation(operation: str) OperationResult[source]
set_operation(operation: str) OperationResult[source]
set_command(command: list[str]) OperationResult[source]
set_output(stdout: str = '', stderr: str = '') OperationResult[source]
set_module(module: str) OperationResult[source]
set_database(database: str) OperationResult[source]
set_addon_name(addon_name: str) OperationResult[source]
set_addons(addons: list[str]) OperationResult[source]
set_error(error: str, error_type: str | None = None) OperationResult[source]
set_custom_data(**kwargs: Any) OperationResult[source]

Add operation-specific data

parse_and_merge_install_results(output: str, **additional_data: Any) OperationResult[source]

Parse install output and merge with existing custom data

parse_and_merge_test_results(output: str, **additional_data: Any) OperationResult[source]

Parse test output and merge with existing custom data

process_with_parsers(output: str, **additional_data: Any) OperationResult[source]

Automatically select and apply appropriate parsers based on operation metadata.

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

Convert ProcessManager result to our standard format

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

Complete the result and return the dictionary

class oduit.OutputFormatter(format_type: str = 'text', non_interactive: bool = False)[source]

Bases: object

Handles different output formats and modes.

__init__(format_type: str = 'text', non_interactive: bool = False)[source]
output(message: str, level: str = 'info', data: dict[str, Any] | None = None) None[source]

Output a message in the configured format.

print_result(data: dict[str, Any], message: str = 'Operation completed') None[source]

Print operation result with data.

print_error_result(error_msg: str, error_code: int = 1) None[source]

Print error result and exit with code.

oduit.configure_output(format_type: str = 'text', non_interactive: bool = False) None[source]

Configure the global output formatter.

oduit.print_info(msg: str, data: dict[str, Any] | None = None) None[source]
oduit.print_success(msg: str, data: dict[str, Any] | None = None) None[source]
oduit.print_warning(msg: str, data: dict[str, Any] | None = None) None[source]
oduit.print_error(msg: str, data: dict[str, Any] | None = None) None[source]
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: BaseProcessManager

Cross-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'])
__init__() None[source]
clear_sudo_password() None[source]

Clear cached sudo password from memory.

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

static run_interactive_shell(cmd: list[str]) int[source]

Run an interactive shell command with proper cross-platform handling

ConfigLoader

class oduit.ConfigLoader(config_dir: str | None = None)[source]

Bases: object

Handles 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.

has_local_config() bool[source]

Check if a local .oduit.toml file exists in current directory.

get_local_config_path() str[source]

Return the absolute path to the local .oduit.toml file.

resolve_config_path(env_name: str) tuple[str, str][source]

Resolve the config path and format for an environment name.

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

Load config from .oduit.toml in current directory.

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

get_available_environments() list[str][source]

Return a list of available environment names based on config files.

load_demo_config(sectioned: bool = False) dict[str, Any][source]

Load a demo configuration for testing without a real Odoo server

Parameters:

sectioned – If True, return configuration in sectioned format

Returns:

Dictionary with demo configuration including demo_mode=True flag

OdooOperations

class oduit.OdooOperations(env_config: dict, verbose: bool = False)[source]

Bases: object

Compatibility facade over smaller internal Odoo operation services.

__init__(env_config: dict, verbose: bool = False)[source]
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.

performance_slow_queries(*, limit: int = 10, database: str | None = None, timeout: float = 30.0) dict[str, Any][source]

Return slow-query metrics from pg_stat_statements when available.

performance_indexes(*, limit: int = 20, database: str | None = None, timeout: float = 30.0) dict[str, Any][source]

Return basic index-usage metrics for PostgreSQL tables.

OdooQuery

class oduit.OdooQuery(config: ConfigProvider | dict[str, Any])[source]

Bases: object

Run 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.

__init__(config: ConfigProvider | dict[str, Any])[source]
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.

search_count(model: str, domain: list[Any] | tuple[Any, ...] | None = None, database: str | None = None, timeout: float = 30.0) dict[str, Any][source]

Return the number of records matching a domain.

get_model_fields(model: str, attributes: list[str] | tuple[str, ...] | None = None, module: str | None = None, database: str | None = None, timeout: float = 30.0) dict[str, Any][source]

Inspect field metadata for a model via fields_get.

OdooCodeExecutor

class oduit.OdooCodeExecutor(config_provider: ConfigProvider)[source]

Bases: object

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

Manages 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: BaseProcessManager

Mock 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

run_shell_command(cmd: list[str] | str, verbose: bool = False, capture_output: bool = True, allow_shell: bool = False, input_data: str | None = None) dict[str, Any][source]

Simulate shell command execution

static run_interactive_shell(cmd: list[str]) int[source]

Simulate interactive shell - just print a message

Utility Classes

OperationResult

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

Bases: object

Builder class for creating standardized operation results

ANSI_ESCAPE_PATTERN = re.compile('\\x1b\\[[0-9;]*m')
TIMESTAMP_PREFIX_PATTERN = re.compile('^\\d{4}-\\d{2}-\\d{2}\\s+\\d{2}:\\d{2}:\\d{2},\\d{3}\\s+\\d+\\s+')
FAILURE_LOG_PREFIX_PATTERN = re.compile('^(?:INFO|WARNING|ERROR|DEBUG)\\s+\\S+\\s+[\\w]+\\.[\\w.]+:\\s*')
FAILURE_PATTERNS = {'generic': [re.compile('ERROR.*FATAL'), re.compile('CRITICAL'), re.compile('Exception.*Traceback')], 'install': [re.compile('odoo\\.modules\\.loading: invalid module names, ignored:'), re.compile('odoo\\.modules\\.loading: Some modules are not loaded, some dependencies or manifest may be missing:'), re.compile('ModuleNotFoundError'), re.compile('No module named'), re.compile('ImportError')], 'test': [re.compile('odoo\\.modules\\.loading: invalid module names, ignored:'), re.compile('odoo\\.modules\\.loading: Some modules are not loaded, some dependencies or manifest may be missing:'), re.compile('ModuleNotFoundError'), re.compile('No module named'), re.compile('ImportError')], 'update': [re.compile('odoo\\.modules\\.loading: invalid module names, ignored:'), re.compile('odoo\\.modules\\.loading: Some modules are not loaded, some dependencies or manifest may be missing:'), re.compile('ModuleNotFoundError'), re.compile('No module named'), re.compile('ImportError')]}

Builder class for creating standardized operation results

__init__(operation: str | None = None, module: str | None = None, database: str | None = None)[source]
result: dict[str, Any]
classmethod from_operation(command_operation: CommandOperation) OperationResult[source]

Factory method to create OperationResult from CommandOperation.

set_success(success: bool, return_code: int = 0) OperationResult[source]
set_new_operation(operation: str) OperationResult[source]
set_operation(operation: str) OperationResult[source]
set_command(command: list[str]) OperationResult[source]
set_output(stdout: str = '', stderr: str = '') OperationResult[source]
set_module(module: str) OperationResult[source]
set_database(database: str) OperationResult[source]
set_addon_name(addon_name: str) OperationResult[source]
set_addons(addons: list[str]) OperationResult[source]
set_error(error: str, error_type: str | None = None) OperationResult[source]
set_custom_data(**kwargs: Any) OperationResult[source]

Add operation-specific data

parse_and_merge_install_results(output: str, **additional_data: Any) OperationResult[source]

Parse install output and merge with existing custom data

parse_and_merge_test_results(output: str, **additional_data: Any) OperationResult[source]

Parse test output and merge with existing custom data

process_with_parsers(output: str, **additional_data: Any) OperationResult[source]

Automatically select and apply appropriate parsers based on operation metadata.

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

Convert ProcessManager result to our standard format

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

Complete the result and return the dictionary

OutputFormatter

class oduit.OutputFormatter(format_type: str = 'text', non_interactive: bool = False)[source]

Bases: object

Handles different output formats and modes.

__init__(format_type: str = 'text', non_interactive: bool = False)[source]
output(message: str, level: str = 'info', data: dict[str, Any] | None = None) None[source]

Output a message in the configured format.

print_result(data: dict[str, Any], message: str = 'Operation completed') None[source]

Print operation result with data.

print_error_result(error_msg: str, error_code: int = 1) None[source]

Print error result and exit with code.

Exceptions

exception oduit.exceptions.ConfigError[source]

Bases: Exception

Error for missing configuration values

exception oduit.exceptions.OdooOperationError(message: str, operation_result: dict | None = None)[source]

Bases: Exception

Base exception for Odoo operations

__init__(message: str, operation_result: dict | None = None)[source]
exception oduit.exceptions.ModuleOperationError(message: str, operation_result: dict | None = None)[source]

Bases: OdooOperationError

Base exception for module operations

exception oduit.exceptions.ModuleUpdateError(message: str, operation_result: dict | None = None)[source]

Bases: ModuleOperationError

Raised when module update fails

exception oduit.exceptions.ModuleInstallError(message: str, operation_result: dict | None = None)[source]

Bases: ModuleOperationError

Raised when module installation fails

exception oduit.exceptions.ModuleUninstallError(message: str, operation_result: dict | None = None)[source]

Bases: ModuleOperationError

Raised when module uninstall fails

exception oduit.exceptions.ModuleNotFoundError(message: str, operation_result: dict | None = None)[source]

Bases: ModuleOperationError

Raised when module doesn’t exist

exception oduit.exceptions.DatabaseOperationError(message: str, operation_result: dict | None = None)[source]

Bases: OdooOperationError

Raised when database operations fail