Scanner¶
scanner
¶
AST-based scanner for Odoo source trees.
Sections
- Constants: ODOO_BASE_CLASSES, FIELD_TYPES, METHOD_SECTION_*, tier marker helpers
- AST helpers: parse, extract, classify Odoo model nodes
- Manifest parsing: delegated to oops.io.manifest
- Scanning: scan_module, scan_tier, odoo_addons_roots
- Root addon discovery: discover_root_addons, tier_root_from_real_path
Functions:
| Name | Description |
|---|---|
build_module_field_refs |
Build a {(model, method_name): [kwarg, ...]} index from a list of model files. |
classify_method |
Decide a method's section. |
discover_root_addons |
Walk repo_path for root-level Odoo addons and group them by tier. |
extract_field_refs |
Return {kwarg: target_method_name} for string-literal kwargs in FIELD_REF_KWARGS. |
get_inherits |
Extract _inherits dict {parent_model: fk_field} from a class body. |
get_model_names |
Extract _name and _inherit values from a class body. |
get_model_type |
Return the Odoo model kind for a class node. |
is_field_assignment |
If stmt assigns a fields.XXX, return (field_name, lineno, field_type). Else None. |
is_odoo_model_class |
Return True if the class directly subclasses an Odoo model base. |
odoo_addons_roots |
Return the standard addons roots inside an Odoo community tree. |
scan_module |
Scan a single Odoo module directory. |
scan_tier |
Scan all addon modules under tier_root. |
tier_root_from_real_path |
Derive the tier root directory from a module's real path. |
build_module_field_refs
¶
Build a {(model, method_name): [kwarg, ...]} index from a list of model files.
Used by the refactor CLI to pre-compute cross-file field→method links within a single module before running per-file analysis.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
List[Path]
|
Python source files from a single Odoo module to index. |
required |
Returns:
| Type | Description |
|---|---|
Dict[Tuple[str, str], List[str]]
|
Mapping of |
Dict[Tuple[str, str], List[str]]
|
that reference the method (e.g. |
Source code in src/oops/kb/scanner.py
classify_method
¶
classify_method(name: str, decorator_names: List[str], referencing_kwargs: Iterable[str] = ()) -> str
Decide a method's section.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
str
|
Method name. |
required |
|
List[str]
|
Flat list of decorator name strings (see
|
required |
|
Iterable[str]
|
Field kwargs that reference this method on the same
model (across all classes/files/modules available at classification
time), e.g. |
()
|
Returns:
| Type | Description |
|---|---|
str
|
One of the |
Priority (first match wins): 1. CRUD name. 2. Standard default-provider name (default_get) → DEFAULT METHODS. 3. @api.depends → COMPUTE METHODS. 4. @api.onchange → ONCHANGE METHODS. 5. @api.constrains → CONSTRAINT METHODS. 6. Referenced by a field via compute=/inverse=/search= → COMPUTE METHODS. 7. Referenced by a field via default= → DEFAULT METHODS. 8. Referenced by a field via selection= → SELECTION METHODS. 9. action_ or button_ prefix → ACTION METHODS. 10. _ prefix → HELPER METHODS. 11. Default → BUSINESS METHODS.
@api.model is intentionally NOT a classification signal. It only marks that
the method receives the model class rather than a recordset as self; this
orthogonal property appears across all sections. Treating it as a signal would
misclassify or produce a new meaningless section.
SELECTION METHODS is ONLY reachable via the referencing_kwargs path
("selection" in the set). There is no Odoo decorator for selection methods
and no established naming convention — selection= on a field declaration is
the sole detection signal.
See docs/reference/method-classification.md for the full rationale behind each rule and guidance on extending the system.
Source code in src/oops/kb/scanner.py
discover_root_addons
¶
discover_root_addons(repo_path: Path, allowed_modules: Optional[Set[str]] = None) -> Dict[str, List[Tuple[str, Path]]]
Walk repo_path for root-level Odoo addons and group them by tier.
Three tiers are recognised
- 'third-party': symlink whose real path contains '/.third-party/'.
- 'apik': symlink whose real path contains '/apik-addons/'.
- 'local': real directory at the repo root with a manifest.
Symlinks that resolve outside the known submodule tiers are logged and skipped.
Returns:
| Type | Description |
|---|---|
Dict[str, List[Tuple[str, Path]]]
|
{ origin: [(module_name, real_module_path), ...] } |
Source code in src/oops/kb/scanner.py
extract_field_refs
¶
Return {kwarg: target_method_name} for string-literal kwargs in FIELD_REF_KWARGS.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
Assign
|
An AST |
required |
Returns:
| Type | Description |
|---|---|
Dict[str, str]
|
Mapping of kwarg name to the referenced method name string. |
Bare callables and lambdas are skipped silently.
Source code in src/oops/kb/scanner.py
get_inherits
¶
Extract _inherits dict {parent_model: fk_field} from a class body.
Returns:
| Type | Description |
|---|---|
Dict[str, str]
|
Mapping of parent model name to the local FK field name, empty if absent. |
Source code in src/oops/kb/scanner.py
get_model_names
¶
Extract _name and _inherit values from a class body.
Returns:
| Type | Description |
|---|---|
Tuple[Optional[str], List[str]]
|
(_name value or None, list of _inherit values — empty if absent) |
Source code in src/oops/kb/scanner.py
get_model_type
¶
Return the Odoo model kind for a class node.
Returns:
| Type | Description |
|---|---|
str
|
|
Source code in src/oops/kb/scanner.py
is_field_assignment
¶
If stmt assigns a fields.XXX, return (field_name, lineno, field_type). Else None.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
stmt
|
An AST statement node to inspect. |
required |
Returns:
| Type | Description |
|---|---|
Optional[Tuple[str, int, str]]
|
|
Optional[Tuple[str, int, str]]
|
|
Source code in src/oops/kb/scanner.py
is_odoo_model_class
¶
Return True if the class directly subclasses an Odoo model base.
Returns:
| Type | Description |
|---|---|
bool
|
True if any base class name is in |
Source code in src/oops/kb/scanner.py
odoo_addons_roots
¶
Return the standard addons roots inside an Odoo community tree.
Odoo community keeps modules in two places:
<root>/addons/standard modules (sale, account…)<root>/odoo/addons/core modules (base, web, mail…)
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
Path
|
Path to the root of an Odoo community checkout. |
required |
Returns:
| Type | Description |
|---|---|
List[Path]
|
List of existing addons root paths. Falls back to |
List[Path]
|
if neither standard subdirectory exists. |
Source code in src/oops/kb/scanner.py
scan_module
¶
Scan a single Odoo module directory.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
Path
|
absolute path to the module (must contain manifest.py). |
required |
|
str
|
tier label ('odoo', 'enterprise', 'third-party', 'apik'). |
required |
|
Path
|
root used to compute relative source_file paths. |
required |
Returns:
| Type | Description |
|---|---|
Dict[str, Any]
|
A ScanResult dict with keys 'modules', 'symbols', and 'field_refs'. |
Source code in src/oops/kb/scanner.py
423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 | |
scan_tier
¶
scan_tier(tier_root: Path, origin: str, allowed_modules: Optional[Set[str]] = None) -> Dict[str, Any]
Scan all addon modules under tier_root.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
Path
|
directory whose immediate children are Odoo modules. |
required |
|
str
|
tier label. |
required |
|
Optional[Set[str]]
|
if set, only modules whose name is in this set are scanned. |
None
|
Returns:
| Type | Description |
|---|---|
Dict[str, Any]
|
Merged ScanResult for all scanned modules. |
Source code in src/oops/kb/scanner.py
tier_root_from_real_path
¶
Derive the tier root directory from a module's real path.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
|
str
|
Tier name (e.g. |
required |
|
Path
|
Resolved (non-symlink) path of the module directory. |
required |
Returns:
| Type | Description |
|---|---|
Optional[Path]
|
The tier root path, or |
Optional[Path]
|
e.g. |