源码分析
# set server timezone in UTC before time module imported
__import__('os').environ['TZ'] = 'UTC'
import odoo
if __name__ == "__main__":
odoo.cli.main()
odoo的启动入口,从这里跟踪到odoo模块下的cli包的__init__.py文件。
import logging
import sys
import os
import odoo
from .command import Command, main
...
在这里可以看到导入了command中的main函数,继续追踪main函数
def main():
# ['D:\\odoo14\\odoo-bin', '-c', 'D:\\odoo14\\odoo.conf', '-u', 'service_request']
# 获取除了脚本名称之外的命令行参数['-c', 'D:\\odoo14\\odoo.conf', '-u', 'service_request']
args = sys.argv[1:]
if len(args) > 1 and args[0].startswith('--addons-path=') and not args[1].startswith("-"):
odoo.tools.config._parse_config([args[0]]) # 解析--addons-path=参数
args = args[1:] # 从参数列表中移除已解析的参数
command = "server" # 默认命令设置为"server"
#检查是否有子命令
if len(args) and not args[0].startswith("-"):
logging.disable(logging.CRITICAL) # 禁用日志
initialize_sys_path() # 初始化系统路径
# 遍历所有模块,检查是否有cli目录,如果有,则导入模块
for module in get_modules():
if isdir(joinpath(get_module_path(module), 'cli')):
__import__('odoo.addons.' + module)
logging.disable(logging.NOTSET) # 恢复日志记录
command = args[0] # 设置实际的命令
args = args[1:] # 更新参数列表,移除已使用的命令
# 检查命令是否存在
if command in commands:
o = commands[command]() # 创建命令实例
o.run(args) # 执行命令
else:
sys.exit('Unknown command %r' % (command,)) # 退出程序并提示未知命令
command.py文件这里可以看到有用到了元类知识,定义了一个元类 CommandType
,它的作用是在创建命令类时自动注册这些类。这通过修改类的 __init__
方法实现,每当创建一个新的命令类时,我们都会将其添加到一个全局的 commands
字典中。这样,我们就可以通过命令名来查找和执行命令了。
接下来,我们定义了一个基础命令类 Command
,使用 CommandType
作为其元类。这样做的目的是确保任何继承自 Command
的子类都会自动注册。
我简单了模仿写了个代码举例子,用来说明元类的用法
commands = {}
class CommandType(type):
def __init__(cls, name, bases, attrs):
super(CommandType, cls).__init__(name, bases, attrs)
cls.name = attrs.get('name', name.lower())
if cls.name != 'command':
commands[cls.name] = cls
# Command = CommandType('Command', (object,), {'run': lambda self, args: None})
# 等同于以下的类定义
class Command(metaclass=CommandType):
def run(self, args):
pass
# 定义 CreateCommand
class CreateCommand(Command):
def run(self, args):
print("create,", ' '.join(args))
# 定义 ShellCommand
class ShellCommand(Command):
def run(self, args):
print("Shell,", ' '.join(args))
def execute_command(command_name, args):
command_class = commands.get(command_name)
if command_class is not None:
command_instance = command_class()
command_instance.run(args)
else:
print("Unknown command:", command_name)
user_input_command = "createcommand"
user_input_args = ["zuru"]
execute_command(user_input_command, user_input_args)
有了上面的铺垫,我自定义一个脚手架,模仿sacffold的子命令,我重命名了一个create的子命令,该类要继承Command, 重载了run函数,引用了自己的代码模版default2,里面可以编写自己想要的代码片段。模版路径存放于odoo\cli\templates
class Custom_Create(Command):
""" Generates an Odoo module skeleton. """
def run(self, cmdargs):
# TODO: bash completion file
parser = argparse.ArgumentParser(
prog="%s costom_create" % sys.argv[0].split(os.path.sep)[-1],
description=self.__doc__,
epilog=self.epilog(),
)
parser.add_argument(
'-t', '--template', type=template, default=template('default2'),
help="Use a custom module template, can be a template name or the"
" path to a module template (default: %(default)s)")
parser.add_argument('name', help="Name of the module to create")
parser.add_argument(
'dest', default='.', nargs='?',
help="Directory to create the module in (default: %(default)s)")
if not cmdargs:
sys.exit(parser.print_help())
args = parser.parse_args(args=cmdargs)
args.template.render_to(
snake(args.name),
directory(args.dest, create=True),
{'name': args.name})
使用自定义的脚手架
以下是如何执行一个命令的示例:
python D:\odoo14\odoo-bin custom_create custom_module D:\odoo14\custom