Metadata-Version: 2.3
Name: odoo-typegen
Version: 0.1.2
Summary: Static type discovery and stub generation for Odoo addons.
License: MIT
Keywords: odoo,typing,stubs,pyright
Author: jb
Author-email: jeanb.rocher@gmail.com
Requires-Python: >=3.10,<3.14
Classifier: Development Status :: 3 - Alpha
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Requires-Dist: astroid (>=3.3.10,<5.0.0)
Requires-Dist: cyclopts (>=3.22.5,<5.0.0)
Requires-Dist: pydantic (>=2.11.7,<3.0.0)
Project-URL: Homepage, https://github.com/jbrocher/odoo-typegen
Project-URL: Issues, https://github.com/jbrocher/odoo-typegen/issues
Project-URL: Repository, https://github.com/jbrocher/odoo-typegen
Description-Content-Type: text/markdown

# odoo-typegen

[![Python versions](https://img.shields.io/badge/python-3.10%E2%80%933.13-blue.svg)](https://www.python.org/downloads/)
[![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/jbrocher/odoo-typegen/blob/master/LICENSE)
[![CI](https://github.com/jbrocher/odoo-typegen/actions/workflows/ci.yml/badge.svg)](https://github.com/jbrocher/odoo-typegen/actions/workflows/ci.yml)

Automatic static type discovery and stub generation for Odoo projects.

odoo-typegen generates type stubs that expose addon-defined fields and methods on models retrieved
through the Odoo environment.

It discovers model extensions across your addons and consolidates them, so expressions such as
`env["crm.lead"]` evaluate to a typed models, inlcuding your own overrides. This enables accurate autocompletion and static type checking—with zero runtime impact.

#### :zap: Quickstart

```bash
pip install odoo-typegen
odoo-typegen my_addons
```

If you have a local Odoo core copy, you can also include interfaces defined by Odoo’s base models and
environment:

```bash
pip install odoo-typegen
odoo-typegen my_addons --odoo-path .vendor/dooo
```

## ✨ Highlights

- 🪄 **Automatic discovery:** scan existing addons without adding imports or changing
  application code.
- 💡 **Typed model lookups:** resolve expressions such as `env["crm.lead"]` to the
  generated model type.
- 🧩 **Model-aware stubs:** collect supported fields and methods across Odoo model
  extensions.
- 🏗️ **Core overlays:** generate stubs for `Environment`, `BaseModel`, and `Model`,
  with richer overlays when an Odoo source path is provided.
- 🪶 **Zero runtime impact:** generated `.pyi` files are consumed exclusively by
  static type checkers.

## 📦 Installing

### 🐍 With pip

```bash
pip install odoo-typegen
```

### ❄️ With Nix

This repository includes a `flake.nix`. If you use Nix, add it to a development
shell like this:

```nix
{
  inputs.odoo-typegen.url = "path:/home/jb/Projects/side-projects/odoo-typegen";

  outputs = { nixpkgs, odoo-typegen, ... }:
  let
    system = "x86_64-linux";
    pkgs = nixpkgs.legacyPackages.${system};
  in {
    devShells.${system}.default = pkgs.mkShell {
      packages = [
        odoo-typegen.packages.${system}.default
      ];
    };
  };
}
```

## 🚀 Usage

### Basic usage

Pass the directory containing your Odoo addons:

```bash
odoo-typegen ./addons
```

The generated stubs are written to `./typings`. Pyright automatically discovers
this conventional stub directory, so its inferred Odoo types are available
without additional configuration.

The explicit `generate` command is equivalent:

```bash
odoo-typegen generate ./addons
```

### With an Odoo source path

Provide a local Odoo checkout to enrich the generated `Environment`,
`BaseModel`, and `Model` stubs with interfaces discovered from Odoo's source:

```bash
odoo-typegen ./addons --odoo-path ./.vendor/odoo
```

### Type-checker integration

#### Pyright

With the default output directory, simply generate the stubs and run Pyright:

```bash
odoo-typegen ./addons
pyright
```

For a custom output directory, pass `--output-path` and set the same directory
as `stubPath` in `pyrightconfig.json`:

```bash
odoo-typegen ./addons --output-path ./generated-stubs
```

```json
{
  "stubPath": "./generated-stubs"
}
```

#### mypy

mypy does not automatically discover `./typings`. Point `mypy_path` at the
generated stubs in `pyproject.toml`:

```toml
[tool.mypy]
mypy_path = "./typings"
```

Then generate the stubs and check your addons:

```bash
odoo-typegen ./addons
mypy ./addons
```

To use a custom location, pass `--output-path` and update `mypy_path` to match.
For a one-off run, you can set it without changing your configuration:

```bash
odoo-typegen ./addons --output-path ./generated-stubs
MYPYPATH=./generated-stubs mypy ./addons
```

## ⚙️ How it works

Suppose you have a few addons that add some methods and attributes to the `crm.lead` model. For instance the first addon add a custom source field:

```python
# addons/crm_base_extension/models/crm_lead.py
from odoo import fields, models


class CrmLead(models.Model):
    _inherit = "crm.lead"

    my_custom_source = fields.Char()

```

The second addon adds a priority code and a helper method:

```python
# addons/crm_followup_extension/models/crm_lead.py
from odoo import fields, models


class CrmLead(models.Model):
    _inherit = "crm.lead"

    priority = fields.Integer()

    def is_high_priorty(self) -> bool:
        return self.priority > 3
```

odoo-typegen follows both extensions and consolidates their members into one
stub for `crm.lead`:

```python
# typings/crm/lead.pyi
# Generated by odoo-typegen.
from odoo.models import Model


class CrmLead(Model):
    my_custom_source: str
    priority: int
    def is_high_priorty(self) -> bool: ...
```

These models are then used in Stubs for Environment, models.Model, and models.BaseModel, which is how
the typing are automatically "injected" in your poject. This is how `self.env["crm.lead"]` will actually
be infered as implementing CrmLead.

```python
# ./typings/odoo/orm/environments.pyi
import typing
from crm.lead import CrmLead


class Environment:
    @typing.overload
    def __getitem__(self, model_name: typing.Literal["crm.lead"]) -> CrmLead: ...
    @typing.overload
    def __getitem__(self, model_name: str) -> typing.Any: ...
```

```python
# ./typings/odoo/orm/models.pyi
from odoo.orm.environments import Environment


class BaseModel:
    env: Environment

class Model(BaseModel):
    ...
```

## 🗺️ Roadmap

- 👀 Add a `--watch` option to regenerate types automatically.
- Include Odoo Core models overrides

