Skip to content

Store

store

SQLite persistence layer for the Odoo KB.

Two databases, same schema: - kb_global.db : Odoo community + enterprise, generated once per version. - kb_project.db : global + third-party + apik, scoped to a project.

Schema (v3)

meta (key, value) sources (origin, path) modules (name, origin, depends) -- depends is a JSON array string symbols (model, name, kind, origin, module, source_file, source_line, field_type, section) field_refs (model, field_name, module, kwarg, target_method) model_origins (model, module, origin, role, model_type, inherit_json, inherits_json, source_file, source_line) role: 'create' | 'extend' | 'prototype' model_type: 'model' | 'transient' | 'abstract'

Indexes

idx_symbols_lookup on symbols(model, name, kind) idx_symbols_module on symbols(module) idx_modules_origin on modules(origin) idx_field_refs_target on field_refs(model, target_method) idx_model_origins_model on model_origins(model) idx_model_origins_role on model_origins(model, role)

Classes:

Name Description
KBReader

Read-only interface to a KB SQLite database.

Functions:

Name Description
write_global_kb

Write (or overwrite) a global KB database.

write_project_kb

Write (or overwrite) a project KB database.

KBReader

KBReader(db_path: Path)

Read-only interface to a KB SQLite database.

Use as a context manager or call close() explicitly when done::

with KBReader(Path(".oops-cache/kb_project.db")) as kb:
    entries = kb.get_symbol("sale.order", "action_confirm", "method")
    modules = kb.get_modules()

Methods:

Name Description
__enter__

Return self for use as a context manager.

__exit__

Close the connection on context-manager exit.

close

Close the underlying SQLite connection.

get_field_refs_for_field

Return kwargs and target methods referenced by a field.

get_field_refs_for_method

Return field references that target a specific method.

get_meta

Return all meta key/value pairs.

get_model_creators

Return all modules recorded as creators of model.

get_model_origin

Return the role of module for model, or None if absent.

get_model_symbols

Return all symbols defined on a model across all upstream modules.

get_modules

Return all modules indexed by name.

get_sources

Return all indexed source roots.

get_symbol

Return all KB entries for a symbol (may span multiple modules).

is_model_creator

Return True if module is a creator (or prototype source) of model.

model_exists

Return True if any upstream module defines or extends this model.

module_exists

Return True if the named module is present in the KB.

symbol_exists

Return True if the symbol exists in any upstream module.

Source code in src/oops/kb/store.py
def __init__(self, db_path: Path) -> None:
    if not db_path.exists():
        raise FileNotFoundError(f"KB database not found: {db_path}")
    self._con = sqlite3.connect(str(db_path))
    self._con.row_factory = sqlite3.Row

__enter__

__enter__() -> KBReader

Return self for use as a context manager.

Source code in src/oops/kb/store.py
def __enter__(self) -> "KBReader":
    """Return self for use as a context manager."""
    return self

__exit__

__exit__(*_: Any) -> None

Close the connection on context-manager exit.

Source code in src/oops/kb/store.py
def __exit__(self, *_: Any) -> None:
    """Close the connection on context-manager exit."""
    self.close()

close

close() -> None

Close the underlying SQLite connection.

Source code in src/oops/kb/store.py
def close(self) -> None:
    """Close the underlying SQLite connection."""
    self._con.close()

get_field_refs_for_field

get_field_refs_for_field(model: str, field_name: str, module: Optional[str] = None) -> List[Dict[str, Any]]

Return kwargs and target methods referenced by a field.

Parameters:

Name Type Description Default
model
str

Dotted model name.

required
field_name
str

Field name to look up.

required
module
Optional[str]

Optional module filter; when given, only entries from that module are returned.

None

Returns:

Type Description
List[Dict[str, Any]]

List of {"kwarg": str, "target_method": str, "module": str} dicts.

Source code in src/oops/kb/store.py
def get_field_refs_for_field(
    self, model: str, field_name: str, module: Optional[str] = None
) -> List[Dict[str, Any]]:
    """Return kwargs and target methods referenced by a field.

    Args:
        model: Dotted model name.
        field_name: Field name to look up.
        module: Optional module filter; when given, only entries from that
            module are returned.

    Returns:
        List of ``{"kwarg": str, "target_method": str, "module": str}`` dicts.
    """
    if module is None:
        rows = self._con.execute(
            "SELECT kwarg, target_method, module FROM field_refs "
            "WHERE model=? AND field_name=? ORDER BY module, kwarg",
            (model, field_name),
        ).fetchall()
    else:
        rows = self._con.execute(
            "SELECT kwarg, target_method, module FROM field_refs "
            "WHERE model=? AND field_name=? AND module=? ORDER BY kwarg",
            (model, field_name, module),
        ).fetchall()
    return [dict(r) for r in rows]

