Skip to content

Commit 61b8b55

Browse files
committed
Trim trailing newlines in ExitCodeException Show instance
Needs fpco#88. Previously, output was assumed to end with a newline, leading to poor `ExitCodeException` rendering (see the "Standard error" header below): ghci> readProcess_ $ proc "sh" ["-c", "nix path-info --json nixpkgs#agda && false"] *** Exception: Received ExitFailure 1 when running Raw command: sh -c "nix path-info --json nixpkgs#agda && false" Standard output: [{"path":"/nix/store/sj2z0h5ywlflqv50dfphwia6p0ij0mlj-agdaWithPackages-2.6.4.3","valid":false}]Standard error: these 5 paths will be fetched (18.30 MiB download, 133.19 MiB unpacked): /nix/store/5q0kb0nqnqcfs7a0ncsjq4fdppwirpxa-Agda-2.6.4.3-bin /nix/store/xmximjjnkn0hm4gw7akc9f20ydz6msmk-Agda-2.6.4.3-data /nix/store/sj2z0h5ywlflqv50dfphwia6p0ij0mlj-agdaWithPackages-2.6.4.3 /nix/store/b49sa2q0yb3fd14ppzh6j6rm8vvgr9n6-ghc-9.6.6-with-packages /nix/store/vharimf7f2glj4fyhiglzws0qyv4xrry-libraries Now, trailing newlines are removed and the correct number of newlines are inserted in order to make the `Show` instance display legibly: ghci> readProcess_ $ proc "sh" ["-c", "nix path-info --json nixpkgs#agda && false"] *** Exception: Received ExitFailure 1 when running Raw command: sh -c "nix path-info --json nixpkgs#agda && false" Standard output: [{"path":"/nix/store/sj2z0h5ywlflqv50dfphwia6p0ij0mlj-agdaWithPackages-2.6.4.3","valid":false}] Standard error: these 5 paths will be fetched (18.30 MiB download, 133.19 MiB unpacked): /nix/store/5q0kb0nqnqcfs7a0ncsjq4fdppwirpxa-Agda-2.6.4.3-bin /nix/store/xmximjjnkn0hm4gw7akc9f20ydz6msmk-Agda-2.6.4.3-data /nix/store/sj2z0h5ywlflqv50dfphwia6p0ij0mlj-agdaWithPackages-2.6.4.3 /nix/store/b49sa2q0yb3fd14ppzh6j6rm8vvgr9n6-ghc-9.6.6-with-packages /nix/store/vharimf7f2glj4fyhiglzws0qyv4xrry-libraries Also, derived `Show` instances will behave correctly now. Previously, the `Show` instance would often end with a newline, leading to clumsy output: ghci> e stdout stderr = ExitCodeException { ... } ghci> data Foo = Foo { a :: Int, b :: ExitCodeException, c :: String } deriving Show ghci> Foo 1 (e "<STDOUT>\n" "") "hello" Foo {a = 1, b = Received ExitFailure 1 when running Raw command: echo Standard output: <STDOUT> , c = "hello"} Now: ghci> Foo 1 (e "<STDOUT>\n" "") "hello" Foo {a = 1, b = Received ExitFailure 1 when running Raw command: echo Standard output: <STDOUT>, c = "hello"}
1 parent 685a67a commit 61b8b55

File tree

2 files changed

+67
-19
lines changed

2 files changed

+67
-19
lines changed

