19.0 vanilla

This commit is contained in:
Ernad Husremovic 2025-10-03 18:07:25 +02:00
parent 0a7ae8db93
commit 991d2234ca
416 changed files with 646602 additions and 300844 deletions

View file

@ -1,69 +1,139 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import logging
import os
import argparse
import contextlib
import re
import sys
from inspect import cleandoc
from pathlib import Path
import odoo
from odoo.modules import get_modules, get_module_path, initialize_sys_path
import odoo.init # import first for core setup
import odoo.cli
from odoo.modules import initialize_sys_path, load_script
from odoo.tools import config
COMMAND_NAME_RE = re.compile(r'^[a-z][a-z0-9_]*$', re.I)
PROG_NAME = Path(sys.argv[0]).name
commands = {}
"""All loaded commands"""
class Command:
name = None
description = None
epilog = None
_parser = None
def __init_subclass__(cls):
cls.name = cls.name or cls.__name__.lower()
commands[cls.name] = cls
module = cls.__module__.rpartition('.')[2]
if not cls.is_valid_name(cls.name):
raise ValueError(
f"Command name {cls.name!r} "
f"must match {COMMAND_NAME_RE.pattern!r}")
if cls.name != module:
raise ValueError(
f"Command name {cls.name!r} "
f"must match Module name {module!r}")
commands[cls.name] = cls
@property
def prog(self):
return f"{PROG_NAME} [--addons-path=PATH,...] {self.name}"
@property
def parser(self):
if not self._parser:
self._parser = argparse.ArgumentParser(
formatter_class=argparse.RawDescriptionHelpFormatter,
prog=self.prog,
description=cleandoc(self.description or self.__doc__ or ""),
epilog=cleandoc(self.epilog or ""),
)
return self._parser
@classmethod
def is_valid_name(cls, name):
return re.match(COMMAND_NAME_RE, name)
ODOO_HELP = """\
Odoo CLI, use '{odoo_bin} --help' for regular server options.
def load_internal_commands():
""" Load ``commands`` from ``odoo.cli`` """
for path in odoo.cli.__path__:
for module in Path(path).iterdir():
if module.suffix != '.py':
continue
__import__(f'odoo.cli.{module.stem}')
Available commands:
{command_list}
Use '{odoo_bin} <command> --help' for individual command help."""
def load_addons_commands(command=None):
"""
Search the addons path for modules with a ``cli/{command}.py`` file.
In case no command is provided, discover and load all the commands.
"""
if command is None:
command = '*'
elif not Command.is_valid_name(command):
return
mapping = {}
initialize_sys_path()
for path in odoo.addons.__path__:
for fullpath in Path(path).glob(f'*/cli/{command}.py'):
if (found_command := fullpath.stem) and Command.is_valid_name(found_command):
# loading as odoo.cli and not odoo.addons.{module}.cli
# so it doesn't load odoo.addons.{module}.__init__
mapping[f'odoo.cli.{found_command}'] = fullpath
for fq_name, fullpath in mapping.items():
with contextlib.suppress(ImportError):
load_script(fullpath, fq_name)
def find_command(name: str) -> Command | None:
""" Get command by name. """
# built-in commands
if command := commands.get(name):
return command
# import from odoo.cli
with contextlib.suppress(ImportError):
__import__(f'odoo.cli.{name}')
return commands[name]
# import from odoo.addons.*.cli
load_addons_commands(command=name)
return commands.get(name)
class Help(Command):
""" Display the list of available commands """
def run(self, args):
padding = max([len(cmd) for cmd in commands]) + 2
command_list = "\n ".join([
" {}{}".format(name.ljust(padding), (command.__doc__ or "").strip())
for name, command in sorted(commands.items())
])
print(ODOO_HELP.format( # pylint: disable=bad-builtin
odoo_bin=Path(sys.argv[0]).name,
command_list=command_list
))
def main():
args = sys.argv[1:]
# The only shared option is '--addons-path=' needed to discover additional
# commands from modules
if len(args) > 1 and args[0].startswith('--addons-path=') and not args[1].startswith("-"):
if len(args) > 1 and args[0].startswith('--addons-path=') and not args[1].startswith('-'):
# parse only the addons-path, do not setup the logger...
odoo.tools.config._parse_config([args[0]])
config._parse_config([args[0]])
args = args[1:]
# Default legacy command
command = "server"
# TODO: find a way to properly discover addons subcommands without importing the world
# Subcommand discovery
if len(args) and not args[0].startswith("-"):
logging.disable(logging.CRITICAL)
initialize_sys_path()
for module in get_modules():
if (Path(get_module_path(module)) / 'cli').is_dir():
__import__('odoo.addons.' + module)
logging.disable(logging.NOTSET)
command = args[0]
if len(args) and not args[0].startswith('-'):
# Command specified, search for it
command_name = args[0]
args = args[1:]
if command in commands:
o = commands[command]()
odoo.cli.COMMAND = command
o.run(args)
elif '-h' in args or '--help' in args:
# No command specified, but help is requested
command_name = 'help'
args = [x for x in args if x not in ('-h', '--help')]
else:
sys.exit('Unknown command %r' % (command,))
# No command specified, default command used
command_name = 'server'
if command := find_command(command_name):
odoo.cli.COMMAND = command_name
command().run(args)
else:
message = (
f"Unknown command {command_name!r}.\n"
f"Use '{PROG_NAME} --help' to see the list of available commands."
)
sys.exit(message)