get_field_refs_for_method

get_field_refs_for_method(model: str, target_method: str) -> List[Dict[str, Any]]

Return field references that target a specific method.

Parameters:

Name Type Description Default
model
str

Dotted model name.

required
target_method
str

Method name to look up.

required

Returns:

Type Description
List[Dict[str, Any]]

List of {"module": str, "field_name": str, "kwarg": str} dicts,

List[Dict[str, Any]]

sorted by module, kwarg, and field name.

Source code in src/oops/kb/store.py
def get_field_refs_for_method(
    self, model: str, target_method: str
) -> List[Dict[str, Any]]:
    """Return field references that target a specific method.

    Args:
        model: Dotted model name.
        target_method: Method name to look up.

    Returns:
        List of ``{"module": str, "field_name": str, "kwarg": str}`` dicts,
        sorted by module, kwarg, and field name.
    """
    rows = self._con.execute(
        """
        SELECT module, field_name, kwarg
        FROM   field_refs
        WHERE  model = ? AND target_method = ?
        ORDER  BY module, kwarg, field_name
        """,
        (model, target_method),
    ).fetchall()
    return [dict(r) for r in rows]

get_meta

get_meta() -> Dict[str, str]

Return all meta key/value pairs.

Returns:

Type Description
Dict[str, str]

Dict mapping meta key to its string value.

Source code in src/oops/kb/store.py
def get_meta(self) -> Dict[str, str]:
    """Return all meta key/value pairs.

    Returns:
        Dict mapping meta key to its string value.
    """
    rows = self._con.execute("SELECT key, value FROM meta").fetchall()
    return {r["key"]: r["value"] for r in rows}

get_model_creators

get_model_creators(model: str) -> List[Dict[str, Any]]

Return all modules recorded as creators of model.

Parameters:

Name Type Description Default
model
str

Dotted model name.

required

Returns:

Type Description
List[Dict[str, Any]]

List of {"module", "origin", "source_file", "source_line"} dicts.

Source code in src/oops/kb/store.py
def get_model_creators(self, model: str) -> List[Dict[str, Any]]:
    """Return all modules recorded as creators of ``model``.

    Args:
        model: Dotted model name.

    Returns:
        List of ``{"module", "origin", "source_file", "source_line"}`` dicts.
    """
    rows = self._con.execute(
        """
        SELECT module, origin, source_file, source_line
        FROM   model_origins
        WHERE  model = ? AND role IN ('create', 'prototype')
        ORDER  BY origin, module
        """,
        (model,),
    ).fetchall()
    return [dict(r) for r in rows]

get_model_origin

get_model_origin(model: str, module: str) -> Optional[str]

Return the role of module for model, or None if absent.

Parameters:

Name Type Description Default
model
str

Dotted model name.

required
module
str

Module name.

required

Returns:

Type Description
Optional[str]

'create', 'extend', 'prototype', or None.

Source code in src/oops/kb/store.py
def get_model_origin(self, model: str, module: str) -> Optional[str]:
    """Return the role of ``module`` for ``model``, or None if absent.

    Args:
        model: Dotted model name.
        module: Module name.

    Returns:
        ``'create'``, ``'extend'``, ``'prototype'``, or ``None``.
    """
    row = self._con.execute(
        "SELECT role FROM model_origins WHERE model = ? AND module = ?",
        (model, module),
    ).fetchone()
    return row["role"] if row else None

get_model_symbols

get_model_symbols(model: str, kind: Optional[str] = None) -> List[Dict[str, Any]]

Return all symbols defined on a model across all upstream modules.

Parameters:

Name Type Description Default
model
str

dotted model name.

required
kind
Optional[str]

optional filter — 'field' or 'method'.

None
Source code in src/oops/kb/store.py
def get_model_symbols(
    self,
    model: str,
    kind: Optional[str] = None,
) -> List[Dict[str, Any]]:
    """Return all symbols defined on a model across all upstream modules.

    Args:
        model: dotted model name.
        kind:  optional filter — 'field' or 'method'.
    """
    if kind:
        rows = self._con.execute(
            """
            SELECT name, kind, origin, module, source_file, source_line,
                   field_type, section
            FROM   symbols
            WHERE  model = ? AND kind = ?
            ORDER  BY name
            """,
            (model, kind),
        ).fetchall()
    else:
        rows = self._con.execute(
            """
            SELECT name, kind, origin, module, source_file, source_line,
                   field_type, section
            FROM   symbols
            WHERE  model = ?
            ORDER  BY kind, name
            """,
            (model,),
        ).fetchall()
    return [dict(r) for r in rows]