src/System/Process/Typed/Internal.hs

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import qualified Control.Exception as E
1717
import Control.Exception hiding (bracket, finally, handle)
1818
import Control.Monad (void)
1919
import qualified System.Process as P
20+
import Data.List (dropWhileEnd)
2021
import Data.Typeable (Typeable)
2122
import System.IO (Handle, hClose, IOMode(ReadWriteMode), withBinaryFile)
2223
import Control.Concurrent.Async (async)
@@ -616,16 +617,24 @@ data ExitCodeException = ExitCodeException
616617
instance Exception ExitCodeException
617618
instance Show ExitCodeException where
618619
show ece =
619-
let stdout = L8.unpack $ eceStdout ece
620-
stderr = L8.unpack $ eceStderr ece
621-
stdout' = if L.null (eceStdout ece)
620+
let stdout = trimTrailingAsciiNewlines $ L8.unpack $ eceStdout ece
621+
stderr = trimTrailingAsciiNewlines $ L8.unpack $ eceStderr ece
622+
623+
isAsciiNewline char = case char of
624+
'\n' -> True
625+
'\r' -> True
626+
_ -> False
627+
trimTrailingAsciiNewlines = dropWhileEnd isAsciiNewline
628+
629+
630+
stdout' = if null stdout
622631
then []
623632
else [ "\n\nStandard output:\n"
624633
, stdout
625634
]
626-
stderr' = if L.null (eceStderr ece)
635+
stderr' = if null stderr
627636
then []
628-
else [ "\nStandard error:\n"
637+
else [ "\n\nStandard error:\n"
629638
, stderr
630639
]
631640
in concat $

test/System/Process/TypedSpec.hs

Lines changed: 53 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -234,9 +234,6 @@ spec = do
234234

235235
describe "Show ExitCodeException" $ do
236236
it "shows ExitCodeException" $ do
237-
-- Note that the `show` output ends with a newline, so functions
238-
-- like `print` will output an extra blank line at the end of the
239-
-- output.
240237
let exitCodeException =
241238
ExitCodeException
242239
{ eceExitCode = ExitFailure 1
@@ -252,7 +249,7 @@ spec = do
252249
++ "Copied OK\n"
253250
++ "\n"
254251
++ "Standard error:\n"
255-
++ "Uh oh!\n"
252+
++ "Uh oh!"
256253

257254
context "without stderr" $ do
258255
it "shows ExitCodeException" $ do
@@ -268,7 +265,7 @@ spec = do
268265
++ "Raw command: show-puppy\n"
269266
++ "\n"
270267
++ "Standard output:\n"
271-
++ "No puppies found???\n"
268+
++ "No puppies found???"
272269

273270
context "without stdout" $ do
274271
it "shows ExitCodeException" $ do
@@ -282,12 +279,55 @@ spec = do
282279
show exitCodeException `shouldBe`
283280
"Received ExitFailure 1 when running\n"
284281
++ "Raw command: show-puppy\n"
282+
++ "\n"
285283
++ "Standard error:\n"
286-
++ "No puppies found???\n"
284+
++ "No puppies found???"
285+
286+
it "trims newlines from stdout/stderr" $ do
287+
-- This keeps the `Show` output looking nice regardless of how many
288+
-- newlines (if any) the command outputs.
289+
--
290+
-- This also makes sure that the `Show` output doesn't end with a
291+
-- spurious trailing newline, making it easier to compose `Show`
292+
-- instances together.
293+
let exitCodeException =
294+
ExitCodeException
295+
{ eceExitCode = ExitFailure 1
296+
, eceProcessConfig = proc "detect-doggies" []
297+
, eceStdout = fromString "puppy\n\n"
298+
, eceStderr = fromString "doggy\r\n"
299+
}
300+
show exitCodeException `shouldBe`
301+
"Received ExitFailure 1 when running\n"
302+
++ "Raw command: detect-doggies\n"
303+
++ "\n"
304+
++ "Standard output:\n"
305+
++ "puppy\n"
306+
++ "\n"
307+
++ "Standard error:\n"
308+
++ "doggy"
309+
310+
it "adds newlines to stdout/stderr" $ do
311+
-- This keeps the `Show` output looking nice when the output
312+
-- doesn't include a trailing newline.
313+
let exitCodeException =
314+
ExitCodeException
315+
{ eceExitCode = ExitFailure 1
316+
, eceProcessConfig = proc "detect-doggies" []
317+
, eceStdout = fromString "puppy"
318+
, eceStderr = fromString "doggy"
319+
}
320+
show exitCodeException `shouldBe`
321+
"Received ExitFailure 1 when running\n"
322+
++ "Raw command: detect-doggies\n"
323+
++ "\n"
324+
++ "Standard output:\n"
325+
++ "puppy\n"
326+
++ "\n"
327+
++ "Standard error:\n"
328+
++ "doggy"
287329

288-
it "does not trim stdout/stderr" $ do
289-
-- This looks weird, and I think it would be better to strip the
290-
-- whitespace from the output.
330+
it "trims newlines but not other whitespace from stdout/stderr" $ do
291331
let exitCodeException =
292332
ExitCodeException
293333
{ eceExitCode = ExitFailure 1
@@ -300,10 +340,10 @@ spec = do
300340
++ "Raw command: detect-doggies\n"
301341
++ "\n"
302342
++ "Standard output:\n"
303-
++ "\n\npuppy\n\n \n"
304-
++ "\n"
343+
++ "\n\npuppy\n\n "
344+
++ "\n\n"
305345
++ "Standard error:\n"
306-
++ "\t \ndoggy\n \t\n"
346+
++ "\t \ndoggy\n \t"
307347

308348
context "without newlines in stdout" $ do
309349
it "shows ExitCodeException" $ do
@@ -324,8 +364,6 @@ spec = do
324364

325365
context "without newlines in stdout or stderr" $ do
326366
it "shows ExitCodeException" $ do
327-
-- If the stderr isn't empty and stdout doesn't end with a newline,
328-
-- the blank line between the two sections disappears.
329367
let exitCodeException =
330368
ExitCodeException
331369
{ eceExitCode = ExitFailure 1
@@ -339,5 +377,6 @@ spec = do
339377
++ "\n"
340378
++ "Standard output:\n"
341379
++ "puppy\n"
380+
++ "\n"
342381
++ "Standard error:\n"
343382
++ "doggy"

0 commit comments

Comments
 (0)