unexpected behavior / bug in try . System.FilePath.Find.findWithHandler

say you want to execute a find function, but abort the computation if it hits any snags, such as an unreadable directory (eg chmod -R a-r dir). Currently try . System.FilePath.Find.findWithHandler will return an exception wrapped in Right, which seems Wrong. For sure it will just get ignored if wrapped in an ErrorT computation and I suspect this could lead to other glitchy/unexpected behavior when used in sysadmin scripts. You do get the expected exception wrapped in Left for certain errors (such as a missing directory), which adds to the confusion. Probable fix: you get the expected behavior if you remove unsafeInterleaveIO from findWithHandler. Does this seem like a reasonable thing to suggest to libraries@haskell? Hacky fix: I haven't actually done this, but I suppose you could trap stdout or stderr, since the exception does get printed although it's not reflected in the result type of the computation. demo of problem: import qualified System.FilePath.Find as F import System.FilePath.Find import System.IO.Error abort path err = fail $ path ++ " , " ++ (show err) prot = "/home/thartman/protected" -- a protected (unreadable) dir {- want result => Left user error (/home/thartman/protected , user error (/home/thartman/protected , /home/thartman/protected: getDirectoryContents: permission denied (Permission denied))) but get result Right *** Exception: user error (/home/thartman/protected , /home/thartman/protected: getDirectoryContents: permission denied (Permission denied)) which means, for example, you would miss the error if you wrapped this in an ErrorT there is probably a workaround from reading stderr, since it does get printed out, but this is awkward compared to just catching the error the right way and certainly feels like unexpected behavior Possible fix: you get the expected behavior if you remove unsafeInterleaveIO from System.FilePath.Find.findWithHandlers don't know why this fixes the problem, I was just messing around I don't know if this breaks anything else, but I was unable to find any breakage when I did a simple test on an unprotected directory What is the gain from unsafeInterleaveIO, and can it be nixed? -} tprot = try . findWithHandler abort (return True) (return True) $ prot tunprot = return . either (error . show) (length) =<< ( try $ findWithHandler abort (return True) (return True) unprot ) unprot = "/home/thartman/unprot" -- Need somewhere to put your code? http://patch-tag.com Want to build a webapp? http://happstack.com

A late reply - but as far as I can see, this has gone unanswered so far. Thomas Hartman wrote:
Currently try . System.FilePath.Find.findWithHandler
(from the FileManip package, I guess)
will return an exception wrapped in Right, which seems Wrong. For sure it will just get ignored if wrapped in an ErrorT computation and I suspect this could lead to other glitchy/unexpected behavior when used in sysadmin scripts.
The short answer is that lazy IO and exceptions don't mix. As the documentation says, findWithHandler returns a list lazily. Lazy IO is (usually) implemented using unsafeInterleaveIO. The 'try' in 'try . System.FilePath.Find.findWithHandler' has no effect after the findWithHandler function returns - which happens immediately after testing whether the given root path is a directory. Now when an error occurs - that happens when the code examines the resulting list from findWithHandler - your exception handler (abort) is executed by findWithHandler, which in your case will raise another exception. This exception will be raised in the context of the code processing the list - and there is no 'try' in effect there. You can avoid that problem by either making IO strict, or by providing a handler that does not raise any exceptions.
Possible fix: you get the expected behavior if you remove unsafeInterleaveIO from System.FilePath.Find.findWithHandlers
Right. That will make findWithHandlers use strict IO, and exceptions will work as expected. On the downside, you'll now read in the whole directory tree into memory before processing it. Do we have an Iterat(or|ee) implementation of directory traversal? Bertram
participants (2)
-
Bertram Felgenhauer
-
Thomas Hartman