Thursday 14 January 2010 — This is almost 15 years old. Be careful.
I recently had need to run a piece of Python code before every invocation of the interpreter. In my case, it was to enable coverage testing of processes spawned by a test suite. Certain tests would run Python code in a subprocess, and I wanted to measure coverage within that process.
I added a function to coverage.py that is suitable for executing on startup. It examines the environment to determine whether to start measuring coverage. (This code isn’t released yet, but if you want to try it, it’s on the trunk at bitbucket.)
Perl has a command line switch (-M) for this, as does Ruby (-r), but Python doesn’t. I came up with two ways to do it for Python, both depending on the site module which is imported automatically on startup.
The first way is to create a sitecustomize.py file on the path somewhere. This module will be imported automatically by site.py. Any lines you add to that file are executed when Python starts:
import coverage
coverage.process_startup()
This is simple, but editing an existing sitecustomize.py could be awkward, and you may not even have permissions to do it.
The second way is to create a .pth file with an executable line. Usually lines in .pth files are directory names, but if the line starts with “import “, it is executed (what a hack!), so you can create a .pth file like this:
import coverage; coverage.process_startup()
This has the advantage that you can create a new file without worrying about touching existing files, all .pth files will be examined and interpreted. Unfortunately, you might have to give the file a strange name, since they are executed alphabetically, and a package you need may only be available after its .pth file has been seen. In my case, I named my .pth file zzz_coverage_process_start.pth to be sure it ran after coverage was added to the path.
Neither of these feels clean or elegant. I’m wondering if there are better techniques I’m overlooking? Python has a clean way to register functions to be called at process exit (atexit), which has been designed so that multiple authors of exit functions don’t need to cooperate to avoid interfering with each other. It’d be nice to have a similar facility for startup functions.
Comments
You can run this to find out where USER_SITE is for your Python installation.
python -c "import site; site._script()"
The usercustomize module is imported late during; after sitecustomize.
01_root_startup.pth
...
50_user_startup.pth
...
99_coverage_process_start.pth
I find this cleaner, but is a matter of taste.
My own solution was as follows: use your sitecustomize.py one (using a temporary directory to store it), but add the following code at the end so that the real sitecustomize.py is also loaded: Yes it's a hack, but it only needs doing once for any sitecustomize setup.
I suppose you would put
import user
at the top of 'myscript.py', using the convention of Andy Jenning's response, without the need to alter your invocations as he suggests. Any imported names in the ~/.pythonrc.py file are appended to the following help(user) text, so for example, if the rc file has
from math import *
you can access 'user.pi' in your sub-process, but 'pi' is not defined. Executable code in the file is, obviously, simply executed. If the rc file does not exist, it is as if your 'user' module import were omitted.
The doc string for 'user' is:
Help on module user:
NAME
user - Hook to allow user-specified customization code to run.
FILE
/usr/lib/python2.7/user.py
MODULE DOCS
http://docs.python.org/library/user
DESCRIPTION
As a policy, Python doesn't run user-specified code on startup of
Python programs (interactive sessions execute the script specified in
the PYTHONSTARTUP environment variable if it exists).
However, some programs or sites may find it convenient to allow users
to have a standard customization file, which gets run when a program
requests it. This module implements such a mechanism. A program
that wishes to use the mechanism must execute the statement
import user
The user module looks for a file .pythonrc.py in the user's home
directory and if it can be opened, execfile()s it in its own global
namespace. Errors during this phase are not caught; that's up to the
program that imports the user module, if it wishes.
The user's .pythonrc.py could conceivably test for sys.version if it
wishes to do different things depending on the Python version.
DATA
home = '/home/'
pythonrc = '/home//.pythonrc.py'
Add a comment: