The twisting, turning path to PYTHONPATH

Wow. The learning process sure is messy, and at times embarrassing.

Here’s how it all started:

I have a folder called ‘tools’ which contains various modules I wrote that I want to be accessible for import to all my Python projects. One of these modules is called ‘DbSession’ — it contains a class for handling various Sqlite database tasks. Each Python project is in its own folder, with the ‘tools’ folder as a ‘sibling’ under the same parent directory, as in this example:

C:\MyPath\Dropbox\projects\tools
C:\MyPath\Dropbox\projects\ProjectA
C:\MyPath\Dropbox\projects\ProjectB

I use Dropbox to sync my projects between two computers, one running Windows XP and one running Windows 7. The path to the Dropbox folder is different on each PC. No matter which PC I’m using, I want the modules in ‘tools’ to be accessible for importing to all my projects.

As previously posted, my first solution was to do a sys.path.append() in my code, but afterward I learned this was not the best solution because it would involve changing my code every time I switched computers or re-arranged directories. I then decided I’d like to be able to set the PATH in each PC appropriately so the imports will work on both without changing my code. After much Googling and sifting through what seemed to be a multitude of possible answers, I found what I *thought* was the solution, but upon further testing, discovered I was mistaken. I thought making the imports work in my project directories was just a matter of adding my ‘tools’ directory to the PATH variable. Here’s what I did (Windows XP machine):

Added ‘tools’ directory to PATH variable and confirmed it is set correctly by typing “PATH” on a command line:

C:\>PATH
PATH=C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem;C:\WINDOWS\System32
\WindowsPowerShell\v1.0\;C:\Python33;C:\Python33\Lib\site-packages;C:\Documents
and Settings\Rob\My Documents\Dropbox\projects\tools

The ‘tools’ directory contains a test ‘Hello World’ program, ‘hello.py’, which I can run from the C:\ directory just by typing the file name. This proves the PATH variable is set correctly to ‘tools’:

C:\hello.py
Hello, world!

..although if I try to run ‘hello.py’ from Python, Python can’t find it!

C:\python hello.py
python: can’t open file ‘hello.py’: [Errno 2] No such file or directory

Here’s my attempt at importing from the module ‘DbSession.py’, which resides in ‘tools.’ First, I ran Python from the C:\ directory. It can’t find the ‘DbSession’ module in ‘tools’, even though the above test with ‘hello.py’ at least showed the ‘tools’ directory was correctly set in PATH. I tried putting a blank ‘__init__.py’ file in ‘tools’, but it still didn’t work:

C:\python
Python 3.3.4 (v3.3.4:7ff62415e426, Feb 10 2014, 18:12:08) [MSC v.1600 32 bit (In
tel)] on win32
Type “help”, “copyright”, “credits” or “license” for more information.
>>>from DbSession import DbSession
Traceback (most recent call last):
File ““, line 1, in
ImportError: No module named ‘DbSession’

When I ran Python from the ‘tools’ directory the import worked fine, proving there’s no problem with the module itself:

c:\Documents and Settings\Rob\My Documents\Dropbox\projects\tools\python
Python 3.3.4 (v3.3.4:7ff62415e426, Feb 10 2014, 18:12:08) [MSC v.1600 32 bit (In
tel)] on win32
Type “help”, “copyright”, “credits” or “license” for more information.
>>>from DbSession import DbSession
>>>

So what’s up? I KNOW I set the PATH right…how come Python can’t import from the ‘tools’ directory into a project directory, when I can run the ‘hello.py’ code, which is also in ‘tools’, from any directory I want? This seemed so insane it tempted me to go back to using sys.path.append().

Until I ran across this one little nugget, in a 5-year-old article, on a site I’d never heard of:

“However, unlike PATH (which tells the operating system which directories to look for executable files in), PYTHONPATH is used by the Python interpreter to find out where to look for modules to import.”

BINGO! EUREKA! HALLELUJAH!

So, I need TWO SEPARATE PATH VARIABLES…the PATH variable to help Python find itself and its various packages:

path

..and the PYTHONPATH variable to help it find modules for importing!

pythonpath

I don’t know if it’s my misfiring brain, but somehow in all my recent travels, this simple truth eluded me. So, now my jolly good mates at Stereoplex have been blessed with a mention and a backlink. Cheers!

A Pythonic Valentines Day

One of my biggest challenges in growing my Python skills is time.  I have a full-time office job, a wife, and am active in mentoring high school and college students in our church’s youth and college group.  I also try to exercise and stay in shape, as I am no longer, by any stretch of the imagination, a young man. These activities are all vital and important to me, but I often find many days can slip by without me doing a single line of code, and all the cool things I learned a couple of weeks ago start to get fuzzy in my mind.  My tendency is to want to hammer away at a single, huge, complicated goal, such as my Tkinter/database interface, while my everyday Python skills languish, such as finding values in a dictionary or reading from a file. Therefore, I’ve decided that in between progress on gallant quests such as The Silvery Mystery Table or the GUI for the Application of Coolness, I’m going to continue working through the Real Python course, chapter by chapter.  After all, I did spend money on it, the least I can do is use it!  I have already found that it’s a great way to learn and reinforce those everyday skills that can make Python useful for me in a more immediate way.   So, without further ado, here’s my latest silly, yet fun and educational, effort.

