251

I used to open files that were in the same directory as the currently running Python script by simply using a command like:

open("Some file.txt", "r")

However, I discovered that when the script was run in Windows by double-clicking it, it would try to open the file from the wrong directory.

Since then I've used a command of the form

open(os.path.join(sys.path[0], "Some file.txt"), "r")

whenever I wanted to open a file. This works for my particular usage, but I'm not sure if sys.path[0] might fail in some other use case.

So my question is: What is the best and most reliable way to open a file that's in the same directory as the currently running Python script?

Here's what I've been able to figure out so far:

  • os.getcwd() and os.path.abspath('') return the "current working directory", not the script directory.

  • os.path.dirname(sys.argv[0]) and os.path.dirname(__file__) return the path used to call the script, which may be relative or even blank (if the script is in the cwd). Also, __file__ does not exist when the script is run in IDLE or PythonWin.

  • sys.path[0] and os.path.abspath(os.path.dirname(sys.argv[0])) seem to return the script directory. I'm not sure if there's any difference between these two.

Edit:

I just realized that what I want to do would be better described as "open a file in the same directory as the containing module". In other words, if I import a module I wrote that's in another directory, and that module opens a file, I want it to look for the file in the module's directory. I don't think anything I've found is able to do that...

martineau
  • 119,623
  • 25
  • 170
  • 301
dln385
  • 11,630
  • 12
  • 48
  • 58
  • 2
    "The wrong directory" is an incorrect analysis. Double-click or no, the script runs in *your* current working directory, not the directory where you saved the script. – tripleee Jan 04 '21 at 11:40
  • If you are assuming that "current working directory" means where your script is stored, that's wrong; these are two different things. Perhaps see also [What exactly is current working directory?](https://stackoverflow.com/questions/45591428/what-exactly-is-current-working-directory) – tripleee Apr 07 '22 at 09:58

9 Answers9

294

I always use:

__location__ = os.path.realpath(
    os.path.join(os.getcwd(), os.path.dirname(__file__)))

The join() call prepends the current working directory, but the documentation says that if some path is absolute, all other paths left of it are dropped. Therefore, getcwd() is dropped when dirname(__file__) returns an absolute path.

Also, the realpath call resolves symbolic links if any are found. This avoids troubles when deploying with setuptools on Linux systems (scripts are symlinked to /usr/bin/ -- at least on Debian).

You may the use the following to open up files in the same folder:

f = open(os.path.join(__location__, 'bundled-resource.jpg'))
# ...

I use this to bundle resources with several Django application on both Windows and Linux and it works like a charm!

Hubert Grzeskowiak
  • 15,137
  • 5
  • 57
  • 74
André Caron
  • 44,541
  • 12
  • 67
  • 125
  • 6
    If `__file__` cannot be used, then use `sys.argv[0]` instead of `dirname(__file__)`. The rest should work as expected. I like using `__file__` because in library code, `sys.argv[0]` might not point to your code at all, expecially if imported via some 3rd party script. – André Caron Oct 30 '10 at 19:17
  • 1
    The problem with this is it will vary if you the file being run is from the interrupter directly or if it is imported. See my answer for the differences between __file__ and sys.argv[0] – Zimm3r Oct 30 '10 at 23:03
  • So is it correct to say that the variation described in Zimm3r's answer is addressed by using `realpath( join( getcwd(), dirname(__file__) ))` as described here? – pianoJames Dec 18 '17 at 16:45
  • 1
    There is **no need to use `getcwd()`**, because the `os.path.abspath()` function **does this for you**. `os.path.realpath()` calls `os.path.abspath()`. – Martijn Pieters Jun 05 '21 at 11:45
  • 1
    The more modern way to do it would be via `open(pathlib.Path(__file__).parent / 'Some file.txt')` – martineau Oct 06 '21 at 19:01
  • If I'm opening a file from a subfolder of location e.g. "Images/bundled-resource.jpg", how should I adapt the instruction to open the file? – 123456frank Nov 26 '22 at 18:08
