121

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,705
  • 3
  • 21
  • 35
jramm
  • 5,853
  • 4
  • 29
  • 61
  • 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

16 Answers16

160

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
  • 47,839
  • 14
  • 137
  • 116
  • 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
  • 19
    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
  • 9
    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
67

Very easy:

Here is what you want:

import os.path as path

two_up =  path.abspath(path.join(__file__ ,"../.."))
Tim Cooper
  • 151,519
  • 37
  • 317
  • 271
Sebi2020
  • 1,633
  • 1
  • 18
  • 34
  • 13
    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
30

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
  • 12,671
  • 6
  • 46
  • 50
27

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
  • 493
  • 5
  • 11
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,454
  • 20
  • 27
4

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

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,705
  • 3
  • 21
  • 35
zhukovgreen
  • 1,362
  • 13
  • 24
  • 2
    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
  • 479
  • 4
  • 12
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
  • 496
  • 3
  • 12
  • @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

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
  • 21
  • 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
0

100% working answer:

os.path.abspath(os.path.join(os.getcwd() ,"../.."))
Jason Akon
  • 116
  • 6
  • 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
0

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
  • 3,668
  • 3
  • 23
  • 25
0

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
  • 395
  • 6
  • 19
-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,267
  • 1
  • 28
  • 20