从烧瓶应用程序调用 python 函数

Posted

技术标签:

【中文标题】从烧瓶应用程序调用 python 函数【英文标题】:calling a python function from flask app 【发布时间】:2018-01-22 19:10:43 【问题描述】:

我正在开发一个在 python 中运行的应用程序(总是 - 它是热泵系统的控制器),我使用烧瓶提供用户界面来控制应用程序。

flask 应用具有不同的控制项,例如用于打开或关闭系统的按钮。

我正在尝试从 python 模块执行特定函数以响应按钮上的“单击”(最终目标是更改 mmap 资源中的值,该值将在另一个模块中读取以更改系统)。

在烧瓶应用程序中,我有类似的东西:

    @app.route('/cntr_hpauto',methods=['GET','POST'])
    @basic_auth.required
    def cntr_hpauto():
        manage_globals.set_from_web()
        return render_template('control.html',cur_hp_mode="auto")

但是,这会产生“内部服务器错误”

完整的烧瓶应用程序是(manage_globals 是我要导入的 *.py 文件,其中包含我要调用的函数):

from flask import Flask, request, render_template
from flask_basicauth import BasicAuth

import sys
import os
import mmap
import manage_globals

app = Flask(__name__)

app.config['BASIC_AUTH_USERNAME'] = '***'
app.config['BASIC_AUTH_PASSWORD'] = '***'

basic_auth = BasicAuth(app)

@app.route('/')

def splash():
        return render_template('splash.html')

@app.route('/dashboard', methods=['GET','POST'])
@basic_auth.required
def dashboard():
        return render_template('dashboard.html')

@app.route('/control',methods=['GET','POST'])
@basic_auth.required
def control():
    return render_template('control.html',cur_hp_mode="none")

@app.route('/cntr_hpauto',methods=['GET','POST'])
@basic_auth.required
def cntr_hpauto():
    manage_globals.set_from_web()
    return render_template('control.html',cur_hp_mode="auto")

@app.route('/cntr_hpon',methods=['GET','POST'])
@basic_auth.required
def cntr_hpon():
    return render_template('control.html',cur_hp_mode="on")

@app.route('/cntr_hpoff',methods=['GET','POST'])
@basic_auth.required
def cntr_hpoff():
    return render_template('control.html',cur_hp_mode="off")

if __name__ == '__main__':
    app.run(ssl_context=('/home/groenhol/certs/groenhol.pem', '/home/groenhol/certs/groenhol.key'))

并且模块(例如,只将映射文件写入日志文件)是:

# 14/08/2017 henk witte / groenholland
# part of geotech project, ann controller dual source heat pump
# this module maintains the global database with mmap

import mmap

""" the mmap file is position dependent!
use readlines and split

    line 1: heatpump auto/on/off
    line 2: userpump off
    line 3: srcselect air
"""
def init_conf_file():
    dummy="a"

def set_from_web():
    with open("geotech.conf", "r+b") as f:
        mm = mmap.mmap(f.fileno(), 0)
        for line in iter(mm.readline, b''):
            with open("globals.log","ab") as f2:
                f2.write(line)
    f2.close()
    mm.close()

if __name__ == '__main__':
    init_conf_file()

flask 应用程序在没有函数调用的情况下运行良好,我自己导入的模块也运行良好。

非常感谢任何帮助!

亨克

按照 Kevin Pasquarella 的建议,我添加了 app.debug = true。但是,当 apache 已经在主启动页面中加载时发生错误(apache 服务器错误),这没有帮助。但我随后查看了 apache 错误日志:

