152

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!

Shubham
  • 2,847
  • 4
  • 24
  • 37
jramm
  • 6,415
  • 4
  • 34
  • 73
  • 2
    I think your solution is perfectly fine. A `pathlib` solution 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. – jme Jan 08 '15 at 17:02
  • Perhaps worth adding the `pip install pathlib2` option to maintain sanity in 2.7. – jonathan Mar 19 '18 at 09:11
  • In case you want to get the directory two levels up of your current working directory you can perfectly use: `os.path.abspath(os.path.join(os.getcwd(), os.path.pardir, os.path.pardir))` – Elias May 03 '22 at 12:29

18 Answers18

204

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.

Ffisegydd
  • 51,807
  • 15
  • 147
  • 125
  • Thats a nice answer thanks and great for py3. For py2 it is possible not any better than my initial attempt as it creates and extra dependency – jramm Jan 09 '15 at 08:31
  • 20
    This should not be the accepted answer as the `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. – Red-Tune-84 Jun 20 '17 at 15:35
  • 1
    You may need to do `p = Path(os.path.abspath(__file__)).parents[1]` – Adam Raudonis Jun 15 '18 at 16:50
  • 6
    @AdamRaudonis Instead of `os.path.abspath`, you can also use `Path(__file__).resolve().parents[1]` – Tulio Casagrande Jul 04 '18 at 15:33
  • 10
    As @Kazanz mentioned, this solution doesn't work when executing from other paths. The best solution is: `p = Path(__file__).resolve().parents[1]`. I also added it as an answer. – pythinker Nov 06 '18 at 09:50
  • Be aware that the return type of parents[x] is not a string but an implementation of PurePath. You can always call str on it to get the actual value. – Fabrice E. Dec 17 '18 at 16:36
  • You need to call resolve on `__file__` like so: `Path(__file__).resolve().parents[1]`. Those recommendations to go back to `os.path` calls when we finally have Pathlib are ill advised. – mattmc3 Jan 13 '20 at 19:56
  • The answer proposed or any variation with .resolve() like the ones posted here, return `NameError: name '__file__' is not defined`. Here's my code just to reassuring what I'm doing: `Brat_Path = Path(__file__).resolve().parents[1]` Any idea of what could be the problem? – Martin Jan 20 '22 at 00:31
  • Never mind. It seems this just work for a file.py, not in a Jupyter or a Python shell for example... I'll add what i think it could be a more general answer that works in other cases too... – Martin Jan 20 '22 at 00:41
77

Very easy:

Here is what you want:

import os.path as path

two_up =  path.abspath(path.join(__file__ ,"../.."))
Sebi2020
  • 1,966
  • 1
  • 23
  • 40
  • 14
    And perhaps use `os.pardir` rather than `..`. – jme Jan 08 '15 at 16:56
  • does sys.argv[0] not return module that was run by python.exe? So this wouldn't necessary work if the module im interested in was imported from some other package..am I right here? – jramm Jan 08 '15 at 17:07
  • If you want to use it in a module instead use `__file__`. – Sebi2020 Jan 08 '15 at 18:33
  • 2
    @jme do you know an OS where you get the parent directory with another string than".."??? – Sebi2020 Jan 08 '15 at 18:42
  • 1
    @Sebi2020 Yep, notably the old Mac OS. Here's a list: https://en.wikipedia.org/wiki/Path_(computing) – jme Jan 08 '15 at 18:49
  • Joining `__file__` and something doesn't always give a valid path. – ebk Mar 31 '20 at 05:28
  • @ebk Can you provide more details? – Sebi2020 Mar 31 '20 at 10:12
  • @Sebi2020 I am sorry that I overlooked the `abspath` part. Your solution has no problem. Because the downvote is being locked by SO until next edit, I will revoke it then. – ebk Apr 01 '20 at 04:33
  • I used `up_three_levels = os.pardir + os.sep + os.pardir + os.sep + os.pardir` followed by `needed_system_path = os.path.abspath(os.path.join(os.getcwd(), up_three_levels))` – brethvoice Jun 14 '22 at 17:11
  • Or even `os.path.abspath(path+'../..')` – kl0z Apr 03 '23 at 22:28
33

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.

andyhasit
  • 14,137
  • 7
  • 49
  • 51
31

The best solution (for python >= 3.4) when executing from any directory is:

from pathlib import Path
two_up = Path(__file__).resolve().parents[1]
pythinker
  • 553
  • 5
  • 12
11

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()) 
zerocog
  • 1,703
  • 21
  • 32
5

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()
4

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)

Shubham
  • 2,847
  • 4
  • 24
  • 37
zhukovgreen
  • 1,551
  • 16
  • 26
  • 3
    Do you have a source for `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. – Nathan Feb 16 '19 at 18:03
  • i could get `parent` working on windows 10 with python 3.6.5. Which version of python are you talking about @Zhukovgeen? – ggulgulia May 13 '20 at 13:05
  • @ggulgulia I believe it was 3.7 – zhukovgreen May 13 '20 at 13:16
3

(pathlib.Path('../../') ).resolve()

Alexis
  • 498
  • 4
  • 13
3

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.

glopes
  • 4,038
  • 3
  • 26
  • 29
2

I have found that the following works well in 2.7.x

import os
two_up = os.path.normpath(os.path.join(__file__,'../'))
Lucidious
  • 21
  • 1
2

You can use this as a generic solution:

import os

def getParentDir(path, level=1):
  return os.path.normpath( os.path.join(path, *([".."] * level)) )
Axel Heider
  • 557
  • 4
  • 14
  • @JanSila: can be more specific, why is it not pythonic? For readability, it could have more comments what this does, yes - but in the end it's using standard python language features, see e.g. https://stackoverflow.com/questions/36901 – Axel Heider Oct 17 '17 at 04:43
  • It is a little dense, but I don't think this is too abstract and not readable. It is just a generalized version of other answers posted here. I would like a standard library way of doing things without the need for implementing a function, though. – ryanjdillon Aug 15 '19 at 07:55
  • Gives the path relative to the working directory not module – jramm Sep 22 '21 at 06:38
1

Assuming you want to access folder named xzy two folders up your python file. This works for me and platform independent.

".././xyz"

1

100% working answer:

os.path.abspath(os.path.join(os.getcwd() ,"../.."))
  • 1
    Nope, that gets the directory two levels up relative to the current working directory, *not* the current module. – jramm Sep 21 '21 at 06:05
1

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()
tek
  • 31
  • 2
  • This has already been mentioned in some of the other answers. When answering older questions that already have answers, please make sure you provide either a novel solution or a significantly better explanation than existing answers. – Tyler2P Nov 04 '21 at 16:46
  • No, it hasn't been mentioned for pathlib. – Martin Jan 20 '22 at 00:04
1

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])

Martin
  • 414
  • 7
  • 21
0

Only with os.path

os.path.abspath(os.path.join(os.path.dirname(__file__),"../.."))
jkhadka
  • 2,443
  • 8
  • 34
  • 56
0

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
Markus Dutschke
  • 9,341
  • 4
  • 63
  • 58
-1

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.

Community
  • 1
  • 1
AlanSE
  • 2,597
  • 2
  • 29
  • 22