get_modules

get_modules() -> Dict[str, Dict[str, Any]]

Return all modules indexed by name.

Returns:

Type Description
Dict[str, Dict[str, Any]]

Mapping of module name to {"origin": str, "depends": [str, ...]}.

Source code in src/oops/kb/store.py
def get_modules(self) -> Dict[str, Dict[str, Any]]:
    """Return all modules indexed by name.

    Returns:
        Mapping of module name to ``{"origin": str, "depends": [str, ...]}``.
    """
    rows = self._con.execute("SELECT name, origin, depends FROM modules").fetchall()
    return {
        r["name"]: {
            "origin": r["origin"],
            "depends": json.loads(r["depends"]),
        }
        for r in rows
    }

get_sources

get_sources() -> Dict[str, str]

Return all indexed source roots.

Returns:

Type Description
Dict[str, str]

Mapping of origin to absolute path string.

Source code in src/oops/kb/store.py
def get_sources(self) -> Dict[str, str]:
    """Return all indexed source roots.

    Returns:
        Mapping of ``origin`` to absolute path string.
    """
    rows = self._con.execute("SELECT origin, path FROM sources").fetchall()
    return {r["origin"]: r["path"] for r in rows}

get_symbol

get_symbol(model: str, name: str, kind: str) -> List[Dict[str, Any]]

Return all KB entries for a symbol (may span multiple modules).

Parameters:

Name Type Description Default
model
str

dotted model name, e.g. 'sale.order'.

required
name
str

field or method name.

required
kind
str

'field' or 'method'.

required

Returns:

Type Description
List[Dict[str, Any]]

List of dicts with keys: origin, module, source_file, source_line,

List[Dict[str, Any]]

field_type, section. Empty list if symbol is not found.

Source code in src/oops/kb/store.py
def get_symbol(
    self,
    model: str,
    name: str,
    kind: str,
) -> List[Dict[str, Any]]:
    """Return all KB entries for a symbol (may span multiple modules).

    Args:
        model: dotted model name, e.g. 'sale.order'.
        name:  field or method name.
        kind:  'field' or 'method'.

    Returns:
        List of dicts with keys: origin, module, source_file, source_line,
        field_type, section. Empty list if symbol is not found.
    """
    rows = self._con.execute(
        """
        SELECT origin, module, source_file, source_line, field_type, section
        FROM   symbols
        WHERE  model = ? AND name = ? AND kind = ?
        ORDER  BY origin  -- stable ordering; resolve.py re-sorts by depends
        """,
        (model, name, kind),
    ).fetchall()
    return [dict(r) for r in rows]

is_model_creator

is_model_creator(model: str, module: str) -> bool

Return True if module is a creator (or prototype source) of model.

Falls back to True when neither module nor any other module has a model_origins creator entry for the model — safe assumption for modules that were not included in the KB scan.

Parameters:

Name Type Description Default
model
str

Dotted model name.

required
module
str

The module being analysed.

required

Returns:

Type Description
bool

True when this module created the model, False when it only extends it.

Source code in src/oops/kb/store.py
def is_model_creator(self, model: str, module: str) -> bool:
    """Return True if ``module`` is a creator (or prototype source) of ``model``.

    Falls back to True when neither ``module`` nor any other module has a
    ``model_origins`` creator entry for the model — safe assumption for modules
    that were not included in the KB scan.

    Args:
        model: Dotted model name.
        module: The module being analysed.

    Returns:
        True when this module created the model, False when it only extends it.
    """
    role = self.get_model_origin(model, module)
    if role is not None:
        return role in ("create", "prototype")
    row = self._con.execute(
        "SELECT 1 FROM model_origins WHERE model = ? AND role IN ('create', 'prototype') LIMIT 1",
        (model,),
    ).fetchone()
    return row is None

model_exists

model_exists(model: str) -> bool

Return True if any upstream module defines or extends this model.

Parameters:

Name Type Description Default
model
str

Dotted model name, e.g. 'sale.order'.

required

Returns:

Type Description
bool

True if at least one symbol for the model exists, False otherwise.

