В написанном мною фляжном приложении я использую внешнюю библиотеку, которую можно настроить с помощью переменных окружения. Примечание: я сам написал эту внешнюю библиотеку. Так что я мог внести изменения при необходимости. При запуске из командной строки и запуске фляжного сервера с:
# env = python virtual environment
ENV_VAR=foo ./env/bin/python myapp/webui.py
все работает, как ожидалось. Но после развертывания его на apache и использования SetEnv
он больше не работает . Фактически, при распечатке os.Environment
в stderr
(так что он отображается в журналах apache, показывает, что процесс wsgi
, похоже, находится в совершенно другой среде (например, os. Environment ['PWD']
, похоже, отключена . Фактически, это указывает на мою папку разработки.
Чтобы помочь идентифицировать проблему, ниже приведены соответствующие части приложения в виде автономное приложение hello-world. Вывод об ошибке и наблюдения находятся в самом конце сообщения.
Приложение Python:
.
├── myapp.ini
├── setup.py
└── testenv
├── __init__.py
├── model
│ └── __init__.py
└── webui.py
Папка Apache ( / var / www / michel / testenv
):
.
├── env
│ ├── [...]
├── logs
│ ├── access.log
│ └── error.log
└── wsgi
└── app.wsgi
[app]
somevar=somevalue
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,
)
# empty
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)
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()
<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>
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
(это не отображается e в этом выводе, но я проверил его, и его там нет!). Таким образом, "преобразователь" конфигурации возвращается к пути по умолчанию.
И мое второе наблюдение - это путь поиска для списков файлов конфигурации / home / users / michel
в качестве возвращаемого значения os.getcwd ()
. Я действительно ожидал чего-то внутри / var / www / michel / testenv
.
Мой инстинкт подсказывает мне, что способ разрешения конфигурации неправильный. В основном потому, что код выполняется во время импорта.Это наводит меня на мысль, что, возможно, код разрешения конфигурации выполняется до того, как среда WSGI будет правильно настроена. Я что-то там понял?
Как вы сделаете разрешение конфигурации в этом случае? Учитывая, что подпапка «model» на самом деле является внешним модулем, который также должен работать в приложениях, отличных от 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']
?
Однако остается самая важная проблема: почему значение устанавливается apache SetEnv
( MYAPP_PATH
в данном случае) не найден в os.environ
?
Ответ @rapadura правильный: у вас нет прямого доступа к значениям SetEnv
в вашей конфигурации Apache, но вы можете обойти это.
Если вы добавите обертку вокруг application
в файл app.wsgi
, вы можете установить os.environ
для каждого запроса. См. Следующий измененный app.wsgi
пример:
activate_this = '/var/www/michel/testenv/env/bin/activate_this.py'
execfile(activate_this, dict(__file__=activate_this))
from os import environ, 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
def application(req_environ, start_response):
environ['MYAPP_CONF'] = req_environ['MYAPP_CONF']
return _application(req_environ, start_response)
Если вы задали больше переменных окружения в своей конфигурации Apache, то вам нужно явно установить каждую из них в функции-обертке application
.