silently-1.2.5.3/0000755000000000000000000000000007346545000011714 5ustar0000000000000000silently-1.2.5.3/CHANGELOG.md0000644000000000000000000000064507346545000013532 0ustar0000000000000000# 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. * Silence warning caused by missing `other-modules` in cabal file. * Add README and CHANGELOG to dist. # 1.2.5.1 July 2019 No changelog for this and earlier versions. silently-1.2.5.3/LICENSE0000644000000000000000000000245107346545000012723 0ustar0000000000000000Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. The name of the author may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. silently-1.2.5.3/README.md0000644000000000000000000000272307346545000013177 0ustar0000000000000000[![Hackage version](https://img.shields.io/hackage/v/silently.svg?label=Hackage&color=informational)](http://hackage.haskell.org/package/silently) [![silently on Stackage Nightly](https://stackage.org/package/silently/badge/nightly)](https://stackage.org/nightly/package/silently) [![Stackage LTS version](https://www.stackage.org/package/silently/badge/lts?label=Stackage)](https://www.stackage.org/package/silently) [![Cabal build](https://github.com/hspec/silently/workflows/Haskell-CI/badge.svg)](https://github.com/hspec/silently/actions) # silently Silently is a package that allows you to run an `IO` action and prevent it from writing to `stdout`, or any other handle, by using `silence`. Or you can capture the output for yourself using `capture`. For example, the program ```haskell import System.IO.Silently main = do putStr "putStrLn: " >> putStrLn "puppies!" putStr "silenced: " >> silence (putStrLn "kittens!") putStrLn "" (captured, result) <- capture (putStr "wookies!" >> return 123) putStr "captured: " >> putStrLn captured putStr "returned: " >> putStrLn (show result) ``` will print: ``` putStrLn: puppies! silenced: 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-1.2.5.3/Setup.lhs0000644000000000000000000000015607346545000013526 0ustar0000000000000000#!/usr/bin/runhaskell > module Main where > import Distribution.Simple > main :: IO () > main = defaultMain silently-1.2.5.3/silently.cabal0000644000000000000000000000453707346545000014554 0ustar0000000000000000cabal-version: >= 1.10 name: silently version: 1.2.5.3 build-type: Simple license: BSD3 license-file: LICENSE copyright: (c) Trystan Spangler 2011 maintainer: Sönke Hahn , Simon Hengel , Andreas Abel homepage: https://github.com/hspec/silently package-url: https://github.com/hspec/silently bug-reports: https://github.com/hspec/silently/issues synopsis: Prevent or capture writing to stdout and other handles. description: Prevent or capture writing to stdout, stderr, and other handles. category: System, Testing author: Trystan Spangler tested-with: GHC == 9.4.1 GHC == 9.2.4 GHC == 9.0.2 GHC == 8.10.7 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 README.md source-repository head type: git location: https://github.com/hspec/silently Library hs-source-dirs: src default-language: Haskell98 exposed-modules: System.IO.Silently build-depends: base >= 4.3 && < 5 , directory , deepseq if os(windows) 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. -- -- NOTE: Cabal 1.10 can not deal with conditional (== if-else) options. This -- is why we depend on silently to test the platform specific implementation. -- -- As a consequence we can not use Hspec for testing, as this would result in -- depending on two different versions of silently at the same time! test-suite spec-specific type: exitcode-stdio-1.0 hs-source-dirs: test main-is: Spec.hs default-language: Haskell98 ghc-options: -Wall -threaded build-depends: base , silently , directory , nanospec , temporary -- This tests the generic implementation, that should work on all platforms. test-suite spec-generic type: exitcode-stdio-1.0 hs-source-dirs: src , test main-is: Spec.hs default-language: Haskell98 ghc-options: -Wall -threaded other-modules: System.IO.Silently build-depends: base , deepseq , directory , nanospec , temporary silently-1.2.5.3/src/System/IO/0000755000000000000000000000000007346545000014276 5ustar0000000000000000silently-1.2.5.3/src/System/IO/Silently.hs0000644000000000000000000000745207346545000016445 0ustar0000000000000000{-# LANGUAGE CPP #-} {-# LANGUAGE ScopedTypeVariables #-} -- | 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'. module System.IO.Silently ( silence, hSilence , capture, capture_, hCapture, hCapture_ ) where import Prelude import qualified Control.Exception as E import Control.DeepSeq ( 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" #elif UNIX mNullDevice = Just "/dev/null" #else mNullDevice = Nothing #endif -- | Run an IO action while preventing all output to stdout. silence :: IO a -> IO a silence = hSilence [stdout] -- | Run an IO action while preventing all output to the given handles. 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 -- 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 ".") where -- NOTE: We can not use `catchIOError` from "System.IO.Error", it is only -- available in base >= 4.4. catchIOError :: IO a -> (IOError -> IO a) -> IO a 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. capture :: IO a -> IO (String, a) capture = hCapture [stdout] -- | Like `capture`, but discards the result of given action. capture_ :: IO a -> IO String capture_ = fmap fst . capture -- | Like `hCapture`, but discards the result of given action. hCapture_ :: [Handle] -> IO a -> IO String 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 :: 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 -> [Handle] -> IO a goBracket go tmpHandle h hs = do buffering <- hGetBuffering h let redirect = do old <- hDuplicate h hDuplicateTo tmpHandle h return old restore old = do hDuplicateTo old h hSetBuffering h buffering hClose old E.bracket redirect restore (\_ -> go hs) silently-1.2.5.3/test/0000755000000000000000000000000007346545000012673 5ustar0000000000000000silently-1.2.5.3/test/Spec.hs0000644000000000000000000000210507346545000014117 0ustar0000000000000000module Main (main) where import Test.Hspec import System.IO import System.IO.Silently import System.Directory import System.IO.Temp import Control.Exception import Control.Monad main :: IO () main = hspec spec spec :: Spec spec = do describe "hSilence" $ do it "prevents output to a given handle" $ let file = "foo.txt" in do h <- openFile file ReadWriteMode hSilence [h] $ do hPutStrLn h "foo bar baz" hFlush h hSeek h AbsoluteSeek 0 hGetContents h `shouldReturn` "" `finally` removeFile file describe "capture" $ do it "captures stdout" $ do capture (putStr "foo" >> return 23) `shouldReturn` ("foo", 23 :: Int) describe "hCapture" $ do forM_ [NoBuffering, LineBuffering, BlockBuffering Nothing] $ \buffering -> do it ("preserves " ++ show buffering) $ do withSystemTempFile "silently" $ \_file h -> do hSetBuffering h buffering _ <- hCapture [h] (return ()) hGetBuffering h `shouldReturn` buffering