Login | Register For Free | Help
Search for: (Advanced)

Mailing List Archive: Python: Python

Relative versus absolute paths on Windows

 

 

Python python RSS feed   Index | Next | Previous | View Threaded


jaraco at jaraco

Nov 19, 2009, 8:37 AM

Post #1 of 8 (385 views)
Permalink
Relative versus absolute paths on Windows

The current implementation of Python (2.6.4, 3.1.1) treats \bar as a
relative path but reports it as an absolute path.

>>> ntpath.isabs('\\bar')
True
>>> ntpath.abspath('\\bar')
'C:\\bar'
>>> os.chdir('d:\\')
>>> ntpath.abspath('\\bar')
'd:\\bar'
>>> os.chdir('\\\\server\\share')
>>> ntpath.abspath('\\bar')
'\\\\server\\share\\bar'

In other words, paths without a drive letter are reported as absolute,
but treated as relative, except in a few special cases.

>>> ntpath.join('d:\\foo', '\\bar')
'\\bar'

In this case, \bar is treated as absolute, and not relative to d:\foo.

This inconsistency means that to effectively resolve one path relative
to another, one has to resort to explicit drive letter manipulation.
See http://stackoverflow.com/questions/1654659/find-a-path-in-windows-relative-to-another
for a case in point.

My understanding is that in Windows, a path is only absolute if it
contains a drive letter or it begins with a double-backslash.

Curiously, the .Net Framework seems to be subject to the same
limitation

# using IronPython 2.6RC2
>>> System.IO.Path.IsPathRooted('\\bar')
True
>>> System.IO.Path.Combine('d:\\foo', '\\bar') # expect d:\bar
'\\bar'