77

On Python 3.4, the pathlib module was added, and the following code will reliably open a file in the same directory as the current script:

from pathlib import Path

p = Path(__file__).with_name('file.txt')
with p.open('r') as f:
    print(f.read())

If you instead need the file path as a string for some open-like API, you can get it using absolute():

p = Path(__file__).with_name('file.txt')
filename = p.absolute()

NOTE: Python REPLs such as running the python command without a target or ipython do not expose __file__.

João Haas
  • 1,883
  • 13
  • 13
52

To quote from the Python documentation:

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. If the script directory is not available (e.g. if the interpreter is invoked interactively or if the script is read from standard input), path[0] is the empty string, which directs Python to search modules in the current directory first. Notice that the script directory is inserted before the entries inserted as a result of PYTHONPATH.

If you are running the script from the terminal, sys.path[0] is what you are looking for.

However, if you have:

barpath/bar.py
    import foopath.foo

foopath/foo.py
    print sys.path[0]  # you get barpath

So watch out!

P i
  • 29,020
  • 36
  • 159
  • 267
RED MONKEY
  • 3,025
  • 2
  • 21
  • 23
  • 17
    And for the full path of the file: `os.path.join(sys.path[0], 'some file.txt')`. That should handle spaces and slashes correctly on all systems. – Jacktose Sep 13 '16 at 04:00
  • This answer to the first question, not the one after the EDIT. – mcoolive Jun 17 '17 at 20:37
  • `sys.argv[0]` is set to whatever the parent process has told the OS to set it to. Use `#!/usr/env python` as the first line of a script named `test.py`, make the file executable, then use `alias foo test.py`. Or create a symlink to the file. Either way, now `sys.argv[0]` will be wrong. Or use one of the [`os.exec*()` functions](https://docs.python.org/3/library/os.html#os.execl) to run the script and pick your own value for the first argument. Do not rely on `sys.argv` to tell you the name of the script! When determining the directory for the script, use `__file__`. – Martijn Pieters Jun 05 '21 at 11:45
21

Ok here is what I do

sys.argv is always what you type into the terminal or use as the file path when executing it with python.exe or pythonw.exe

For example you can run the file text.py several ways, they each give you a different answer they always give you the path that python was typed.

    C:\Documents and Settings\Admin>python test.py
    sys.argv[0]: test.py
    C:\Documents and Settings\Admin>python "C:\Documents and Settings\Admin\test.py"
    sys.argv[0]: C:\Documents and Settings\Admin\test.py

Ok so know you can get the file name, great big deal, now to get the application directory you can know use os.path, specifically abspath and dirname

    import sys, os
    print os.path.dirname(os.path.abspath(sys.argv[0]))

That will output this:

   C:\Documents and Settings\Admin\

it will always output this no matter if you type python test.py or python "C:\Documents and Settings\Admin\test.py"

The problem with using __file__ Consider these two files test.py

import sys
import os

def paths():
        print "__file__: %s" % __file__
        print "sys.argv: %s" % sys.argv[0]

        a_f = os.path.abspath(__file__)
        a_s = os.path.abspath(sys.argv[0])

        print "abs __file__: %s" % a_f
        print "abs sys.argv: %s" % a_s

if __name__ == "__main__":
    paths()

import_test.py

import test
import sys

test.paths()

print "--------"
print __file__
print sys.argv[0]

Output of "python test.py"

C:\Documents and Settings\Admin>python test.py
__file__: test.py
sys.argv: test.py
abs __file__: C:\Documents and Settings\Admin\test.py
abs sys.argv: C:\Documents and Settings\Admin\test.py

Output of "python test_import.py"

C:\Documents and Settings\Admin>python test_import.py
__file__: C:\Documents and Settings\Admin\test.pyc
sys.argv: test_import.py
abs __file__: C:\Documents and Settings\Admin\test.pyc
abs sys.argv: C:\Documents and Settings\Admin\test_import.py
--------
test_import.py
test_import.py

So as you can see file gives you always the python file it is being run from, where as sys.argv[0] gives you the file that you ran from the interpreter always. Depending on your needs you will need to choose which one best fits your needs.

Zimm3r
  • 3,369
  • 5
  • 35
  • 53
  • 4
    This is an elaborate proof that the implementation reflects the documentation. `__file__` is *supposed* to "always give you the path to the current file", and `sys.argv[0]` is *supposed* to "always give the path of the script that initiated the process". In any case, using `__file__` in the script that gets invoked always gives you precise results. – André Caron Oct 31 '10 at 01:06
  • If you have the reference to `__file__` at the top level of the script, it will work as expected. – Matthew Schinckel Apr 13 '12 at 01:20
  • 1
    `sys.argv[0]` is set to whatever the parent process has told the OS to set it to. Use `#!/usr/env python` as the first line of `test.py`, make the file executable, then use `alias foo test.py`. Or create a symlink to the file. Either way, now `sys.argv[0]` will be wrong. Or use one of the [`os.exec*()` functions](https://docs.python.org/3/library/os.html#os.execl) to run the script and pick your own value for the first argument. Do not rely on `sys.argv` to tell you the name of the script! When determining the directory for the script, use `__file__`. – Martijn Pieters Jun 05 '21 at 11:41
2

I commonly use the following. It works for testing and probably other use cases as well.

with open(os.path.join(os.path.dirname(__file__), 'some_file.txt'), 'r') as f:

This answer is recommended in https://stackoverflow.com/questions/10174211/how-to-make-an-always-relative-to-current-module-file-path


Martin
  • 619
  • 5
  • 13
1

Can you try this simple approach just like this:

    import os

    my_local_file = os.path.join(os.path.dirname(__file__), 'some_file.txt')

    f = open(my_local_file,  "r")
    my_local_data = f.read()
Fernando Nogueira
  • 1,302
  • 1
  • 14
  • 22
0

Because I get an error trying to use __file__ or sys.argv[0] from emacs I do it that way:

from inspect import getfile
from pathlib import Path


script_path = getfile(lambda: None)
print(script_path)
parent_path = Path(script_path).parent
print(parent_path)

with open(parent_path/'Some file.txt', 'r') as obFile:
    print(obFile.read())
Raoul HATTERER
  • 522
  • 5
  • 5
-1

After trying all of this solutions, I still had different problems. So what I found the simplest way was to create a python file: config.py, with a dictionary containing the file's absolute path and import it into the script. something like

import config as cfg 
import pandas as pd 
pd.read_csv(cfg.paths['myfilepath'])

where config.py has inside:

paths = {'myfilepath': 'home/docs/...'}

It is not automatic but it is a good solution when you have to work in different directory or different machines.

  • The question is explicitly asking for the directory of the Python script, not a hard-coded value, regardless of how you "import it" – OneCricketeer Jan 30 '21 at 05:48
-2

I'd do it this way:

from os.path import abspath, exists

f_path = abspath("fooabar.txt")

if exists(f_path):
    with open(f_path) as f:
        print f.read()

The above code builds an absolute path to the file using abspath and is equivalent to using normpath(join(os.getcwd(), path)) [that's from the pydocs]. It then checks if that file actually exists and then uses a context manager to open it so you don't have to remember to call close on the file handle. IMHO, doing it this way will save you a lot of pain in the long run.

dcolish
  • 22,727
  • 1
  • 24
  • 25
  • This does not answer the poster's question. dln385 specifically said that `os.path.abspath` does not resolve paths to files in the same folder as the script if the script is not in the current directory. – André Caron Oct 30 '10 at 19:35
  • AH! I assumed the user was running this script in the same dir as the file they wanted to read, *NOT* in the module dir of something in their PYTHONPATH. That'll teach me to make assumptions... – dcolish Oct 30 '10 at 19:40
  • abspath will not work as it is impossible for python runtime to search on OS file system using function like this. – akshat thakar Sep 25 '19 at 09:43