2

我有一个基于配置动态导入模块的脚本。我正在尝试python-daemon在脚本上实现一个守护进程上下文(使用模块),它似乎干扰了 python 找到有问题的模块的能力。

我这样mymodule/__init__.pysetup()

load_modules(args, config, logger)

try:
    with daemon.DaemonContext(
            files_preserve = getLogfileHandlers(logger)
            ):
        main_loop(config)

我接到了setup()里面的电话mymodule/__main__.py,我正在以这种方式加载整个内容: PYTHONPATH=. python -m mymodule

这工作正常,但是在 load_modules() 中设置的侦听端口被新添加的守护进程上下文关闭,所以我想在守护进程上下文中移动该函数调用,如下所示:

try:
    with daemon.DaemonContext(
            files_preserve = getLogfileHandlers(logger)
            ):
        load_modules(args, config, logger)
        main_loop(config)

模块以这种方式加载load_modules()

for mysubmodule in modules:
    try:        
        i = importlib.import_module("mymodule.{}".format(mysubmodule))
    except ImportError as err:
        logger.error("import of mymodule.{} failed: {}".format(
            mysubmodule, err))

load_modules()守护程序上下文之外,这可以正常工作。当我将它移动到守护进程上下文中时,它似乎无法找到它正在寻找的模块。我明白了:

import of mymodule.submodule failed: No module named submodule

它看起来像是某种命名空间问题——我注意到异常只涉及我尝试导入的模块名称的子模块部分——但我已经比较了我在守护进程上下文内部和外部能想到的所有内容,并且我找不到重要的区别。 sys.path没有改变,守护进程上下文没有清除环境或 chrooting。cwd 当然会更改为/,但这不应该对 python 查找模块的能力产生任何影响,因为绝对路径.出现在sys.path.

我在这里想念什么?

编辑:我正在添加一个SSCCE以使情况更加清晰。以下三个文件创建了一个名为“mymodule”的模块,可以从命令行以PYTHONPATH=. python -m mymodule. 有两个调用load_module()in __init__.py,一个被注释掉。您可以通过交换评论中的哪一个来证明问题。

mymodule/__main__.py

from mymodule import setup
import sys

if __name__ == "__main__":
    sys.exit(setup())

mymodule/__init__.py

import daemon
import importlib
import logging

def main_loop():
    logger = logging.getLogger('loop')
    logger.debug("Code runs here.")

def load_module():
    logger = logging.getLogger('load_module')
    submodule = 'foo'
    try:
        i = importlib.import_module("mymodule.{}".format(submodule))
    except ImportError as e:
        logger.error("import of mymodule.{} failed: {}".format(
            submodule, e))

def setup_logging():
    logfile = 'mymodule.log'
    fh = logging.FileHandler(logfile)
    root_logger = logging.getLogger()
    root_logger.addHandler(fh)
    root_logger.setLevel(logging.DEBUG)

def get_logfile_handlers(logger):
    handlers = []
    for handler in logger.handlers:
        handlers.append(handler.stream.fileno())
    return handlers

def setup():
    setup_logging()
    logger = logging.getLogger()

    # load_module()
    with daemon.DaemonContext(
            files_preserve = get_logfile_handlers(logger)
            ):
        load_module()
        main_loop()

mymodule/foo.py

import logging

logger=logging.getLogger('foo')
logger.debug("Inside foo.py")
4

2 回答 2

1

当我在自己的项目中遇到这个问题时,我花了 4 个小时来尝试解决这个问题。线索在这里:

如果要导入的模块应该包含在包中,则传递给父包的第二个参数find_module()用作__path__路径的来源。

(来自https://docs.python.org/2/reference/simple_stmts.html#import

成功导入mymodule后,python2 不再用于sys.path搜索子模块,而是使用sys.modules["mymodule"].__path__. 当你 import 时mymodule,python2 将其设置__path__为它存储的相对目录:

mymodule.__path__ = ['mymodule']

守护进程后,python 的 CWD 设置为/,导入内部搜索的唯一mysubmodule位置是/mymodule.

我通过os.chdir()在守护进程后将 CWD 更改回旧目录来解决此问题:

oldcwd = os.getcwd()
with DaemonizeContext():
    os.chdir(oldcwd)
    # ... daemon things
于 2017-03-13T22:22:40.537 回答
-1

这工作正常,但是在 load_modules() 中设置的监听端口被新添加的守护进程上下文关闭,所以

load_modules()应该加载模块。它不应该打开端口。

如果您需要保留在上下文之外打开的文件或套接字,请将其传递给files_preserve. 如果可能的话,最好在上下文中简单地打开文件等,正如我上面建议的那样。

于 2015-04-13T13:16:00.440 回答