The documentation for Combine raises this issue in the Community
Content (http://msdn.microsoft.com/en-us/library/fyy7a5kt.aspx).

Furthermore, the Windows API utility is also consistent with this odd
behavior (http://msdn.microsoft.com/en-us/library/bb773660%28VS.
85%29.aspx).

The discussion here (http://groups.google.com/group/comp.os.ms-
windows.programmer.win32/browse_thread/thread/b2ff7a9d1d7c9b5e)
describes absolute paths consistent with my understanding:

Absolute paths have these characteristics.
Length is at least 2 characters, AND
( Second character is ":", OR First two characters is "\\" )

And according to WikiPedia (http://en.wikipedia.org/wiki/Path_
%28computing%29), "[an] absolute path is a path that points to the
same location on one file system regardless of the working directory."
By this definition, \bar is a relative path on Windows.

Ultimately, I don't care what the definition is. It seems to me,
however, that Python should have a function that can resolve one path
name relative to another, but due to these limitations, it does not. I
should point out that os.path.relpath is not the solution either as
the first parameter is always treated as relative to the current
directory (more info at http://bugs.python.org/issue7195).

I've built workarounds in https://svn.jaraco.com/jaraco/python/jaraco.windows/jaraco/windows/filesystem.py
as join() and resolve_path(). I'm happy to continue using these
workarounds, but I wanted to bring this issue to the attention of the
community for any comments or suggestions or answers to the following
questions.

What is the benefit of treating \path as absolute?
Should Python have built-in support for resolving one path relative to
another (such as is jaraco.windows.filesystem.resolve_path does)?
Given the long established behavior of Python and other platforms for
handling absolute paths in Windows, is there a way forward that
handles these cases more elegantly, or is the best approach to just
mumble something nasty under our breath and work around these issues
on a case-by-case basis?
--
http://mail.python.org/mailman/listinfo/python-list


ethan at stoneleaf

Nov 20, 2009, 12:52 PM

Post #2 of 8 (361 views)
Permalink
Re: Relative versus absolute paths on Windows [In reply to]

Jason R. Coombs wrote:
> The current implementation of Python (2.6.4, 3.1.1) treats \bar as a
> relative path but reports it as an absolute path.
>
>
>>>>ntpath.isabs('\\bar')
>
> True
>
>>>>ntpath.abspath('\\bar')
>
> 'C:\\bar'
>
>>>>os.chdir('d:\\')
>>>>ntpath.abspath('\\bar')
>
> 'd:\\bar'
>
>>>>os.chdir('\\\\server\\share')
>>>>ntpath.abspath('\\bar')
>
> '\\\\server\\share\\bar'
>
> In other words, paths without a drive letter are reported as absolute,
> but treated as relative, except in a few special cases.
>
>
>>>>ntpath.join('d:\\foo', '\\bar')
>
> '\\bar'
>
> In this case, \bar is treated as absolute, and not relative to d:\foo.

It is often said on this list that 'Python is not Java'. It is also
true that 'Windows is not Unix'.

Unlike the *nix world where there is a *single* root, and everything
else is relative to that, in the Windows world there are several roots
-- every drive has one!

So \bar is both an absolute path on whichever drive is active (or
specified), as well as relative if you happen to have more than one
drive, but it will not ever be relative on any specific drive. If you
want 'bar' to be relative to the current directory, do not specify a
leading '\'.

[snip]

> Ultimately, I don't care what the definition is. It seems to me,
> however, that Python should have a function that can resolve one path
> name relative to another, but due to these limitations, it does not. I
> should point out that os.path.relpath is not the solution either as
> the first parameter is always treated as relative to the current
> directory (more info at http://bugs.python.org/issue7195).

Please note that there *is not* a relative path that can take you from
c:\foo to d:\bar (see the point about multiple roots above).

> I've built workarounds in https://svn.jaraco.com/jaraco/python/jaraco.windows/jaraco/windows/filesystem.py
> as join() and resolve_path(). I'm happy to continue using these
> workarounds, but I wanted to bring this issue to the attention of the
> community for any comments or suggestions or answers to the following
> questions.
>
> What is the benefit of treating \path as absolute?

That was not Python's decision, but Windows'.

> Should Python have built-in support for resolving one path relative to
> another (such as is jaraco.windows.filesystem.resolve_path does)?

As I noted above, you are not returning a relative path when the paths
cross drive letter boundaries, or one of the paths specified is a
drive-absolute path (such as '\bar').

> Given the long established behavior of Python and other platforms for
> handling absolute paths in Windows, is there a way forward that
> handles these cases more elegantly, or is the best approach to just
> mumble something nasty under our breath and work around these issues
> on a case-by-case basis?

I just go with the mumbling, myself.

Hope this helps.

~Ethan~

P.S.
And now that I look up the comments in the bug-tracker, I see this was
all already pointed out to you.
--
http://mail.python.org/mailman/listinfo/python-list


greg.ewing at canterbury

Nov 20, 2009, 4:57 PM

Post #3 of 8 (362 views)
Permalink
Re: Relative versus absolute paths on Windows [In reply to]

Jason R. Coombs wrote:

> In other words, paths without a drive letter are reported as absolute,
> but treated as relative, except in a few special cases.

It's not clear what the result ought to be here, since
Windows drive-relative paths don't really fit into the
unix absolute/relative dichotomy. Arguments could be
made either way, and what's right probably depends on
the reason you're asking.

For cross-platform code, it's probably safest to avoid
drive-relative paths altogether -- convert them to
fully absolute paths at the earliest opportunity.

>>>>ntpath.join('d:\\foo', '\\bar')
>
> '\\bar'

This does seem like a bug, though -- the correct result
should really be 'd:\\bar', since that's what you would
get if you used the name '\\bar' with 'd:' as your current
drive.

--
Greg
--
http://mail.python.org/mailman/listinfo/python-list


jaraco at jaraco

Nov 23, 2009, 1:59 PM

Post #4 of 8 (339 views)
Permalink
Re: Relative versus absolute paths on Windows [In reply to]

On Nov 20, 3:52 pm, Ethan Furman <et...@stoneleaf.us> wrote:
> It is often said on this list that 'Python is not Java'.  It is also
> true that 'Windows is not Unix'.
>
> Unlike the *nix world where there is a *single* root, and everything
> else is relative to that, in the Windows world there are several roots
> -- every drive has one!

I acknowledge that the Windows paradigm is messy and less elegant than
Unix. I'm not trying to place blame here. I pointed out that even the
Windows interfaces don't seem to provide the functions needed to do
what I want to do. However, my point is that there is a need for path
resolution that respects a rooted path as relative to another path.

> So \bar is both an absolute path on whichever drive is active (or
> specified), as well as relative if you happen to have more than one
> drive, but it will not ever be relative on any specific drive.  If you
> want 'bar' to be relative to the current directory, do not specify a
> leading '\'.

Precisely. And my point is that if the path "specified" has a drive (d:
\foo) and one wants "\bar" relative to d:\foo, the result should be d:
\bar, not \bar, which is relative to the current drive.

I elaborate below.

> > Ultimately, I don't care what the definition is. It seems to me,
> > however, that Python should have a function that can resolve one path
> > name relative to another, but due to these limitations, it does not. I
> > should point out that os.path.relpath is not the solution either as
> > the first parameter is always treated as relative to the current
> > directory (more info athttp://bugs.python.org/issue7195).
>
> Please note that there *is not* a relative path that can take you from
> c:\foo to d:\bar  (see the point about multiple roots above).

I'm not looking for a relative path from c:\foo to d:\bar. I know the
"shortest relative path" from c:\foo to d:\bar is the absolute path d:
\bar. What I am looking for is a way to resolve one path relative to
another irrespective of the current directory.

Here's the use case: A symlink or shortcut (they can both be treated
for symbolic links for the sake of this discussion) is defined in a
particular location, let's say d:\foo. This symlink points to "\bar".

On Windows, the target of the symlink is treated as relative to the
location of the symlink itself, not the current directory when
accessed. In other words, \bar relative to d:\foo always resolves to d:
\bar. To effectively track down the target of a symbolic link, it
becomes necessary to perform this resolution.

> > What is the benefit of treating \path as absolute?
>
> That was not Python's decision, but Windows'.

Maybe so. My feeling is there's a good reason for it, based on the
multiple implementations that follow the same approach. Nevertheless,
that doesn't change the fact that despite the APIs, Windows does
internally resolve symlinks relative to the symlink location.

> > Should Python have built-in support for resolving one path relative to
> > another (such as is jaraco.windows.filesystem.resolve_path does)?
>
> As I noted above, you are not returning a relative path when the paths
> cross drive letter boundaries, or one of the paths specified is a
> drive-absolute path (such as '\bar').

To be clear, I'm not trying to find a relative path between two paths.
I'm trying to resolve one path relative to another. On Unix,
os.path.join does this. On Windows, there is no such function (except
jaraco.windows.filesystem.join).

> Hope this helps.

Thanks for taking the time to write a detailed response. I think I
understand the problem. What is frustrating is that the current
behavior seems buggy (others words, not mine) and a function that does
what many would expect isn't available.

> P.S.
> And now that I look up the comments in the bug-tracker, I see this was
> all already pointed out to you.

Based on the name and specification, I expected something different
from relpath, but after the discussion, I decided it wasn't the tool I
was seeking.
--
http://mail.python.org/mailman/listinfo/python-list


ethan at stoneleaf

Nov 23, 2009, 3:37 PM

Post #5 of 8 (338 views)
Permalink
Re: Relative versus absolute paths on Windows [In reply to]

Jason R. Coombs wrote:
> On Nov 20, 3:52 pm, Ethan Furman <et...@stoneleaf.us> wrote:
>
>>It is often said on this list that 'Python is not Java'. It is also
>>true that 'Windows is not Unix'.
>>
>>Unlike the *nix world where there is a *single* root, and everything
>>else is relative to that, in the Windows world there are several roots
>>-- every drive has one!
>
>
> I acknowledge that the Windows paradigm is messy and less elegant than
> Unix. I'm not trying to place blame here. I pointed out that even the
> Windows interfaces don't seem to provide the functions needed to do
> what I want to do. However, my point is that there is a need for path
> resolution that respects a rooted path as relative to another path.

I think for this discussion you would be better off using the word
'combine' rather than 'relative'.

>>So \bar is both an absolute path on whichever drive is active (or
>>specified), as well as relative if you happen to have more than one
>>drive, but it will not ever be relative on any specific drive. If you
>>want 'bar' to be relative to the current directory, do not specify a
>>leading '\'.
>
>
> Precisely. And my point is that if the path "specified" has a drive (d:
> \foo) and one wants "\bar" relative to d:\foo, the result should be d:
> \bar, not \bar, which is relative to the current drive.

s/relative/combined with/

> I elaborate below.
>
>
>>>Ultimately, I don't care what the definition is. It seems to me,
>>>however, that Python should have a function that can resolve one path
>>>name relative to another, but due to these limitations, it does not. I
>>>should point out that os.path.relpath is not the solution either as
>>>the first parameter is always treated as relative to the current
>>>directory (more info athttp://bugs.python.org/issue7195).
>>
>>Please note that there *is not* a relative path that can take you from
>>c:\foo to d:\bar (see the point about multiple roots above).
>
>
> I'm not looking for a relative path from c:\foo to d:\bar. I know the
> "shortest relative path" from c:\foo to d:\bar is the absolute path d:
> \bar. What I am looking for is a way to resolve one path relative to
> another irrespective of the current directory.
>
> Here's the use case: A symlink or shortcut (they can both be treated
> for symbolic links for the sake of this discussion) is defined in a
> particular location, let's say d:\foo. This symlink points to "\bar".
>
> On Windows, the target of the symlink is treated as relative to the
> location of the symlink itself, not the current directory when
> accessed. In other words, \bar relative to d:\foo always resolves to d:
> \bar. To effectively track down the target of a symbolic link, it
> becomes necessary to perform this resolution.
>
>
>>>What is the benefit of treating \path as absolute?
>>
>>That was not Python's decision, but Windows'.
>
>
> Maybe so. My feeling is there's a good reason for it, based on the
> multiple implementations that follow the same approach. Nevertheless,
> that doesn't change the fact that despite the APIs, Windows does
> internally resolve symlinks relative to the symlink location.

The multiple implementations exist because that's the way is has always
been on the Microsoft side. You have to play by their rules if you are
going to use their OS.

>>>Should Python have built-in support for resolving one path relative to
>>>another (such as is jaraco.windows.filesystem.resolve_path does)?
>>
>>As I noted above, you are not returning a relative path when the paths
>>cross drive letter boundaries, or one of the paths specified is a
>>drive-absolute path (such as '\bar').
>
>
> To be clear, I'm not trying to find a relative path between two paths.
> I'm trying to resolve one path relative to another. On Unix,
> os.path.join does this. On Windows, there is no such function (except
> jaraco.windows.filesystem.join).
>
>
>>Hope this helps.
>
>
> Thanks for taking the time to write a detailed response. I think I
> understand the problem. What is frustrating is that the current
> behavior seems buggy (others words, not mine) and a function that does
> what many would expect isn't available.
>
>
>>P.S.
>>And now that I look up the comments in the bug-tracker, I see this was
>>all already pointed out to you.
>
>
> Based on the name and specification, I expected something different
> from relpath, but after the discussion, I decided it wasn't the tool I
> was seeking.

relpath is definitely not the tool you are looking for. I do agree with
you that a function to combine two paths would be very handy... of
course, since relpath does work if the current drive is the one
specified (I think, haven't tested that statement), a wrapper function
that saved the current path, then switched drives, did the relpath
lookup, then switched back... hmmmm -- that would be a pain if the
drive didn't exist on the system doing the work... oh well.

Yup, if your function works, keep it! :D

~Ethan~
--
http://mail.python.org/mailman/listinfo/python-list


lists at cheimes

Nov 23, 2009, 5:23 PM

Post #6 of 8 (338 views)
Permalink
Re: Relative versus absolute paths on Windows [In reply to]

Gregory Ewing wrote:
>>>>> ntpath.join('d:\\foo', '\\bar')
>> '\\bar'
>
> This does seem like a bug, though -- the correct result
> should really be 'd:\\bar', since that's what you would
> get if you used the name '\\bar' with 'd:' as your current
> drive.

No, it's not a bug. Since \bar is an absolute path, all path segments
before the absolute path are ignored. This is documented at
http://docs.python.org/library/os.path.html#os.path.join

>>> ntpath.isabs("\\bar")
True
>>> ntpath.join("ignored", "\\bar")
'\\bar'

Posixpath follows the same rules, too.

>>> posixpath.join("..", "egg", "/var")
'/var'
>>> posixpath.join("..", "egg", "var")
'../egg/var'

Christian

--
http://mail.python.org/mailman/listinfo/python-list


ethan at stoneleaf

Nov 23, 2009, 5:44 PM

Post #7 of 8 (338 views)
Permalink
Re: Relative versus absolute paths on Windows [In reply to]

Christian Heimes wrote:
> Gregory Ewing wrote:
>
>>>>>>ntpath.join('d:\\foo', '\\bar')
>>>
>>>'\\bar'
>>
>>This does seem like a bug, though -- the correct result
>>should really be 'd:\\bar', since that's what you would
>>get if you used the name '\\bar' with 'd:' as your current
>>drive.
>
>
> No, it's not a bug. Since \bar is an absolute path, all path segments
> before the absolute path are ignored. This is documented at
> http://docs.python.org/library/os.path.html#os.path.join

Given that it's documented to work that way, I'll agree that this is not
a bug -- however, that doesn't mean it's /right/.

From the documentation:
Join one or more path components *intelligently*.

To me, that means taking into account the bizarre in the MS world of
multiple roots on a system... I know I have looked at os.path.join a
couple times also, and found the documented behavior to be completely
unsatisfactory. In the MS world a drive letter specifier should trump a
mere backslash, not be trumped by it.

Are there real world situations where the reverse is desired?

>>>>ntpath.isabs("\\bar")
>
> True
>
>>>>ntpath.join("ignored", "\\bar")
>
> '\\bar'
>
> Posixpath follows the same rules, too.
>
>
>>>>posixpath.join("..", "egg", "/var")
>
> '/var'
>
>>>>posixpath.join("..", "egg", "var")
>
> '../egg/var'

Posix has the luxury of running on sane systems with only one root.

~Ethan~
--
http://mail.python.org/mailman/listinfo/python-list


nobody at nowhere

Nov 23, 2009, 10:02 PM

Post #8 of 8 (336 views)
Permalink
Re: Relative versus absolute paths on Windows [In reply to]

On Tue, 24 Nov 2009 02:23:19 +0100, Christian Heimes wrote:

> Gregory Ewing wrote:
>>>>>> ntpath.join('d:\\foo', '\\bar')
>>> '\\bar'
>>
>> This does seem like a bug, though -- the correct result
>> should really be 'd:\\bar', since that's what you would
>> get if you used the name '\\bar' with 'd:' as your current
>> drive.
>
> No, it's not a bug. Since \bar is an absolute path, all path segments
> before the absolute path are ignored.

Except that a path without a drive letter *isn't* an absolute path in
any meaningful sense. The fact that os.path.isabs() disagrees is a defect
in os.path.isabs() (I won't call it a bug, as it's documented as behaving
that way).

The upshot is that the standard library is missing important functionality
on Windows, i.e. testing whether a path is absolute, and resolving a
relative path relative to an absolute path.

--
http://mail.python.org/mailman/listinfo/python-list

Python python RSS feed   Index | Next | Previous | View Threaded
 
 


Interested in having your list archived? Contact Gossamer Threads
 
  Web Applications & Managed Hosting Powered by Gossamer Threads Inc.