4

It seems like realpath() does not resolve symlink (not shortcut - *.lnk) in Windows. I found an open bug for python3 here: https://bugs.python.org/issue9949

Is there any workaround? I'm mostly interested in Python 2.

Dmitry Petrov
  • 1,490
  • 1
  • 19
  • 34
  • Looks like the recommended approach is to delegate to Windows Shell, see [Reading the target of a .lnk file in Python?](http://stackoverflow.com/questions/397125/reading-the-target-of-a-lnk-file-in-python) and maybe click through the links [here](http://stackoverflow.com/q/6805881/699305). – alexis Apr 10 '17 at 22:16
  • 1
    This answer is about shortcuts (*.lnk files). But I'm looking for reading actual Windows symlinks. Windows command: `mklink` – Dmitry Petrov Apr 10 '17 at 22:18
  • I'm using Python 2 – Dmitry Petrov Apr 10 '17 at 22:24
  • My bad, I didn't realize they're two different things... – alexis Apr 10 '17 at 22:33
  • @alexis No problem. It is easy to be confused. It is actually hard links which Microsoft calls symlink - https://technet.microsoft.com/en-us/library/cc753194(v=ws.11).aspx. Or backward? I'm confused too. – Dmitry Petrov Apr 10 '17 at 22:37
  • 1
    Your best bet might be to look at one of the [patches](https://bugs.python.org/review/9949/patch/13878/51626) submitted for that bug (along with the context in the bug tracker) and see if you can pull that code into your own project. (Be aware of licensing issues.) – user2357112 Apr 10 '17 at 22:40
  • Well a hard link in the Unix sense is indistinguishable from the original filename, so it wouldn't need any extra work to handle. God knows what Microsoft means by it, though. – alexis Apr 10 '17 at 22:40
  • @alexis, it means the same thing. A hardlink is another name for a file in a volume, but only a file since NTFS doesn't support directory hardlinks. The Windows API has `CreateHardlink` to make a link, and `FindFirstFileNameW` and `FindNextFileNameW` to list them for a given file. – Eryk Sun Apr 10 '17 at 23:04
  • 1
    How much of that patch do you need? If you just want to resolve a link whose target exists and is accessible to the current user, then you can use ctypes to call `CreateFile` and `GetFinalPathNameByHandle`, without too much effort. – Eryk Sun Apr 10 '17 at 23:08
  • @eryksun Unix doesn't _allow_ hard links to directories either; otherwise the directory structure would no longer be a tree, which would create serious traversal problems. – alexis Apr 11 '17 at 14:43
  • @alexis, Unix filesystems support it in principle. The "." and ".." entries are hardlinks. Some Unix systems have allowed root to create hardlinks to directories, and apparently OS X uses it for its "Time Machine". In NTFS there are no directory hard links at all, and "." and ".." don't exist on disk; they're faked in the directory listing. – Eryk Sun Apr 11 '17 at 15:29
  • Right, I should have mentioned that root could do it; but I didn't know the situation on NTFS, thanks. I stand corrected. – alexis Apr 11 '17 at 20:19

2 Answers2

4

The Python function os.path.realpath() returns the canonical path of the given path, eliminating simlinks.

On Windows 7, this function does not work as expected as it fails to follow symbolic links (created with mklink. Since the bug has been opened for more than 7 years, I started looking for a workaround.

The solution I found was to replace

realpath = os.path.realpath(path)

by

realpath = path if not os.path.islink(path) else os.readlink(path)

Function os.readlink() does work correctly on Windows 7.

Étienne
  • 407
  • 4
  • 15
  • 2
    The workaround is not quite the same, because it will only follow a symlink for the full path - e.g. if `/foo` is a symlink to `/bar`, `realpath('/foo/qux')` would give you `'/bar/qux'` (where it works). It works well enough for many use cases, though. – Thomas K Jan 24 '18 at 09:06
  • 1
    `os.readlink()` was [fixed in python 3.7](https://bugs.python.org/issue29248#msg312082). – Tom Hale Jul 30 '20 at 06:37
1

The jaraco.windows project supplies jaraco.windows.filesystem.get_final_path which may be what you're looking for. With the 3.9 release, it also has a jaraco.windows.filesystem.backports module, which presents the realpath function as proposed for that Python bug. Please give one or both of those functions a try and report back how it works.

If the realpath function works well for you, there is a patch_os_module, which as its name suggests, can patch the os module such that os.path.realpath has the behavior from the backport. At the time of this writing, it doesn't do that, but if that would be useful for your use-case, I'd be happy to add it.

Feel free to contribute to the project in Github. And please excuse the docs - they don't build properly on a Unix system such as RTD.

Jason R. Coombs
  • 41,115
  • 10
  • 83
  • 93