How to refer to relative paths of resources when working with a code repository
Solution 1
Try to use a filename relative to the current files path. Example for './my_file':
fn = os.path.join(os.path.dirname(__file__), 'my_file')
In Python 3.4+ you can also use pathlib:
fn = pathlib.Path(__file__).parent / 'my_file'
Solution 2
If you are using setup tools or distribute (a setup.py install) then the "right" way to access these packaged resources seem to be using package_resources.
In your case the example would be
import pkg_resources
my_data = pkg_resources.resource_string(__name__, "foo.dat")
Which of course reads the resource and the read binary data would be the value of my_data
If you just need the filename you could also use
resource_filename(package_or_requirement, resource_name)
Example:
resource_filename("MyPackage","foo.dat")
The advantage is that its guaranteed to work even if it is an archive distribution like an egg.
See http://packages.python.org/distribute/pkg_resources.html#resourcemanager-api
Solution 3
In Python, paths are relative to the current working directory, which in most cases is the directory from which you run your program. The current working directory is very likely not as same as the directory of your module file, so using a path relative to your current module file is always a bad choice.
Using absolute path should be the best solution:
import os
package_dir = os.path.dirname(os.path.abspath(__file__))
thefile = os.path.join(package_dir,'test.cvs')
Solution 4
I often use something similar to this:
import os
DATA_DIR = os.path.abspath(os.path.join(os.path.dirname(__file__), 'datadir'))
# if you have more paths to set, you might want to shorten this as
here = lambda x: os.path.abspath(os.path.join(os.path.dirname(__file__), x))
DATA_DIR = here('datadir')
pathjoin = os.path.join
# ...
# later in script
for fn in os.listdir(DATA_DIR):
f = open(pathjoin(DATA_DIR, fn))
# ...
The variable
__file__
holds the file name of the script you write that code in, so you can make paths relative to script, but still written with absolute paths. It works quite well for several reasons:
- path is absolute, but still relative
- the project can still be deployed in a relative container
But you need to watch for platform compatibility - Windows' os.pathsep is different than UNIX.
Solution 5
import os
cwd = os.getcwd()
path = os.path.join(cwd, "my_file")
f = open(path)
You also try to normalize your cwd
using os.path.abspath(os.getcwd())
. More info here.
Related videos on Youtube
olamundo
Updated on October 21, 2021Comments
-
olamundo over 2 years
We are working with a code repository which is deployed to both Windows and Linux - sometimes in different directories. How should one of the modules inside the project refer to one of the non-Python resources in the project (CSV files, etc.)?
If we do something like:
thefile = open('test.csv')
or:
thefile = open('../somedirectory/test.csv')
It will work only when the script is run from one specific directory, or a subset of the directories.
What I would like to do is something like:
path = getBasePathOfProject() + '/somedirectory/test.csv' thefile = open(path)
Is it possible?
-
olamundo almost 15 yearsI think this solution will only work if the resource is in the same directory of the python file, or in a sub directory of it. How do you solve it when you have the following tree structure: /Project_Root_dir /python_files_dir /Some more subdirs here py_file.py /resources /some subdirs here resource_file.csv
-
olamundo almost 15 yearsSorry, the file tree got garbled on that last message... second try: you have your file at /Project_Root_dir/python_files_dir/some_subdirs/py_file.py and you have your resource file at /Project_Root_dir/resources/some_subdirs/resource_file.csv
-
c089 almost 15 yearsYou should be able to get to the parent directory using join(foo, '..'). So from /root/python_files/module/myfile, use os.path.join(os.path.dirname(
__file__
), '..', '..', 'resources') -
Paul Fisher over 13 yearsThis will only work if you're running the Python program from the same directory as the .py file in question. And in that case, you could just do
open('your/subfolder/of/choice')
anyway. -
user183037 almost 13 yearsand the OP mentioned that the code needs to work on both Windows and Linux. This will not.
-
davidchambers over 11 years
os.pardir
is slightly better than'..'
, though the two are equivalent on both POSIX and Windows. -
cedbeu over 11 years@davidchambers is it equivalent, or is it better, in the end?
-
cedbeu over 11 yearsvery few use-cases where the
cwd
is the path of a module, though -
c089 over 11 years@cedbeu: It is equivalent on every system I ever came across and I think every system python runs on today (please correct me if i'm wrong here). However, if you expect python to be ported to a system using a different path separator in the future and want your code to be ready for it, os.pardir will be more portable. I'd make the case that every programmer, even one who never read any python knows the meaning of "..", while "os.pardir" is a level o f indirection one would have to look up in the documentation so personally I'd stick to "..".
-
Pykler about 10 yearsI know this is an old answer, my preferred way is(/was maybe?) to use pkg_resources, but with the disappearance of zipped eggs, is there any harm in just using
__file__
like the good old days? -
trench about 8 yearsWill this work on a shared folder? Like if I have files and data on my PC in a shared folder, and then my coworker on the network runs the .py file, I need the code to reference the correct files.
-
deepelement almost 7 yearsThis is a solid approach. Even if the egg convention is going away, setuptools isn't and many are still installing deps against git repos where the egg is built at runtime
-
alexandra about 5 yearsit doesn't work inside a package, just from the same dir (or the working dir) set by the script.
-
Pro Q about 5 yearsDoes this have any security risks if I'm using it to expose files to a user via Flask? (In other words, will the user be able to see my entire directory structure, and if so, is that a bad thing?)
-
sgrpwr over 4 yearsThis won't work if user runs program using absolute path from different directory. e.g. python3 /usr/someone/test.py
-
Adam Parkin about 4 yearsWith the pathlib solution I get
AttributeError: 'PosixPath' object has no attribute 'split'
Python 3.7.5 -
LudvigH over 2 yearsIn Python 3.7+ you should prefer
importlib.resources
instead. The same stuff, but standard library and better performance.