Source code in src/oops/kb/store.py
def model_exists(self, model: str) -> bool:
    """Return True if any upstream module defines or extends this model.

    Args:
        model: Dotted model name, e.g. ``'sale.order'``.

    Returns:
        True if at least one symbol for the model exists, False otherwise.
    """
    row = self._con.execute("SELECT 1 FROM symbols WHERE model = ? LIMIT 1", (model,)).fetchone()
    return row is not None

module_exists

module_exists(name: str) -> bool

Return True if the named module is present in the KB.

Parameters:

Name Type Description Default
name
str

Module name to look up.

required

Returns:

Type Description
bool

True if the module exists, False otherwise.

Source code in src/oops/kb/store.py
def module_exists(self, name: str) -> bool:
    """Return True if the named module is present in the KB.

    Args:
        name: Module name to look up.

    Returns:
        True if the module exists, False otherwise.
    """
    row = self._con.execute("SELECT 1 FROM modules WHERE name = ?", (name,)).fetchone()
    return row is not None

symbol_exists

symbol_exists(model: str, name: str, kind: str) -> bool

Return True if the symbol exists in any upstream module.

Parameters:

Name Type Description Default
model
str

Dotted model name, e.g. 'sale.order'.

required
name
str

Symbol name.

required
kind
str

'field' or 'method'.

required

Returns:

Type Description
bool

True if at least one upstream entry matches, False otherwise.

Source code in src/oops/kb/store.py
def symbol_exists(self, model: str, name: str, kind: str) -> bool:
    """Return True if the symbol exists in any upstream module.

    Args:
        model: Dotted model name, e.g. ``'sale.order'``.
        name: Symbol name.
        kind: ``'field'`` or ``'method'``.

    Returns:
        True if at least one upstream entry matches, False otherwise.
    """
    row = self._con.execute(
        "SELECT 1 FROM symbols WHERE model=? AND name=? AND kind=?",
        (model, name, kind),
    ).fetchone()
    return row is not None

write_global_kb

write_global_kb(db_path: Path, odoo_version: str, sources: Dict[str, str], scan_results: List[Dict[str, Any]]) -> None

Write (or overwrite) a global KB database.

Parameters:

Name Type Description Default

db_path

Path

destination .db file (created if absent).

required

odoo_version

str

e.g. '17.0'.

required

sources

Dict[str, str]

{ origin: absolute_path_string }.

required

scan_results

List[Dict[str, Any]]

list of ScanResult dicts from scanner.scan_tier().

required
Source code in src/oops/kb/store.py
def write_global_kb(
    db_path: Path,
    odoo_version: str,
    sources: Dict[str, str],
    scan_results: List[Dict[str, Any]],
) -> None:
    """Write (or overwrite) a global KB database.

    Args:
        db_path:      destination .db file (created if absent).
        odoo_version: e.g. '17.0'.
        sources:      { origin: absolute_path_string }.
        scan_results: list of ScanResult dicts from scanner.scan_tier().
    """
    _write_kb(
        db_path=db_path,
        layer="global",
        odoo_version=odoo_version,
        project=None,
        scope=None,
        sources=sources,
        scan_results=scan_results,
    )

write_project_kb

write_project_kb(db_path: Path, odoo_version: str, project: str, scope: List[str], sources: Dict[str, str], scan_results: List[Dict[str, Any]]) -> None

Write (or overwrite) a project KB database.

Parameters:

Name Type Description Default

db_path

Path

destination .db file.

required

odoo_version

str

e.g. '17.0'.

required

project

str

project slug string.

required

scope

List[str]

sorted list of module names in scope.

required

sources

Dict[str, str]

{ origin: absolute_path_string }.

required

scan_results

List[Dict[str, Any]]

list of ScanResult dicts (global already merged in by caller).

required
Source code in src/oops/kb/store.py
def write_project_kb(
    db_path: Path,
    odoo_version: str,
    project: str,
    scope: List[str],
    sources: Dict[str, str],
    scan_results: List[Dict[str, Any]],
) -> None:
    """Write (or overwrite) a project KB database.

    Args:
        db_path:      destination .db file.
        odoo_version: e.g. '17.0'.
        project:      project slug string.
        scope:        sorted list of module names in scope.
        sources:      { origin: absolute_path_string }.
        scan_results: list of ScanResult dicts (global already merged in by caller).
    """
    _write_kb(
        db_path=db_path,
        layer="project",
        odoo_version=odoo_version,
        project=project,
        scope=scope,
        sources=sources,
        scan_results=scan_results,
    )