150

Say I have a Python project that is structured as follows:

project
    /data
        test.csv
    /package
        __init__.py
        module.py
    main.py

__init__.py:

from .module import test

module.py:

import csv

with open("..data/test.csv") as f:
    test = [line for line in csv.reader(f)]

main.py:

import package

print(package.test)

When I run main.py I get the following error:

 C:\Users\Patrick\Desktop\project>python main.py
Traceback (most recent call last):
  File "main.py", line 1, in <module>
    import package
  File "C:\Users\Patrick\Desktop\project\package\__init__.py", line 1, in <module>
    from .module import test
  File "C:\Users\Patrick\Desktop\project\package\module.py", line 3, in <module>
    with open("../data/test.csv") as f:
FileNotFoundError: [Errno 2] No such file or directory: '../data/test.csv'

However, if I run module.py from the package directory, I don’t get any errors. So it seems that the relative path used in open(...) is only relative to where the originating file is being run from (i.e __name__ == "__main__")? How can deal with this, using relative paths only?

2

6 Answers 6

243

Relative paths are relative to current working directory. If you do not want your path to be relative, it must be absolute.

But there is an often used trick to build an absolute path from current script: use its __file__ special attribute:

from pathlib import Path

path = Path(__file__).parent / "../data/test.csv"
with path.open() as f:
    test = list(csv.reader(f))

This requires python 3.4+ (for the pathlib module).

If you still need to support older versions, you can get the same result with:

import csv
import os.path

my_path = os.path.abspath(os.path.dirname(__file__))
path = os.path.join(my_path, "../data/test.csv")
with open(path) as f:
    test = list(csv.reader(f))

[2020 edit: python3.4+ should now be the norm, so I moved the pathlib version inspired by jpyams' comment first]

Sign up to request clarification or add additional context in comments.

1 Comment

Of course, if you're using Python 3.4+, you can just use pathlib.Path: Path(__file__).parent.resolve()
52

For Python 3.4+:

import csv
from pathlib import Path

base_path = Path(__file__).parent
file_path = (base_path / "../data/test.csv").resolve()

with open(file_path) as f:
    test = [line for line in csv.reader(f)]

Comments

5

This worked for me.

with open('data/test.csv') as f:

 

2 Comments

This probably only works when you're running the code from the parent directory of data/, but not in general, so it's not likely to reliable solution.
In fact, it's not an answer to the question at all, since OP specifically asked about a solution that would work when run from anywhere rather than just from a specific folder.
4

My Python version is Python 3.5.2 and the solution proposed in the accepted answer didn't work for me. I've still were given an error

FileNotFoundError: [Errno 2] No such file or directory

when I was running my_script.py from the terminal. Although it worked fine when I run it through Run/Debug Configurations from the PyCharm IDE (PyCharm 2018.3.2 (Community Edition)).

Solution:

instead of using:

my_path = os.path.abspath(os.path.dirname(__file__)) + some_rel_dir_path

as suggested in the accepted answer, I used:

my_path = os.path.abspath(os.path.dirname(os.path.abspath(__file__))) + some_rel_dir_path

Explanation:

Changing os.path.dirname(__file__) to os.path.dirname(os.path.abspath(__file__)) solves the following problem:

When we run our script like that: python3 my_script.py the __file__ variable has a just a string value of "my_script.py" without path leading to that particular script. That is why method dirname(__file__) returns an empty string "". That is also the reason why my_path = os.path.abspath(os.path.dirname(__file__)) + some_rel_dir_path is actually the same thing as my_path = some_rel_dir_path. Consequently FileNotFoundError: [Errno 2] No such file or directory is given when trying to use open method because there is no directory like "some_rel_dir_path".

Running script from PyCharm IDE Running/Debug Configurations worked because it runs a command python3 /full/path/to/my_script.py (where "/full/path/to" is specified by us in "Working directory" variable in Run/Debug Configurations) instead of justpython3 my_script.py like it is done when we run it from the terminal.

1 Comment

It is indeed a caveat. It's so uncommon to run script explicitly that you seem to be the first to notice (usually you would put #!/usr/bin/env python at the top, mark it executable and run it as ./myscript.py). The pathlib.Path version does not have this gotcha though as is probably a better option since python3.4.
0

Try

with open(f"{os.path.dirname(sys.argv[0])}/data/test.csv", newline='') as f:

Comments

0

I was surprised when the following code worked.

import os

for file in os.listdir("../FutureBookList"):
    if file.endswith(".adoc"):
        filename, file_extension = os.path.splitext(file)
        print(filename)
        print(file_extension)
        continue
    else:
        continue

So, I checked the documentation and it says:

Changed in version 3.6: Accepts a path-like object.

path-like object:

An object representing a file system path. A path-like object is either a str or...

I did a little more digging and the following also works:

with open("../FutureBookList/file.txt") as file:
   data = file.read()

1 Comment

This is not really an answer to the question, which is about making such an open statement work from somewhere else than a specific directory.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.