ValueError:在非包中尝试相对导入以运行独立脚本在Flask Web App

时间:2017-09-13 06:42:39

标签: python flask-sqlalchemy daemons flask-script

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

/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代码

来自..common导入统计信息

来自..method import msapi,dataformater

来自..db_client导入db_connection

def run_daemon():

......

......

......

如果名称 ==" 主要":

run_daemon()

因此,当我尝试运行此daemon1.py时,它会抛出ValueError:在非包中尝试相对导入

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

提前致谢。

2 个答案:

答案 0 :(得分: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。所以,这只有在我启动工人和最初的crontabs时才会运行。

这是我扔在一起的黑客攻击(我确信有更简洁的方法):

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”。对于下一步,我们假设您已将其更改为“project_name”。

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

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

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

总结:确保所有__init__都有导入,确定要导入的模块的路径,将路径附加到PATH变量。

我希望有所帮助。

答案 1 :(得分:0)

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

所以为了让事情有效,我已经改变了我的代码。除了在daemon1.py的main中调用run_daemon()之外,我直接调用了函数run_daemon()。

来自app.cron.daemon1的

python -m'导入run_daemon(); run_daemon()'

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