Find root of path
Asked Answered
T

7

10

I have a path:

path = foo/bar/baz

and I would like to determine what the base is. In this example it should return "foo".

There are a few ways I have tried:

root = re.search('(.+?)/(.+)', path).group(1)

paths = path.split('/')[0]
root = paths[0] if paths[0] or len(paths) <= 1 else '/'.join(paths[0:2])

def rootname(path):
  head,tail = os.path.split(path)
  if head != '':
   return rootname(head)
  else:
   return path
root = rootname(path)

Is there a more 'Pythonic' way to access the root directory?

i.e.

root = os.path.''rootname''(path)
Thermic answered 18/12, 2012 at 19:43 Comment(14)
Why in this case should it not return just /?Fusible
Could you define what you mean by the base?Caldwell
os.path.join(os.path.sep, '/foo/bar/baz'.split(os.path.sep)[1]) ?Stenger
Are you looking for os.path.dirname(os.path.dirname(x))? Calling it "base" when that's the exact opposite of what "basename" usually means in POSIX (and Python, etc.) is a bit confusing. Even if arguably it's POSIX that was originally confusing, it's such standard practice by now that you shouldn't fight it.Cumbrous
@DavidRobinson: You are correct. I edited my question to reflect my intent.Thermic
@NPE: What I had meant by 'base' is the root or highest level directory found in the path. Usually in python os.path modules it moves to lower directories but my desire is to find the highest in the path given which in this case is 'foo'.Thermic
@abarnert: a recursive call for this would result in what I'm looking for but I was hoping python already had something like this implemented.Thermic
@ThePracticalOne: What you're asking for is kind of weird. I'd implement this by first solving the more general function—writing a fullsplit that calls os.path.split iteratively (or recursively) until it ends—and then this function is trivial (it's parts[0] or os.path.join(*parts[:2])) instead of doing regexps and string functions. But I don't think you're going to get any simpler than that.Cumbrous
@mmgp: you have given the most accurate answer... however I think that now index [0] is enough rather than [1].Thermic
@abarnert: There is no python module/class that implements finding the 'rootname' of a path (not OS root name)? mmgp is almost precisely what I'm looking for. I was just hoping to make it even more simple...Thermic
@ThePracticalOne: Right, there is no built-in or stdlib module. There are lots of third-party modules. For example, I believe the path module that was rejected for stdlib inclusion a few times (which I think is an ancestor of both forkedpath and Unipath on PyPI, but I'm not sure) had a root that may or may not be what you want, and a splitall that you can build what you want from trivially.Cumbrous
Could you provide more examples. What do you want to get for: "foo", "./foo", "foo/", "/foo", "/foo/", "/foo/bar", "../bar", "/../bar" (note: leading slash), "foo/../bar", "/" (root directory), ".", "" (it might mean current directory)?Ume
"foo.d\ ir/bar/baz" -> "foo.d\ ir" is enough for me... Anything else is irrelavent.Thermic
@ThePracticalOne: if everything else is irrelevant then "foo/bar/baz".partition("/")[0] works for this particular narrow case but it is not applicable to paths in general and I wouldn't recommend it.Ume
C
3

If you're looking for a built-in or stdlib function that does exactly what you want, there is none.

If you're looking for a third-party library, try searching PyPI and ActiveState. You'll find path-manipulation libraries like pathlib (that has been included since Python 3.4), Unipath and forked-path (both based on an earlier library, a modified version of which was considered but never accepted for inclusion in Python 2), and dozens more. (Or, if you're using a framework like twisted or PyQt, it may come with one built in.)

Using such a library, you can generally get the root path in one line, like:

pathlib.Path(mypath).parts[0]
Unipath.Path(mypath).split_root()[0]
Unipath.Path(mypath).components()[0]
path.path(mypath).splitall()[0]

Their definition of "root" might not be exactly the same as yours. (As J.F. Sebastian points out, we don't actually know exactly what your definition of "root" is, so it's hard to guess whether it will match…) So, you might still need this kind of code:

components = path.path(mypath).splitall()[0]
return components[0] if len(components[0]) > 1 else components[0]/components[1]

But regardless, it'll be better than doing regexps and string manipulation.

(In fact, even if you don't use a third-party library, you should try to build everything out of os.path functions instead of string functions—that way, when you try it on Windows next year, there's a good chance it'll work out of the box, and if not it'll probably require only minor changes, as opposed to being absolutely guaranteed it won't work and might need a complete rewrite.)

Cumbrous answered 18/12, 2012 at 23:13 Comment(4)
also pathlib module defines .parts property. There is pep 428 for including it in Python 3.4.Ume
@J.F.Sebastian: Thanks. I'll have to read this over (I'm guessing it's roughtly similar to the one Jason Orendorff wrote that got rejected for Python 2 years ago), but I'll incorporate it into my answer.Cumbrous
@Cumbrous "If you're looking for a built-in or stdlib function that does exactly what you want, there is none." That is all I need to know... I will group os.path functions instead.Thermic
@J.F.Sebastian: On a quick glance, I like the way it incorporates the best of the Orendorff design, handles the NT/Posix division in a clean way (so you can think about it, but don't have to), and doesn't try to do everything under the sun like the earlier PEP 355 based on the Orendorff design. Unless I had to handle Python 2.6 or earlier, I'd probably use this if I were the OP, so I moved it to the top of the list of examples.Cumbrous
P
18
>>> import os
>>> path = '/foo/bar/baz'
>>> path = path.lstrip(os.sep)  # Get rid of leading "/" if any
>>> root = path[:path.index(os.sep)] if os.sep in path else path
>>> root
'foo'
Prefatory answered 8/12, 2013 at 2:17 Comment(3)
This should be the accepted answer, no need for a library when it can be solved with a one-liner.Fromenty
hmmm, seems not to be working for python3.4? the on-liner returns an empty string?Luxuriance
Probably your path starts with "/". I've edited the answer for this.Prefatory
C
3

If you're looking for a built-in or stdlib function that does exactly what you want, there is none.

If you're looking for a third-party library, try searching PyPI and ActiveState. You'll find path-manipulation libraries like pathlib (that has been included since Python 3.4), Unipath and forked-path (both based on an earlier library, a modified version of which was considered but never accepted for inclusion in Python 2), and dozens more. (Or, if you're using a framework like twisted or PyQt, it may come with one built in.)

Using such a library, you can generally get the root path in one line, like:

pathlib.Path(mypath).parts[0]
Unipath.Path(mypath).split_root()[0]
Unipath.Path(mypath).components()[0]
path.path(mypath).splitall()[0]

Their definition of "root" might not be exactly the same as yours. (As J.F. Sebastian points out, we don't actually know exactly what your definition of "root" is, so it's hard to guess whether it will match…) So, you might still need this kind of code:

components = path.path(mypath).splitall()[0]
return components[0] if len(components[0]) > 1 else components[0]/components[1]

But regardless, it'll be better than doing regexps and string manipulation.

(In fact, even if you don't use a third-party library, you should try to build everything out of os.path functions instead of string functions—that way, when you try it on Windows next year, there's a good chance it'll work out of the box, and if not it'll probably require only minor changes, as opposed to being absolutely guaranteed it won't work and might need a complete rewrite.)

Cumbrous answered 18/12, 2012 at 23:13 Comment(4)
also pathlib module defines .parts property. There is pep 428 for including it in Python 3.4.Ume
@J.F.Sebastian: Thanks. I'll have to read this over (I'm guessing it's roughtly similar to the one Jason Orendorff wrote that got rejected for Python 2 years ago), but I'll incorporate it into my answer.Cumbrous
@Cumbrous "If you're looking for a built-in or stdlib function that does exactly what you want, there is none." That is all I need to know... I will group os.path functions instead.Thermic
@J.F.Sebastian: On a quick glance, I like the way it incorporates the best of the Orendorff design, handles the NT/Posix division in a clean way (so you can think about it, but don't have to), and doesn't try to do everything under the sun like the earlier PEP 355 based on the Orendorff design. Unless I had to handle Python 2.6 or earlier, I'd probably use this if I were the OP, so I moved it to the top of the list of examples.Cumbrous
I
1

If I understand the requirements, you want the directory off the root directory, unless its a relative path then you want the directory off of whereever you are relative? You wont find a built in function to handle something like that. But if that is really what you need, use something like your second "way". I'd use os.path.sep instead of '/' though.

Iona answered 18/12, 2012 at 20:17 Comment(1)
I edited my requirements, all I would like is the root (not OS root) directory. I think that the os.path.sep may work too.Thermic
U
1

To get root's subdirectory the path belongs to — a "base" directory:

p = os.path
unc, rest = getattr(p, 'splitunc', lambda s: ('', s))(p.abspath(path))
drive, rest = p.splitdrive(rest)
basedir = p.join(unc, drive, p.sep, rest and rest.split(p.sep, 2)[1])

On Unix the code can be simplified: splitunc(), splitdrive() may be omitted.

Ume answered 18/12, 2012 at 21:12 Comment(2)
I am not looking for the OS root directory, sorry if I did not make this clear. This path could refer to another machine.Thermic
@ThePracticalOne: For Windows-style paths unc may point to another machine. For Unix-style paths / is always a root directory. Resources from different hosts are mounted under it i.e., they also have the usual structure: /some/path/.Ume
I
1

Similar to Peter's answer:

root = path.lstrip(os.sep).split(os.sep)[0]
Imitable answered 27/1, 2020 at 11:18 Comment(0)
G
0

A one-liner os.path.splitdrive(checkedpath)[0]

Gaff answered 5/11, 2014 at 14:37 Comment(0)
T
0

Using Python >= 3.10 (to have negative indices in parents):

from pathlib import Path
path = Path("foo/bar/baz")
print(path.parents[-2])      # prints 'foo'
path = Path("/foo/bar/baz")
print(path.parents[-2])      # prints '/foo'
print(path.parents[-2].stem) # prints 'foo'

The -2 index is because -1 will print ".".

Twentyfour answered 16/9, 2024 at 12:9 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.