conduit-extra 1.1.1 → 1.1.2
raw patch · 4 files changed
+134/−3 lines, 4 filesdep +asyncdep +processdep +stmdep ~streaming-commonsPVP ok
version bump matches the API change (PVP)
Dependencies added: async, process, stm
Dependency ranges changed: streaming-commons
API changes (from Hackage documentation)
+ Data.Conduit.Process: instance (r ~ (), MonadIO m, i ~ ByteString) => InputSource (ConduitM i o m r)
+ Data.Conduit.Process: instance (r ~ (), MonadIO m, o ~ ByteString) => OutputSink (ConduitM i o m r)
+ Data.Conduit.Process: instance (r ~ (), r' ~ (), MonadIO m, MonadIO n, i ~ ByteString) => InputSource (ConduitM i o m r, n r')
+ Data.Conduit.Process: instance (r ~ (), r' ~ (), MonadIO m, MonadIO n, o ~ ByteString) => OutputSink (ConduitM i o m r, n r')
+ Data.Conduit.Process: sourceCmdWithConsumer :: MonadIO m => String -> Consumer ByteString m a -> m (ExitCode, a)
+ Data.Conduit.Process: sourceProcessWithConsumer :: MonadIO m => CreateProcess -> Consumer ByteString m a -> m (ExitCode, a)
Files
- Data/Conduit/Binary.hs +3/−1
- Data/Conduit/Process.hs +59/−0
- conduit-extra.cabal +11/−2
- test/Data/Conduit/ProcessSpec.hs +61/−0
Data/Conduit/Binary.hs view
@@ -140,7 +140,9 @@ sinkHandle :: MonadIO m => IO.Handle -> Consumer S.ByteString m ()-sinkHandle h = awaitForever $ liftIO . S.hPut h+sinkHandle h = awaitForever $ \bs -> liftIO $ do+ S.hPut h bs+ IO.hFlush h -- | An alternative to 'sinkHandle'. -- Instead of taking a pre-opened 'IO.Handle', it takes an action that opens
+ Data/Conduit/Process.hs view
@@ -0,0 +1,59 @@+{-# LANGUAGE FlexibleInstances #-}+{-# LANGUAGE TypeFamilies #-}+{-# LANGUAGE RankNTypes #-}+{-# OPTIONS_GHC -fno-warn-orphans #-}+-- | A full tutorial for this module is available on FP School of Haskell:+-- <https://www.fpcomplete.com/user/snoyberg/library-documentation/data-conduit-process>.+--+-- Note that this is a very thin layer around the @Data.Streaming.Process@ module. In particular, it:+--+-- * Provides orphan instances for conduit+--+-- * Provides some useful helper functions+module Data.Conduit.Process+ ( -- * Functions+ sourceCmdWithConsumer+ , sourceProcessWithConsumer+ -- * Reexport+ , module Data.Streaming.Process+ ) where++import Data.Streaming.Process+import Data.Streaming.Process.Internal+import System.Exit (ExitCode (..))+import Control.Monad.IO.Class (MonadIO, liftIO)+import System.IO (hClose)+import Data.Conduit+import Data.Conduit.Binary (sourceHandle, sinkHandle)+import Data.ByteString (ByteString)++instance (r ~ (), MonadIO m, i ~ ByteString) => InputSource (ConduitM i o m r) where+ isStdStream = (\(Just h) -> return $ sinkHandle h, Just CreatePipe)+instance (r ~ (), r' ~ (), MonadIO m, MonadIO n, i ~ ByteString) => InputSource (ConduitM i o m r, n r') where+ isStdStream = (\(Just h) -> return (sinkHandle h, liftIO $ hClose h), Just CreatePipe)++instance (r ~ (), MonadIO m, o ~ ByteString) => OutputSink (ConduitM i o m r) where+ osStdStream = (\(Just h) -> return $ sourceHandle h, Just CreatePipe)+instance (r ~ (), r' ~ (), MonadIO m, MonadIO n, o ~ ByteString) => OutputSink (ConduitM i o m r, n r') where+ osStdStream = (\(Just h) -> return (sourceHandle h, liftIO $ hClose h), Just CreatePipe)++-- | Given a @CreateProcess@, run the process, with its output being used as a+-- @Source@ to feed the provided @Consumer@. Once the process has completed,+-- return a tuple of the @ExitCode@ from the process and the output collected+-- from the @Consumer@.+--+-- Since 1.1.2+sourceProcessWithConsumer :: MonadIO m => CreateProcess -> Consumer ByteString m a -> m (ExitCode, a)+sourceProcessWithConsumer cp consumer = do+ (ClosedStream, (source, close), ClosedStream, cph) <- streamingProcess cp+ res <- source $$ consumer+ close+ ec <- waitForStreamingProcess cph+ return (ec, res)++-- | Like @sourceProcessWithConsumer@ but providing the command to be run as+-- a @String@.+--+-- Since 1.1.2+sourceCmdWithConsumer :: MonadIO m => String -> Consumer ByteString m a -> m (ExitCode, a)+sourceCmdWithConsumer cmd = sourceProcessWithConsumer (shell cmd)
conduit-extra.cabal view
@@ -1,5 +1,5 @@ Name: conduit-extra-Version: 1.1.1+Version: 1.1.2 Synopsis: Batteries included conduit: adapters for common libraries. Description: The conduit package itself maintains relative small dependencies. The purpose of this package is to collect commonly used utility functions wrapping other library dependencies, without depending on heavier-weight dependencies. The basic idea is that this package should only depend on haskell-platform packages and conduit.@@ -24,6 +24,7 @@ Data.Conduit.Lazy Data.Conduit.Network Data.Conduit.Network.UDP+ Data.Conduit.Process Data.Conduit.Text Data.Conduit.Zlib if !os(windows)@@ -46,8 +47,10 @@ , filepath , network >= 2.3 , primitive >= 0.5+ , process , resourcet >= 1.1- , streaming-commons >= 0.1.2+ , stm+ , streaming-commons >= 0.1.4 ghc-options: -Wall @@ -62,15 +65,20 @@ , base , hspec >= 1.3 + , async , attoparsec , blaze-builder , bytestring , exceptions+ , process , resourcet+ , stm , text , transformers , transformers-base ghc-options: -Wall+ if os(windows)+ cpp-options: -DWINDOWS other-modules: Data.Conduit.AttoparsecSpec Data.Conduit.BinarySpec Data.Conduit.BlazeSpec@@ -78,6 +86,7 @@ Data.Conduit.FilesystemSpec Data.Conduit.LazySpec Data.Conduit.NetworkSpec+ Data.Conduit.ProcessSpec Data.Conduit.TextSpec Data.Conduit.ZlibSpec
+ test/Data/Conduit/ProcessSpec.hs view
@@ -0,0 +1,61 @@+{-# LANGUAGE CPP #-}+module Data.Conduit.ProcessSpec (spec, main) where++import Test.Hspec+import Test.Hspec.QuickCheck (prop)+import Data.Conduit+import qualified Data.Conduit.List as CL+import Data.Conduit.Process+import Control.Concurrent.Async (concurrently)+import qualified Data.ByteString.Lazy as L+import qualified Data.ByteString as S+import System.Exit+import Control.Concurrent (threadDelay)++main :: IO ()+main = hspec spec++spec :: Spec+spec = describe "Data.Conduit.Process" $ do+#ifndef WINDOWS+ prop "cat" $ \wss -> do+ let lbs = L.fromChunks $ map S.pack wss+ ((sink, closeStdin), source, Inherited, cph) <- streamingProcess (shell "cat")+ ((), bss) <- concurrently+ (do+ mapM_ yield (L.toChunks lbs) $$ sink+ closeStdin)+ (source $$ CL.consume)+ L.fromChunks bss `shouldBe` lbs+ ec <- waitForStreamingProcess cph+ ec `shouldBe` ExitSuccess++ it "closed stream" $ do+ (ClosedStream, source, Inherited, cph) <- streamingProcess (shell "cat")+ bss <- source $$ CL.consume+ bss `shouldBe` []++ ec <- waitForStreamingProcess cph+ ec `shouldBe` ExitSuccess++ it "handles sub-process exit code" $ do+ (sourceCmdWithConsumer "exit 0" CL.sinkNull)+ `shouldReturn` (ExitSuccess, ())+ (sourceCmdWithConsumer "exit 11" CL.sinkNull)+ `shouldReturn` (ExitFailure 11, ())+ (sourceCmdWithConsumer "exit 12" CL.sinkNull)+ `shouldReturn` (ExitFailure 12, ())+#endif+ it "blocking vs non-blocking" $ do+ (ClosedStream, ClosedStream, ClosedStream, cph) <- streamingProcess (shell "sleep 1")++ mec1 <- getStreamingProcessExitCode cph+ mec1 `shouldBe` Nothing++ threadDelay 1500000++ mec2 <- getStreamingProcessExitCode cph+ mec2 `shouldBe` Just ExitSuccess++ ec <- waitForStreamingProcess cph+ ec `shouldBe` ExitSuccess