我有一个根据配置动态导入模块的脚本。我正在尝试在脚本上实现守护进程上下文(使用 python-daemon 模块),它似乎干扰了 python 查找相关模块的能力。
在 setup()
中的 mymodule/__init__.py
我这样做:
load_modules(args, config, logger)
try:
with daemon.DaemonContext(
files_preserve = getLogfileHandlers(logger)
):
main_loop(config)
我在 mymodule/__main__.py
中调用了 setup()
,并且我以这种方式加载整个内容:
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
未更改,守护进程上下文未清除环境或 chroot。当然,cwd 会更改为 /
,但这不会对 python 查找模块的能力产生任何影响,因为 .
的绝对路径出现在 sys.path 中。路径
。
我在这里缺少什么?
编辑:我正在添加 SSCCE以使情况更加清楚。以下三个文件创建一个名为“mymodule”的模块,该模块可以从命令行作为 PYTHONPATH= 运行。 python -m mymodule
. __init__.py
中有两次对 load_module()
的调用,其中一个被注释掉了。您可以通过交换评论的内容来演示该问题。
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 个小时试图解决这个问题。线索在这里:
If the module being imported is supposed to be contained within a package then the second argument passed to
find_module()
,__path__
on the parent package, is used as the source of paths.
(来自 https://docs.python.org/2/reference/simple_stmts.html#import )
成功导入mymodule
后,python2不再使用sys.path
来搜索子模块,而是使用sys.modules["mymodule"] .__路径__
。当您导入 mymodule
时,python2 无助地将其 __path__
设置为其存储的相对目录:
mymodule.__path__ = ['mymodule']
守护进程后,Python 的 CWD 设置为 /
,mysubmodule
的导入内部搜索的唯一位置位于 /mymodule
.
我通过使用 os.chdir()
在守护进程后将 CWD 更改回旧目录来解决此问题:
oldcwd = os.getcwd()
with DaemonizeContext():
os.chdir(oldcwd)
# ... daemon things
关于python importlib 在守护进程上下文中找不到模块,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29595193/