silently 1.2.5.2 → 1.2.5.3
raw patch · 4 files changed
+106/−76 lines, 4 filesdep ~basePVP: major bump suggested
API removals or changes: PVP suggests a major version bump
Dependency ranges changed: base
API changes (from Hackage documentation)
- System.IO.Silently: hCapture :: [Handle] -> IO a -> IO (String, a)
+ System.IO.Silently: hCapture :: forall a. [Handle] -> IO a -> IO (String, a)
- System.IO.Silently: hSilence :: [Handle] -> IO a -> IO a
+ System.IO.Silently: hSilence :: forall a. [Handle] -> IO a -> IO a
Files
- CHANGELOG.md +7/−0
- README.md +9/−0
- silently.cabal +24/−16
- src/System/IO/Silently.hs +66/−60
CHANGELOG.md view
@@ -1,3 +1,10 @@+# 1.2.5.3 August 2022++* Tested with GHC 7.0 - 9.4.1.+* Remove remnants of GHC 6.x support.+* Silence incomplete pattern matching warning, refactor code.+* Add section about limitations to README.+ # 1.2.5.2 November 2021 * Tested with GHC 7.0 - 9.2.
README.md view
@@ -28,3 +28,12 @@ captured: wookies! returned: 123 ```++## Limitations++Capturing/silencing might not work as expected if the action uses the FFI+or conceals output under `unsafePerformIO` or similar unsafe operations.++Examples:+- FFI: https://github.com/hspec/silently/issues/3+- `unsafePerformIO`: https://github.com/bos/filemanip/issues/22
silently.cabal view
@@ -1,6 +1,6 @@-name: silently-version: 1.2.5.2 cabal-version: >= 1.10+name: silently+version: 1.2.5.3 build-type: Simple license: BSD3 license-file: LICENSE@@ -13,21 +13,23 @@ description: Prevent or capture writing to stdout, stderr, and other handles. category: System, Testing author: Trystan Spangler+ tested-with:- GHC == 7.0.4- GHC == 7.2.2- GHC == 7.4.2- GHC == 7.6.3- GHC == 7.8.4- GHC == 7.10.3- GHC == 8.0.2- GHC == 8.2.2- GHC == 8.4.4- GHC == 8.6.5- GHC == 8.8.4+ GHC == 9.4.1+ GHC == 9.2.4+ GHC == 9.0.2 GHC == 8.10.7- GHC == 9.0.1- GHC == 9.2.1+ GHC == 8.8.4+ GHC == 8.6.5+ GHC == 8.4.4+ GHC == 8.2.2+ GHC == 8.0.2+ GHC == 7.10.3+ GHC == 7.8.4+ GHC == 7.6.3+ GHC == 7.4.2+ GHC == 7.2.2+ GHC == 7.0.4 extra-source-files: CHANGELOG.md@@ -46,7 +48,7 @@ System.IO.Silently build-depends:- base >=4 && <=5+ base >= 4.3 && < 5 , directory , deepseq @@ -54,6 +56,12 @@ cpp-options: -DWINDOWS if os(linux) || os(osx) || os(freebsd) || os(openbsd) || os(netbsd) cpp-options: -DUNIX++ ghc-options:+ -Wall+ if impl(ghc >= 8)+ ghc-options:+ -Wcompat -- This tests the platform specific implementation. --
src/System/IO/Silently.hs view
@@ -1,29 +1,32 @@ {-# LANGUAGE CPP #-}--- | Need to prevent output to the terminal, a file, or stderr? Need to capture it and use it for--- your own means? Now you can, with 'silence' and 'capture'.+{-# LANGUAGE ScopedTypeVariables #-} -module System.IO.Silently (- silence,- hSilence,- capture,- capture_,- hCapture,- hCapture_,-) where+-- | Need to prevent output to the terminal, a file, or stderr?+-- Need to capture it and use it for your own means?+-- Now you can, with 'silence' and 'capture'. -import Prelude+module System.IO.Silently+ ( silence, hSilence+ , capture, capture_, hCapture, hCapture_+ ) where -#if __GLASGOW_HASKELL__ >= 612-import GHC.IO.Handle (hDuplicate, hDuplicateTo)-#else-import GHC.Handle (hDuplicate, hDuplicateTo)-#endif+import Prelude -import System.IO import qualified Control.Exception as E import Control.DeepSeq-import System.Directory (removeFile,getTemporaryDirectory)+ ( deepseq ) +import GHC.IO.Handle+ ( hDuplicate, hDuplicateTo )++import System.Directory+ ( getTemporaryDirectory, removeFile )+import System.IO+ ( Handle, IOMode(AppendMode), SeekMode(AbsoluteSeek)+ , hClose, hFlush, hGetBuffering, hGetContents, hSeek, hSetBuffering+ , openFile, openTempFile, stdout+ )+ mNullDevice :: Maybe FilePath #ifdef WINDOWS mNullDevice = Just "\\\\.\\NUL"@@ -38,27 +41,34 @@ silence = hSilence [stdout] -- | Run an IO action while preventing all output to the given handles.-hSilence :: [Handle] -> IO a -> IO a-hSilence handles action = case mNullDevice of- Just nullDevice -> E.bracket (openFile nullDevice AppendMode)- hClose- prepareAndRun+hSilence :: forall a. [Handle] -> IO a -> IO a+hSilence handles action =+ case mNullDevice of+ Just nullDevice ->+ E.bracket (openFile nullDevice AppendMode)+ hClose+ prepareAndRun+ Nothing -> withTempFile "silence" prepareAndRun+ where+ prepareAndRun :: Handle -> IO a+ prepareAndRun tmpHandle = go handles+ where+ go [] = action+ go (h:hs) = goBracket go tmpHandle h hs - Nothing -> do- tmpDir <- getTempOrCurrentDirectory- E.bracket (openTempFile tmpDir "silence")- cleanup- (prepareAndRun . snd) - where- cleanup (tmpFile,tmpHandle) = do- hClose tmpHandle- removeFile tmpFile- prepareAndRun tmpHandle = go handles- where- go [] = action- go hs = goBracket go tmpHandle hs-+-- Provide a tempfile for the given action and remove it afterwards.+withTempFile :: String -> (Handle -> IO a) -> IO a+withTempFile tmpName action = do+ tmpDir <- getTempOrCurrentDirectory+ E.bracket (openTempFile tmpDir tmpName)+ cleanup+ (action . snd)+ where+ cleanup :: (FilePath, Handle) -> IO ()+ cleanup (tmpFile, tmpHandle) = do+ hClose tmpHandle+ removeFile tmpFile getTempOrCurrentDirectory :: IO String getTempOrCurrentDirectory = getTemporaryDirectory `catchIOError` (\_ -> return ".")@@ -69,7 +79,8 @@ catchIOError = E.catch -- | Run an IO action while preventing and capturing all output to stdout.--- This will, as a side effect, create and delete a temp file in the temp directory or current directory if there is no temp directory.+-- This will, as a side effect, create and delete a temp file in the temp directory+-- or current directory if there is no temp directory. capture :: IO a -> IO (String, a) capture = hCapture [stdout] @@ -82,29 +93,24 @@ hCapture_ handles = fmap fst . hCapture handles -- | Run an IO action while preventing and capturing all output to the given handles.--- This will, as a side effect, create and delete a temp file in the temp directory or current directory if there is no temp directory.-hCapture :: [Handle] -> IO a -> IO (String, a)-hCapture handles action = do- tmpDir <- getTempOrCurrentDirectory- E.bracket (openTempFile tmpDir "capture")- cleanup- (prepareAndRun . snd)- where- cleanup (tmpFile,tmpHandle) = do- hClose tmpHandle- removeFile tmpFile- prepareAndRun tmpHandle = go handles- where- go [] = do- a <- action- mapM_ hFlush handles- hSeek tmpHandle AbsoluteSeek 0- str <- hGetContents tmpHandle- str `deepseq` return (str,a)- go hs = goBracket go tmpHandle hs+-- This will, as a side effect, create and delete a temp file in the temp directory+-- or current directory if there is no temp directory.+hCapture :: forall a. [Handle] -> IO a -> IO (String, a)+hCapture handles action = withTempFile "capture" prepareAndRun+ where+ prepareAndRun :: Handle -> IO (String, a)+ prepareAndRun tmpHandle = go handles+ where+ go [] = do+ a <- action+ mapM_ hFlush handles+ hSeek tmpHandle AbsoluteSeek 0+ str <- hGetContents tmpHandle+ str `deepseq` return (str, a)+ go (h:hs) = goBracket go tmpHandle h hs -goBracket :: ([Handle] -> IO a) -> Handle -> [Handle] -> IO a-goBracket go tmpHandle (h:hs) = do+goBracket :: ([Handle] -> IO a) -> Handle -> Handle -> [Handle] -> IO a+goBracket go tmpHandle h hs = do buffering <- hGetBuffering h let redirect = do old <- hDuplicate h