packages feed

shelly 1.1.0.0 → 1.2.0.0

raw patch · 3 files changed

+105/−58 lines, 3 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

+ Shelly: ErrorHandle :: Handle -> ReusedHandle
+ Shelly: InHandle :: Handle -> ReusedHandle
+ Shelly: OutHandle :: Handle -> ReusedHandle
+ Shelly: data ReusedHandle
+ Shelly: transferFoldHandleLines :: a -> FoldCallback a -> Handle -> Handle -> IO a
+ Shelly: transferLinesAndCombine :: Handle -> Handle -> IO Text
- Shelly: runHandles :: FilePath -> [Text] -> (Maybe Handle) -> (Handle -> Handle -> Sh a) -> Sh a
+ Shelly: runHandles :: FilePath -> [Text] -> [ReusedHandle] -> (Handle -> Handle -> Handle -> Sh a) -> Sh a

Files

shelly.cabal view
@@ -1,6 +1,6 @@ Name:       shelly -Version:     1.1.0.0+Version:     1.2.0.0 Synopsis:    shell-like (systems) programming in Haskell  Description: Shelly provides convenient systems programming in Haskell,
src/Shelly.hs view
@@ -26,12 +26,17 @@          Sh, ShIO, shelly, shellyNoDir, sub, silently, verbosely, escaping, print_stdout, print_commands, tracing, errExit           -- * Running external commands.-         , run, run_, runFoldLines, cmd, FoldCallback, runHandle, runHandles+         , run, run_, runFoldLines, cmd, FoldCallback          , (-|-), lastStderr, setStdin, lastExitCode          , command, command_, command1, command1_          , sshPairs, sshPairs_          , ShellArg (..) +         -- * Running commands Using handles+         , runHandle, runHandles, transferLinesAndCombine, transferFoldHandleLines+         , ReusedHandle(..)++          -- * Modifying and querying environment.          , setenv, get_env, get_env_text, getenv, get_env_def, appendToPath @@ -94,6 +99,7 @@ import Data.Foldable (foldl') import Data.Maybe import System.IO ( hClose, stderr, stdout, openTempFile )+import System.IO.Error (isPermissionError, catchIOError, isEOFError, isIllegalOperation) import System.Exit import System.Environment import Control.Applicative@@ -105,20 +111,18 @@ import qualified Data.Text.Encoding as TE import qualified Data.Text.Encoding.Error as TE import System.Process( CmdSpec(..), StdStream(CreatePipe, UseHandle), CreateProcess(..), createProcess, waitForProcess, terminateProcess, ProcessHandle )-import System.IO.Error (isPermissionError)  import qualified Data.Text as T import qualified Data.ByteString as BS import Data.ByteString (ByteString) -import Data.Monoid (mempty)+import Data.Monoid (Monoid, mempty, mappend) #if __GLASGOW_HASKELL__ < 704-import Data.Monoid (Monoid, mappend) infixr 5 <> (<>) :: Monoid m => m -> m -> m (<>) = mappend #else-import Data.Monoid ( mappend, (<>))+import Data.Monoid ((<>)) #endif  import Filesystem.Path.CurrentOS hiding (concat, fromText, (</>), (<.>))@@ -169,7 +173,6 @@ instance ShellArg FilePath where toTextArg = toTextIgnore instance ShellArg String   where toTextArg = T.pack - -- | used to create the variadic function 'cmd' class ShellCommand t where     cmdAll :: FilePath -> [Text] -> t@@ -236,27 +239,38 @@ fromText :: Text -> FilePath fromText = FP.fromText -printGetTextList :: Handle -> Handle -> IO Text-printGetTextList h1 h2 = printFoldHandleLines mempty (|>) h1 h2 >>=+-- | Transfer from one handle to another+-- For example, send contents of a process output to stdout.+-- does not close the write handle.+--+-- Also, return the complete contents being streamed line by line.+transferLinesAndCombine :: Handle -> Handle -> IO Text+transferLinesAndCombine h1 h2 = transferFoldHandleLines mempty (|>) h1 h2 >>=     return . lineSeqToText  lineSeqToText :: Seq Text -> Text lineSeqToText = T.intercalate "\n" . foldl' (flip (:)) [] -{--getContent :: Handle -> IO Text-getContent h = fmap B.toLazyText $ foldHandleLines (B.fromText "") foldBuilder h--}- type FoldCallback a = (a -> Text -> a) -printFoldHandleLines :: a -> FoldCallback a -> Handle -> Handle -> IO a-printFoldHandleLines start foldLine readHandle writeHandle = go start+-- | Transfer from one handle to another+-- For example, send contents of a process output to stdout.+-- does not close the write handle.+--+-- Also, fold over the contents being streamed line by line+transferFoldHandleLines :: a -> FoldCallback a -> Handle -> Handle -> IO a+transferFoldHandleLines start foldLine readHandle writeHandle = go start   where+    catchIOErrors action = catchIOError+                   (fmap Just action)+                   (\e -> if isEOFError e || isIllegalOperation e -- handle was closed+                           then return Nothing+                           else ioError e)     go acc = do-      line <- TIO.hGetLine readHandle-      TIO.hPutStrLn writeHandle line >> go (foldLine acc line)-     `catchany` \_ -> return acc+        mLine <- catchIOErrors (TIO.hGetLine readHandle)+        case mLine of+            Nothing -> return acc+            Just line -> TIO.hPutStrLn writeHandle line >> go (foldLine acc line)  foldHandleLines :: a -> FoldCallback a -> Handle -> IO a foldHandleLines start foldLine readHandle = go start@@ -277,13 +291,13 @@   stateVar <- ask   liftIO (writeIORef stateVar newState) -runCommandNoEscape :: (Maybe Handle) -> State -> FilePath -> [Text] -> Sh (Handle, Handle, Handle, ProcessHandle)-runCommandNoEscape mstdin st exe args = liftIO $ shellyProcess mstdin st $+runCommandNoEscape :: [ReusedHandle] -> State -> FilePath -> [Text] -> Sh (Handle, Handle, Handle, ProcessHandle)+runCommandNoEscape handles st exe args = liftIO $ shellyProcess handles st $     ShellCommand $ T.unpack $ T.intercalate " " (toTextIgnore exe : args) -runCommand :: (Maybe Handle) -> State -> FilePath -> [Text] -> Sh (Handle, Handle, Handle, ProcessHandle)-runCommand mstdin st exe args = findExe exe >>= \fullExe ->-  liftIO $ shellyProcess mstdin st $+runCommand :: [ReusedHandle] -> State -> FilePath -> [Text] -> Sh (Handle, Handle, Handle, ProcessHandle)+runCommand handles st exe args = findExe exe >>= \fullExe ->+  liftIO $ shellyProcess handles st $     RawCommand (unpack fullExe) (map T.unpack args)   where     findExe :: FilePath -> Sh FilePath@@ -310,23 +324,54 @@   -shellyProcess :: (Maybe Handle) -> State -> CmdSpec -> IO (Handle, Handle, Handle, ProcessHandle)-shellyProcess mstdin st cmdSpec =  do-  (Just hin, Just hout, Just herr, pHandle) <- createProcess CreateProcess {-        cmdspec = cmdSpec-      , cwd = Just $ unpack $ sDirectory st-      , env = Just $ sEnvironment st-      , std_in = case mstdin of-                   Nothing     -> CreatePipe-                   Just handle -> UseHandle handle-      , std_out = CreatePipe, std_err = CreatePipe-      , close_fds = False+shellyProcess :: [ReusedHandle] -> State -> CmdSpec -> IO (Handle, Handle, Handle, ProcessHandle)+shellyProcess reusedHandles st cmdSpec =  do+    (createdInH, createdOutH, createdErrorH, pHandle) <- createProcess CreateProcess {+          cmdspec = cmdSpec+        , cwd = Just $ unpack $ sDirectory st+        , env = Just $ sEnvironment st+        , std_in  = reuse mIn+        , std_out = reuse mOut+        , std_err = reuse mError+        , close_fds = False #if MIN_VERSION_process(1,1,0)-      , create_group = False+        , create_group = False #endif-      }-  return (hin, hout, herr, pHandle)+        }+    return (just $ createdInH <|> mInH,+            just $ createdOutH <|> mOutH,+            just $ createdErrorH <|> mErrorH, pHandle)+  where+    just :: Maybe a -> a+    just Nothing  = error "error in shelly creating process"+    just (Just j) = j +    mInH    = getHandle mIn reusedHandles+    mOutH   = getHandle mOut reusedHandles+    mErrorH = getHandle mError reusedHandles++    reuse :: (ReusedHandle -> Maybe Handle) -> StdStream+    reuse mHandle = createOrUse $ getHandle mHandle reusedHandles++    getHandle :: (ReusedHandle -> Maybe Handle) -> [ReusedHandle] -> Maybe Handle+    getHandle _ [] = Nothing+    getHandle mHandle (h:hs) = case mHandle h of+        Just already -> Just already+        Nothing -> getHandle mHandle hs++    createOrUse :: Maybe Handle -> StdStream+    createOrUse mHandle = case mHandle of+        Just already -> UseHandle already+        Nothing -> CreatePipe++    mIn, mOut, mError :: (ReusedHandle -> Maybe Handle)+    mIn (InHandle h) = Just h+    mIn _ = Nothing+    mOut (OutHandle h) = Just h+    mOut _ = Nothing+    mError (ErrorHandle h) = Just h+    mError _ = Nothing+ {- -- | use for commands requiring usage of sudo. see 'run_sudo'. --  Use this pattern for priveledge separation@@ -935,15 +980,13 @@           -> (Handle -> Sh a) -- ^ stdout handle           -> Sh a runHandle exe args withHandle = -  runHandles exe args Nothing $ \outH errH -> do+  runHandles exe args [] $ \_ outH errH -> do     errVar <- liftIO $ do       errVar' <- newEmptyMVar-      _ <- forkIO $ printGetTextList errH stderr >>= putMVar errVar'+      _ <- forkIO $ transferLinesAndCombine errH stderr >>= putMVar errVar'       return errVar'-      -- liftIO_ $ forkIO $ getContent errH >>= putMVar errVar-      ---    res <- withHandle outH +    res <- withHandle outH     errs <- liftIO $ takeMVar errVar      modify $ \state' -> state' { sStderr = errs }@@ -952,10 +995,10 @@ -- | Similar to 'run' but gives direct access to all input and output handles. runHandles :: FilePath -- ^ command            -> [Text] -- ^ arguments-           -> (Maybe Handle) -- ^ stdin-           -> (Handle -> Handle -> Sh a) -- ^ stdout and stderr+           -> [ReusedHandle] -- ^ optionally connect process i/o handles to existing handles+           -> (Handle -> Handle -> Handle -> Sh a) -- ^ stdin, stdout and stderr            -> Sh a-runHandles exe args mStdinHandle withHandles = do+runHandles exe args reusedHandles withHandles = do     -- clear stdin before beginning command execution     origstate <- get     let mStdin = sStdin origstate@@ -967,22 +1010,22 @@     trace cmdString      bracketOnWindowsError-      ((sRun state) mStdinHandle state exe args)+      ((sRun state) reusedHandles state exe args)       (\(_,_,_,procH) -> (liftIO $ terminateProcess procH))       (\(inH,outH,errH,procH) -> do         liftIO $ case mStdin of-          Just input ->-            TIO.hPutStr inH input >> hClose inH-            -- stdin is cleared from state below+          Just input -> TIO.hPutStr inH input           Nothing -> return () -        result <- withHandles outH errH+        result <- withHandles inH outH errH          (ex, code) <- liftIO $ do+          ex' <- waitForProcess procH+           hClose outH           hClose errH+          hClose inH -          ex' <- waitForProcess procH           return $ case ex' of             ExitSuccess -> (ex', 0)             ExitFailure n -> (ex', n)@@ -1013,19 +1056,19 @@ -- stderr is still being placed in memory under the assumption it is always relatively small runFoldLines :: a -> FoldCallback a -> FilePath -> [Text] -> Sh a runFoldLines start cb exe args =-  runHandles exe args Nothing $ \outH errH -> do+  runHandles exe args [] $ \inH outH errH -> do     (errVar, outVar) <- liftIO $ do+      hClose inH -- setStdin was taken care of before the process even ran       errVar' <- newEmptyMVar       outVar' <- newEmptyMVar-      _ <- forkIO $ printGetTextList errH stderr >>= putMVar errVar'+      _ <- forkIO $ transferLinesAndCombine errH stderr >>= putMVar errVar'       return (errVar', outVar')-      -- liftIO_ $ forkIO $ getContent errH >>= putMVar errVar      state <- get     errs <- liftIO $ do       void $ if sPrintStdout state         then-          forkIO $ printFoldHandleLines start cb outH stdout >>= putMVar outVar+          forkIO $ transferFoldHandleLines start cb outH stdout >>= putMVar outVar         else           forkIO $ foldHandleLines start cb outH >>= putMVar outVar       takeMVar errVar
src/Shelly/Base.hs view
@@ -6,7 +6,7 @@ -- However, Shelly went back to exposing a single module module Shelly.Base   (-    ShIO, Sh, unSh, runSh, State(..), FilePath, Text,+    ShIO, Sh, unSh, runSh, State(..), ReusedHandle(..), FilePath, Text,     relPath, path, absPath, canonic, canonicalize,     test_d, test_s,     unpack, gets, get, modify, trace,@@ -67,13 +67,17 @@    , sDirectory :: FilePath -- ^ working directory    , sPrintStdout :: Bool   -- ^ print stdout of command that is executed    , sPrintCommands :: Bool -- ^ print command that is executed-   , sRun :: (Maybe Handle) -> State -> FilePath -> [Text] -> Sh (Handle, Handle, Handle, ProcessHandle) -- ^ command runner, a different runner is used when escaping, probably better to just hold the escaping flag+   , sRun :: [ReusedHandle] -> State -> FilePath -> [Text] -> Sh (Handle, Handle, Handle, ProcessHandle) -- ^ command runner, a different runner is used when escaping, probably better to just hold the escaping flag    , sEnvironment :: [(String, String)]    , sPathExecutables :: Maybe [(FilePath, S.Set FilePath)] -- ^ cache of executables in the PATH    , sTracing :: Bool -- ^ should we trace command execution    , sTrace :: Text -- ^ the trace of command execution    , sErrExit :: Bool -- ^ should we exit immediately on any error    }++data ReusedHandle = InHandle Handle+                  | OutHandle Handle+                  | ErrorHandle Handle  -- | A monadic-conditional version of the "when" guard. whenM :: Monad m => m Bool -> m () -> m ()