6

I want to pass in environment variables through Apache + mod_wsgi to tell my app whether it's running in a development or production environment. (This needs to happen when the app is launched, before any requests have come in.) For example:

<VirtualHost *:80>
    ...
    SetEnv ENVTYPE production
    WSGIScriptAlias /myapp  /apps/www/80/wsgi-scripts/myapp/run.py
</VirtualHost>
<VirtualHost *:8080>
    ...
    SetEnv ENVTYPE development
    WSGIScriptAlias /myapp  /apps/www/80/wsgi-scripts/myapp/run.py
</VirtualHost>

Based on the answer given to "Apache SetEnv not working as expected with mod_wsgi", I have setup run.py and the main __init__.py like this:

Old run.py:

from myapp import app as application

if __name__ == '__main__':
    application.run(debug=True, threaded=True)

New run.py :

import os
from myapp import app as _application

def application(environ, start_response):
    os.environ['ENVTYPE'] = environ['ENVTYPE']
    return _application(environ, start_response)

if __name__ == '__main__':
    _application.run(debug=True, threaded=True)

__init__.py

app = Flask(__name__)
app.config.from_object(__name__)
if os.environ.get('ENVTYPE') == 'production'
    # Setup DB and other stuff for prod environment
else:
    # Setup DB and other stuff for dev environment

Two problems

  1. This does not actually work. Within __init__.py, there is no 'ENVTYPE' key in os.envrion. Why not?

  2. I also don't know how to fix the if __name__ == '__main__' section so that I can run run.py as a local Flask app on my PC. What I put in my new run.py works, but only by calling _application instead of the wrapper function application. As a result _application doesn't have access to the environment variables I defined. How can I fix that line?

Thank you!

Community
  • 1
  • 1
George Adams
  • 458
  • 4
  • 15

1 Answers1

8

I solved problem #1 thanks to this tip

Note that the Flask app is imported inside the def application block — if you import it outside of this block, you won't be able to use the environment variables at the Flask app level or any file which is imported on application load. This is because the WSGI application hasn't loaded at the time you import the Flask application, so it can't pass the environment variables yet.

So the working version of run.py is:

import os

def application(environ, start_response):
    os.environ['ENVTYPE'] = environ['ENVTYPE']
    from myapp import app as _application
    return _application(environ, start_response)

if __name__ == '__main__':
    _application.run(debug=True, threaded=True)

I still haven't solved problem #2, however - I don't know how to call the program directly (if __name__ == '__main__') and have access to the ENVTYPE environment variable. Suggestions would still be appreciated. Maybe I'll break that issues out into its own StackOverflow question.

George Adams
  • 458
  • 4
  • 15
  • It is bad practice to update ``os.environ`` each request based on value from per request WSGI ``environ`` dictionary. This can cause subtle problems. You should really be using daemon mode, which you aren't, and base environment off the name of the process group using ``import mod_wsgi; name = mod_wsgi.process_group``. See http://blog.dscpl.com.au/2012/10/why-are-you-using-embedded-mode-of.html – Graham Dumpleton Jul 16 '16 at 08:52
  • @Graham, I thought I was. I even read your article a couple of weeks ago and thought I was setting it up right. Production is Apache 2.4.17 using the "event" MPM. Apache configuration includes the line `WSGIDaemonProcess myapp-prod user=gadams group=myteam threads=15 python-path=/apps/www/80/wsgi-scripts/MyApp:/apps/myteam/.virtualenvs/myapp/lib/python3.5/site-packages`, followed by `WSGIScriptAlias /myapp /apps/www/80/wsgi-scripts/MyApp/run.py`. Where did I go wrong? – George Adams Jul 18 '16 at 13:58
  • Oh, and there are further Apache configuration lines: `WSGIProcessGroup myapp-prod` and `WSGIApplicationGroup %{GLOBAL}` – George Adams Jul 18 '16 at 14:17
  • Graham, I also ran your test script from http://modwsgi.readthedocs.io/en/develop/user-guides/reloading-source-code.html and got back "DAEMON MODE" . – George Adams Jul 18 '16 at 14:28
  • So instead of using a per request environ variable, use ``import mod_wsgi; name = mod_wsgi.process_group`` and then check whether ``name`` is ``myapp-prod`` with an ``if``` statement. This can be done at global scope in WSGI script file. Protect the whole lot with ``except`` clause for ``ImportError`` so that if used without mod_wsgi can handle that case and take some other action. – Graham Dumpleton Jul 18 '16 at 14:35