Friday, October 7, 2022

[SOLVED] How to test if Python is running from a virtualenv during package installation

Issue

I have a Python software which includes a configuration file and a manpage. To install these, I have the following line in my setup.py (as described on href="http://docs.python.org/2/distutils/setupscript.html#installing-additional-files" rel="nofollow">http://docs.python.org/2/distutils/setupscript.html#installing-additional-files):

data_files = [('/etc/foo', ['foo.conf']), ('/usr/share/man/man1', ['foo.1'])]

This works just fine when I want to install the software as root with python setup.py install, but of course fails in a virtualenv, as the user is not permitted to write to /etc and /usr/share/man.

What is the best practice to fix that? Check for VIRTUAL_ENV in the current environment and just not install those files at all? The software will look for foo.conf in the local directory, so that should be no problem. The user would miss the manpage, but there is no sane way to install it anyways, as man won't look for it anywhere near the virtualenv.


Solution

Ultimately it seems that your question is really about how to detect whether the running Python is in a virtualenv. To get to the bottom of this we must understand how virtualenv actually works.

When you run the activate script inside a virtualenv, it does two things:

  • it updates the PATH environment variable to include the bin directory from the virtualenv, so that when you run python the binary from the virtualenv will run.
  • it sets a variable VIRTUAL_ENV so that the activate script itself can keep track of the activation.

It's perfectly acceptable to directly run the python from the virtualenv, and at runtime python does not use the VIRTUAL_ENV variable at all. Instead, it determines the directory containing the running pythonbinary and uses the parent directory as its "prefix".

You can determine the system's prefix by importing the sys module and consulting sys.prefix. However, it would be a bad idea to depend on the value of this when a virtualenv is not activated because this is a build-time setting for Python that can easily be customized, and it will definitely vary between platforms.

However, Python does have one slight runtime difference when it runs from a virtualenv prefix vs. its compiled-in prefix: the sys package has an additional variable real_prefix that returns the prefix that is compiled into the Python binary. Therefore one could use this to recognize that Python is running in a non-default location, which is reasonably likely to mean it's running from a virtualenv:

import sys

if getattr(sys, "real_prefix", None) is not None:
    print "Maybe in a virtualenv"
else:
    print "Probably not in a virtualenv"

However, even this is not an exact science. All this really tells you is that the python binary is not at the location specified at compile time. It does not tell you whether the current user has access to write to /usr/share/man -- there are some (possibly edge) cases where this won't give you the right answer:

  • If the user has compiled his own Python from source in his home directory and its compiled prefix is /home/johnd/local-python then real_prefix won't be set but the user still has write access to his Python lib directory, and probably not write access to /etc or /usr/share/man

  • Likewise, on some systems the administrator may have granted group-write privileges on /usr/lib/python2.7 to a certain group of app developers so that they can install Python modules, but not have granted them write access to other system files.

So I think in the end the best you can do is a heuristic and it may be better to instead just avoid using absolute paths in data_files for any module you expect to be used inside a virtualenv. A compromise might be to simply split your module into two distributions, one representing the localizable source files and another representing the system-wide configuration to make it run. The latter can depend on the former so that users can still easily install it, but those using virtualenv have the option of using the other former directly.



Answered By - Martin Atkins
Answer Checked By - Pedro (WPSolving Volunteer)