Root-Relative Symlink Resolution

Windows documents that symlinks can target root-relative paths, for example \Target.

Python abides, and if the symlink is C:\Path\Symlink to \Target then C:\Target is used by os.stat and just about every built-in function and module.

The problem is that in reality, in Windows 10 and I think since a long time ago, the shell treats all relative paths the same.
Basically, they normalize C:\Path\Symlink\..\Target which in Windows is purely lexical and .. just removes the previous name.

The actual behavior in Windows Explorer and if you open the symlink’s properties is that it resolves to C:\Path\Target and not C:\Target.

I found a mention of this issue from 2012 here.

I kinda doubt that MS will fix that anytime soon.

What should take precedence, the documentation or the longstanding behavior?
Should Python at least use the OS version to match the actual end-user experience when reading symlinks, which I think is all versions?
Should it just fail on any root-relative symlink due to ambiguity and not end up in the wrong location?

Python users the WIN32 APIs to implement its filename handling.
If cmd.exe has processing that is in conflict wittb the WIN32 API then the API behaviour wis used.

Can you show that a program written in C/C++ treats the symlinks as CMD.exe when using the WIN32 API?

Hi, Barry.

I wish I could. GetFinalPathNameByHandleA is very convenient, but it does have bugs related to root-relative paths.
In C, if I could do it, I would resolve DOS paths walking the path like POSIX, applying symlinks encountered, and at the end calling GetFinalPathNameByHandleA to get the case-sensitive value.

But I wonder if it could be addressed at the Python level, when using os.realpath.
If you use os.readlink, you either get an absolute path, or a relative one, but relative to what?
It’s not relative to the CWD but relative to the symlink’s path, so there’s code path applies ntpath.join to it.
Should it just apply .. to relative symlinks?

My view (not a core dev) is that python must use the Win32 API under the os module. Its for an app to implement rules like the CMD.exe one, not python.

Also it is not appropiate to impose the Posix rules on a Windows system.


Also it is not appropiate to impose the Posix rules on a Windows system.

To clarify, I did not mean to impose the rules, just meant iterating through each component kinda like ntpath.realpath does, but in C, if there’s no C/C++ APIs that treat the symlinks as Windows Explorer.

I suppose you mean not for dot and dot-dot components, and trailing slashes.

My view (not a core dev) is that python must use the Win32 API under the os module.

I think that’s perfectly valid. I’d be interesing for me to get opinions about this Win32 API bug, reported here.
The API, exposed with nt._getfullpathname, can make paths with a single leading slash into UNC paths. That’s without following symlinks, but it’s the same behavior as GetFinalPathNameByHandleW.

That’s clearly wrong.

If it was an open-sourced API then it be better to report or contribute to that project and it would hopefully trickle down to OS distributions. Could the same be said about a Win32 API?

Would it be better for Python to develop its own C functions that replace or wrap GetFullPathNameW and GetFinalPathNameByHandleW so it has control over it’s bugs?

Hasn’t Windows had a POSIX layer for a very long time?

Ah! The answer is basically “NO”.

Thanks Juancarlo.

I confess, I fail to see how that applies.
But I get the answer is no.

Doesn’t Python already wrap GetFinalPathNameByHandleW from the Win32 API and calls it with the VOLUME_NAME_DOS flag to resolve paths?

In any case, not sure that built-in functions that take paths as argument should ignore the other bugs in the Win32 API.

I apologize. Mi mistake.

I haven’t used Windows in decades, and my understanding was that it implemented a POSIX subsystem. It is currently not so.