pipes-cliff-0.6.0.0: Streaming to and from subprocesses using Pipes

Safe HaskellSafe-Inferred
LanguageHaskell2010

Pipes.Cliff.Core

Contents

Description

This contains the innards of Cliff. You probably won't need anything that's in here; Pipes.Cliff re-exports the most useful bindings. But nothing will break if you use what's in here, so it's here if you need it.

Synopsis

Data types

data CmdSpec Source

Like CmdSpec in System.Process, but also has an instance for Show.

Errors

data Activity Source

When dealing with a Handle, errors can occur when reading from, writing to, or closing the handle.

Constructors

Reading 
Writing 
Closing 

data HandleDesc Source

Describes a handle. From the perspective of the subprocess; for example, Input means that this handle is connected to the process's standard input.

Constructors

Input 
Output 
Error 

data HandleOopsie Source

Describes IO errors tha occur when dealing with a Handle.

data Oopsie Source

Describes all IO exceptions. The Oopsie contains the IOException itself, along with the CmdSpec that was running when the exception occurred. If the exception occurred while dealing with a Handle, there is also a HandleOopsie. If there is no HandleOopsie, this means that the exception arose when running terminateProcess.

The exceptions that are caught and placed into an Oopsie may arise from reading data from or writing data to a Handle. In these errors, the associated Producer or Consumer will terminate (which may trigger various cleanup actions in the MonadSafe computation) but the exception itself is not re-thrown; rather, it is passed to the handler. Similarly, an exception may occur while closing a handle; these exceptions are caught, not rethrown, and are passed to the handler. If an exception arises when terminating a process (I'm not sure this is possible) then it is also caught, not rethrown, and passed to the handler.

If an exception arises when creating a process--such as a command not being found--the exception is not caught, handled, or passed to the handler. Also, an Oopsie is created only for an IOException; no other exceptions of any kind are caught or handled. However, exceptions of any kind will still trigger appropriate cleanup actions in the MonadSafe computation.

Instances

renderOopsie Source

Arguments

:: String

The name of the currently runnning program

-> Oopsie 
-> String 

Formats an Oopsie for display.

defaultHandler :: Oopsie -> IO () Source

The default handler when receiving an Oopsie; simply uses renderOopsie to format it nicely and put it on standard error.

Configuration types

data NonPipe Source

How will the subprocess get its information for this stream? A NonPipe is used for streams that will not be assigned to a Proxy but, instead, will be inherited from the parent or directed from an existing Handle.

Constructors

Inherit

Use whatever stream that the parent process has.

UseHandle Handle

Use the given handle for input or output

data CreateProcess Source

Like CreateProcess in System.Process, this gives the necessary information to create a subprocess. All but one of these fields is also present in CreateProcess, and they all have the same meaning; the only field that is different is the handler field.

Constructors

CreateProcess 

Fields

cmdspec :: CmdSpec

Executable and arguments, or shell command

cwd :: Maybe FilePath

A new current working directory for the subprocess; if Nothing, use the calling process's working directory.

env :: Maybe [(String, String)]

The environment for the subprocess; if Nothing, use the calling process's working directory.

close_fds :: Bool

If True, close all file descriptors other than the standard descriptors. See the documentation for close_fds for details on how this works in Windows.

create_group :: Bool

If True, create a new process group.

delegate_ctlc :: Bool

See delegate_ctlc in the System.Process module for details.

handler :: Oopsie -> IO ()

Whenever an IO exception arises during the course of various IO actios, the exception is caught and placed into an Oopsie that indicates why and where the exception happened. The handler determines what happens when an Oopsie comes in. See Oopsie for details.

The default handler created by procSpec is defaultHandler, which will simply print the exceptions to standard error. You may not want to see the exceptions at all. For example, many exceptions come from broken pipes. A broken pipe might be entirely normal in your circumstance. For example, if you are streaming a large set of values to a pager such as less and you expect that the user will often quit the pager without viewing the whole result, a broken pipe will result, which will print a warning message. That can be a nuisance.

If you don't want to see the exceptions at all, just set handler to squelch, which simply discards the exceptions.

Conceivably you could rig up an elaborate mechanism that puts the Oopsies into a Pipes.Concurrent mailbox or something. Indeed, when using defaultHandler each thread will print its warnings to standard error at any time. If you are using multiple processes and each prints warnings at the same time, total gibberish can result as the text gets mixed in. You could solve this by putting the errors into a Pipes.Concurrent mailbox and having a single thread print the errors; building this sort of functionality directly in to the library would clutter up the API somewhat so I have been reluctant to do it.

squelch :: Monad m => a -> m () Source

Do not show or do anything with exceptions; useful to use as a handler.

procSpec Source

Arguments

:: String

The name of the program to run, such as less.

-> [String]

Command-line arguments

-> CreateProcess 

Create a CreateProcess record with default settings. The default settings are:

  • a raw command (as opposed to a shell command) is created
  • the current working directory is not changed from the parent process
  • the environment is not changed from the parent process
  • the parent's file descriptors (other than standard input, standard output, and standard error) are inherited
  • no new process group is created
  • delegate_ctlc is False
  • storeProcessHandle is Nothing
  • handler is defaultHandler

ErrSpec

data ErrSpec Source

Contains data necessary to deal with exceptions.

Constructors

ErrSpec 

Exception handling

handleException :: MonadIO m => Maybe HandleOopsie -> IOException -> ErrSpec -> m () Source

Sends an exception using the exception handler specified in the ErrSpec.

handleErrors :: (MonadCatch m, MonadIO m) => Maybe HandleOopsie -> ErrSpec -> m () -> m () Source

Run an action, taking all IO errors and sending them to the handler.

closeHandleNoThrow :: (MonadCatch m, MonadIO m) => Handle -> HandleDesc -> ErrSpec -> m () Source

Close a handle. Catches any exceptions and passes them to the handler.

terminateProcess :: (MonadCatch m, MonadIO m) => ProcessHandle -> ErrSpec -> m () Source

Terminates a process; sends any IO errors to the handler.

acquire Source

Arguments

:: MonadSafe m 
=> Base m a

Acquirer.

-> (a -> Base m ())

Destroyer.

-> m a 

Acquires a resource and ensures it will be destroyed when the MonadSafe computation completes.

Threads

background :: MonadSafe m => IO a -> m (Async a) Source

Runs a thread in the background. The thread is terminated when the MonadSafe computation completes.

conveyor :: MonadSafe m => Effect (SafeT IO) () -> m () Source

Runs in the background an effect, typically one that is moving data from one process to another. For examples of its usage, see Pipes.Cliff.Examples. The associated thread is killed when the MonadSafe computation completes.

waitForThread :: MonadIO m => Async a -> m a Source

A version of wait with an overloaded MonadIO return type. Allows you to wait for the return value of threads launched with background. If the thread throws an exception, waitForThread will throw that same exception.

waitForProcess :: MonadIO m => ProcessHandle -> m ExitCode Source

An overloaded version of the waitForProcess from System.Process.

Mailboxes

messageBuffer :: Buffer a Source

A buffer that holds 1 message. I have no idea if this is the ideal size. Don't use an unbounded buffer, though, because with unbounded producers an unbounded buffer will fill up your RAM.

Since the buffer just holds one size, you might think "why not just use an MVar"? At least, I have been silly enough to think that. Using Pipes.Concurrent also give the mailbox the ability to be sealed; sealing the mailbox signals to the other side that it won't be getting any more input or be allowed to send any more output, which tells the whole pipeline to start shutting down.

newMailbox :: (MonadIO m, MonadSafe mi, MonadSafe mo) => m (Consumer a mi (), Producer a mo ()) Source

Creates a new mailbox and returns Proxy that stream values into and out of the mailbox. Each Proxy is equipped with a finalizer that will seal the mailbox immediately after production or consumption has completed, even if such completion is not due to an exhausted mailbox. This will signal to the other side of the mailbox that the mailbox is sealed.

Production from and consumption to Handles

bufSize :: Int Source

I have no idea what this should be. I'll start with a simple small value and see how it works.

produceFromHandle :: (MonadSafe m, MonadCatch (Base m)) => HandleDesc -> Handle -> ErrSpec -> Producer ByteString m () Source

Create a Producer that produces from a Handle. Takes ownership of the Handle; closes it when the Producer terminates. If any IO errors arise either during production or when the Handle is closed, they are caught and passed to the handler.

consumeToHandle :: (MonadSafe m, MonadCatch (Base m)) => Handle -> ErrSpec -> Consumer ByteString m () Source

Runs a Consumer; registers the handle so that it is closed when consumption finishes. If any IO errors arise either during consumption or when the Handle is closed, they are caught and passed to the handler.

backgroundSendToProcess :: MonadSafe m => Handle -> Producer ByteString (SafeT IO) () -> ErrSpec -> m () Source

Creates a background thread that will consume to the given Handle from the given Producer. Takes ownership of the Handle and closes it when done.

backgroundReceiveFromProcess :: MonadSafe m => HandleDesc -> Handle -> Consumer ByteString (SafeT IO) () -> ErrSpec -> m () Source

Creates a background thread that will produce from the given Handle into the given Consumer. Takes possession of the Handle and closes it when done.

runInputHandle :: (MonadSafe m, MonadSafe mi) => Handle -> ErrSpec -> m (Consumer ByteString mi ()) Source

Does everything necessary to run a Handle that is created to a process standard input. Creates mailbox, runs background thread that pumps data out of the mailbox and into the process standard input, and returns a Consumer that consumes and places what it consumes into the mailbox for delivery to the background process.

runOutputHandle :: (MonadSafe m, MonadSafe mo) => HandleDesc -> Handle -> ErrSpec -> m (Producer ByteString mo ()) Source

Does everything necessary to run a Handle that is created to a process standard output or standard error. Creates mailbox, runs background thread that pumps data from the process output Handle into the mailbox, and returns a Producer that produces what comes into the mailbox.

Creating subprocesses

createProcess :: (MonadSafe m, MonadCatch (Base m)) => CreateProcess -> ErrSpec -> m (Maybe Handle, Maybe Handle, Maybe Handle, ProcessHandle) Source

Creates a subprocess. Registers destroyers for each handle created, as well as for the ProcessHandle.

runCreateProcess Source

Arguments

:: (MonadSafe m, MonadCatch (Base m)) 
=> Maybe NonPipe

Standard input

-> Maybe NonPipe

Standard output

-> Maybe NonPipe

Standard error

-> CreateProcess 
-> m (Maybe Handle, Maybe Handle, Maybe Handle, ErrSpec, ProcessHandle) 

Convenience wrapper for createProcess. The subprocess is terminated and all its handles destroyed when the MonadSafe computation completes.

Creating Proxy

pipeNone Source

Arguments

:: (MonadSafe m, MonadCatch (Base m)) 
=> NonPipe

Standard input

-> NonPipe

Standard output

-> NonPipe

Standard error

-> CreateProcess 
-> m ProcessHandle 

Do not create any Proxy to or from the process.

pipeInput Source

Arguments

:: (MonadSafe mi, MonadSafe m, MonadCatch (Base m)) 
=> NonPipe

Standard output

-> NonPipe

Standard error

-> CreateProcess 
-> m (Consumer ByteString mi (), ProcessHandle)

A Consumer for standard input

Create a Consumer for standard input.

pipeOutput Source

Arguments

:: (MonadSafe mo, MonadSafe m, MonadCatch (Base m)) 
=> NonPipe

Standard input

-> NonPipe

Standard error

-> CreateProcess 
-> m (Producer ByteString mo (), ProcessHandle)

A Producer for standard output

Create a Producer for standard output.

pipeError Source

Arguments

:: (MonadSafe m, MonadCatch (Base m)) 
=> NonPipe

Standard input

-> NonPipe

Standard output

-> CreateProcess 
-> m (Producer ByteString m (), ProcessHandle)

A Producer for standard error

Create a Producer for standard error.

pipeInputOutput Source

Arguments

:: (MonadSafe mi, MonadSafe mo, MonadSafe m, MonadCatch (Base m)) 
=> NonPipe

Standard error

-> CreateProcess 
-> m ((Consumer ByteString mi (), Producer ByteString mo ()), ProcessHandle)

A Consumer for standard input, a Producer for standard output

Create a Consumer for standard input and a Producer for standard output.

pipeInputError Source

Arguments

:: (MonadSafe mi, MonadSafe me, MonadSafe m, MonadCatch (Base m)) 
=> NonPipe

Standard output

-> CreateProcess 
-> m ((Consumer ByteString mi (), Producer ByteString me ()), ProcessHandle)

A Consumer for standard input, a Producer for standard error

Create a Consumer for standard input and a Producer for standard error.

pipeOutputError Source

Arguments

:: (MonadSafe mo, MonadSafe me, MonadSafe m, MonadCatch (Base m)) 
=> NonPipe

Standard input

-> CreateProcess 
-> m ((Producer ByteString mo (), Producer ByteString me ()), ProcessHandle)

A Producer for standard output, a Producer for standard error

Create a Producer for standard output and a Producer for standard error.

pipeInputOutputError Source

Arguments

:: (MonadSafe mi, MonadSafe mo, MonadSafe me, MonadSafe m, MonadCatch (Base m)) 
=> CreateProcess 
-> m ((Consumer ByteString mi (), Producer ByteString mo (), Producer ByteString me ()), ProcessHandle)

A Consumer for standard input, a Producer for standard output, a Producer for standard error

Create a Consumer for standard input, a Producer for standard output, and a Producer for standard error.