os.path.islink on windows with python os.path.islink on windows with python windows windows

os.path.islink on windows with python


The root problem is that you're using too old a version of Python. If you want to stick to 2.x, you will not be able to take advantage of new features added after early 2010.

One of those features is handling NTFS symlinks. That functionality was added in 3.2 in late 2010. (See the 3.2, 3.1, and 2.7 source for details.)

The reason Python didn't handle NTFS symlinks before then is that there was no such thing until late 2009. (IIRC, support was included in the 6.0 kernel, but userland support requires a service pack on Vista/2008; only 7/2008R2 and newer come with it built in. Plus, you need a new-enough MSVCRT to be able to access that userland support, and Python has an explicit policy of not upgrading to new Visual Studio versions within a minor release.)

The reason the code wasn't ported back to 2.x is that there will never be a 2.8, and bug fix releases like 2.7.3 (or 2.7.4) don't get new features, only bug fixes.

This has been reported as issue 13143, and the intended fix is to change the 2.7 docs to clarify that islink always returns False on Windows.

So, if you want to read NTFS symlinks under Windows, either upgrade to Python 3.2+, or you have to use win32api, ctypes, etc. to do it yourself.

Or, as Martijn Pieters suggests, instead of doing it yourself, use a third-party library like jaraco.windows that does it and/or borrow their code.

Or, if you really want, borrow the code from the 3.2 source and build a C extension module around it. If you trace down from ntpath to os to nt (which is actually posixmodule.c), I believe the guts of it are in win32_xstat_impl and win32_xstat_impl_w.


This is what I ended up using to determine if a file or a directory is a link in Windows 7:

from subprocess import check_output, CalledProcessErrorimport os.pathimport ctypesdef isLink(path):    if os.path.exists(path):        if os.path.isdir(path):            FILE_ATTRIBUTE_REPARSE_POINT = 0x0400            attributes = ctypes.windll.kernel32.GetFileAttributesW(unicode(path))            return (attributes & FILE_ATTRIBUTE_REPARSE_POINT) > 0        else:            command = ['dir', path]            try:                with open(os.devnull, 'w') as NULL_FILE:                    o0 = check_output(command, stderr=NULL_FILE, shell=True)            except CalledProcessError as e:                print e.output                return False            o1 = [s.strip() for s in o0.split('\n')]            if len(o1) < 6:                return False            else:                return 'SYMLINK' in o1[5]    else:        return False

EDIT: Modified code as per suggestions of Zitrax and Annan

EDIT: Added include statements as per the suggestion of shioko


For directories:

import os, ctypesdef IsSymlink(path):    FILE_ATTRIBUTE_REPARSE_POINT = 0x0400    return os.path.isdir(path) and (ctypes.windll.kernel32.GetFileAttributesW(unicode(path)) & FILE_ATTRIBUTE_REPARSE_POINT):

Source