Python 101: How to Find the Path of a Running Script

This topic is actually more complicated then it first appears. In this article, we’ll spend a little time looking at this problem and some of the solutions.

Several years ago, one of my friends on the wxPython users mailing list told me that he uses the following:

import os
script_path = os.path.dirname(os.path.abspath( __file__ ))

This works for me and is what I currently use. The code above returns the absolute path, by the way. According to the documentation, it is the equivalent of

import os
os.pathnormpath(join(os.getcwd(), path))

I’ve also seen people recommending the following similar solution:

import os

The documentation states that realpath will return the canonical path of the specified filename, eliminating any symbolic links encountered in the path, which sounds like it may be better than the solution I’ve been using.

Regardless, as some are likely to point out, you cannot use __file__ from within IDLE / the interpreter. If you do, you’ll get the following error:

Traceback (most recent call last):
  File "<pyshell#0>", line 1, in <module>
NameError: name '__file__' is not defined

You’ll end up with the same error if you happen to “freeze” your application by creating an executable with something like py2exe. For cases like this, some would recommend the following as an alternative:

import os

Now this will not work if you happen to call your script from another script. I’m also pretty sure that when I tried this with a frozen application and called the executable from a shortcut, it was returning the shortcut’s path instead of the executable’s. However, I may be getting that confused with os.getcwd() which will definitely not work reliably.

The solution I ended up with for the executables I created with py2exe was this one:

import os, sys

I’m pretty sure one of the core developers from wxPython had recommended using that, but I can’t be sure as I don’t seem to have that email any longer. Regardless, Mark Pilgrim, author of Dive Into Python, also recommends using os.path.abspath.

For now I think I will stick with either os.path.abspath or os.path.realpath for scripts and the above variation for my frozen Windows applications. I would be interested in hearing about your solution though. Let me know if you’ve found anything that works cross-platform and/or for frozen scripts.

Further Reading

Print Friendly
  • There is big difference between abspath and realpath.

    Try the following:

    – save your script as $HOME/scripts/tests/

    – symlink it to bin:

    ln -s ~/scripts/tests/ ~/bin/myscript

    – run it via latter (python ~/bin/myscript, or just myscript if bin is in PATH)

  • I suspect I never had to deal with this because I used to program for Windows almost exclusively. Thanks for the insight!

  • eryksun

    Assuming you have the SeCreateSymbolicLinkPrivilege NT user right (e.g. Administrators), you can create symbolic links with os.symlink or from the cmd shell with mklink. Test for links with os.path.islink.

    Currently ntpath.realpath aliases ntpath.abspath. But ntpath._getfinalpathname calls WinAPI
    `GetFinalPathNameByHandle` to get the real path. This could be used to implement ntpath.realpath in NT 6.0+.

  • Yeah, I’m aware that Windows supports it, but none of the computers I programmed for used that functionality and at that time, I didn’t know you could do that anyway.

  • Sean Ahern

    sys.path is the Python library module include path. It has nothing to do with directory the current script is in.

    % python
    >>> import sys
    >>> sys.path[0]

  • Ok, fine, the documentation:

    Search for `sys.path`, and:
    “As initialized upon program startup, the first item of this list, path[0], is *the directory containing the script* that was used to invoke the Python interpreter.”

    (Your example is a special case described later in that paragraph, and not relevant to the case in the post.)

  • Sean Ahern


    Yep, looks like you’re exactly right. My special case is a red herring.

  • Sean Ahern

    In fact, I’m tempted to delete my thread for fear of confusing the issue.