Skip to content

Commit

Permalink
Merge pull request #80 from 9999years/make-exit-code-exception-constr…
Browse files Browse the repository at this point in the history
…uctable

Add `ExitCodeException` smart constructors
  • Loading branch information
snoyberg authored Aug 19, 2024
2 parents de19b4e + c4505b1 commit 3109782
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 9 deletions.
63 changes: 57 additions & 6 deletions src/System/Process/Typed.hs
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,8 @@ module System.Process.Typed

-- * Exceptions
, ExitCodeException (..)
, exitCodeExceptionWithOutput
, exitCodeExceptionNoOutput
, ByteStringOutputException (..)

-- * Re-exports
Expand Down Expand Up @@ -638,12 +640,7 @@ checkExitCodeSTM p = do
ec <- readTMVar (pExitCode p)
case ec of
ExitSuccess -> return ()
_ -> throwSTM ExitCodeException
{ eceExitCode = ec
, eceProcessConfig = clearStreams (pConfig p)
, eceStdout = L.empty
, eceStderr = L.empty
}
_ -> throwSTM $ exitCodeExceptionNoOutput p ec

-- | Returns the PID (process ID) of a subprocess.
--
Expand Down Expand Up @@ -682,6 +679,18 @@ getStdout = pStdout
getStderr :: Process stdin stdout stderr -> stderr
getStderr = pStderr

-- | Get a process's configuration.
--
-- This is useful for constructing 'ExitCodeException's.
--
-- Note that the stdin, stdout, and stderr streams are stored in the 'Process',
-- not the 'ProcessConfig', so the returned 'ProcessConfig' will always have
-- empty stdin, stdout, and stderr values.
--
-- @since 0.2.12.0
getProcessConfig :: Process stdin stdout stderr -> ProcessConfig () () ()
getProcessConfig = pConfig

-- | Take 'System.Process.ProcessHandle' out of the 'Process'.
-- This method is needed in cases one need to use low level functions
-- from the @process@ package. Use cases for this method are:
Expand All @@ -703,3 +712,45 @@ getStderr = pStderr
-- @since 0.1.1
unsafeProcessHandle :: Process stdin stdout stderr -> P.ProcessHandle
unsafeProcessHandle = pHandle

-- | Get an 'ExitCodeException' containing the process's stdout and stderr data.
--
-- Note that this will call 'waitExitCode' to block until the process exits, if
-- it has not exited already.
--
-- Unlike 'checkExitCode' and similar, this will return an 'ExitCodeException'
-- even if the process exits with 'ExitSuccess'.
--
-- @since 0.2.12.0
exitCodeExceptionWithOutput :: MonadIO m
=> Process stdin (STM L.ByteString) (STM L.ByteString)
-> m ExitCodeException
exitCodeExceptionWithOutput process = liftIO $ atomically $ do
exitCode <- waitExitCodeSTM process
stdout <- getStdout process
stderr <- getStderr process
pure ExitCodeException
{ eceExitCode = exitCode
, eceProcessConfig = pConfig process
, eceStdout = stdout
, eceStderr = stderr
}

-- | Get an 'ExitCodeException' containing no data other than the exit code and
-- process config.
--
-- Unlike 'checkExitCode' and similar, this will return an 'ExitCodeException'
-- even if the process exits with 'ExitSuccess'.
--
-- @since 0.2.12.0
exitCodeExceptionNoOutput :: Process stdin stdout stderr
-> ExitCode
-> ExitCodeException
exitCodeExceptionNoOutput process exitCode =
ExitCodeException
{ eceExitCode = exitCode
, eceProcessConfig = pConfig process
, eceStdout = L.empty
, eceStderr = L.empty
}

10 changes: 7 additions & 3 deletions src/System/Process/Typed/Internal.hs
Original file line number Diff line number Diff line change
Expand Up @@ -590,13 +590,17 @@ useHandleOpen h = mkStreamSpec (P.UseHandle h) $ \_ _ -> return ((), return ())
useHandleClose :: Handle -> StreamSpec anyStreamType ()
useHandleClose h = mkStreamSpec (P.UseHandle h) $ \_ _ -> return ((), hClose h)

-- | Exception thrown by 'checkExitCode' in the event of a non-success
-- exit code. Note that 'checkExitCode' is called by other functions
-- as well, like 'runProcess_' or 'readProcess_'.
-- | Exception thrown by 'System.Process.Typed.checkExitCode' in the event of a
-- non-success exit code. Note that 'System.Process.Typed.checkExitCode' is
-- called by other functions as well, like 'System.Process.Typed.runProcess_'
-- or 'System.Process.Typed.readProcess_'.
--
-- Note that several functions that throw an 'ExitCodeException' intentionally do not populate 'eceStdout' or 'eceStderr'.
-- This prevents unbounded memory usage for large stdout and stderrs.
--
-- Functions which do include 'eceStdout' or 'eceStderr' (like
-- 'System.Process.Typed.readProcess_') state so in their documentation.
--
-- @since 0.1.0.0
data ExitCodeException = ExitCodeException
{ eceExitCode :: ExitCode
Expand Down

0 comments on commit 3109782

Please sign in to comment.