[Tue Aug 15 21:33:14.638580 2017] [mpm_event:notice] [pid 959:tid 3067240448] AH00489: Apache/2.4.18 (Ubuntu) OpenSSL/1.0.2g mod_wsgi/4.5.17 Python/3.4 configured -- resuming normal operations
[Tue Aug 15 21:33:14.639152 2017] [core:notice] [pid 959:tid 3067240448] AH00094: Command line: '/usr/sbin/apache2'
[Tue Aug 15 21:33:19.825211 2017] [wsgi:error] [pid 2461:tid 3031819312] [remote 192.168.178.85:9676] mod_wsgi (pid=2461): Target WSGI script '/home/groenhol/py_control/ui/webapp/main.wsgi' cannot be loaded as Python module.
[Tue Aug 15 21:33:19.826502 2017] [wsgi:error] [pid 2461:tid 3031819312] [remote 192.168.178.85:9676] mod_wsgi (pid=2461): Exception occurred processing WSGI script '/home/groenhol/py_control/ui/webapp/main.wsgi'.
[Tue Aug 15 21:33:19.967421 2017] [wsgi:error] [pid 2461:tid 3031819312] [remote 192.168.178.85:9676] Traceback (most recent call last):
[Tue Aug 15 21:33:19.970377 2017] [wsgi:error] [pid 2461:tid 3031819312] [remote 192.168.178.85:9676]   File "/home/groenhol/py_control/ui/webapp/main.wsgi", line 4, in <module>
[Tue Aug 15 21:33:19.970581 2017] [wsgi:error] [pid 2461:tid 3031819312] [remote 192.168.178.85:9676]     from main import app as application
[Tue Aug 15 21:33:19.971031 2017] [wsgi:error] [pid 2461:tid 3031819312] [remote 192.168.178.85:9676]   File "/home/groenhol/py_control/ui/webapp/main.py", line 41

然后我搜索了 mod_wsgi cannot be loaded as python module

答案表明我使用的 python 版本 (3.4) 和 wsgi 版本之间存在差异。

所以我检查了 /etc/apache2/mods-enabled/mod-wsgi.load 中的 wsgi 版本:

LoadModule wsgi_module "/home/groenhol/miniconda3/lib/python3.4/site-packages/mod_wsgi/server/mod_wsgi-py34.cpython-34m.so" WSGIPythonHome "/home/groenhol/miniconda3"

所以好像用python 3.4版本。

为了确保我使用搜索过程中发现的 ldd:

groenhol@arm:~/mod_wsgi-4.5.15$ ldd LoadModule wsgi_module "/home/groenhol/miniconda3/lib/python3.4/site-packages/mod_wsgi/server/mod_wsgi-py34.cpython-34m.so"
LoadModule:
ldd: ./LoadModule: No such file or directory
wsgi_module:
ldd: ./wsgi_module: No such file or directory
/home/groenhol/miniconda3/lib/python3.4/site-packages/mod_wsgi/server/mod_wsgi-py34.cpython-34m.so:
        linux-vdso.so.1 =>  (0xbee90000)
        libpython3.4m.so.1.0 => /home/groenhol/miniconda3/lib/libpython3.4m.so.1.0 (0xb6d40000)
        libpthread.so.0 => /lib/arm-linux-gnueabihf/libpthread.so.0 (0xb6d0f000)
        libc.so.6 => /lib/arm-linux-gnueabihf/libc.so.6 (0xb6c23000)
        /lib/ld-linux-armhf.so.3 (0x7f64d000)
        libdl.so.2 => /lib/arm-linux-gnueabihf/libdl.so.2 (0xb6c10000)
        libutil.so.1 => /lib/arm-linux-gnueabihf/libutil.so.1 (0xb6bfd000)
        libm.so.6 => /lib/arm-linux-gnueabihf/libm.so.6 (0xb6b85000)
        libgcc_s.so.1 => /lib/arm-linux-gnueabihf/libgcc_s.so.1 (0xb6b5c000)
groenhol@arm:~/mod_wsgi-4.5.15$ WSGIPythonHome "/home/groenhol/miniconda3"
-bash: WSGIPythonHome: command not found