Now that February is upon us, the Valentines Day displays at stores are out in full force.  But, for an aspiring Pythonian, what better way to impress his or her significant other than code that generates romantic poetry? I recently completed this assignment for the Real Python course, to test my understanding of lists, just in time to shore up the skills of shaky Shakespearians. There was a word list assigned which I originally hard-coded in as a dictionary, but I took it a step higher and wrote in some code to read word lists from a *.csv file.  That way, I was able to ask my wife Donna to supply the romantic words of her choice in a simple Notepad file, and voilà! – with the press of F5, out came words to woo her by!  Well, not quite…but at least it was a learning and skill-strengthening experience, and I was able to get her interested in my coding progress.  And as of now, we’re still married, so the poems couldn’t have been that bad.

Here’s the code, followed by a sample word list that can be copied and pasted into a *.csv text file.  Enjoy, and Happy Valentines Day!

"""Poetry 3.0
My project from "Real Python" 6.1.
In my humble opinion my code is more efficient than the "official"
solution.
This program reads lists of words from a *.csv file and uses them
to create random poems."""

from random import choice
import csv
import os

# A dictionary of word types. Key = word type, values = word list.
# We are creating empty lists in preparation for appending from
# CSV file

word_lists = {"nouns":[],"verbs":[],"adjectives":[],
              "prepositions":[],"adverbs":[]}

# The path to the folder where this code and the *.csv file reside.
# Change this as needed.
myPath = "D:\Dropbox\RealPython"

"""Assume the *.csv file is "words.csv." Each row is a set of words.
The first column of each row must be the word type. Example:
nouns,delight,smile,eyes,Valentine,heart,bowels,liver,hair
verbs,puckers,tinkles,blossoms,smiles,glow,dash
"""
with open(os.path.join(myPath, "words.csv"), "rb") as myFile:
    myFileReader = csv.reader(myFile)
    for row in myFileReader:
        try:
            if row[0] == "nouns":
                for w in range(1,len(row)):
                    word_lists["nouns"].append(row[w])
            elif row[0] == "verbs":
                for w in range(1,len(row)):
                    word_lists["verbs"].append(row[w])
            elif row[0] == "adjectives":
                for w in range(1,len(row)):
                    word_lists["adjectives"].append(row[w])
            elif row[0] == "prepositions":
                for w in range(1,len(row)):
                    word_lists["prepositions"].append(row[w])
            elif row[0] == "adverbs":
                for w in range(1,len(row)):
                    word_lists["adverbs"].append(row[w])

        except (IndexError):
            break

def create_list(kind, num):
# type = string = "nouns", "verbs", etc.
# num = number of words of type

    word_list = []

    # Add the first random choice word to the list
    selection = choice(word_lists[kind])
    word_list.append(selection)

    # Add the additional required words
    for n in range(0,num-1):

        # If the selected word is already in the list,
        # choose another random word.
        while selection in word_list:
            selection = choice(word_lists[kind])

        # Now that we know the selected word isn't already in the list,
        # append it.
        word_list.append(selection)

    return word_list

def makePoem():
    # Create the differnt types of word lists
    poem_nouns = create_list('nouns',3)
    poem_verbs = create_list('verbs',3)
    poem_adj = create_list('adjectives',3)
    poem_prep = create_list('prepositions',2)
    poem_adv = create_list('adverbs',1)

    # Check if first letter of first adjective is a vowel;
    # if it is, the starting word is "An", else it's "A".
    if "aeiou".find(poem_adj[0][0]) != -1:
        article = "An"
    else:
        article = "A"

    poem = "{} {} {}".format(article, poem_adj[0], poem_nouns[0]) + "\n" + "\n"
    poem = poem + "{} {} {} {} {} the {} {}".format(article, poem_adj[0],
                                            poem_nouns[0],poem_verbs[0],
                                            poem_prep[0],poem_adj[1],
                                            poem_nouns[1]) + "\n"
    poem = poem + "{}, the {} {}".format(poem_adv[0],poem_nouns[0],
                                         poem_verbs[1]) + "\n"
    poem = poem + "the {} {} {} a {} {}".format(poem_nouns[1],poem_verbs[2],
                                        poem_prep[1],poem_adj[2],poem_nouns[2]) + "\n"

    return poem

print makePoem()

Sample word list:

nouns,delight,smile,eyes,Valentine,heart,bowels,liver,hair,sunshine,hobbit,sky,dance,arms,arrow
verbs,puckers,tinkles,blossoms,smiles,glowing,lights,dashing,flashing,soaring,dashing
adjectives,gracious,ruby red,fastidious,quirky,passionate,toffeebrown,deep,cherished,straight,fierce,shiny,ecstatic,monstrous,golden,elvish
prepositions,of,by,after,along,from,to,toward,for,amid,beside,in,into,like,near,over,up,with,soon,somewhere,rather,terribly,
adverbs,succinctly,fetchingly,intently,magically,adoringly,recklessly,effortlessly,eagerly,gracefully