2

我有烧瓶网络应用程序,其结构如下:

/app  
    /__init__.py  
    /wsgi.py
    /app  
        /__init__.py
        /views.py  
        /models.py 
        /method.py
        /common.py
        /db_client.py
        /amqp_client.py
        /cron
            /__init.py__
            /daemon1.py
            /daemon2.py
        /static/  
            /main.css
        /templates/  
            /base.html
    /scripts  
    /nginx
    /supervisor 
    /Dockerfile 
    /docker-compose.yml

在 app/app/cron 我已经编写了独立的守护进程,我想在 docker 之外调用它们。例如python daemon1.py

daemon1.py 代码

from ..common 进口统计

从 ..method 导入 msapi,数据格式化程序

从 ..db_client 导入 db_connection

def run_daemon():

......

......

......

如果名称==“主要”:

run_daemon()

因此,当我尝试运行此 daemon1.py 时,它会抛出 ValueError: Attempted relative import in non-package

请建议正确的导入方法以及构建这些守护程序。

提前致谢。

4

2 回答 2

0

我在运行 Flask 和 Celery 的应用程序中遇到了完全相同的问题。我花了太多时间在谷歌上搜索应该是一个简单的答案。唉,没有。

我不喜欢“python -m”语法,因为这对于在运行代码中调用函数不太实用。由于我的大脑看似很小,我无法掌握任何其他答案。

所以......有错误的方式和漫长的方式。他们都工作(对我来说),我相信我会受到社区的抨击。

错误的方法

imp您可以像这样使用包直接调用模块:

import imp
common = imp.load_source('common', os.path.dirname(os.path.abspath('__file__')) + '/common.py')
result = common.stats()  #not sure how you call stats, but you hopefully get the idea

我快速搜索了那些说不可以的参考资料,但我找不到它们……抱歉。

漫漫长路

此方法涉及将您的每个模块临时附加到您的 PATH 中。无论容器的目录结构如何,这对我的 Docker 部署都很有效。以下是步骤:

1)您必须从__init__文件中的父目录导入相关模块。这确实是重点__init__- 允许其包中的模块是可调用的。因此,在您的情况下,cron/__init__应包含:

from . import common

看起来您的目录并没有比这更高,但是您也可以对任何其他级别的包执行相同的操作。

2)现在您需要将模块的路径附加到 PATH 变量中。您可以通过运行以下命令来查看其中的内容:

sys.path

正如预期的那样,您可能不会在其中看到任何模块。common这意味着,当您调用模块时,Python 无法确定您想要什么。为了添加路径,您需要弄清楚目录结构。您将希望使其动态化以考虑更改目录。

值得注意的是,每次模块运行时都需要运行它。我不确定你的cron模块是什么,但就我而言,它是 Celery。因此,这仅在我启动工作人员和初始 crontab 时运行。

这是我拼凑的技巧(我确信有一种更清洁的方法):

curr_path = os.getcwd()   #current path where cron is running
parrent_path = os.path.abspath(os.path.join(os.getcwd(), '..'))   #the parent directory path
parrent_dir = os.path.basename(os.path.abspath(parrent_path))   #the parent directory name
while parrent_dir <> 'project_name':    #loop until you get to the top directory - should be the project name
    parrent_path = os.path.abspath(os.path.join(par_path, '..'))  
    parrent_dir = os.path.basename(os.path.abspath(parrent_path))

在您的情况下,这可能是一个挑战,因为您有两个名为“app”的目录。您的顶级“应用程序”是我的“项目名称”。对于下一步,假设您已将其更改为“project_name”。

3) 现在您可以将每个模块的路径附加到 PATH 变量中:

sys.path.append(parrent_dir + '/app')

现在,如果您sys.path再次运行,您应该会看到其中的路径/app

总而言之:确保您的所有__init__'s 都有导入,确定要导入的模块的路径,将路径附加到 PATH 变量。

我希望这会有所帮助。

于 2017-09-13T08:11:27.717 回答
0

@greenbergé 感谢您的解决方案。我试过但没有为我工作。

所以为了让事情顺利进行,我稍微改变了我的代码。除了在 daemon1.py 的 main 中调用 run_daemon() 之外,我还直接调用了函数 run_daemon()。

python -m '从 app.cron.daemon1 导入 run_daemon(); run_daemon()'

因为这不是问题的确切解决方案,但事情对我有用。

于 2017-09-14T05:39:23.413 回答