Odoo中文社区可以通过以下两个域名访问:shine-it.net , odoo.net.cn

由于系统升迁的原因,本论坛部分较早期的内容存在格式和链接损坏失效的问题,并非本论坛系统本身的缺陷,望谅解

本社区没有维护任何QQ群讨论组,任何与本社区同名的QQ群讨论组的言论与本社区无关!

开发人员可以登录gitter讨论组: http://gitter.im/odoo-china/Talk, 需要github账号

如果您登录系统碰到问题,请在微信公众号留言:

开发环境中,自动重启Openerp-Server的方法



  • 在开发的时候,因为不停的修改代码(py文件),经常需要重启Openerp-Server。

    借鉴了以前的项目,写了一个小方法让程序自启动,抛砖引玉之用。

    Step 1, 在/openerp-server/openerp/tools/ 目录下添加 autoreload.py。代码如下:

    <br /><br /># Autoreloading launcher.<br /># Borrowed from Peter Hunt and the CherryPy project (http://www.cherrypy.org).<br /># Some taken from Ian Bicking&#039;s Paste (http://pythonpaste.org/).<br /># Adjustments made by Michael Elsdoerfer ([email protected]).<br />#<br /># Portions copyright (c) 2004, CherryPy Team ([email protected])<br /># All rights reserved.<br />#<br /># Redistribution and use in source and binary forms, with or without modification,<br /># are permitted provided that the following conditions are met:<br />#<br />#&nbsp; &nbsp;  * Redistributions of source code must retain the above copyright notice,<br />#&nbsp; &nbsp; &nbsp;  this list of conditions and the following disclaimer.<br />#&nbsp; &nbsp;  * Redistributions in binary form must reproduce the above copyright notice,<br />#&nbsp; &nbsp; &nbsp;  this list of conditions and the following disclaimer in the documentation<br />#&nbsp; &nbsp; &nbsp;  and/or other materials provided with the distribution.<br />#&nbsp; &nbsp;  * Neither the name of the CherryPy Team nor the names of its contributors<br />#&nbsp; &nbsp; &nbsp;  may be used to endorse or promote products derived from this software<br />#&nbsp; &nbsp; &nbsp;  without specific prior written permission.<br />#<br /># THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS &quot;AS IS&quot; AND<br /># ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED<br /># WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE<br /># DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE<br /># FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL<br /># DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR<br /># SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER<br /># CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,<br /># OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE<br /># OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.<br /><br />import os, sys, time<br /><br />try:<br />&nbsp; &nbsp; import thread<br />except ImportError:<br />&nbsp; &nbsp; import dummy_thread as thread<br /><br /># This import does nothing, but it&#039;s necessary to avoid some race conditions<br /># in the threading module. See http://code.djangoproject.com/ticket/2330 .<br />try:<br />&nbsp; &nbsp; import threading<br />except ImportError:<br />&nbsp; &nbsp; pass<br /><br /><br />RUN_RELOADER = True<br /><br />_mtimes = {}<br />_win = (sys.platform == &quot;win32&quot;)<br /><br />def code_changed():<br />&nbsp; &nbsp; global _mtimes, _win<br />&nbsp; &nbsp; for filename in filter(lambda v: v, map(lambda m: getattr(m, &quot;__file__&quot;, None), sys.modules.values())):<br />&nbsp; &nbsp; &nbsp; &nbsp; if filename.endswith(&quot;.pyc&quot;) or filename.endswith(&quot;.pyo&quot;):<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; filename = filename[:-1]<br />&nbsp; &nbsp; &nbsp; &nbsp; if not os.path.exists(filename):<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; continue # File might be in an egg, so it can&#039;t be reloaded.<br />&nbsp; &nbsp; &nbsp; &nbsp; stat = os.stat(filename)<br />&nbsp; &nbsp; &nbsp; &nbsp; mtime = stat.st_mtime<br />&nbsp; &nbsp; &nbsp; &nbsp; if _win:<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; mtime -= stat.st_ctime<br />&nbsp; &nbsp; &nbsp; &nbsp; if filename not in _mtimes:<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; _mtimes[filename] = mtime<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; continue<br />&nbsp; &nbsp; &nbsp; &nbsp; if mtime != _mtimes[filename]:<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; _mtimes = {}<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return True<br />&nbsp; &nbsp; return False<br /><br />def reloader_thread(softexit=False):<br />&nbsp; &nbsp; &quot;&quot;&quot;If ``soft_exit`` is True, we use sys.exit(); otherwise ``os_exit``<br />&nbsp; &nbsp; will be used to end the process.<br />&nbsp; &nbsp; &quot;&quot;&quot;<br />&nbsp; &nbsp; while RUN_RELOADER:<br />&nbsp; &nbsp; &nbsp; &nbsp; if code_changed():<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; # force reload<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if softexit:<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; sys.exit(3)<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; else:<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; os._exit(3)<br />&nbsp; &nbsp; &nbsp; &nbsp; time.sleep(1)<br /><br />def restart_with_reloader():<br />&nbsp; &nbsp; while True:<br />&nbsp; &nbsp; &nbsp; &nbsp; args = [sys.executable] + sys.argv<br />&nbsp; &nbsp; &nbsp; &nbsp; if sys.platform == &quot;win32&quot;:<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; args = &#91;&#039;&quot;%s&quot;&#039; % arg for arg in args]<br />&nbsp; &nbsp; &nbsp; &nbsp; new_environ = os.environ.copy()<br />&nbsp; &nbsp; &nbsp; &nbsp; new_environ[&quot;RUN_MAIN&quot;] = &#039;true&#039;<br />&nbsp; &nbsp; &nbsp; &nbsp; exit_code = os.spawnve(os.P_WAIT, sys.executable, args, new_environ)<br />&nbsp; &nbsp; &nbsp; &nbsp; if exit_code != 3:<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return exit_code<br /><br />def python_reloader(main_func, args, kwargs, check_in_thread=True):<br />&nbsp; &nbsp; &quot;&quot;&quot;<br />&nbsp; &nbsp; If ``check_in_thread`` is False, ``main_func`` will be run in a separate<br />&nbsp; &nbsp; thread, and the code checker in the main thread. This was the original<br />&nbsp; &nbsp; behavior of this module: I (Michael Elsdoerfer) changed the default<br />&nbsp; &nbsp; to be the reverse: Code checker in thread, main func in main thread.<br />&nbsp; &nbsp; This was necessary to make the thing work with Twisted<br />&nbsp; &nbsp; (http://twistedmatrix.com/trac/ticket/4072).<br />&nbsp; &nbsp; &quot;&quot;&quot;<br />&nbsp; &nbsp; if os.environ.get(&quot;RUN_MAIN&quot;) == &quot;true&quot;:<br />&nbsp; &nbsp; &nbsp; &nbsp; if check_in_thread:<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; thread.start_new_thread(reloader_thread, (), {&#039;softexit&#039;: False})<br />&nbsp; &nbsp; &nbsp; &nbsp; else:<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; thread.start_new_thread(main_func, args, kwargs)<br /><br />&nbsp; &nbsp; &nbsp; &nbsp; try:<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if not check_in_thread:<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; reloader_thread(softexit=True)<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; else:<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; main_func(*args, **kwargs)<br />&nbsp; &nbsp; &nbsp; &nbsp; except KeyboardInterrupt:<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; pass<br />&nbsp; &nbsp; else:<br />&nbsp; &nbsp; &nbsp; &nbsp; try:<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; sys.exit(restart_with_reloader())<br />&nbsp; &nbsp; &nbsp; &nbsp; except KeyboardInterrupt:<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; pass<br /><br />def jython_reloader(main_func, args, kwargs):<br />&nbsp; &nbsp; from _systemrestart import SystemRestart<br />&nbsp; &nbsp; thread.start_new_thread(main_func, args)<br />&nbsp; &nbsp; while True:<br />&nbsp; &nbsp; &nbsp; &nbsp; if code_changed():<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; raise SystemRestart<br />&nbsp; &nbsp; &nbsp; &nbsp; time.sleep(1)<br /><br /><br />def main(main_func, args=None, kwargs=None, **more_options):<br />&nbsp; &nbsp; if args is None:<br />&nbsp; &nbsp; &nbsp; &nbsp; args = ()<br />&nbsp; &nbsp; if kwargs is None:<br />&nbsp; &nbsp; &nbsp; &nbsp; kwargs = {}<br />&nbsp; &nbsp; if sys.platform.startswith(&#039;java&#039;):<br />&nbsp; &nbsp; &nbsp; &nbsp; reloader = jython_reloader<br />&nbsp; &nbsp; else:<br />&nbsp; &nbsp; &nbsp; &nbsp; reloader = python_reloader<br />&nbsp; &nbsp; reloader(main_func, args, kwargs, **more_options)<br /><br />
    




    Step 2,在 /openerp-server/openerp/tools/init.py 中导出:

    <br /><br />.....<br /><br />from autoreload import main as autoreload_main<br /><br />.....<br /><br />
    




    Step 3, 加入启动代码: /openerp-server/openerp-server 文件

    <br />if __name__ == &quot;__main__&quot;:<br />&nbsp; &nbsp; .......<br />&nbsp; &nbsp; openerp.tools.autoreload_main(openerp.cli.main)<br />&nbsp; &nbsp; <br />
    





  • [quote author=ccdos link=topic=5992.msg15043#msg15043 date=1366354353]

    [/quote]

    实在懒得Ctrl+C再回车了。



  • 杨兄大作学习了 ~ 

    我这里也写了一个脚本,托管在 https://github.com/buke/autoreload br />
    与杨兄实现方法不同的地方是:
    1、采取 popen 开启OE进程,可适用于 OE 7  开启 workers 参数启用多进程的情况
    2、无需对 OE 源码修改,只需要把OE 执行脚本作为参数即可。如 $ ./autoreload "openerp-server -c openerp-server.conf" (windows 请执行 python autoreload "python openerp-server -c openerp-server.conf")

    更多请移步本人博客 [检测到链接无效,已移除]

    欢迎指正!



  • Update:

    linux 下使用 inotify 事件通知机制,抛弃丑陋的定时轮询。

    代码已同步到 https://github.com/buke/autoreload br />
    详情请查看本人博客 http://buke.github.io/blog/2013/07/01/using-inotify-detected-python-source-file-and-autoreload-process/



  • 强啊。
    经常拜读你的博客。


Log in to reply