How do I check if a disk is in a drive using python?
Question: Say I want to manipulate some files on a floppy drive or a USB card reader.
How do I check to see if the drive in question is ready? (That is, has a disk
physically inserted.)

The drive letter _exists_ , so os.exists() will always return True in this
case. Also, at this point in the process I don't yet know any file names, so
checking to see if a given file exists also won't work.

Some clarification: the issue here is exception handling. Most of the win32
API calls in question just throw an exception when you try to access a drive
that isn't ready. Normally, this would work fine - look for something like the
free space, and then catch the raised exception and assume that means there
isn't a disk present. However, even when I catch any and all exceptions, I
still get an angry exception dialog box from Windows telling me the floppy /
card reader isn't ready. So, I guess the real question is - how do I suppress
the windows error box?


Answer: And the answer, as with so many things, turns out to be in [an article about
C++/Win32 programming from a decade
ago](http://bcbjournal.org/articles/vol2/9806/Detecting%5Fdisk%5Ferrors.htm).

The issue, in a nutshell, is that Windows handles floppy disk errors slightly
differently than other kinds of drive errors. By default, no matter what you
program does, or **thinks** it's doing, Windows will intercept any errors
thrown by the device and present a dialog box to the user rather than letting
the program handle it - the exact issue I was having.

But, as it turns out, there's a Win32 API call to solve this issue, primarily
`SetErrorMode()`

In a nutshell (and I'm handwaving a way a lot of the details here), we can use
`SetErrorMode()` to get Windows to stop being quite so paranoid, do our thing
and let the program handle the situation, and then reset the Windows error
mode back to what it was before as if we had never been there. (There's
probably a Keyser Soze joke here, but I've had the wrong amount of caffeine
today to be able to find it.)

Adapting the C++ sample code from the linked article, that looks about like
this:

    
    
    int OldMode; //a place to store the old error mode
    //save the old error mode and set the new mode to let us do the work:
    OldMode = SetErrorMode(SEM_FAILCRITICALERRORS); 
    // Do whatever we need to do that might cause an error
    SetErrorMode(OldMode); //put things back the way they were
    

Under C++, detecting errors the right way needs the `GetLastError()' function,
which we fortunately don't need to worry about here, since this is a Python
question. In our case, Python's exception handling works fine. This, then, is
the function I knocked together to check a drive letter for "readiness", all
ready for copy-pasting if anyone else needs it:

    
    
    import win32api
    def testDrive( currentLetter ):
        """
        Tests a given drive letter to see if the drive is question is ready for 
        access. This is to handle things like floppy drives and USB card readers
        which have to have physical media inserted in order to be accessed.
        Returns true if the drive is ready, false if not.
        """
        returnValue = False
        #This prevents Windows from showing an error to the user, and allows python 
        #to handle the exception on its own.
        oldError = win32api.SetErrorMode( 1 ) #note that SEM_FAILCRITICALERRORS = 1
        try:
        	freeSpace = win32file.GetDiskFreeSpaceEx( letter )
        except:
        	returnValue = False
        else:
        	returnValue = True
        #restore the Windows error handling state to whatever it was before we
        #started messing with it:
        win32api.SetErrorMode( oldError )
        return returnValue
    

I've been using this quite a bit the last few days, and it's been working
beautifully for both floppies and USB card readers.

A few notes: pretty much any function needing disk access will work in the try
block - all we're looking for in an exception due to the media not being
present.

Also, while the python `win32api` package exposes all the functions we need,
it dones't seem to have any of the flag constants. After a trip to the ancient
bowels of MSDN, it turns out that SEM_FAILCRITICALERRORS is equal to 1, which
makes our life awfully easy.

I hope this helps someone else with a similar problem!

