ModuleManager

ModuleManager is the addon discovery and dependency-analysis API.

class oduit.module_manager.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

Usage Examples

from oduit import ConfigLoader, ModuleManager

loader = ConfigLoader()
config = loader.load_config("dev")
manager = ModuleManager(config["addons_path"])

addons = manager.find_modules()
sale_manifest = manager.get_manifest("sale")
install_order = manager.get_install_order("sale", "purchase")
reverse_deps = manager.get_reverse_dependencies("sale")

Key Methods

  • find_module_dirs() and find_modules(): discover addons from addons_path

  • find_module_path() and get_manifest(): inspect one addon

  • get_module_codependencies(): direct manifest depends entries; maintained for compatibility with older naming

  • get_direct_dependencies(): direct external dependencies for one or more target addons

  • get_dependency_tree() and get_formatted_dependency_tree(): dependency trees

  • get_install_order(): dependency-resolved installation order

  • get_reverse_dependencies(): reverse-dependency and update impact analysis

  • find_missing_dependencies(): missing addon detection

  • detect_odoo_series(): infer Odoo series from addon versions