Ok...I dont know where module x is, but I know that I need to get the path to the directory two levels up.
So, is there a more elegant way to do:
import os
two_up = os.path.dirname(os.path.dirname(__file__))
Solutions for both Python 2 and 3 are welcome!
18 Answers 18
You can use pathlib. Unfortunately this is only available in the stdlib for Python 3.4. If you have an older version you'll have to install a copy from PyPI here. This should be easy to do using pip.
from pathlib import Path
p = Path(__file__).parents[1]
print(p)
# /absolute/path/to/two/levels/up
This uses the parents sequence which provides access to the parent directories and chooses the 2nd one up.
Note that p in this case will be some form of Path object, with their own methods. If you need the paths as string then you can call str on them.
10 Comments
Path class's parents is dependent upon the execution location. If you execute your __file__ from its current directory the Path class will have no parents. Either @Sebi2020's answer should be accepted, or your original method should be used. I believe your original method is more readable.p = Path(os.path.abspath(__file__)).parents[1]os.path.abspath, you can also use Path(__file__).resolve().parents[1]p = Path(__file__).resolve().parents[1]. I also added it as an answer.Very easy:
Here is what you want:
import os.path as path
two_up = path.abspath(path.join(__file__ ,"../.."))
10 Comments
os.pardir rather than ...__file__.I was going to add this just to be silly, but also because it shows newcomers the potential usefulness of aliasing functions and/or imports.
Having written it, I think this code is more readable (i.e. lower time to grasp intention) than the other answers to date, and readability is (usually) king.
from os.path import dirname as up
two_up = up(up(__file__))
Note: you only want to do this kind of thing if your module is very small, or contextually cohesive.
Comments
The best solution (for python>= 3.4) when executing from any directory is:
from pathlib import Path
two_up = Path(__file__).resolve().parents[1]
Comments
For getting the directory 2 levels up:
import os.path as path
curr_dir=Path(os.path.dirname(os.path.abspath(__file__)))
two_dir_up_=os.fspath(Path(curr_dir.parent.parent).resolve())
I have done the following to go up two and drill down on other dir
default_config_dir=os.fspath(Path(curr_dir.parent.parent,
'data/config').resolve())
Personally, I find that using the os module is the easiest method as outlined below. If you are only going up one level, replace ('../..') with ('..').
import os
os.chdir('../..')
--Check:
os.getcwd()
2 Comments
More cross-platform implementation will be:
import pathlib
two_up = (pathlib.Path(__file__) / ".." / "..").resolve()
Using parent is not supported on Windows. Also need to add .resolve(), to:
Make the path absolute, resolving all symlinks on the way and also normalizing it (for example turning slashes into backslashes under Windows)
3 Comments
parent not working on Windows? This seems to have been fixed sometime since you wrote this comment. It works fine for me using Python 3.7.2 on Windows 10.parent working on windows 10 with python 3.6.5. Which version of python are you talking about @Zhukovgeen?100% working answer:
os.path.abspath(os.path.join(os.getcwd() ,"../.."))
1 Comment
Surprisingly it seems no one has yet explored this nice one-liner option:
import os
two_up = os.path.normpath(__file__).rsplit(os.sep, maxsplit=2)[0]
rsplit is interesting since the maxsplit parameter directly represents how many parent folders to move up and it always returns a result in just one pass through the path.
Comments
I have found that the following works well in 2.7.x
import os
two_up = os.path.normpath(os.path.join(__file__,'../'))
Comments
You can use this as a generic solution:
import os
def getParentDir(path, level=1):
return os.path.normpath( os.path.join(path, *([".."] * level)) )
3 Comments
(pathlib.Path('../../') ).resolve()
1 Comment
There is already an accepted answer, but for two levels up I think a chaining approach is arguably more readable:
pathlib.Path(__file__).parent.parent.resolve()
2 Comments
Assuming you want to access folder named xzy two folders up your python file. This works for me and platform independent.
".././xyz"
1 Comment
With Pathlib (recommended after Python 3.5, the/a general solution that works not only in file.py files, but also in Jupyter (or other kind of) notebook and Python shell is:
p = Path.cwd().resolve().parents[1]
You only need to substitute (__file__) for cwd() (current working directory).
Indeed it would even work just with:
p = Path().resolve().parents[1]
(and of course with .parent.parent instead of parents[1])
Comments
Only with os.path
os.path.abspath(os.path.join(os.path.dirname(__file__),"../.."))
Comments
pathlib: Path.parts is more flexible then Path.parents
what library to use
I would recommend using pathlib.Path. Its the modern and object oriented way of handling paths in python.
Hence pathlib is the first proposed library for File and Directory Access in the python docs: https://docs.python.org/3/library/filesys.html
how to handle paths
I want to point out the option to use the parts method. Simply because its a bit more flexible.
With parents, you start at the end of the path and navigate upwards. It is not really pratical to get up to the root of the path, as negative indexing is not supported.
With parts on the other hand side, you simply split the path into a tuple and can operate on with with all list operations python offers.
>>> from pathlib import Path
>>> p = Path("/a/b/c/d/e.txt")
>>> p.parts
('/', 'a', 'b', 'c', 'd', 'e.txt')
So a small comparison of different usecases with parts and parents:
# get two levels up
>>> p.parents[1]
PosixPath('/a/b/c')
>>> Path(*p.parts[:-2])
PosixPath('/a/b/c')
# get second level after root
>>> p.parents[len(p.parents) - 3]
PosixPath('/a/b')
>>> Path(*p.parts[:3])
PosixPath('/a/b')
# unfortunately negative indexing is not supported for the parents method
>>> p.parents[-3]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/home/user/.conda/envs/space221118/lib/python3.8/pathlib.py", line 620, in __getitem__
raise IndexError(idx)
IndexError: -3
Comments
I don't yet see a viable answer for 2.7 which doesn't require installing additional dependencies and also starts from the file's directory. It's not nice as a single-line solution, but there's nothing wrong with using the standard utilities.
import os
grandparent_dir = os.path.abspath( # Convert into absolute path string
os.path.join( # Current file's grandparent directory
os.path.join( # Current file's parent directory
os.path.dirname( # Current file's directory
os.path.abspath(__file__) # Current file path
),
os.pardir
),
os.pardir
)
)
print grandparent_dir
And to prove it works, here I start out in ~/Documents/notes just so that I show the current directory doesn't influence outcome. I put the file grandpa.py with that script in a folder called "scripts". It crawls up to the Documents dir and then to the user dir on a Mac.
(testing)AlanSE-OSX:notes AlanSE$ echo ~/Documents/scripts/grandpa.py
/Users/alancoding/Documents/scripts/grandpa.py
(testing)AlanSE-OSX:notes AlanSE$ python2.7 ~/Documents/scripts/grandpa.py
/Users/alancoding
This is the obvious extrapolation of the answer for the parent dir. Better to use a general solution than a less-good solution in fewer lines.
Comments
Explore related questions
See similar questions with these tags.
pathlibsolution is a little nicer and more readable, but isn't included with Python 2.7. I'd say stick with what you've got, maybe add a comment.pip install pathlib2option to maintain sanity in 2.7.os.path.abspath(os.path.join(os.getcwd(), os.path.pardir, os.path.pardir))