学习odoo12(第一天)——程序启动源码学习

1,659 阅读3分钟

命令启动odoo

./odoo-bin --addons-path=addons,./myproject -d dev12 -u library_app
./myproject 自己定义的模块目录

odoo.cli.main() 程序入口

根据程序入口找到odoo/cli/command.py里面的def main()函数

def main():
    args = sys.argv[1:] 
    # 根据输入的启动命令 args:['--addons-path=addons,./myproject', '-d', 'dev12', '-u', 'library_app']
    
    # 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("-"):
        # 只分析加载项路径,不设置记录器…
        odoo.tools.config._parse_config([args[0]])
        args = args[1:]
    # args[0]:--addons-path=addons,./myproject, args[1]开头为-  所以条件不成立

    # 默认的 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) # 禁用等级为CRITICAL的日志
        # get_modules()为返回模块名称列表
        for module in get_modules():
            #isdir 判断是否为目录
            if isdir(joinpath(get_module_path(module), 'cli')):
                __import__('odoo.addons.' + module)
            # 这里的条件是不成立的
            
        logging.disable(logging.NOTSET)
        command = args[0]   
        args = args[1:] 
    
    # 这里commands为{'help': <class 'odoo.cli.command.Help'>, 'scaffold': <class 'odoo.cli.scaffold.Scaffold'>,
    # 'deploy': <class 'odoo.cli.deploy.Deploy'>, 'shell': <class 'odoo.cli.shell.Shell'>, 'server': <class 'odoo.cli.server.Server'>, 'start': <class 'odoo.cli.start.Start'>}
    if command in commands:    # server在其中条件成立
        o = commands[command]()  # 运行得知o为<odoo.cli.server.Server object at 0x7fb3513f1ef0>
        o.run(args)   # 调用cli下的Server.py 文件中的Server方法下的run函数
    else:
        sys.exit('Unknow command %r' % (command,))

接下来继续运行run()函数

class Server(Command):
    """Start the odoo server (default command)"""
    def run(self, args):
        main(args)  

run()函数了解到接下来要运行main()函数

def main(args):
    check_root_user() # 判断是否为POSIX系统中的root用户
    # 如果是的话 则判断当前用户是否为root,因为使用系统用户root作为odoo的user是一个不安全的做法
    odoo.tools.config.parse_config(args) # 解析配置文件和命令行参数,这里是将所有的命令添加到Python包里
    check_postgres_user() # 检测到用户是‘postgres’会拒绝登陆,并且弹出错误信息
    report_configuration() # 日志输出 odoo的版本,配置文件路径,插件路径,数据库信息(user,host,port);该方法假定配置已被初始化:

    config = odoo.tools.config

    # the default limit for CSV fields in the module is 128KiB, which is not
    # quite sufficient to import images to store in attachment. 500MiB is a
    # bit overkill, but better safe than sorry I guess
    csv.field_size_limit(500 * 1024 * 1024)   # 设置csv文件的大小

    preload = []
    if config['db_name']:    # config['db_name']:dev12
        preload = config['db_name'].split(',')  # preload:dev12
        for db_name in preload:
            try:
                #创建一个名为dev12的数据库
                odoo.service.db._create_empty_database(db_name) 
                config['init']['base'] = True 
            except ProgrammingError as err:
                if err.pgcode == errorcodes.INSUFFICIENT_PRIVILEGE:
                    # We use an INFO loglevel on purpose in order to avoid
                    # reporting unnecessary warnings on build environment
                    # using restricted database access.
                    _logger.info("Could not determine if database %s exists, "
                                 "skipping auto-creation: %s", db_name, err)
                else:
                    raise err
            except odoo.service.db.DatabaseExists:
                pass

    if config["translate_out"]:    # 为空 False
        export_translation()
        sys.exit(0)

    if config["translate_in"]:  # 为空 False
        import_translation()
        sys.exit(0)

    # This needs to be done now to ensure the use of the multiprocessing
    # signaling mecanism for registries loaded with -d
    if config['workers']:  # 0
        odoo.multi_process = True

    stop = config["stop_after_init"]   # stop = False

    setup_pid_file() # 使用写入的进程ID创建文件
    # 接着开始调用odoo/service/server.py 文件
    rc = odoo.service.server.start(preload=preload, stop=stop)
    sys.exit(rc)

接着开始start()函数的运行

def start(preload=None, stop=False):
    """ 启动odoo HTTP服务器和cron处理器.
    """
    global server  # 设置全局变量server

    load_server_wide_modules() # 加载openerp模块 如:base,web
    # 将它们转换为str,以便在python 2和python 3之间具有一致的行为
    odoo.service.wsgi_server._patch_xmlrpc_marshaller()

    if odoo.evented:   # odoo下的evented为False不成立
        server = GeventServer(odoo.service.wsgi_server.application)
    elif config['workers']:    # Fales不成立
        if config['test_enable'] or config['test_file']:
            _logger.warning("Unit testing in workers mode could fail; use --workers 0.")

        server = PreforkServer(odoo.service.wsgi_server.application)

        # Workaround for Python issue24291, fixed in 3.6 (see Python issue26721)
        if sys.version_info[:2] == (3,5):
            # turn on buffering also for wfile, to avoid partial writes (Default buffer = 8k)
            werkzeug.serving.WSGIRequestHandler.wbufsize = -1
    else:
        # 进入到这里,创建一个线程    
        server = ThreadedServer(odoo.service.wsgi_server.application)

    watcher = None
    if 'reload' in config['dev_mode'] and not odoo.evented # config['dev_mode']为空条件不成立
        if inotify:
            watcher = FSWatcherInotify()
            watcher.start()
        elif watchdog:
            watcher = FSWatcherWatchdog()
            watcher.start()
        else:
            if os.name == 'posix' and platform.system() != 'Darwin':
                module = 'inotify'
            else:
                module = 'watchdog'
            _logger.warning("'%s' module not installed. Code autoreload feature is disabled", module)
    if 'werkzeug' in config['dev_mode']:
        server.app = DebuggedApplication(server.app, evalex=True)

    rc = server.run(preload, stop)   # 运行线程服务

    if watcher:
        watcher.stop()
    # like the legend of the phoenix, all ends with beginnings
    if getattr(odoo, 'phoenix', False):
        _reexec()

    return rc if rc else 0

可以看出从程序入口文件一直到程序成功运行,需要整理好并传输程序运行所需的的配置,根据配置创建好所需要的数据库,加载好模块,运行客户端,然后通过ThreadedServer创建的线程来运行服务

参考:blog.csdn.net/weixin_3573…
blog.csdn.net/qq_33826977…