safer-file-handles 0.3.0.1 → 0.4
raw patch · 4 files changed
+92/−192 lines, 4 filesbuild-type:Customsetup-changedPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
- System.IO.SaferFileHandles: Err :: Standard W
- System.IO.SaferFileHandles: In :: Standard R
- System.IO.SaferFileHandles: Out :: Standard W
- System.IO.SaferFileHandles: Std :: Standard ioMode -> File ioMode
- System.IO.SaferFileHandles: data Standard ioMode
- System.IO.SaferFileHandles: stderr :: (MonadCatchIO pr) => RegionT s pr (RegionalFileHandle W (RegionT s pr))
- System.IO.SaferFileHandles: stdin :: (MonadCatchIO pr) => RegionT s pr (RegionalFileHandle R (RegionT s pr))
- System.IO.SaferFileHandles: stdout :: (MonadCatchIO pr) => RegionT s pr (RegionalFileHandle W (RegionT s pr))
Files
- Setup.hs +42/−2
- System/IO/SaferFileHandles.hs +14/−134
- System/IO/SaferFileHandles/Internal.hs +34/−54
- safer-file-handles.cabal +2/−2
Setup.hs view
@@ -1,3 +1,43 @@-import Distribution.Simple+#! /usr/bin/env runhaskell -main = defaultMain+{-# LANGUAGE NoImplicitPrelude, UnicodeSyntax #-}++module Main (main) where+++-------------------------------------------------------------------------------+-- Imports+-------------------------------------------------------------------------------++-- from base+import Data.Function ( ($) )+import System.IO ( IO )++-- from cabal+import Distribution.Simple ( defaultMainWithHooks+ , simpleUserHooks+ , UserHooks(haddockHook)+ )++import Distribution.Simple.LocalBuildInfo ( LocalBuildInfo, withPrograms )+import Distribution.Simple.Program ( userSpecifyArgs )+import Distribution.Simple.Setup ( HaddockFlags )+import Distribution.PackageDescription ( PackageDescription )+++-------------------------------------------------------------------------------+-- Setup program which sets the CPP define '__HADDOCK __' when haddock is run.+-------------------------------------------------------------------------------++main ∷ IO ()+main = defaultMainWithHooks $ simpleUserHooks { haddockHook = haddockHook' }++-- Define __HADDOCK__ for CPP when running haddock.+haddockHook' ∷ PackageDescription → LocalBuildInfo → UserHooks → HaddockFlags → IO ()+haddockHook' pkg lbi =+ haddockHook simpleUserHooks pkg (lbi { withPrograms = p })+ where+ p = userSpecifyArgs "haddock" ["--optghc=-D__HADDOCK__"] (withPrograms lbi)+++-- The End ---------------------------------------------------------------------
System/IO/SaferFileHandles.hs view
@@ -4,7 +4,6 @@ , GADTs , TypeFamilies , RankNTypes- , ViewPatterns , MultiParamTypeClasses #-} @@ -49,26 +48,7 @@ -- -- @darcs get <http://code.haskell.org/~basvandijk/code/safer-file-handles-examples>@ ----- Note that this package is early work and still very experimental. Take note--- of the following warnings:------ * /WARNING:/ You are able to lift an arbitrary @IO@ action into a region. This--- action may throw an @IOError@ which may contain a low-level handle to a--- file. This handle can be retrieved from the @IOError@ using @ioeGetHandle@--- from @System.IO.Error@. So when an @IOError@ is thrown you will be able to--- manually close the respected file! This will defeat the safety-guarantees--- that this package promises to provide. /TODO: Think about how to solve this.../--- The solution that Oleg provides in his paper is to filter out the low-level--- handle in an @IOError@ when it's thrown. I can't easily do this because I have--- to modify the @catch@ method of the 'MonadCatchIO' instance for 'RegionT' in--- the @regions@ package. It feels like an ugly hack to solve this--- @safer-file-handles@ specific problem in the independent general @regions@--- package.------ * /WARNING:/ Currenly the handling of the standard files ('stdin', 'stdout' and--- 'stderr') is not to my liking. See the documentation for details.------ * /NOTE:/ This module also provides functions from @System.IO@ which don't+-- /NOTE:/ This module also provides functions from @System.IO@ which don't -- directly work with file handles like 'putStrLn' or 'getLine' for -- example. These functions implicitly use the standard handles. I actually -- provide more general versions of these that work in any 'MonadIO'. It could@@ -83,7 +63,6 @@ ( -- * Files with explicit IO modes as scarce resources File(..) , Binary- , Standard(..) , FilePath @@ -114,13 +93,6 @@ , IOMode(..) - -- ** Standard handles- -- $stdHndls-- , stdin, stdout, stderr-- -- $TODO_cast- -- * Operations on regional file handles -- ** Determining and changing the size of a file , hFileSize@@ -262,7 +234,7 @@ import Data.Bool ( Bool(False, True) ) import Data.Char ( Char, String ) import Data.Int ( Int )-import Data.Maybe ( Maybe(Just) )+import Data.Maybe ( Maybe, fromJust ) import Text.Show ( Show ) import Text.Read ( Read ) import Foreign.Ptr ( Ptr )@@ -282,6 +254,10 @@ import Control.Monad.Trans.Region -- (re-exported entirely) import Control.Monad.Trans.Region.Unsafe ( internalHandle ) +#ifdef __HADDOCK__+import Control.Resource ( Resource )+#endif+ -- from explicit-iomodes import System.IO.ExplicitIOModes ( IOMode(..) , R, W, A, RW@@ -365,100 +341,6 @@ withFile filePath ioMode = with $ File False filePath ioMode --- ** Standard handles--{- $stdHndls--/WARNING:/ I'm not satisfied with my current implementation of the standard-handles ('stdin', 'stdout' and 'stderr')! Currently the standard handles are-regional computations that return the regional file handles to the respected-standard handles. There are 4 problems with this approach:--* When the region terminates in which you call one of the standard handles the-respected handle will be closed. I think this is not the expected behaviour. I-would expect the standard handles to always remain open.--* In 'System.IO' the standard handles are pure values. My standard handles are-monadic computations which makes them harder to work with.--* There is no way to explicitly close a standard handle. Indeed, the whole-purpose of lightweight monadic regions is to automatically close-handles. However, when writing a Unix daemon for example, you need to be able to-explicitly close the standard handles.--* When reading 'man stdin' I'm confused if the standard handles are /always/-open on program startup:--quote 'man stdin':--\".../Under normal circumstances/ every Unix program has three streams opened for-it when it starts up, one for input, one for output, and one for printing-diagnostic or error messages...\"--\"...The stdin, stdout, and stderr macros conform to C89 and this standard also-stipulates that these three streams /shall be open/ at program startup....\"--So now I'm confused... are these standard file handles always open on program-startup or are there /abnormal/ situations when they are closed?--Maybe I just have to believe the documentation in @System.IO@ which specifies-that they are always initially open.--If the standard handles are closed on startup using a handle returned from one-of the standard handles will result in an exception! This would be a violation-of my safety guarantees which is unacceptable.--Does anyone have a solution?--}---- TODO: I need to review these:---- | Convenience function for returning a regional handle to standard--- input. This provides a safer replacement for @System.IO.'SIO.stdin'@.------ Note that: @stdin = 'open' $ 'Std' 'In'@.-stdin ∷ MonadCatchIO pr- ⇒ RegionT s pr (RegionalFileHandle R (RegionT s pr))-stdin = open $ Std In---- | Convenience function for returning a regional handle to standard--- output. This provides a safer replacement for @System.IO.'SIO.stdout'@.------ Note that: @stdin = 'open' $ 'Std' 'Out'@.-stdout ∷ MonadCatchIO pr- ⇒ RegionT s pr (RegionalFileHandle W (RegionT s pr))-stdout = open $ Std Out---- | Convenience function for returning a regional handle to standard--- error. This provides a safer replacement for @System.IO.'SIO.stderr'@.------ Note that: @stdin = 'open' $ 'Std' 'Err'@.-stderr ∷ MonadCatchIO pr- ⇒ RegionT s pr (RegionalFileHandle W (RegionT s pr))-stderr = open $ Std Err--{- $TODO_cast--/TODO:/--The standard handles have concrete IOModes by default which work for the-majority of cases. In the rare occasion that you know these handles have-different IOModes you should be able to 'cast' them to the expected IOMode.--The @explicit-iomodes@ package defines this @cast@ function. I should also-define it here:--@-cast :: forall anyIOMode castedIOMode- . (pr \`ParentOf\` cr, LiftIO cr, CheckMode castedIOMode)- => RegionalFileHandle anyIOMode pr- -> cr (Maybe (RegionalFileHandle castedIOMode pr))-@--However I'm not sure yet how to implement it...--}-- ------------------------------------------------------------------------------- -- * Operations on regional file handles -------------------------------------------------------------------------------@@ -763,14 +645,15 @@ → Template → RegionT s pr (FilePath, RegionalFileHandle RW (RegionT s pr)) genOpenTempFile binary filePath template = do- rh@(internalHandle → FileHandle (Just fp) _) ← open $ TempFile binary- filePath- template+ rh ← open $ TempFile binary filePath template #if MIN_VERSION_base(4,2,0)- False+ False #endif- return (fp, rh)+ return (generatedFilePath rh, rh) +generatedFilePath ∷ RegionalFileHandle ioMode r → FilePath+generatedFilePath = fromJust ∘ mbFilePath ∘ internalHandle+ -- | Open a temporary file yielding a regional handle to it paired with the -- generated file path. This provides a safer replacement for -- @System.IO.'SIO.openTempFile'@.@@ -798,11 +681,8 @@ → Template → RegionT s pr (FilePath, RegionalFileHandle RW (RegionT s pr)) genOpenTempFileWithDefaultPermissions binary filePath template = do- rh@(internalHandle → FileHandle (Just fp) _) ← open $ TempFile binary- filePath- template- True- return (fp, rh)+ rh ← open $ TempFile binary filePath template True+ return (generatedFilePath rh, rh) -- | Open a temporary file with default permissions yielding a regional handle -- to it paired with the generated file path. This provides a safer replacement
System/IO/SaferFileHandles/Internal.hs view
@@ -4,7 +4,6 @@ , GADTs , TypeFamilies , RankNTypes- , ViewPatterns , MultiParamTypeClasses #-} @@ -20,12 +19,14 @@ module System.IO.SaferFileHandles.Internal where -- from base:-import Control.Monad ( fmap , return )-import Data.Function ( ($) )-import Data.Tuple ( uncurry )-import Data.Bool ( Bool(False, True) )-import Data.Char ( String )-import Data.Maybe ( Maybe(Nothing, Just) )+import Control.Applicative ( (<$>) )+import Data.Function ( ($) )+import Data.Tuple ( uncurry )+import Data.Bool ( Bool(False, True) )+import Data.Char ( String )+import Data.Maybe ( Maybe(Nothing, Just) )+import System.IO.Error ( modifyIOError )+import GHC.IO.Exception ( ioe_handle ) -- from base-unicode-symbols: import Data.Function.Unicode ( (∘) )@@ -40,15 +41,10 @@ import Control.Monad.Trans.Region.Unsafe ( internalHandle ) -- from explicit-iomodes-import System.IO.ExplicitIOModes ( IOMode(..)- , R, W, RW- , IO- , FilePath- )+import System.IO.ExplicitIOModes ( IOMode(..), RW, IO, FilePath ) import qualified System.IO.ExplicitIOModes as E ( Handle- , stdin, stdout, stderr , openFile , openBinaryFile , openTempFile@@ -65,8 +61,8 @@ -- * Files with explicit IO modes as scarce resources ------------------------------------------------------------------------------- -{-| A file scarce resource parameterized by the IOMode in which you want to open-the file.+{-| A file scarce resource parameterized by the 'IOMode' in which you want to+open the file. Note that this module provides an instance for 'Resource' for 'File' @ioMode@. This allows you to 'open' files in a region which are automatically@@ -83,9 +79,6 @@ #endif → File RW - -- TODO: I need to review the handling of standard files:- Std ∷ Standard ioMode → File ioMode- -- | Should the file be opened in binary mode? type Binary = Bool @@ -97,36 +90,23 @@ type DefaultPermissions = Bool #endif --- | The standard files parameterized by concrete IOModes which work for the--- majority of cases.-data Standard ioMode where- In ∷ Standard R- Out ∷ Standard W- Err ∷ Standard W---- | Internally used function to convert a standard file to the corresponding--- handle.-stdHndl ∷ Standard ioMode → E.Handle ioMode-stdHndl In = E.stdin-stdHndl Out = E.stdout-stdHndl Err = E.stderr- instance Resource (File ioMode) where- data R.Handle (File ioMode) = FileHandle (Maybe FilePath)- -- The optional file path is needed- -- because opening a temporary file- -- also yields the generated file- -- path.- (E.Handle ioMode)+ data R.Handle (File ioMode) =+ FileHandle { mbFilePath ∷ Maybe FilePath+ -- ^ Get the optional file path. This is needed because+ -- opening a temporary file also yields the generated file+ -- path.+ , handle ∷ E.Handle ioMode+ } openResource (File isBinary filePath ioMode) =- fmap (FileHandle Nothing) $- (if isBinary then E.openBinaryFile else E.openFile)- filePath ioMode+ FileHandle Nothing <$>+ (if isBinary then E.openBinaryFile else E.openFile)+ filePath ioMode #if MIN_VERSION_base(4,2,0) openResource (TempFile isBinary filePath template defaultPerms) = do- fmap (uncurry (FileHandle ∘ Just)) $+ uncurry (FileHandle ∘ Just) <$> (case (isBinary, defaultPerms) of (False, False) → E.openTempFile (True, False) → E.openBinaryTempFile@@ -135,17 +115,14 @@ ) filePath template #else openResource (TempFile isBinary filePath template) = do- fmap (uncurry (FileHandle ∘ Just)) $- (if isBinary then E.openBinaryTempFile else E.openTempFile)- filePath template+ uncurry (FileHandle ∘ Just) <$>+ (if isBinary then E.openBinaryTempFile else E.openTempFile)+ filePath template #endif- -- TODO: I need to review the handling of standard files:- openResource (Std std) = return $ FileHandle Nothing $ stdHndl std-- closeResource (FileHandle _ h) = E.hClose h+ closeResource = sanitizeIOError ∘ E.hClose ∘ handle -- | A handy type synonym for a regional handle to an opened file parameterized--- by the IOMode in which you opened the file and the region in which it was+-- by the 'IOMode' in which you opened the file and the region in which it was -- created. type RegionalFileHandle ioMode r = RegionalHandle (File ioMode) r @@ -155,22 +132,25 @@ -------------------------------------------------------------------------------- regularHandle ∷ RegionalFileHandle ioMode r → E.Handle ioMode-regularHandle (internalHandle → FileHandle _ h) = h+regularHandle = handle ∘ internalHandle wrap ∷ MonadIO m ⇒ (E.Handle ioMode → IO α) → (RegionalFileHandle ioMode r → m α)-wrap f = \h → liftIO $ f (regularHandle h)+wrap f = \h → liftIO $ sanitizeIOError $ f (regularHandle h) wrap2 ∷ MonadIO m ⇒ (E.Handle ioMode → β → IO α) → (RegionalFileHandle ioMode r → β → m α)-wrap2 f = \h y → liftIO $ f (regularHandle h) y+wrap2 f = \h y → liftIO $ sanitizeIOError $ f (regularHandle h) y wrap3 ∷ MonadIO m ⇒ (E.Handle ioMode → γ → β → IO α) → (RegionalFileHandle ioMode r → γ → β → m α)-wrap3 f = \h z y → liftIO $ f (regularHandle h) z y+wrap3 f = \h z y → liftIO $ sanitizeIOError $ f (regularHandle h) z y++sanitizeIOError ∷ IO α → IO α+sanitizeIOError = modifyIOError $ \e -> e { ioe_handle = Nothing } -- The End ---------------------------------------------------------------------
safer-file-handles.cabal view
@@ -1,7 +1,7 @@ name: safer-file-handles-version: 0.3.0.1+version: 0.4 cabal-version: >=1.6-build-type: Simple+build-type: Custom license: BSD3 license-file: LICENSE copyright: 2010 Bas van Dijk