据我所知 (http://modwsgi.readthedocs.io/en/develop/user-guides/checking-your-installation.html#python-shared-library) 这似乎没问题?

好的,那么下一步?

【问题讨论】:

在您的应用文件中的if __name__ == '__main__': app.run(...) 下,添加app.debug = True,以便您可以追溯错误及其来源,并将其添加到帖子中 从 traceback 看来这里有一个错误:from main import app as application 也许,但只有当我尝试从我导入的 *.py 模块调用函数时才会发生错误。似乎只有烧瓶函数在这种情况下有效。当我执行它时,模块本身运行良好,只是无法从烧瓶 main.py 中调用它,并在 apache 加载页面/应用程序时引发 wsgi 错误。 到目前为止,我发现可能的问题是编译 wsgi 所针对的 python 版本不同,以及关于据称需要在虚拟 python 环境中运行的评论。 嗯。当您在本地运行应用程序时会发生什么?是的,运行虚拟环境将有助于解决依赖性和版本问题 【参考方案1】:

结果证明解决方案非常简单:mod_wsgi 不喜欢你用空格和制表符来标识。我将所有 idents 更改为选项卡,然后代码运行!

我通过将代码更改为非常简单的代码来发现这一点,只需返回一个字符串并将其打印在烧瓶模板创建的网页上。然后我可以在 apache 日志中看到 wsgi 错误。在完整的代码中出现了其他错误,因此很难找出究竟是什么导致了错误。

我还处理了 Graham Dumpleton 的评论(即 apache 无法写入目录),我创建了一个共享目录 (/home/py_shared),并将其添加到 www-data 组(python 用户和apache 是该组的成员)。然后我将文件夹的组设置为 www-data 并使用 chmod g+w py_shared 和 chmod g+s py_shared 设置正确的权限。

这个话题在几个页面上讨论过,例如:

https://unix.stackexchange.com/questions/154776/create-files-that-both-www-data-and-myuser-can-edit

感谢您的所有建议!

【讨论】:

制表符和空格的混合是 Python 的一个普遍问题,并不特定于 mod_wsgi。如果您已覆盖编辑器设置以使选项卡宽度显示为 8 个字符以外的任何内容,则会出现问题。最好设置一个编辑器自动用空格替换选项卡。 感谢 Graham,对编程并不陌生(主要是 c/fortran),但这是我的第一个 python 项目,还有很多“功能”有待发现。【参考方案2】:

代码:

def set_from_web():
    with open("geotech.conf", "r+b") as f:
        mm = mmap.mmap(f.fileno(), 0)
        for line in iter(mm.readline, b''):
            with open("globals.log","ab") as f2:
                f2.write(line)
    f2.close()
    mm.close()

这将是一个问题,因为您使用的是文件的相对路径名。

进程的当前工作目录将不是您的代码所在的位置,也对 Apache 用户不可写。您需要使用绝对路径并确保 Apache 用户对文件具有写入权限。

见:

http://modwsgi.readthedocs.io/en/develop/user-guides/application-issues.html#application-working-directory http://modwsgi.readthedocs.io/en/develop/user-guides/application-issues.html#access-rights-of-apache-user

【讨论】:

谢谢你,我同意,还没有考虑过。我需要确保这得到纠正!但是,为了检查这是否是我的问题的原因,我将函数更改为 simpe dummy = "b" 语句。当在 main.py 中给出引用(而不是调用!)这个函数时,服务器会在加载时抛出错误。 问题是你没有从错误日志中包含完整的 Python 回溯,以及异常名称和描述。除非你能提供,否则只能猜测错误的来源。

以上是关于从烧瓶应用程序调用 python 函数的主要内容,如果未能解决你的问题,请参考以下文章

如何在不使用请求的情况下直接从代码调用烧瓶端点

你如何序列化一个python烧瓶变量?

记录烧瓶应用程序

从烧瓶中的 PostgreSQL 函数提交事务

无法从另一个 docker 容器中的烧瓶应用程序对驻留在 docker 容器中的 django 应用程序进行“获取”调用 [重复]

如何在烧瓶中重新加载python模块?