os.path.islink on windows with python
Asked Answered
T

5

14

On Windows 7 with Python 2.7 how can I detect if a path is a symbolic link? This does not work os.path.islink(), it says it returns false if false or not supported and the path I'm providing is definitely a symbolic link so I'm assuming it's not supported on windows? What can I do?

Tetroxide answered 6/3, 2013 at 21:32 Comment(3)
This may provide some help.Pentha
take a look at the jaraco.windows package; it has to use ctypes to support this. Python 3.2 does support Windows symlinks properly.Creight
The docs ( #15259006 ) say "Always False if symbolic links are not supported by the Python runtime." The Windows Python runtime does not support symbolic links. So, as far as the docs are concerned - even if it is not useful - it does work as intended. lolVaria
B
21

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.

Blackstone answered 6/3, 2013 at 22:5 Comment(2)
You must be misinformed. reparse points do exist from windows NT 5 (windows 2000) with NTFS 3.0. And it was functional. python implementation is simply lazy or also misinformed.Drud
Doesn't work for directory junctions in python 3.6: os.path.islink('c:\\users\\erik\\Local Settings')Canvass
W
7

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, CalledProcessError
import os.path
import ctypes
def 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

Wish answered 26/11, 2014 at 23:51 Comment(4)
It's worth catching CalledProcessErrors from check_output when the file does not exist. You can also pass stderr=subprocess.PIPE to hide error output going to stdin. This solution is really hacky, but more workable than the other answer.Reincarnation
This answer does not work to check if a directory is a link, sine the dir command will just list the content.Oldest
Where do you import the unicode function? You should include all the import statements.Noonberg
Thank you @shioko. Incomplete code is very irritating. An oversight corrected.Wish
O
6

For directories:

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

Source

Oldest answered 10/3, 2016 at 11:55 Comment(2)
the source is deadEnough
comparing the source you are missing the return statement. You can check if the result is > 0 else the result is 1024Enough
W
2

You can also use the pywin32 module: GetFileAttributes is available in the win32api sub-module and FILE_ATTRIBUTE_REPARSE_POINT in the win32con module. For instance, to test if a given path is a symlink to a directory, the code becomes:

import os
import win32api
import win32con

def is_directory_symlink(path):
    return bool(os.path.isdir(path) 
                and (win32api.GetFileAttributes(path) &
                     win32con.FILE_ATTRIBUTE_REPARSE_POINT))

If using Python 2 and the path may contain non-ascii characters, GetFileAttributes requires a unicode string. However, simply using unicode(path) will generally fail: you should test if path is a str and, if so, use its decode method.

Wellman answered 10/8, 2018 at 15:25 Comment(0)
K
-3

Just using if file[-4:len(file)] != ".lnk": works for me

Kolnick answered 9/8, 2018 at 13:17 Comment(1)
this was downvoted, because the user was asking about reparse points, not .lnk filesCanvass

© 2022 - 2024 — McMap. All rights reserved.