I would like to see what is the best way to determine the current script directory in Python.
I discovered that, due to the many ways of calling Python code, it is ha
In Python 3.4+ you can use the simpler pathlib module:
from inspect import currentframe, getframeinfo
from pathlib import Path
filename = getframeinfo(currentframe()).filename
parent = Path(filename).resolve().parent
You can also use __file__
to avoid the inspect
module altogether:
from pathlib import Path
parent = Path(__file__).resolve().parent
.py
scripts as well as interactive usage:I frequently use the directory of my scripts (for accessing files stored along side them), but I also frequently run these scripts in an interactive shell for debugging purposes. I define __dirpath__
as:
.py
file, the file's base directory. This is always the correct path..ipyn
notebook, the current working directory. This is always the correct path, since Jupyter sets the working directory as the .ipynb
base directory.from pathlib import Path
__dirpath__ = Path(globals().get("__file__", "./_")).absolute().parent
import os
__dirpath__ = os.path.dirname(os.path.abspath(globals().get("__file__", "./_")))
globals()
returns all the global variables as a dictionary..get("__file__", "./_")
returns the value from the key "__file__"
if it exists in globals()
, otherwise it returns the provided default value "./_"
.__file__
(or "./_"
) into an absolute filepath, and then returns the filepath's base directory.Here is a partial solution, still better than all published ones so far.
import sys, os, os.path, inspect
#os.chdir("..")
if '__file__' not in locals():
__file__ = inspect.getframeinfo(inspect.currentframe())[0]
print os.path.dirname(os.path.abspath(__file__))
Now this works will all calls but if someone use chdir()
to change the current directory, this will also fail.
Notes:
sys.argv[0]
is not going to work, will return -c
if you execute the script with python -c "execfile('path-tester.py')"
If you really want to cover the case that a script is called via execfile(...)
, you can use the inspect
module to deduce the filename (including the path). As far as I am aware, this will work for all cases you listed:
filename = inspect.getframeinfo(inspect.currentframe()).filename
path = os.path.dirname(os.path.abspath(filename))
Hopefully this helps:-
If you run a script/module from anywhere you'll be able to access the __file__
variable which is a module variable representing the location of the script.
On the other hand, if you're using the interpreter you don't have access to that variable, where you'll get a name NameError
and os.getcwd()
will give you the incorrect directory if you're running the file from somewhere else.
This solution should give you what you're looking for in all cases:
from inspect import getsourcefile
from os.path import abspath
abspath(getsourcefile(lambda:0))
I haven't thoroughly tested it but it solved my problem.
#!/usr/bin/env python
import inspect
import os
import sys
def get_script_dir(follow_symlinks=True):
if getattr(sys, 'frozen', False): # py2exe, PyInstaller, cx_Freeze
path = os.path.abspath(sys.executable)
else:
path = inspect.getabsfile(get_script_dir)
if follow_symlinks:
path = os.path.realpath(path)
return os.path.dirname(path)
print(get_script_dir())
It works on CPython, Jython, Pypy. It works if the script is executed using execfile()
(sys.argv[0]
and __file__
-based solutions would fail here). It works if the script is inside an executable zip file (/an egg). It works if the script is "imported" (PYTHONPATH=/path/to/library.zip python -mscript_to_run
) from a zip file; it returns the archive path in this case. It works if the script is compiled into a standalone executable (sys.frozen
). It works for symlinks (realpath
eliminates symbolic links). It works in an interactive interpreter; it returns the current working directory in this case.