在我编写的flask应用程序中,我使用了一个外部库,该库可以使用环境变量进行配置。注意:我自己编写了这个外部库。因此,如有必要,我可以进行更改。从命令行运行时,运行带有以下内容的烧瓶服务器:
# env = python virtual environment ENV_VAR=foo ./env/bin/python myapp/webui.py
一切都如预期。但将它部署到Apache后,用SetEnv它不工作了。事实上,打印出os.environ到stderr(所以它在Apache日志中显示出来显示,该wsgi过程似乎是在一个非常不同的环境(一个,os.environ['PWD']好像是这样了。其实,它指向我的发展的文件夹。
SetEnv
os.environ
stderr
os.environ['PWD']
为了帮助确定问题,以下是作为独立的hello-world应用程序的应用程序的相关部分。错误输出和观察都在帖子的最后。
应用文件夹的布局: Python应用程式:
. ├── myapp.ini ├── setup.py └── testenv ├── __init__.py ├── model │ └── __init__.py └── webui.py
Apache文件夹(/var/www/michel/testenv):
/var/www/michel/testenv
. ├── env │ ├── [...] ├── logs │ ├── access.log │ └── error.log └── wsgi └── app.wsgi
myapp.ini
[app] somevar=somevalue
setup.py
from setuptools import setup, find_packages setup( name="testenv", version='1.0dev1', description="A test app", long_description="Hello World!", author="Some Author", author_email="author@example.com", license="BSD", include_package_data=True, install_requires = [ 'flask', ], packages=find_packages(exclude=["tests.*", "tests"]), zip_safe=False, )
testenv / init .py
# empty
testenv / model / init .py
from os.path import expanduser, join, exists from os import getcwd, getenv, pathsep import logging import sys __version__ = '1.0dev1' LOG = logging.getLogger(__name__) def find_config(): """ Searches for an appropriate config file. If found, return the filename, and the parsed search path """ path = [getcwd(), expanduser('~/.mycompany/myapp'), '/etc/mycompany/myapp'] env_path = getenv("MYAPP_PATH") config_filename = getenv("MYAPP_CONFIG", "myapp.ini") if env_path: path = env_path.split(pathsep) detected_conf = None for dir in path: conf_name = join(dir, config_filename) if exists(conf_name): detected_conf = conf_name break return detected_conf, path def load_config(): """ Load the config file. Raises an OSError if no file was found. """ from ConfigParser import SafeConfigParser conf, path = find_config() if not conf: raise OSError("No config file found! Search path was %r" % path) parser = SafeConfigParser() parser.read(conf) LOG.info("Loaded settings from %r" % conf) return parser try: CONF = load_config() except OSError, ex: # Give a helpful message instead of a scary stack-trace print >>sys.stderr, str(ex) sys.exit(1)
testenv / webui.py
from testenv.model import CONF from flask import Flask app = Flask(__name__) @app.route('/') def index(): return "Hello World %s!" % CONF.get('app', 'somevar') if __name__ == '__main__': app.debue = True app.run()
Apache配置
<VirtualHost *:80> ServerName testenv-test.my.fq.dn ServerAlias testenv-test WSGIDaemonProcess testenv user=michel threads=5 WSGIScriptAlias / /var/www/michel/testenv/wsgi/app.wsgi SetEnv MYAPP_PATH /var/www/michel/testenv/config <Directory /var/www/michel/testenv/wsgi> WSGIProcessGroup testenv WSGIApplicationGroup %{GLOBAL} Order deny,allow Allow from all </Directory> ErrorLog /var/www/michel/testenv/logs/error.log LogLevel warn CustomLog /var/www/michel/testenv/logs/access.log combined </VirtualHost>
app.wsgi
activate_this = '/var/www/michel/testenv/env/bin/activate_this.py' execfile(activate_this, dict(__file__=activate_this)) from os import getcwd import logging, sys from testenv.webui import app as application # You may want to change this if you are using another logging setup logging.basicConfig(stream=sys.stderr, level=logging.DEBUG) LOG = logging.getLogger(__name__) LOG.debug('Current path: {0}'.format(getcwd())) # Application config application.debug = False # vim: set ft=python :
错误与观察 这是apache错误日志的输出。
[Thu Jan 26 10:48:15 2012] [error] No config file found! Search path was ['/home/users/michel', '/home/users/michel/.mycompany/myapp', '/etc/mycompany/myapp'] [Thu Jan 26 10:48:15 2012] [error] [client 10.115.192.101] mod_wsgi (pid=17946): Target WSGI script '/var/www/michel/testenv/wsgi/app.wsgi' cannot be loaded as Python module. [Thu Jan 26 10:48:15 2012] [error] [client 10.115.192.101] mod_wsgi (pid=17946): SystemExit exception raised by WSGI script '/var/www/michel/testenv/wsgi/app.wsgi' ignored. [Thu Jan 26 10:48:15 2012] [error] [client 10.115.192.101] Traceback (most recent call last): [Thu Jan 26 10:48:15 2012] [error] [client 10.115.192.101] File "/var/www/michel/testenv/wsgi/app.wsgi", line 10, in <module> [Thu Jan 26 10:48:15 2012] [error] [client 10.115.192.101] from testenv.webui import app as application [Thu Jan 26 10:48:15 2012] [error] [client 10.115.192.101] File "/var/www/michel/testenv/env/lib/python2.6/site-packages/testenv-1.0dev1-py2.6.egg/testenv/webui.py", line 1, in <module> [Thu Jan 26 10:48:15 2012] [error] [client 10.115.192.101] from testenv.model import CONF [Thu Jan 26 10:48:15 2012] [error] [client 10.115.192.101] File "/var/www/michel/testenv/env/lib/python2.6/site-packages/testenv-1.0dev1-py2.6.egg/testenv/model/__init__.py", line 51, in <module> [Thu Jan 26 10:48:15 2012] [error] [client 10.115.192.101] sys.exit(1) [Thu Jan 26 10:48:15 2012] [error] [client 10.115.192.101] SystemExit: 1
我的第一个观察结果是环境变量MYAPP_PATH未出现os.environ(在此输出中不可见,但我对其进行了测试,并且不存在!)。这样,配置“解析器”将退回到默认路径。
我的第二个观察结果是配置文件列表的搜索路径,其/home/users/michel返回值为os.getcwd()。我实际上在期待里面的东西/var/www/michel/testenv。
我的直觉告诉我,我做配置解析的方式不正确。主要是因为代码是在导入时执行的。这使我想到,可能在正确设置WSGI环境之前执行配置解析代码。我在那儿吗?
简短讨论/切线问题 在这种情况下,你将如何进行配置解析?考虑到“模型”子文件夹实际上是一个外部模块,该模块也应在非wsgi应用程序中运行,并应提供一种配置数据库连接的方法。
就个人而言,我喜欢搜索配置文件的方式,但仍然能够覆盖它。只是,在导入时执行代码的事实使我的蜘蛛感像疯了似的。其背后的原理是:使用此模块的其他开发人员完全隐藏了配置处理(抽象屏障),并且“有效”。他们只需要导入模块(当然具有现有的配置文件),就可以直接跳入而无需了解任何数据库详细信息。这也为他们提供了一种使用不同数据库(开发/测试/部署)并在它们之间轻松切换的简便方法。
现在,在mod_wsgi内部,它不再:(
更新: 刚才,为了测试我的上述想法,我将其更改webui.py为以下内容:
import os from flask import Flask, jsonify app = Flask(__name__) @app.route('/') def index(): return jsonify(os.environ) if __name__ == '__main__': app.debue = True app.run()
该网页上的输出如下:
{ LANG: "C", APACHE_RUN_USER: "www-data", APACHE_PID_FILE: "/var/run/apache2.pid", PWD: "/home/users/michel/tmp/testenv", APACHE_RUN_GROUP: "www-data", PATH: "/usr/local/bin:/usr/bin:/bin", HOME: "/home/users/michel/" }
这显示了与其他调试方法所看到的环境相同的环境。所以我最初的想法是错误的。但是现在我意识到了一个陌生的东西。os.environment['PWD']设置为我的开发文件所在的文件夹。这不是在所有的应用程序运行的位置。陌生人os.getcwd()回来了/home/users/michel吗?这与我在中看到的不一致os.environ。应该不一样os.environ['PWD']吗?
os.environment['PWD']
os.getcwd()
/home/users/michel
但是,最重要的问题仍然存在:为什么在中找不到apache SetEnv(``MYAPP_PATH在这种情况下)设置的值os.environ?
apache SetEnv(``MYAPP_PATH
请注意,WSGI环境是在每次请求时通过应用environ程序对象的参数传递给应用程序的。该环境与保存在其中的过程环境完全无关os.environ。该SetEnv指令不起作用,os.environ并且无法通过Apache配置指令来影响流程环境中的内容。
environ
因此,你必须要做其他事情,getenviron或者要从apache中os.environ['PWD']获取MY_PATH。
getenviron
apache
MY_PATH
Flask将wsgi周围环境添加到请求中,而不是app.environ由下层完成werkzeug。因此,例如,在对应用程序的每个请求中,apache都会添加MYAPP_CONF密钥,并且你可以在任何可以访问请求的地方访问它request.environ.get('MYAPP_CONFIG')。
app.environ
werkzeug
MYAPP_CONF
request.environ.get('MYAPP_CONFIG')