coquina 0.1.0.1 → 0.2.0.0
raw patch · 6 files changed
+49/−30 lines, 6 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
+ Coquina.Internal: ignoreSigPipe :: IO () -> IO ()
- Coquina: Shell :: ExceptT Int (WriterT (Text, Text) m) a -> Shell (m :: Type -> Type) a
+ Coquina: Shell :: ExceptT Int (WriterT (Text, Text) m) a -> Shell m a
- Coquina: StreamingProcess :: !Shell m ExitCode -> !Shell m () -> !Shell m (Maybe ExitCode) -> StreamingProcess (m :: Type -> Type)
+ Coquina: StreamingProcess :: !Shell m ExitCode -> !Shell m () -> !Shell m (Maybe ExitCode) -> StreamingProcess m
- Coquina: [_streamingProcess_getProcessExitCode] :: StreamingProcess (m :: Type -> Type) -> !Shell m (Maybe ExitCode)
+ Coquina: [_streamingProcess_getProcessExitCode] :: StreamingProcess m -> !Shell m (Maybe ExitCode)
- Coquina: [_streamingProcess_terminateProcess] :: StreamingProcess (m :: Type -> Type) -> !Shell m ()
+ Coquina: [_streamingProcess_terminateProcess] :: StreamingProcess m -> !Shell m ()
- Coquina: [_streamingProcess_waitForProcess] :: StreamingProcess (m :: Type -> Type) -> !Shell m ExitCode
+ Coquina: [_streamingProcess_waitForProcess] :: StreamingProcess m -> !Shell m ExitCode
- Coquina: [unShell] :: Shell (m :: Type -> Type) a -> ExceptT Int (WriterT (Text, Text) m) a
+ Coquina: [unShell] :: Shell m a -> ExceptT Int (WriterT (Text, Text) m) a
- Coquina: class Monad m => MonadShell (m :: Type -> Type)
+ Coquina: class Monad m => MonadShell m
- Coquina: data StreamingProcess (m :: Type -> Type)
+ Coquina: data StreamingProcess m
- Coquina: hoistShell :: (forall x. () => m x -> n x) -> Shell m a -> Shell n a
+ Coquina: hoistShell :: (forall x. m x -> n x) -> Shell m a -> Shell n a
- Coquina: inTempDirectory :: forall (m :: Type -> Type) a. MonadIO m => String -> (FilePath -> Shell IO a) -> Shell m a
+ Coquina: inTempDirectory :: MonadIO m => String -> (FilePath -> Shell IO a) -> Shell m a
- Coquina: newtype Shell (m :: Type -> Type) a
+ Coquina: newtype Shell m a
- Coquina: run :: forall (m :: Type -> Type). MonadIO m => CreateProcess -> Shell m ()
+ Coquina: run :: MonadIO m => CreateProcess -> Shell m ()
- Coquina: shellCreateProcess :: forall (m :: Type -> Type). MonadIO m => CreateProcess -> Shell m ()
+ Coquina: shellCreateProcess :: MonadIO m => CreateProcess -> Shell m ()
- Coquina: shellCreateProcessWith :: forall (m :: Type -> Type). MonadIO m => (CreateProcess -> IO (ExitCode, Text, Text)) -> CreateProcess -> Shell m ()
+ Coquina: shellCreateProcessWith :: MonadIO m => (CreateProcess -> IO (ExitCode, Text, Text)) -> CreateProcess -> Shell m ()
- Coquina: shellCreateProcessWithEnv :: forall (m :: Type -> Type). MonadIO m => Map String String -> CreateProcess -> Shell m ()
+ Coquina: shellCreateProcessWithEnv :: MonadIO m => Map String String -> CreateProcess -> Text -> Shell m ()
- Coquina: shellCreateProcessWithStdOut :: forall (m :: Type -> Type). MonadIO m => Handle -> CreateProcess -> Shell m ()
+ Coquina: shellCreateProcessWithStdOut :: MonadIO m => Handle -> CreateProcess -> Shell m ()
- Coquina: shellStreamableProcess :: forall (m :: Type -> Type). (MonadIO m, MonadMask m) => (ByteString -> IO ()) -> (ByteString -> IO ()) -> CreateProcess -> Shell m (StreamingProcess m)
+ Coquina: shellStreamableProcess :: (MonadIO m, MonadMask m) => (ByteString -> IO ()) -> (ByteString -> IO ()) -> CreateProcess -> Shell m (StreamingProcess m)
- Coquina: shellStreamableProcessBuffered :: forall (m :: Type -> Type). (MonadIO m, MonadMask m) => CreateProcess -> Shell m (StreamingProcess m, IO ByteString, IO ByteString)
+ Coquina: shellStreamableProcessBuffered :: (MonadIO m, MonadMask m) => CreateProcess -> Shell m (StreamingProcess m, IO ByteString, IO ByteString)
- Coquina.Internal: readAndDecodeCreateProcess :: CreateProcess -> IO (ExitCode, Text, Text)
+ Coquina.Internal: readAndDecodeCreateProcess :: CreateProcess -> Text -> IO (ExitCode, Text, Text)
Files
- CHANGELOG.md +4/−0
- README.lhs +0/−3
- README.md +0/−3
- coquina.cabal +1/−1
- src/Coquina.hs +5/−4
- src/Coquina/Internal.hs +39/−19
CHANGELOG.md view
@@ -1,5 +1,9 @@ # Revision history for coquina +## 0.2.0.0++* `shellCreateProcessWithEnv` now takes an additional argument for stdin+ ## 0.1.0.1 * Loosen version bounds
README.lhs view
@@ -1,6 +1,3 @@-# MOVED TO [GITHUB](https://github.com/obsidiansystems/coquina)-- # coquina [](https://haskell.org) [](https://hackage.haskell.org/package/coquina) [](https://matrix.hackage.haskell.org/#/package/coquina) [](https://github.com/obsidiansystems/coquina/actions) [](https://github.com/obsidiansystems/coquina/blob/master/LICENSE)
README.md view
@@ -1,6 +1,3 @@-# MOVED TO [GITHUB](https://github.com/obsidiansystems/coquina)-- # coquina [](https://haskell.org) [](https://hackage.haskell.org/package/coquina) [](https://matrix.hackage.haskell.org/#/package/coquina) [](https://github.com/obsidiansystems/coquina/actions) [](https://github.com/obsidiansystems/coquina/blob/master/LICENSE)
coquina.cabal view
@@ -1,6 +1,6 @@ cabal-version: >=1.10 name: coquina-version: 0.1.0.1+version: 0.2.0.0 synopsis: Yet another shell monad. description: A simple monad for shelling out from Haskell programs. bug-reports: https://github.com/obsidiansystems/coquina/issues
src/Coquina.hs view
@@ -158,7 +158,7 @@ -- | Run a 'CreateProcess' in a 'Shell' shellCreateProcess :: MonadIO m => CreateProcess -> Shell m ()-shellCreateProcess = shellCreateProcessWithEnv mempty+shellCreateProcess p = shellCreateProcessWithEnv mempty p "" -- | Run a 'CreateProcess' in a 'Shell' run :: MonadIO m => CreateProcess -> Shell m ()@@ -269,18 +269,19 @@ :: MonadIO m => Map String String -> CreateProcess+ -> Text -- ^ stdin -> Shell m ()-shellCreateProcessWithEnv envOverrides = shellCreateProcessWith f+shellCreateProcessWithEnv envOverrides cmd' stdin = shellCreateProcessWith f cmd' where f cmd = do envWithOverrides <- liftIO $ if Map.null envOverrides then return $ env cmd else Just . Map.toList . Map.union envOverrides . Map.fromList <$> getEnvironment- readAndDecodeCreateProcess $ cmd { env = envWithOverrides }+ readAndDecodeCreateProcess (cmd { env = envWithOverrides }) stdin -- | Execute a shell process with environment variables runCreateProcessWithEnv :: Map String String -> CreateProcess -> IO (ExitCode, Text, Text)-runCreateProcessWithEnv menv p = execShell $ shellCreateProcessWithEnv menv p+runCreateProcessWithEnv menv p = execShell $ shellCreateProcessWithEnv menv p "" -- | Execute a shell process runCreateProcess :: CreateProcess -> IO (ExitCode, Text, Text)
src/Coquina/Internal.hs view
@@ -1,29 +1,31 @@ module Coquina.Internal where -import Control.Concurrent (MVar, newEmptyMVar, forkIO, putMVar, takeMVar, killThread)+import Control.Concurrent (MVar, forkIO, killThread, newEmptyMVar, putMVar, takeMVar) import Control.DeepSeq (rnf)-import Control.Exception (SomeException, evaluate, mask, try, throwIO, onException)+import Control.Exception (SomeException, evaluate, mask, onException, throwIO, try)+import qualified Control.Exception as C+import Control.Monad import Data.ByteString (hGetContents) import Data.Text (Text)+import qualified Data.Text as T import Data.Text.Encoding (decodeUtf8)-import System.Process+import qualified Data.Text.IO as T+import Foreign.C.Error (Errno(..), ePIPE)+import GHC.IO.Exception (IOErrorType(..), IOException(..)) import System.Exit import System.IO (hClose)---- | The code below is taken from System.Process which unfortunately does not export this function-withForkWait :: IO () -> (IO () -> IO a) -> IO a-withForkWait async body = do- waitVar <- newEmptyMVar :: IO (MVar (Either SomeException ()))- mask $ \restore -> do- tid <- forkIO $ try (restore async) >>= putMVar waitVar- let wait = takeMVar waitVar >>= either throwIO return- restore (body wait) `onException` killThread tid+import System.Process --- | Like readCreateProcess, but ignores stdin and decodes bytes, assuming utf-8-readAndDecodeCreateProcess :: CreateProcess -> IO (ExitCode, Text, Text)-readAndDecodeCreateProcess cp =- withCreateProcess (cp { std_out = CreatePipe, std_err = CreatePipe }) $ \_ mouth merrh ph -> case (mouth, merrh) of- (Just outh, Just errh) -> do+-- | Like readCreateProcess+readAndDecodeCreateProcess :: CreateProcess -> Text -> IO (ExitCode, Text, Text)+readAndDecodeCreateProcess cp input =+ withCreateProcess (cp { std_in = CreatePipe, std_out = CreatePipe, std_err = CreatePipe }) $ \mstdin mouth merrh ph -> case (mstdin, mouth, merrh) of+ (Just inh, Just outh, Just errh) -> do+ -- Write stdin+ unless (T.null input) $ do+ ignoreSigPipe $ T.hPutStr inh input+ -- hClose performs implicit hFlush, and thus may trigger a SIGPIPE+ ignoreSigPipe $ hClose inh out <- fmap decodeUtf8 $ hGetContents outh err <- fmap decodeUtf8 $ hGetContents errh withForkWait (evaluate $ rnf out) $ \waitOut ->@@ -34,5 +36,23 @@ hClose errh exitCode <- waitForProcess ph return (exitCode, out, err)- (Nothing, _) -> error "readAndDecodeCreateProcess: Failed to get std_out handle"- (Just _, Nothing) -> error "readAndDecodeCreateProcess: Failed to get std_err handle"+ (Nothing, _, _) -> error "readAndDecodeCreateProcess: Failed to get std_in handle"+ (_, Nothing, _) -> error "readAndDecodeCreateProcess: Failed to get std_out handle"+ (_, _, Nothing) -> error "readAndDecodeCreateProcess: Failed to get std_err handle"++-- * The code below is taken from System.Process which unfortunately does not export these functions++-- | From System.Process+withForkWait :: IO () -> (IO () -> IO a) -> IO a+withForkWait async body = do+ waitVar <- newEmptyMVar :: IO (MVar (Either SomeException ()))+ mask $ \restore -> do+ tid <- forkIO $ try (restore async) >>= putMVar waitVar+ let wait = takeMVar waitVar >>= either throwIO return+ restore (body wait) `onException` killThread tid++-- | From System.Process+ignoreSigPipe :: IO () -> IO ()+ignoreSigPipe = C.handle $ \e -> case e of+ IOError { ioe_type = ResourceVanished, ioe_errno = Just ioe } | Errno ioe == ePIPE -> return ()+ _ -> throwIO e