TL;DR: I discovered the PYTHONSTARTUP environment variable, and therefore the possibilities of .pythonrc.py files.

Published by

Happy accident, and context

I’ve been using Python since some time around 2004-2006. At least I think so. I remember Django was definitely pre-1.0, and it was some time during the rise of Ruby On Rails. I was deep into PHP, and expanding to other languages.

I can hardly believe I’ve been using it so long and not remember seeing mention of or have utilized the PYTHONSTARTUP environment variable. Alas, today was a perfect storm, for a few reasons.

After giving a quick tutorial on some Django details, I was skimming the docs for the manage.py shell command and the following block caught my eye:

…Disables reading the startup script for the “plain” Python interpreter. By default, the script pointed to by the PYTHONSTARTUP environment variable or the ~/.pythonrc.py script is read.

Recently I’ve been setting up a new machine, so dotfiles of all kinds are fresh on my mind. Having been in and out of my .zshrc several times today that .pythonrc.py filename stuck out.

Some quick “googling” and I found this gist and Dan’s post from 2014. They both have some cool examples that immediately inspired me to put this to use. It’s funny to me that Dan also mentions being a Python developer for “around 8 years” before discovering this feature. Somehow we have that in common.

It runs code when Python starts

The PYTHONSTARTUP environment variable can be set to point at a file containing python code, and according to the 2.x docs:

… the Python commands in that file are executed before the first prompt is displayed in interactive mode.

This is great, and both examples I found use a global .pythonrc.py in the user’s home directory.

I’m a fan of things like autoenv, however, and tend to use .env files in the base of most of my projects, to auto-activate a virtualenv, and other things. I wanted the same flexbility with my new found super powers.

autoenv + PYTHONSTARTUP = dynamic super powers

I’m definitely still a simpleton when it comes to bash/zsh scripting, but I was able to dynamically set a project-based startup file by adding this in my project’s .env:

export PYTHONSTARTUP=`pwd`/.pythonrc.py

Now, whenever I enter the project directory, the PYTHONSTARTUP environment variable is set to point at the .pythonrc.py file in the same directory.

Here’s an example of what I added, when trying this out and getting started (‘viewmaster’ is the name of an app I work on):

# Filename: .pythonrc.py
import datetime
import os
import pdb
import sys
from pprint import pprint

print 'Imported the following items for your convenience:'
print 'sys, os, datetime, pdb, pprint.pprint as pprint'

if 'DJANGO_SETTINGS_MODULE' in os.environ:

    from viewmaster import models as m
    print "viewmaster.models imported as m"

Yes, this works with the Django shell

Back to where I started. Not only does invoking python run this code, but the Django management shell command does as well – as mentioned in the docs where I first noticed this.

This is incredibly handy. I’ve often been debugging or toying with something, and had to constantly kill/reload the Django shell. Each time, I’d have to re-import my models, etc.

Now, this is what I see when I run ./manage.py shell in my project’s directory:

$ ./manage.py shell
Imported the following items for your convenience:
sys, os, datetime, pdb, pprint.pprint as pprint
viewmaster.models imported as m
Django settings imported as settings
imported Django 'django.utils.timezone' as timezone
Python 2.7.13 (default, Apr  4 2017, 08:47:57)
[GCC 4.2.1 Compatible Apple LLVM 8.1.0 (clang-802.0.38)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>>

… and I can immediately start using those things, as if I imported them myself! And that “models as m” shortcut allowed me to immediately start toying with a new model method I was testing.

Here’s the full output, from initial invocation, to show how convenient this is:

$ ./manage.py shell
Imported the following items for your convenience:
sys, os, datetime, pdb, pprint.pprint as pprint
viewmaster.models imported as m
Django settings imported as settings
imported Django 'django.utils.timezone' as timezone
Python 2.7.13 (default, Apr  4 2017, 08:47:57)
[GCC 4.2.1 Compatible Apple LLVM 8.1.0 (clang-802.0.38)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> i = m.Image.objects.first()
>>> i
<Image: Image #13; 2TI.1.9; Spanish (ES); TG!>
>>> timezone.now()
datetime.datetime(2017, 6, 3, 20, 38, 5, 705893, tzinfo=<UTC>)

Add some convenience to your life!

Because you’re doing awesome things, you deserve some convenience. Here’s some recommendations:

  1. Use something like autoenv,
  2. Set up a .pythonrc.py file with some convenient methods, imports, helpers for your project.
  3. Use your env management to set PYTHONSTARTUP to point at your new Python startup stuff!
  4. … or use a global file and load it for everything. Do what you want!
  5. Profit! (All that extra time!)

Did you know about this?

Are you already using this for something cool? Have I overlooked something useful?

Maybe you’re like me, and just discovered this functionality?

I’d love to hear about it… tweet at me!