Wednesday, September 16, 2009

Following symlinks in Python

Today's Python trivia question: you have the path of a symbolic link. How do you get the full destination that link points to? If your answer is "use os.readlink", well it's not quite that easy. I'm not alone in finding the docs here confusing when they say: "the result may be either an absolute or relative pathname" and then only tell you how to interpret the result if it's relative. This guy wonders the same thing I did, which is how to know whether the returned value is a relative or absolute path?

I found a clue as to the way to handle both cases in the PathModule code, which is that you use os.path.isabs on the result to figure out what you got back. That module is a lot of baggage to pull in if you just want to correct this one issue, though, so here's a simpler function that knows how to handle both cases:

def readlinkabs(l):
Return an absolute path for the destination
of a symlink
assert (os.path.islink(l))
p = os.readlink(l)
if os.path.isabs(p):
return p
return os.path.join(os.path.dirname(l), p)

I hope my search engine cred bubbles me up so someone else trying to look this up like I did doesn't have to bother reinventing this particular tiny wheel.


Eric said...

Thanks for the tip. I was wondering about this myself.

Greg Smith said...

Chris Laskey writes:

The unpredictable behavior of os.readlink() means your function needs a simple addition to completely tame it.

The problem is when os.readlink() returns a relative path, it may or may not prepend "./" to the returned path. If it is prepended, the final os.path.join() will return an incorrect absolute path.

A simple solution is to simply wrap the p=os.readlink(l) line with a call to os.path.normpath(), making the new line:

p = os.path.normpath(os.readlink(l))