jramm
jramm

Reputation: 6655

python: get directory two levels up

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!

Upvotes: 160

Views: 227065

Answers (18)

Markus Dutschke
Markus Dutschke

Reputation: 10606

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

Upvotes: 0

jkhadka
jkhadka

Reputation: 2543

Only with os.path

os.path.abspath(os.path.join(os.path.dirname(__file__),"../.."))

Upvotes: 0

Martin
Martin

Reputation: 454

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

Upvotes: 1

glopes
glopes

Reputation: 4370

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.

Upvotes: 3

tek
tek

Reputation: 51

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

Upvotes: 1

Jason Lim Ji Chen
Jason Lim Ji Chen

Reputation: 202

100% working answer:

os.path.abspath(os.path.join(os.getcwd() ,"../.."))

Upvotes: 4

zerocog
zerocog

Reputation: 1763

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

Upvotes: 11

Alexis
Alexis

Reputation: 518

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

Upvotes: 3

user11770816
user11770816

Reputation: 29

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

".././xyz"

Upvotes: 1

Sebi2020
Sebi2020

Reputation: 2150

Very easy:

Here is what you want:

import os.path as path

two_up =  path.abspath(path.join(__file__ ,"../.."))

Upvotes: 83

pythinker
pythinker

Reputation: 573

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

from pathlib import Path
two_up = Path(__file__).resolve().parents[1]

Upvotes: 33

zhukovgreen
zhukovgreen

Reputation: 1648

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)

Upvotes: 4

andyhasit
andyhasit

Reputation: 15289

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.

Upvotes: 33

Axel Heider
Axel Heider

Reputation: 626

You can use this as a generic solution:

import os

def getParentDir(path, level=1):
  return os.path.normpath( os.path.join(path, *([".."] * level)) )

Upvotes: 2

Lucidious
Lucidious

Reputation: 21

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

import os
two_up = os.path.normpath(os.path.join(__file__,'../'))

Upvotes: 2

AlanSE
AlanSE

Reputation: 2775

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.

Upvotes: -1

user4816510
user4816510

Reputation:

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

Upvotes: 5

Ffisegydd
Ffisegydd

Reputation: 53678

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.

Upvotes: 219

Related Questions