1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482
|
====================
Virtual Environments
====================
This document contains information about how to use Python virtual
environments with mod_wsgi. You can use a Python virtual environment
created using `virtualenv`_ and `virtualenvwrapper`_, or if using Python 3,
the ``pyvenv`` or ``python -m venv`` commands.
The purpose of a Python virtual environments is to allow one to create
multiple distinct Python environments for the same version of Python, but
with different sets of Python modules and packages installed. It is
recommended that you always use Python virtual environments and not install
additional Python packages direct into your Python installation.
A Python virtual environment is also required where it is necessary to run
multiple WSGI applications which have conflicting requirements as to what
version of a Python module or package needs to be installed. They can also
be used when distinct mod_wsgi daemon process groups are used to host WSGI
applications for different users and each user needs to be able to
separately install their own Python modules and packages.
How you configure mod_wsgi or setup your WSGI application script file for a
Python virtual environment will depend on your specific requirements. The
more common scenarios are explained below.
Location of the Virtual Environment
-----------------------------------
Whichever method you use to create a Python virtual environment, before you
use it with mod_wsgi, you should validate what the location of the Python
virtual environment is. If using `virtualenvwrapper`_ this may be a non
obvious directory hidden away under your home directory.
The way to determine the location of the Python virtual environment is to
activate the Python virtual environment from an interactive shell so it is
being used, and then run the command::
python -c 'import sys; print(sys.prefix)'
This will output the directory path you will use when setting up mod_wsgi
to use the Python virtual environment. For the purposes of the examples
below, it is assumed the location of any Python virtual environments are
under the ``/usr/local/venvs`` directory. A specific Python virtual
environment may thus return for ``sys.prefix``::
/usr/local/venvs/example
Note that this should be the root directory of the Python virtual
environment, which in turn contains the ``bin`` and ``lib`` directories for
the Python virtual environment. It is a common mistake when setting up a
Python virtual environment with mod_wsgi to use the full path to the
``python`` executable instead of the root directory. That will not work, so
do not use the path for the ``python`` executable as the location of the
Python virtual environment, it has to be the root directory.
Do be aware that the user that Apache runs your code as will need to be
able to access the Python virtual environment. On some Linux distributions,
the home directory of a user account is not accessible to other users.
Rather than change the permissions on your home directory, it might be
better to consider locating your WSGI application code and any Python
virtual environment outside of your home directory.
Virtual Environment and Python Version
--------------------------------------
When using a Python virtual environment with mod_wsgi, it is very important
that it has been created using the same Python installation that mod_wsgi
was originally compiled for. It is not possible to use a Python virtual
environment to force mod_wsgi to use a different Python version, or even a
different Python installation.
You cannot for example force mod_wsgi to use a Python virtual environment
created using Python 3.5 when mod_wsgi was originally compiled for Python
2.7. This is because the Python library for the Python installation it was
originally compiled against is linked directly into the mod_wsgi module.
In other words, Python is embedded within mod_wsgi. When mod_wsgi is used
it does not run the command line ``python`` program to run the interpreter
and thus why you can't force it to use a different Python installation.
The problem in trying to force mod_wsgi to use a different Python
installation than what it was compiled for, even where it is the same
Python version, is that the Python installation may itself not have been
compiled with the same options. This is especially a problem when it comes
to issues around how Python stores Unicode characters in memory.
The end result is that if you want to use a different Python installation
or version than what mod_wsgi was originally compiled for, you would need
to re-install mod_wsgi such that it is compiled for the Python installation
or version you do want to use. Do not try and use a Python virtual
environment from one Python installation or version with mod_wsgi, when
mod_wsgi was compiled for a different one.
Daemon Mode (Single Application)
--------------------------------
The preferred way of setting up mod_wsgi is to run each WSGI application
in its own daemon process group. This is called daemon mode. A typical
configuration for running a WSGI application in daemon mode would be::
WSGIDaemonProcess myapp
WSGIProcessGroup myapp
WSGIApplicationGroup %{GLOBAL}
WSGIScriptAlias / /some/path/project/myapp.wsgi
<Directory /some/path/project>
Require all granted
</Directory>
The ``WSGIDaemonProcess`` directive defines the daemon process group. The
``WSGIProcessGroup`` directive indicates that the WSGI application should be
run within the defined daemon process group.
As only the single application is being run within the daemon process
group, the ``WSGIApplicationGroup`` directive is also being used. When this
is used with the ``%{GLOBAL}`` value, it forces the WSGI application to run
in the main Python interpreter context of each process. This is preferred
in this scenario as some third party packages for Python which include C
extensions will not run in the Python sub interpreter contexts which
mod_wsgi would use by default. By using the main Python interpreter context
you eliminate the possibility of such third party packages for Python
causing problems.
To modify the configuration for this scenario to use a Python virtual
environment, all you need to do is add the ``python-home`` option to the
``WSGIDaemonProcess`` directive resulting in::
WSGIDaemonProcess myapp python-home=/usr/local/venvs/myapp
All the additonal Python packages and modules would then be installed into
that Python virtual environment.
Daemon Mode (Multiple Applications)
-----------------------------------
If instead of running each WSGI application in a separate daemon process
group as is the recommended practice, you are running multiple WSGI
applications in one daemon process group, a different approach to using
Python virtual environments is required.
For this scenario there are various ways the configuration could be set
up. If mounting each WSGI application explicitly you might be using::
WSGIDaemonProcess myapps
WSGIProcessGroup myapps
WSGIScriptAlias /myapp3 /some/path/project/myapp3.wsgi
WSGIScriptAlias /myapp2 /some/path/project/myapp2.wsgi
WSGIScriptAlias / /some/path/project/myapp1.wsgi
<Directory /some/path/project>
Require all granted
</Directory>
If instead the directory containing the WSGI application script files is
being mounted, you might be using::
WSGIDaemonProcess myapps
WSGIProcessGroup myapps
WSGIScriptAlias / /some/path/project/
<Directory /some/path/project>
Require all granted
</Directory>
The use of the ``WSGIDaemonProcess`` and ``WSGIProcessGroup`` is the same as
before, however the ``WSGIApplicationGroup`` directive is not being used.
When the ``WSGIApplicationGroup`` directive isn't being used to override
which Python interpreter context is being used, each WSGI application will
be run in its own Python sub interpreter context of the processes. This is
necessary as often WSGI application frameworks (Django being a prime
example), do not support running more than one instance of a WSGI
application using the framework, in the same Python interpreter context at
the same time.
In this scenario of running multiple WSGI applications in the same daemon
process group, more than one change is possibly required. The changes
required depend on whether or not all WSGI applications should share the
same Python virtual environment.
If all of the WSGI applications should share the same Python virtual
environment, then the same change as was performed above for the single
application case would be made. That is, add the ``python-home`` option
to the ``WSGIDaemonProcess`` directive::
WSGIDaemonProcess myapp python-home=/usr/local/venvs/myapps
All the additonal Python packages and modules that any of the WSGI
applications required would then be installed into that Python virtual
environment. Because it is a shared environment, they must all use the same
version of any specific Python package or module.
If instead of all WSGI applications using the same Python virtual
environment each needed their own, then a change will instead need to be
made in each of the WSGI script files for the applications.
How this is done will depend on how the Python virtual environment is
created.
If the Python virtual environment is created using `virtualenv`_ or
`virtualenvwrapper`_, the WSGI script for each application should be
modified to include code of the following form::
python_home = '/usr/local/envs/myapp1'
activate_this = python_home + '/bin/activate_this.py'
execfile(activate_this, dict(__file__=activate_this))
Because each WSGI application is to use a separate Python virtual
environment, the value of the ``python_home`` variable would be set
differently for each WSGI script file, with it referring to the root
directory of the respective Python virtual environments.
This code should be placed in the WSGI script file before any other module
imports in the WSGI script file, with the exception of ``from __future__``
imports used to enable Python feature flags.
Important to note is that when the Python virtual environment is activated
from within the WSGI script, what happens is a bit different to when the
``python-home`` option to ``WSGIDaemonProcess`` is used.
When activating the Python virtual environment from within the WSGI script
file, only the ``site-packages`` directory from the Python virtual
environment is being used. This directory will be added to the Python
module search path, along with any additional directories related to the
``site-packages`` directory registered using ``.pth`` files present in the
``site-packages`` directory. This will be placed at the start of the
existing ``sys.path``.
The consequence of this is that the Python virtual environment isn't
completely overriding the original Python installation the Python virtual
environment was created from. This means that if the main Python
installation had additional Python packages installed they will also
potentially be visible to the WSGI application.
That this occurs could cause confusion as you might for example think you
had all the packages you require listed in your ``requirements.txt`` file
for ``pip``, but didn't and so a package may not have been installed. If
that package was installed in the main Python installation, it would be
picked up from there, but it might be the wrong version and have
dependencies on versions of other packages for which you have different
versions installed in your Python virtual environment and which are found
instead of those in the main Python installation.
To avoid such problems, when activating the Python virtual environment
from within the WSGI script file, it is necessary to still set the
``python-home`` option of the ``WSGIDaemonProcess`` directive, but set it to
an empty Python virtual environment which has had no additional packages
installed::
WSGIDaemonProcess myapp python-home=/usr/local/venvs/empty
By doing this, the main Python installation will not be consulted and
instead it will fallback to the empty Python virtual environment. This
Python virtual environment should remain empty and you should not install
additional Python packages or modules into it, or you will cause the same
sort of conflicts that can arise with the main Python installation when it
was being used.
When needing to activate the Python virtual environment from within the
WSGI script file as described, it is preferred that you be using the either
`virtualenv`_ or `virtualenvwrapper`_ to create the Python virtual
environment. This is because they both provide the ``activate_this.py``
script file which does all the work of setting up ``sys.path``. When you
use either ``pyvenv`` or ``python -m venv`` with Python 3, no such
activation script is provided.
So use `virtualenv`_ or `virtualenvwrapper`_ if you can. If you cannot for
some reason and are stuck with ``pyvenv`` or ``python -m venv``, you can
instead use the following code in the WSGI script file::
python_home = '/usr/local/envs/myapp1'
import sys
import site
# Calculate path to site-packages directory.
python_version = '.'.join(map(str, sys.version_info[:2]))
site_packages = python_home + '/lib/python%s/site-packages' % python_version
# Add the site-packages directory.
site.addsitedir(site_packages)
As before this code should be placed in the WSGI script file before any
other module imports in the WSGI script file, with the exception of ``from
__future__`` imports used to enable Python feature flags.
When using this method, do be aware that the additions to the Python module
search path are made at the end of ``sys.path``. For that reason, you must
set the ``python-home`` option to ``WSGIDaemonProcess`` to the location of
an empty Python virtual environment. If you do not do this, any additional
Python package installed in the main Python installation will hide those in
the Python virtual environment for the application.
There is extra code you could add which would reorder ``sys.path`` to make
it work in an equivalent way to the ``activate_this.py`` script provided
when you use `virtualenv`_ or `virtualenvwrapper`_ but it is messy and more
trouble than it is worth::
python_home = '/usr/local/envs/myapp1'
import sys
import site
# Calculate path to site-packages directory.
python_version = '.'.join(map(str, sys.version_info[:2]))
site_packages = python_home + '/lib/python%s/site-packages' % python_version
site.addsitedir(site_packages)
# Remember original sys.path.
prev_sys_path = list(sys.path)
# Add the site-packages directory.
site.addsitedir(site_packages)
# Reorder sys.path so new directories at the front.
new_sys_path = []
for item in list(sys.path):
if item not in prev_sys_path:
new_sys_path.append(item)
sys.path.remove(item)
sys.path[:0] = new_sys_path
It is better to avoid needing to manually activate the Python virtual
environment from inside of a WSGI script by using a separate daemon process
group per WSGI application. At the minimum, at least avoid ``pyvenv`` and
``python -m venv``.
Embedded Mode (Single Application)
----------------------------------
The situation for running a single WSGI application in embedded mode is not
much different to running a single WSGI application in daemon mode. In the
case of embedded mode, there is though no ``WSGIDaemonProcess`` directive.
The typical configuration when running a single WSGI application in
embedded module might be::
WSGIScriptAlias / /some/path/project/myapp.wsgi
WSGIApplicationGroup %{GLOBAL}
<Directory /some/path/project>
Require all granted
</Directory>
The ``WSGIDaemonProcess`` and ``WSGIProcessGroup`` directives are gone, but
the ``WSGIApplicationGroup`` directive is still used to force the WSGI
application to run in the main Python interpreter context of each of the
Apache worker processes. This is to avoid those issues with some third
party packages for Python with C extensions as mentioned before.
In this scenario, to set the location of the Python virtual environment
to be used, the ``WSGIPythonHome`` directive is used::
WSGIPythonHome /usr/local/envs/myapp
Note that if the WSGI application is being setup within the context of an
Apache ``VirtualHost``, the ``WSGIPythonHome`` cannot be placed inside of
the ``VirtualHost``. Instead it must be placed outside of all
``VirtualHost`` definitions. This is because it applies to the whole Apache
instance and not just the single ``VirtualHost``.
Embedded Mode (Multiple Applications)
-------------------------------------
Running multiple applications in embedded mode is also similar to when
running multiple WSGI applications in one daemon process group. You still
need to ensure each WSGI application runs in its own Python sub interpreter
context to avoid potential issues with Python web frameworks that don't
allow more than one WSGI application to be using it at the same time in a
Python interpreter context.
If mounting each WSGI application explicitly you might be using::
WSGIScriptAlias /myapp3 /some/path/project/myapp3.wsgi
WSGIScriptAlias /myapp2 /some/path/project/myapp2.wsgi
WSGIScriptAlias / /some/path/project/myapp1.wsgi
<Directory /some/path/project>
Require all granted
</Directory>
If instead the directory containing the WSGI application script files is
being mounted, you might be using::
WSGIScriptAlias / /some/path/project/
<Directory /some/path/project>
Require all granted
</Directory>
In this scenario, to set the location of the Python virtual environment
to be used by all WSGI application, the ``WSGIPythonHome`` directive is used::
WSGIPythonHome /usr/local/envs/myapps
If the WSGI application is being setup within the context of an Apache
``VirtualHost``, the ``WSGIPythonHome`` cannot be placed inside of the
``VirtualHost``. Instead it must be placed outside of all ``VirtualHost``
definitions. This is because it applies to the whole Apache instance and
not just the single ``VirtualHost``.
If each WSGI application needs its own Python virtual environment, then
activation of the Python virtual environment needs to be performed in the
WSGI script itself as explained previously for the case of daemon mode
being used. The ``WSGIPythonHome`` directive should be used to refer to an
empty Python virtual environment if needed to ensure that any additional
Python packages in the main Python installation don't interfere with what
packages are installed in the Python virtual environment for each WSGI
application.
Adding Additional Module Directories
------------------------------------
The ``python-home`` option to ``WSGIDaemonProcess`` and the
``WSGIPythonHome`` directive are the preferred way of specifying the
location of the Python virtual environment to be used. If necessary,
activation of the Python virtual environment can also be performed from the
WSGI script file itself.
If you need to add additional directories to search for Python packages or
modules this can also be done. You may want to do this where you need to
specify where the actual WSGI application is located, where a WSGI script
file needs to import application specific modules.
If you are using daemon mode and want to add additional directories to the
Python module search path, you can use the ``python-path`` option to
``WSGIDaemonProcess``::
WSGIDaemonProcess myapp python-path=/some/path/project
This option would be in addition to the ``python-home`` option used to
specify where the Python virtual environment is located.
If you are using embedded mode, you can use the ``WSGIPythonPath``
directive::
WSGIPythonPath /some/path/project
This directive is in addition to the ``WSGIPythonHome`` directive used to
specify where the Python virtual environment is located.
In either case, if you need to specify more than one directory, they can be
separated using a ':' character.
If you are having to activate the Python virtual enviromment from within a
WSGI script and need to add additional directories to the Python module
search path, you should modify ``sys.path`` directly from the WSGI script
file.
Note that prior practice was that these ways of setting the Python module
search path were used to specify the location of the Python virtual
environment. Specifically, they were used to add the ``site-packages``
directory of the Python virtual environment. You should not do that.
The better way to specify the location of the Python virtual environment is
using the ``python-home`` option of the ``WSGIDaemonProcess`` directive for
daemon mode, or the ``WSGIPythonHome`` directive for embedded mode. These
ways of specifying the Python virtual environment have been available since
mod_wsgi 3.0 and Linux distributions have not shipped such an old version
of mod_wsgi for quite some time. If you are using the older way, please
update your configurations.
.. _virtualenv: http://pypi.python.org/pypi/virtualenv
.. _virtualenvwrapper: https://pypi.python.org/pypi/virtualenvwrapper
|