packages feed

polysemy-process 0.13.0.1 → 0.14.0.0

raw patch · 33 files changed

+516/−164 lines, 33 filesdep +asyncdep ~basedep ~incipit-coredep ~pathPVP ok

version bump matches the API change (PVP)

Dependencies added: async

Dependency ranges changed: base, incipit-core, path, path-io, polysemy, polysemy-conc, polysemy-plugin, polysemy-resume, polysemy-test, polysemy-time, posix-pty, process, stm-chans, tasty, tasty-expected-failure, typed-process, unix

API changes (from Hackage documentation)

+ Polysemy.Process: data Interrupt :: Effect
+ Polysemy.Process: interpretInterrupt :: Members [Critical, Race, Async, Embed IO] r => InterpreterFor Interrupt r
+ Polysemy.Process: interpretInterruptNull :: InterpreterFor Interrupt r
+ Polysemy.Process: interpretInterruptOnce :: Members [Critical, Race, Async, Embed IO] r => InterpreterFor Interrupt r
+ Polysemy.Process.Effect.Interrupt: [Interrupted] :: Interrupt m Bool
+ Polysemy.Process.Effect.Interrupt: [KillOnQuit] :: Text -> m a -> Interrupt m (Maybe a)
+ Polysemy.Process.Effect.Interrupt: [Quit] :: Interrupt m ()
+ Polysemy.Process.Effect.Interrupt: [Register] :: Text -> IO () -> Interrupt m ()
+ Polysemy.Process.Effect.Interrupt: [Unregister] :: Text -> Interrupt m ()
+ Polysemy.Process.Effect.Interrupt: [WaitQuit] :: Interrupt m ()
+ Polysemy.Process.Effect.Interrupt: data Interrupt :: Effect
+ Polysemy.Process.Effect.Interrupt: interrupted :: forall r_ad2q. Member Interrupt r_ad2q => Sem r_ad2q Bool
+ Polysemy.Process.Effect.Interrupt: killOnQuit :: forall r_ad2r a_X0. Member Interrupt r_ad2r => Text -> Sem r_ad2r a_X0 -> Sem r_ad2r (Maybe a_X0)
+ Polysemy.Process.Effect.Interrupt: killOnQuit_ :: Member Interrupt r => Text -> Sem r a -> Sem r ()
+ Polysemy.Process.Effect.Interrupt: quit :: forall r_ad2o. Member Interrupt r_ad2o => Sem r_ad2o ()
+ Polysemy.Process.Effect.Interrupt: register :: forall r_ad2j. Member Interrupt r_ad2j => Text -> IO () -> Sem r_ad2j ()
+ Polysemy.Process.Effect.Interrupt: unregister :: forall r_ad2m. Member Interrupt r_ad2m => Text -> Sem r_ad2m ()
+ Polysemy.Process.Effect.Interrupt: waitQuit :: forall r_ad2p. Member Interrupt r_ad2p => Sem r_ad2p ()
+ Polysemy.Process.Interpreter.Interrupt: interpretInterrupt :: Members [Critical, Race, Async, Embed IO] r => InterpreterFor Interrupt r
+ Polysemy.Process.Interpreter.Interrupt: interpretInterruptNull :: InterpreterFor Interrupt r
+ Polysemy.Process.Interpreter.Interrupt: interpretInterruptOnce :: Members [Critical, Race, Async, Embed IO] r => InterpreterFor Interrupt r
+ Polysemy.Process.Interpreter.Interrupt: interpretInterruptWith :: Members [Critical, Race, Async, Embed IO] r => ((SignalInfo -> IO ()) -> Handler) -> InterpreterFor Interrupt r
- Polysemy.Process.Effect.ProcessInput: encode :: forall a_adrc r_adv1. Member (ProcessInput a_adrc) r_adv1 => a_adrc -> Sem r_adv1 ByteString
+ Polysemy.Process.Effect.ProcessInput: encode :: forall a_adUY r_adYN. Member (ProcessInput a_adUY) r_adYN => a_adUY -> Sem r_adYN ByteString
- Polysemy.Process.Effect.ProcessOutput: chunk :: forall p_adza a_adzb r_adBu. Member (ProcessOutput p_adza a_adzb) r_adBu => ByteString -> ByteString -> Sem r_adBu ([a_adzb], ByteString)
+ Polysemy.Process.Effect.ProcessOutput: chunk :: forall p_ae2W a_ae2X r_ae5g. Member (ProcessOutput p_ae2W a_ae2X) r_ae5g => ByteString -> ByteString -> Sem r_ae5g ([a_ae2X], ByteString)
- Polysemy.Process.Effect.Pty: handle :: forall r_agXj. Member Pty r_agXj => Sem r_agXj Handle
+ Polysemy.Process.Effect.Pty: handle :: forall r_ahtI. Member Pty r_ahtI => Sem r_ahtI Handle
- Polysemy.Process.Effect.Pty: resize :: forall r_agXk. Member Pty r_agXk => Rows -> Cols -> Sem r_agXk ()
+ Polysemy.Process.Effect.Pty: resize :: forall r_ahtJ. Member Pty r_ahtJ => Rows -> Cols -> Sem r_ahtJ ()
- Polysemy.Process.Effect.Pty: size :: forall r_agXn. Member Pty r_agXn => Sem r_agXn (Rows, Cols)
+ Polysemy.Process.Effect.Pty: size :: forall r_ahtM. Member Pty r_ahtM => Sem r_ahtM (Rows, Cols)
- Polysemy.Process.Effect.SystemProcess: pid :: forall r_ai0Y. Member SystemProcess r_ai0Y => Sem r_ai0Y Pid
+ Polysemy.Process.Effect.SystemProcess: pid :: forall r_aixn. Member SystemProcess r_aixn => Sem r_aixn Pid
- Polysemy.Process.Effect.SystemProcess: readStderr :: forall r_ai0V. Member SystemProcess r_ai0V => Sem r_ai0V ByteString
+ Polysemy.Process.Effect.SystemProcess: readStderr :: forall r_aixk. Member SystemProcess r_aixk => Sem r_aixk ByteString
- Polysemy.Process.Effect.SystemProcess: readStdout :: forall r_ai0U. Member SystemProcess r_ai0U => Sem r_ai0U ByteString
+ Polysemy.Process.Effect.SystemProcess: readStdout :: forall r_aixj. Member SystemProcess r_aixj => Sem r_aixj ByteString
- Polysemy.Process.Effect.SystemProcess: signal :: forall r_ai0Z. Member SystemProcess r_ai0Z => Signal -> Sem r_ai0Z ()
+ Polysemy.Process.Effect.SystemProcess: signal :: forall r_aixo. Member SystemProcess r_aixo => Signal -> Sem r_aixo ()
- Polysemy.Process.Effect.SystemProcess: wait :: forall r_ai11. Member SystemProcess r_ai11 => Sem r_ai11 ExitCode
+ Polysemy.Process.Effect.SystemProcess: wait :: forall r_aixq. Member SystemProcess r_aixq => Sem r_aixq ExitCode
- Polysemy.Process.Effect.SystemProcess: writeStdin :: forall r_ai0W. Member SystemProcess r_ai0W => ByteString -> Sem r_ai0W ()
+ Polysemy.Process.Effect.SystemProcess: writeStdin :: forall r_aixl. Member SystemProcess r_aixl => ByteString -> Sem r_aixl ()
- Polysemy.Process.ProcessOutput: chunk :: forall p_adza a_adzb r_adBu. Member (ProcessOutput p_adza a_adzb) r_adBu => ByteString -> ByteString -> Sem r_adBu ([a_adzb], ByteString)
+ Polysemy.Process.ProcessOutput: chunk :: forall p_ae2W a_ae2X r_ae5g. Member (ProcessOutput p_ae2W a_ae2X) r_ae5g => ByteString -> ByteString -> Sem r_ae5g ([a_ae2X], ByteString)
- Polysemy.Process.Pty: handle :: forall r_agXj. Member Pty r_agXj => Sem r_agXj Handle
+ Polysemy.Process.Pty: handle :: forall r_ahtI. Member Pty r_ahtI => Sem r_ahtI Handle
- Polysemy.Process.Pty: resize :: forall r_agXk. Member Pty r_agXk => Rows -> Cols -> Sem r_agXk ()
+ Polysemy.Process.Pty: resize :: forall r_ahtJ. Member Pty r_ahtJ => Rows -> Cols -> Sem r_ahtJ ()
- Polysemy.Process.Pty: size :: forall r_agXn. Member Pty r_agXn => Sem r_agXn (Rows, Cols)
+ Polysemy.Process.Pty: size :: forall r_ahtM. Member Pty r_ahtM => Sem r_ahtM (Rows, Cols)
- Polysemy.Process.SystemProcess: pid :: forall r_ai0Y. Member SystemProcess r_ai0Y => Sem r_ai0Y Pid
+ Polysemy.Process.SystemProcess: pid :: forall r_aixn. Member SystemProcess r_aixn => Sem r_aixn Pid
- Polysemy.Process.SystemProcess: readStderr :: forall r_ai0V. Member SystemProcess r_ai0V => Sem r_ai0V ByteString
+ Polysemy.Process.SystemProcess: readStderr :: forall r_aixk. Member SystemProcess r_aixk => Sem r_aixk ByteString
- Polysemy.Process.SystemProcess: readStdout :: forall r_ai0U. Member SystemProcess r_ai0U => Sem r_ai0U ByteString
+ Polysemy.Process.SystemProcess: readStdout :: forall r_aixj. Member SystemProcess r_aixj => Sem r_aixj ByteString
- Polysemy.Process.SystemProcess: signal :: forall r_ai0Z. Member SystemProcess r_ai0Z => Signal -> Sem r_ai0Z ()
+ Polysemy.Process.SystemProcess: signal :: forall r_aixo. Member SystemProcess r_aixo => Signal -> Sem r_aixo ()
- Polysemy.Process.SystemProcess: wait :: forall r_ai11. Member SystemProcess r_ai11 => Sem r_ai11 ExitCode
+ Polysemy.Process.SystemProcess: wait :: forall r_aixq. Member SystemProcess r_aixq => Sem r_aixq ExitCode
- Polysemy.Process.SystemProcess: writeStdin :: forall r_ai0W. Member SystemProcess r_ai0W => ByteString -> Sem r_ai0W ()
+ Polysemy.Process.SystemProcess: writeStdin :: forall r_aixl. Member SystemProcess r_aixl => ByteString -> Sem r_aixl ()

Files

changelog.md view
@@ -1,3 +1,9 @@+# 0.14.0.0++## Breaking++* Move `Interrupt` here from `polysemy-conc`.+ # 0.10.0.0  * Add `oneshot` variants of `Process` interpreters that send `Stop` to the individual actions inside the scope, modeling
lib/Polysemy/Process.hs view
@@ -1,4 +1,4 @@--- |Description: Polysemy Effects for System Processes+-- | Description: Polysemy Effects for System Processes module Polysemy.Process (   -- * Introduction   -- $intro@@ -35,6 +35,10 @@   Pty,   withPty, +  -- ** Signal Handling+  -- $signal+  Interrupt,+   -- * Interpreters   -- ** Process   interpretProcess,@@ -92,6 +96,11 @@   -- ** Pty   interpretPty, +  -- ** Signal Handling+  interpretInterrupt,+  interpretInterruptOnce,+  interpretInterruptNull,+   -- * Tools   resolveExecutable, ) where@@ -104,6 +113,7 @@ import Polysemy.Process.Data.ProcessOptions (ProcessOptions (ProcessOptions)) import Polysemy.Process.Data.ProcessOutputParseResult (ProcessOutputParseResult (..)) import Polysemy.Process.Data.SystemProcessError (SystemProcessError, SystemProcessScopeError)+import Polysemy.Process.Effect.Interrupt (Interrupt) import Polysemy.Process.Effect.Process (   Process (..),   recv,@@ -118,6 +128,7 @@ import Polysemy.Process.Effect.Pty (Pty, withPty) import Polysemy.Process.Effect.SystemProcess (SystemProcess, withSystemProcess, withSystemProcess_) import Polysemy.Process.Executable (resolveExecutable)+import Polysemy.Process.Interpreter.Interrupt (interpretInterrupt, interpretInterruptNull, interpretInterruptOnce) import Polysemy.Process.Interpreter.Process (   interpretInputHandle,   interpretInputHandleBuffered,@@ -185,3 +196,6 @@ -- -- The effect 'Pty' abstracts pseudo terminals. -- See "Polysemy.Process.Pty" for its constructors.++-- $signal+-- #signal#
lib/Polysemy/Process/Data/Pid.hs view
@@ -1,7 +1,7 @@--- |Pid data type, Internal+-- | Pid data type, Internal module Polysemy.Process.Data.Pid where --- |A process ID.+-- | A process ID. newtype Pid =   Pid { unPid :: Int }   deriving stock (Eq, Show, Read)
lib/Polysemy/Process/Data/ProcessError.hs view
@@ -1,20 +1,20 @@ {-# options_haddock prune #-} --- |Description: ProcessError, Internal+-- | Description: ProcessError, Internal module Polysemy.Process.Data.ProcessError where  import System.Exit (ExitCode)  import Polysemy.Process.Data.SystemProcessError (SystemProcessScopeError) --- |Signal error for 'Polysemy.Process.Process'.+-- | Signal error for 'Polysemy.Process.Process'. data ProcessError =-  -- |Something broke.+  -- | Something broke.   Unknown Text   |-  -- |The process failed to start.+  -- | The process failed to start.   StartFailed SystemProcessScopeError   |-  -- |The process terminated with exit code.+  -- | The process terminated with exit code.   Exit ExitCode   deriving stock (Eq, Show)
lib/Polysemy/Process/Data/ProcessKill.hs view
@@ -1,16 +1,16 @@--- |Data type indicating whether to kill a process after exiting its scope.+-- | Data type indicating whether to kill a process after exiting its scope. module Polysemy.Process.Data.ProcessKill where  import Polysemy.Time (NanoSeconds) --- |Indicate whether to kill a process after exiting the scope in which it was used, if it hasn't terminated.+-- | Indicate whether to kill a process after exiting the scope in which it was used, if it hasn't terminated. data ProcessKill =-  -- |Wait for the specified interval, then kill.+  -- | Wait for the specified interval, then kill.   KillAfter NanoSeconds   |-  -- |Kill immediately.+  -- | Kill immediately.   KillImmediately   |-  -- |Wait indefinitely for the process to terminate.+  -- | Wait indefinitely for the process to terminate.   KillNever   deriving stock (Eq, Show)
lib/Polysemy/Process/Data/ProcessOptions.hs view
@@ -1,17 +1,17 @@--- |Data type containing options for processes.+-- | Data type containing options for processes. module Polysemy.Process.Data.ProcessOptions where  import qualified Polysemy.Process.Data.ProcessKill as ProcessKill import Polysemy.Process.Data.ProcessKill (ProcessKill) --- |Controls the behaviour of 'Polysemy.Process.Process' interpreters.+-- | Controls the behaviour of 'Polysemy.Process.Process' interpreters. data ProcessOptions =   ProcessOptions {-    -- |Whether to discard output chunks if the queue is full.+    -- | Whether to discard output chunks if the queue is full.     discard :: Bool,-    -- |Maximum number of chunks allowed to be queued for each of the three standard pipes.+    -- | Maximum number of chunks allowed to be queued for each of the three standard pipes.     qsize :: Int,-    -- |What to do if the process hasn't terminated when exiting the scope.+    -- | What to do if the process hasn't terminated when exiting the scope.     kill :: ProcessKill   }   deriving stock (Eq, Show)
lib/Polysemy/Process/Data/ProcessOutputParseResult.hs view
@@ -1,7 +1,7 @@--- |A data type encoding the result of an incremental parser for process output.+-- | A data type encoding the result of an incremental parser for process output. module Polysemy.Process.Data.ProcessOutputParseResult where --- |An incremental parse result, potentially a partial result containing a continuation function.+-- | An incremental parse result, potentially a partial result containing a continuation function. data ProcessOutputParseResult a =   Done { value :: a, leftover :: ByteString }   |
lib/Polysemy/Process/Data/PtyError.hs view
@@ -1,9 +1,9 @@ {-# options_haddock prune #-} --- |PtyError ADT, Internal+-- | PtyError ADT, Internal module Polysemy.Process.Data.PtyError where --- |Internal error used by an interpreter for 'Polysemy.Process.Pty'.+-- | Internal error used by an interpreter for 'Polysemy.Process.Pty'. data PtyError =   PtyError Text   deriving stock (Eq, Show)
lib/Polysemy/Process/Data/PtyResources.hs view
@@ -1,13 +1,13 @@ {-# options_haddock prune #-} --- |Description: PtyResources ADT, Internal+-- | Description: PtyResources ADT, Internal module Polysemy.Process.Data.PtyResources where  import System.IO (Handle) import System.Posix (Fd) import System.Posix.Pty (Pty) --- |The resources used by the default interpreter for 'Polysemy.Process.Pty'.+-- | The resources used by the default interpreter for 'Polysemy.Process.Pty'. data PtyResources =   PtyResources {     primary :: Fd,
lib/Polysemy/Process/Data/SystemProcessError.hs view
@@ -1,19 +1,19 @@ {-# options_haddock prune #-} --- |Description: SystemProcessError, Internal+-- | Description: SystemProcessError, Internal module Polysemy.Process.Data.SystemProcessError where --- |Error for 'Polysemy.Process.SystemProcess'.+-- | Error for 'Polysemy.Process.SystemProcess'. data SystemProcessError =-  -- |The process terminated.+  -- | The process terminated.   Terminated Text   |-  -- |Stdio was requested, but the process was started without pipes.+  -- | Stdio was requested, but the process was started without pipes.   NoPipes   deriving stock (Eq, Show) --- |Error for the scope of 'Polysemy.Process.SystemProcess'.+-- | Error for the scope of 'Polysemy.Process.SystemProcess'. data SystemProcessScopeError =-  -- |The process couldn't start.+  -- | The process couldn't start.   StartFailed Text   deriving stock (Eq, Show)
+ lib/Polysemy/Process/Effect/Interrupt.hs view
@@ -0,0 +1,44 @@+{-# options_haddock prune #-}+-- | Description: Interrupt effect+module Polysemy.Process.Effect.Interrupt where++-- | The interrupt handler effect allows three kinds of interaction for interrupt signals:+--+-- - Execute a callback when a signal is received+-- - Block a thread until a signal is received+-- - Kill a thread when a signal is received+--+-- For documentation on the constructors, see the module "Polysemy.Process.Effect.Interrupt".+--+-- @+-- import qualified Polysemy.Process.Effect.Interrupt as Interrupt+--+-- prog = do+--   Interrupt.register "task 1" (putStrLn "interrupted")+--   Interrupt.killOnQuit $ forever do+--    doSomeWork+-- @+data Interrupt :: Effect where+  -- | Add a computation to be executed on interrupt, using the first argument as a key.+  Register :: Text -> IO () -> Interrupt m ()+  -- | Remove the previously added handler with the given key.+  Unregister :: Text -> Interrupt m ()+  -- | Manually trigger the interrupt.+  Quit :: Interrupt m ()+  -- | Block until an interrupt is triggered.+  WaitQuit :: Interrupt m ()+  -- | Indicate whether an interrupt was triggered.+  Interrupted :: Interrupt m Bool+  -- | Execute a computation, waiting for it to finish, killing its thread on interrupt.+  KillOnQuit :: Text -> m a -> Interrupt m (Maybe a)++makeSem ''Interrupt++-- | Variant of 'killOnQuit' that returns @()@.+killOnQuit_ ::+  Member Interrupt r =>+  Text ->+  Sem r a ->+  Sem r ()+killOnQuit_ desc ma =+  void (killOnQuit desc ma)
lib/Polysemy/Process/Effect/Process.hs view
@@ -1,6 +1,6 @@ {-# options_haddock prune #-} --- |Description: Process Effect, Internal+-- | Description: Process Effect, Internal module Polysemy.Process.Effect.Process where  import Polysemy.Input (Input (Input))@@ -8,7 +8,7 @@ import Polysemy.Resume (interpretResumable, restop, type (!!)) import Prelude hiding (send) --- |Abstraction of a process with input and output.+-- | Abstraction of a process with input and output. -- -- This effect is intended to be used in a scoped_ manner: --@@ -36,20 +36,20 @@  makeSem_ ''Process --- |Obtain a chunk of output.+-- | Obtain a chunk of output. recv ::   ∀ i o r .   Member (Process i o) r =>   Sem r o --- |Send data to stdin.+-- | Send data to stdin. send ::   ∀ i o r .   Member (Process i o) r =>   i ->   Sem r () --- |Create a scoped_ resource for 'Process'.+-- | Create a scoped_ resource for 'Process'. -- The process configuration may depend on the provided value of type @param@. -- This variant models daemon processes that are expected to run forever, with 'Polysemy.Resume.Stop' being sent to this -- function, if at all.@@ -61,7 +61,7 @@ withProcess =   scoped @param --- |Create a scoped_ resource for 'Process'.+-- | Create a scoped_ resource for 'Process'. -- The process configuration may depend on the provided value of type @param@. -- This variant models processes that are expected to terminate, with 'Polysemy.Resume.Stop' being sent to individual -- actions within the scope.@@ -73,7 +73,7 @@ withProcessOneshot =   scoped @param --- |Create a scoped_ resource for 'Process'.+-- | Create a scoped_ resource for 'Process'. -- The process configuration is provided to the interpreter statically. -- This variant models daemon processes that are expected to run forever, with 'Polysemy.Resume.Stop' being sent to this -- function, if at all.@@ -84,7 +84,7 @@ withProcess_ =   scoped_ --- |Create a scoped_ resource for 'Process'.+-- | Create a scoped_ resource for 'Process'. -- The process configuration is provided to the interpreter statically. -- This variant models processes that are expected to terminate, with 'Polysemy.Resume.Stop' being sent to individual -- actions within the scope.@@ -95,7 +95,7 @@ withProcessOneshot_ =   scoped_ --- |Convert 'Output' and 'Input' to 'Process' for a daemon process.+-- | Convert 'Output' and 'Input' to 'Process' for a daemon process. runProcessIO ::   ∀ i o r .   Member (Process i o) r =>@@ -109,7 +109,7 @@     Output o ->       send @i @o o --- |Convert 'Output' and 'Input' to 'Process' for a oneshot process.+-- | Convert 'Output' and 'Input' to 'Process' for a oneshot process. runProcessOneshotIO ::   ∀ i o err r .   Member (Process i o !! err) r =>
lib/Polysemy/Process/Effect/ProcessInput.hs view
@@ -1,14 +1,14 @@ {-# options_haddock prune #-} --- |Description: ProcessInput effect, Internal.+-- | Description: ProcessInput effect, Internal. module Polysemy.Process.Effect.ProcessInput where --- |This effect is used by the effect 'Polysemy.Process.Process' to encode values for process input.+-- | This effect is used by the effect 'Polysemy.Process.Process' to encode values for process input. -- example using a parser. data ProcessInput a :: Effect where-  -- |Encode a value for enqueueing it to a process' stdin.+  -- | Encode a value for enqueueing it to a process' stdin.   Encode ::-    -- |The value to encode.+    -- | The value to encode.     a ->     ProcessInput a m ByteString 
lib/Polysemy/Process/Effect/ProcessOutput.hs view
@@ -1,28 +1,28 @@ {-# options_haddock prune #-} --- |Description: ProcessOutput effect, Internal.+-- | Description: ProcessOutput effect, Internal. module Polysemy.Process.Effect.ProcessOutput where --- |Kind tag for selecting the 'ProcessOutput' handler for stdout/stderr.+-- | Kind tag for selecting the 'ProcessOutput' handler for stdout/stderr. data OutputPipe =-  -- |Tag for stdout.+  -- | Tag for stdout.   Stdout   |-  -- |Tag for stderr.+  -- | Tag for stderr.   Stderr   deriving stock (Eq, Show) --- |This effect is used by the effect 'Polysemy.Process.Process' to accumulate and decode chunks of 'ByteString's, for+-- | This effect is used by the effect 'Polysemy.Process.Process' to accumulate and decode chunks of 'ByteString's, for -- example using a parser. -- The interpreter may be stateful or stateless, since the constructor 'Chunk' is expected to be called with both the -- accumulated unprocessed output as well as the new chunk. data ProcessOutput (p :: OutputPipe) a :: Effect where-  -- |Add a chunk of output to the accumulator, returning any number of successfully parsed values and the leftover+  -- | Add a chunk of output to the accumulator, returning any number of successfully parsed values and the leftover   -- output.   Chunk ::-    -- |The accumulation of the previous leftovers.+    -- | The accumulation of the previous leftovers.     ByteString ->-    -- |The new chunk read from the process.+    -- | The new chunk read from the process.     ByteString ->     ProcessOutput p a m ([a], ByteString) 
lib/Polysemy/Process/Effect/Pty.hs view
@@ -1,34 +1,34 @@ {-# options_haddock prune #-} --- |Description: Pty Effect, Internal+-- | Description: Pty Effect, Internal module Polysemy.Process.Effect.Pty where  import System.IO (Handle) --- |Horizontal size of a pseudo terminal in characters.+-- | Horizontal size of a pseudo terminal in characters. newtype Rows =   Rows { unRows :: Int }   deriving stock (Eq, Show, Generic)   deriving newtype (Num, Real, Enum, Integral, Ord) --- |Vertical size of a pseudo terminal in characters.+-- | Vertical size of a pseudo terminal in characters. newtype Cols =   Cols { unCols :: Int }   deriving stock (Eq, Show, Generic)   deriving newtype (Num, Real, Enum, Integral, Ord) --- |A pseudo terminal, to be scoped with 'withPty'.+-- | A pseudo terminal, to be scoped with 'withPty'. data Pty :: Effect where-  -- |The file descriptor that can be connected to stdio of a process.+  -- | The file descriptor that can be connected to stdio of a process.   Handle :: Pty m Handle-  -- |Set the size of the terminal.+  -- | Set the size of the terminal.   Resize :: Rows -> Cols -> Pty m ()-  -- |Get the size of the terminal.+  -- | Get the size of the terminal.   Size :: Pty m (Rows, Cols)  makeSem ''Pty --- |Bracket an action with the creation and destruction of a pseudo terminal.+-- | Bracket an action with the creation and destruction of a pseudo terminal. withPty ::   Member (Scoped_ Pty) r =>   InterpreterFor Pty r
lib/Polysemy/Process/Effect/SystemProcess.hs view
@@ -1,6 +1,6 @@ {-# options_haddock prune #-} --- |Description: SystemProcess Effect, Internal+-- | Description: SystemProcess Effect, Internal module Polysemy.Process.Effect.SystemProcess where  import Polysemy.Resume (type (!!))@@ -10,25 +10,25 @@  import Polysemy.Process.Data.Pid (Pid) --- |Low-level interface for a process, operating on raw chunks of bytes.+-- | Low-level interface for a process, operating on raw chunks of bytes. -- Interface is modeled after "System.Process". data SystemProcess :: Effect where-  -- |Read a chunk from stdout.+  -- | Read a chunk from stdout.   ReadStdout :: SystemProcess m ByteString-  -- |Read a chunk from stderr.+  -- | Read a chunk from stderr.   ReadStderr :: SystemProcess m ByteString-  -- |Write a 'ByteString' to stdin.+  -- | Write a 'ByteString' to stdin.   WriteStdin :: ByteString -> SystemProcess m ()-  -- |Obtain the process ID.+  -- | Obtain the process ID.   Pid :: SystemProcess m Pid-  -- |Send a 'System.Posix.Signal' to the process.+  -- | Send a 'System.Posix.Signal' to the process.   Signal :: Signal -> SystemProcess m ()-  -- |Wait for the process to terminate, returning its exit code.+  -- | Wait for the process to terminate, returning its exit code.   Wait :: SystemProcess m ExitCode  makeSem ''SystemProcess --- |Create a scoped resource for 'SystemProcess'.+-- | Create a scoped resource for 'SystemProcess'. -- The process configuration may depend on the provided value of type @param@. withSystemProcess ::   ∀ param err r .@@ -38,7 +38,7 @@ withSystemProcess =   scoped @param --- |Create a scoped resource for 'SystemProcess'.+-- | Create a scoped resource for 'SystemProcess'. -- The process configuration is provided to the interpreter statically. withSystemProcess_ ::   ∀ err r .@@ -47,21 +47,21 @@ withSystemProcess_ =   scoped_ --- |Send signal INT(2) to the process.+-- | Send signal INT(2) to the process. interrupt ::   Member SystemProcess r =>   Sem r () interrupt =   signal Signal.sigINT --- |Send signal TERM(15) to the process.+-- | Send signal TERM(15) to the process. term ::   Member SystemProcess r =>   Sem r () term =   signal Signal.sigTERM --- |Send signal KILL(9) to the process.+-- | Send signal KILL(9) to the process. kill ::   Member SystemProcess r =>   Sem r ()
lib/Polysemy/Process/Executable.hs view
@@ -1,6 +1,6 @@ {-# options_haddock prune #-} --- |Description: Executable helpers, Internal+-- | Description: Executable helpers, Internal module Polysemy.Process.Executable where  import Path (Abs, File, Path, Rel, toFilePath)@@ -26,12 +26,12 @@     pathText =       toText (toFilePath path) --- |Find a file in @$PATH@, verifying that it is executable by this process.+-- | Find a file in @$PATH@, verifying that it is executable by this process. resolveExecutable ::   Member (Embed IO) r =>-  -- |Executable name, for @$PATH@ lookup and error messages+  -- | Executable name, for @$PATH@ lookup and error messages   Path Rel File ->-  -- |Explicit override to be checked for adequate permissions+  -- | Explicit override to be checked for adequate permissions   Maybe (Path Abs File) ->   Sem r (Either Text (Path Abs File)) resolveExecutable exe = \case
+ lib/Polysemy/Process/Interpreter/Interrupt.hs view
@@ -0,0 +1,244 @@+{-# options_haddock prune #-}+{-# language FieldSelectors #-}++-- | Description: Interrupt interpreters+module Polysemy.Process.Interpreter.Interrupt where++import qualified Control.Concurrent.Async as A+import Control.Concurrent.Async (AsyncCancelled)+import Control.Concurrent.STM (TVar, newTVarIO)+import qualified Data.Map.Strict as Map+import qualified Data.Set as Set+import qualified Data.Text.IO as Text+import qualified Polysemy.Conc.Effect.Critical as Critical+import Polysemy.Conc.Effect.Critical (Critical)+import Polysemy.Conc.Effect.Race (Race)+import qualified Polysemy.Conc.Effect.Sync as Sync+import Polysemy.Conc.Interpreter.Sync (interpretSync)+import Polysemy.Conc.Race (race_)+import Polysemy.Internal.Tactics (liftT)+import Polysemy.Time (Seconds (Seconds))+import System.IO (stderr)+import System.Posix.Signals (+  Handler (Catch, CatchInfo, CatchInfoOnce, CatchOnce),+  SignalInfo,+  installHandler,+  keyboardSignal,+  )++import Polysemy.Process.Effect.Interrupt (Interrupt (..))++putErr ::+  Member (Embed IO) r =>+  Text ->+  Sem r ()+putErr =+  embed . Text.hPutStrLn stderr++data InterruptState =+  InterruptState {+    quit :: !(MVar ()),+    finished :: !(MVar ()),+    listeners :: !(Set Text),+    original :: !(SignalInfo -> IO ()),+    handlers :: !(Map Text (IO ()))+  }++modListeners :: (Set Text -> Set Text) -> InterruptState -> InterruptState+modListeners f s@InterruptState {listeners} =+  s {listeners = f listeners}++modHandlers :: (Map Text (IO ()) -> Map Text (IO ())) -> InterruptState -> InterruptState+modHandlers f s@InterruptState {handlers} =+  s {handlers = f handlers}++waitQuit ::+  Members [AtomicState InterruptState, Embed IO] r =>+  Sem r ()+waitQuit = do+  mv <- atomicGets quit+  embed (readMVar mv)++checkListeners ::+  Members [AtomicState InterruptState, Embed IO] r =>+  Sem r ()+checkListeners =+  whenM (atomicGets (Set.null . listeners)) do+    fin <- atomicGets finished+    void (embed (tryPutMVar fin ()))++onQuit ::+  Members [AtomicState InterruptState, Embed IO] r =>+  Text ->+  Sem r a ->+  Sem r a+onQuit name ma = do+  atomicModify' (modListeners (Set.insert name))+  waitQuit+  a <- ma+  atomicModify' (modListeners (Set.delete name))+  checkListeners+  pure a++processHandler ::+  Member (Embed IO) r =>+  Text ->+  IO () ->+  Sem r ()+processHandler name thunk = do+  putErr ("processing interrupt handler: " <> name)+  embed thunk++execInterrupt ::+  Members [AtomicState InterruptState, Embed IO] r =>+  Sem r (SignalInfo -> Sem r ())+execInterrupt = do+  InterruptState quitSignal finishSignal _ orig _ <- atomicGet+  whenM (embed (tryPutMVar quitSignal ())) do+    traverse_ (uncurry processHandler) . Map.toList =<< atomicGets handlers+    checkListeners+    embed (takeMVar finishSignal)+  embed . orig <$ putErr "interrupt handlers finished"++registerHandler ::+  Member (AtomicState InterruptState) r =>+  Text ->+  IO () ->+  Sem r ()+registerHandler name handler =+  atomicModify' (modHandlers (Map.insert name handler))++awaitOrKill ::+  Members [AtomicState InterruptState, Critical, Race, Async, Embed IO] r =>+  Text ->+  A.Async (Maybe a) ->+  Sem r (Maybe a)+awaitOrKill desc handle = do+  interpretSync @() do+    race_ (catchCritical (await handle)) kill+  where+    catchCritical =+      maybe waitKill (pure . Just) <=< Critical.catchAs @AsyncCancelled Nothing+    waitKill =+      Nothing <$ Sync.wait @() (Seconds 1)+    kill = do+      onQuit desc do+        putErr ("killing " <> desc)+        cancel handle+        putErr ("killed " <> desc)+        Sync.putBlock ()+        pure Nothing++interpretInterruptState ::+  Members [AtomicState InterruptState, Critical, Race, Async, Embed IO] r =>+  InterpreterFor Interrupt r+interpretInterruptState =+  interpretH \case+    Register name handler ->+      liftT (registerHandler name handler)+    Unregister name ->+      liftT $ atomicModify' \ s@InterruptState {handlers} -> s {handlers = Map.delete name handlers}+    WaitQuit ->+      liftT waitQuit+    Quit ->+      liftT do+        putErr "manual interrupt"+        void execInterrupt+    Interrupted ->+      liftT . fmap isJust . embed . tryReadMVar =<< atomicGets quit+    KillOnQuit desc ma -> do+      maT <- runT ma+      ins <- getInspectorT+      handle <- raise (interpretInterruptState (async maT))+      result <- liftT (awaitOrKill desc handle)+      pure (join . fmap (inspect ins) <$> result)+{-# inline interpretInterruptState #-}++broadcastInterrupt ::+  Members [AtomicState InterruptState, Embed IO] r =>+  SignalInfo ->+  Sem r ()+broadcastInterrupt sig = do+  putErr "caught interrupt signal"+  orig <- execInterrupt+  orig sig++-- The original handler is either the default handler that kills all threads or a handler installed by an environment+-- like ghcid.+-- In the latter case, not calling it results in ghcid misbehaving.+-- To distinguish the two cases, the constructor used by the default is 'Catch', while a custom handler should usually+-- use 'CatchOnce', since you don't want to catch repeated occurences of SIGINT, as it will surely cause problems.+originalHandler :: Handler -> (SignalInfo -> IO ())+originalHandler (CatchOnce thunk) =+  (const thunk)+originalHandler (CatchInfoOnce thunk) =+  thunk+originalHandler (Catch thunk) =+  (const thunk)+originalHandler (CatchInfo thunk) =+  thunk+originalHandler _ =+  const unit+{-# inline originalHandler #-}++installSignalHandler ::+  TVar InterruptState ->+  ((SignalInfo -> IO ()) -> Handler) ->+  IO Handler+installSignalHandler state consHandler =+  installHandler keyboardSignal (consHandler handler) Nothing+  where+    handler sig =+      runFinal $ embedToFinal @IO $ runAtomicStateTVar state (broadcastInterrupt sig)++-- | Interpret 'Interrupt' by installing a signal handler.+--+-- Takes a constructor for 'Handler'.+interpretInterruptWith ::+  Members [Critical, Race, Async, Embed IO] r =>+  ((SignalInfo -> IO ()) -> Handler) ->+  InterpreterFor Interrupt r+interpretInterruptWith consHandler sem = do+  quitMVar <- embed newEmptyMVar+  finishMVar <- embed newEmptyMVar+  state <- embed (newTVarIO (InterruptState quitMVar finishMVar Set.empty (const unit) Map.empty))+  orig <- embed $ installSignalHandler state consHandler+  runAtomicStateTVar state do+    atomicModify' \ s -> s {original = originalHandler orig}+    interpretInterruptState $ raiseUnder sem++-- | Interpret 'Interrupt' by installing a signal handler.+--+-- Catches repeat invocations of SIGINT.+interpretInterrupt ::+  Members [Critical, Race, Async, Embed IO] r =>+  InterpreterFor Interrupt r+interpretInterrupt =+  interpretInterruptWith CatchInfo++-- | Interpret 'Interrupt' by installing a signal handler.+--+-- Catches only the first invocation of SIGINT.+interpretInterruptOnce ::+  Members [Critical, Race, Async, Embed IO] r =>+  InterpreterFor Interrupt r+interpretInterruptOnce =+  interpretInterruptWith CatchInfoOnce++-- | Eliminate 'Interrupt' without interpreting.+interpretInterruptNull ::+  InterpreterFor Interrupt r+interpretInterruptNull =+  interpretH \case+    Register _ _ ->+      pureT ()+    Unregister _ ->+      pureT ()+    WaitQuit ->+      pureT ()+    Quit ->+      pureT ()+    Interrupted ->+      pureT False+    KillOnQuit _ _ ->+      pureT Nothing
lib/Polysemy/Process/Interpreter/Process.hs view
@@ -1,6 +1,6 @@ {-# options_haddock prune #-} --- |Description: Process Interpreters, Internal+-- | Description: Process Interpreters, Internal module Polysemy.Process.Interpreter.Process where  import Control.Concurrent.STM.TBMQueue (TBMQueue)@@ -132,7 +132,7 @@ withQueues qSize action =   withSTMResources qSize \ qs -> interpretQueues qs action --- |Call a chunk reading action repeatedly, pass the bytes to 'ProcessOutput' and enqueue its results.+-- | Call a chunk reading action repeatedly, pass the bytes to 'ProcessOutput' and enqueue its results. -- As soon as an empty chunk is encountered, the queue is closed if the gating action returns 'False'. -- The conditional closing is for the purpose of keeping the queue alive until the last producer has written all -- received chunks – this is important when both stdout and stderr are written to the same queue.@@ -242,7 +242,7 @@     raiseUnder $     queues @err options sem --- |Interpret 'Process' with a system process resource whose file descriptors are connected to three 'TBMQueue's,+-- | Interpret 'Process' with a system process resource whose file descriptors are connected to three 'TBMQueue's, -- deferring decoding of stdout and stderr to the interpreters of two 'ProcessOutput' effects. -- This variant: -- - Models a daemon process that is not expected to terminate, causing 'Stop' to be sent to the scope callsite instead@@ -270,7 +270,7 @@       mapStop ProcessError.StartFailed do         pscope @SystemProcessScopeError options (raise . raiseUnder . proc) p (insertAt @4 sem) --- |Interpret 'Process' with a system process resource whose file descriptors are connected to three 'TBMQueue's,+-- | Interpret 'Process' with a system process resource whose file descriptors are connected to three 'TBMQueue's, -- deferring decoding of stdout and stderr to the interpreters of two 'ProcessOutput' effects. -- This variant: -- - Models a daemon process that is not expected to terminate, causing 'Stop' to be sent to the scope callsite instead@@ -293,7 +293,7 @@       mapStop ProcessError.StartFailed do         scope @SystemProcessScopeError options (insertAt @4 sem) --- |Interpret 'Process' as a native 'Polysemy.Process.SystemProcess'.+-- | Interpret 'Process' as a native 'Polysemy.Process.SystemProcess'. -- This variant: -- - Models a daemon process that is not expected to terminate, causing 'Stop' to be sent to the scope callsite instead --   of individual 'Process' actions.@@ -312,7 +312,7 @@   interpretProcess options (insertAt @0 . proc) .   raiseUnder --- |Interpret 'Process' as a native 'Polysemy.Process.SystemProcess'.+-- | Interpret 'Process' as a native 'Polysemy.Process.SystemProcess'. -- This variant: -- - Models a daemon process that is not expected to terminate, causing 'Stop' to be sent to the scope callsite instead --   of individual 'Process' actions.@@ -329,7 +329,7 @@   interpretProcess_ options .   raiseUnder --- |Reinterpret 'Input' and 'Output' as 'Process'.+-- | Reinterpret 'Input' and 'Output' as 'Process'. interpretInputOutputProcess ::   ∀ i o r .   Member (Process i o) r =>@@ -338,7 +338,7 @@   runOutputSem (Process.send @i @o) .   runInputSem (Process.recv @i @o) --- |Interpret 'Input ByteString' by polling a 'Handle' and stopping with 'ProcessError' when it fails.+-- | Interpret 'Input ByteString' by polling a 'Handle' and stopping with 'ProcessError' when it fails. interpretInputHandleBuffered ::   Member (Embed IO) r =>   Handle ->@@ -348,7 +348,7 @@     Input ->       stopNote (Unknown "handle closed") =<< tryMaybe (hGetSome handle 4096) --- |Interpret 'Input ByteString' by polling a 'Handle' and stopping with 'ProcessError' when it fails.+-- | Interpret 'Input ByteString' by polling a 'Handle' and stopping with 'ProcessError' when it fails. -- This variant deactivates buffering for the 'Handle'. interpretInputHandle ::   Member (Embed IO) r =>@@ -358,7 +358,7 @@   tryAny_ (hSetBuffering handle NoBuffering)   interpretInputHandleBuffered handle sem --- |Interpret 'Output ByteString' by writing to a 'Handle' and stopping with 'ProcessError' when it fails.+-- | Interpret 'Output ByteString' by writing to a 'Handle' and stopping with 'ProcessError' when it fails. interpretOutputHandleBuffered ::   Member (Embed IO) r =>   Handle ->@@ -368,7 +368,7 @@     Output o ->       stopNote (Unknown "handle closed") =<< tryMaybe (hPut handle o) --- |Interpret 'Output ByteString' by writing to a 'Handle' and stopping with 'ProcessError' when it fails.+-- | Interpret 'Output ByteString' by writing to a 'Handle' and stopping with 'ProcessError' when it fails. -- This variant deactivates buffering for the 'Handle'. interpretOutputHandle ::   Member (Embed IO) r =>@@ -378,7 +378,7 @@   tryAny_ (hSetBuffering handle NoBuffering)   interpretOutputHandleBuffered handle sem --- |Interpret 'Process' in terms of 'Input' and 'Output'.+-- | Interpret 'Process' in terms of 'Input' and 'Output'. -- Since the @i@ and @o@ parameters correspond to the abstraction of stdio fds of an external system process, @i@ is -- written by 'Output' and @o@ is read from 'Input'. -- This is useful to abstract the current process's stdio as an external process, with input and output swapped.@@ -395,7 +395,7 @@   interpretResumable (handleProcessWithQueues (stop . Unknown)) .   raiseUnder2 --- |Interpret 'Process' in terms of two 'Handle's.+-- | Interpret 'Process' in terms of two 'Handle's. -- This is useful to abstract the current process's stdio as an external process, with input and output swapped. -- The first 'Handle' argument corresponds to the @o@ parameter, the second one to @i@, despite the first one usually -- being the current process's stdin.@@ -414,7 +414,7 @@   interpretProcessIO @i @o @ProcessError @ProcessError options .   raiseUnder2 --- |Interpret 'Process' using the current process's stdin and stdout.+-- | Interpret 'Process' using the current process's stdin and stdout. -- This mirrors the usual abstraction of an external process, to whose stdin would be /written/, while the current one's -- is /read/. interpretProcessCurrent ::
lib/Polysemy/Process/Interpreter/ProcessIO.hs view
@@ -1,4 +1,4 @@--- |Interpreters for 'ProcessOutput' and 'ProcessInput', Internal+-- | Interpreters for 'ProcessOutput' and 'ProcessInput', Internal module Polysemy.Process.Interpreter.ProcessIO where  import Polysemy.Process.Effect.ProcessInput (ProcessInput)@@ -12,7 +12,7 @@   interpretProcessOutputTextLines,   ) --- |The effects used by 'Polysemy.Process.Process' to send and receive chunks of bytes to and from a process.+-- | The effects used by 'Polysemy.Process.Process' to send and receive chunks of bytes to and from a process. type ProcessIO i o =   [     ProcessInput i,@@ -20,7 +20,7 @@     ProcessOutput 'Stderr o   ] --- |Interpret 'ProcessIO' with plain 'ByteString's without chunking.+-- | Interpret 'ProcessIO' with plain 'ByteString's without chunking. -- Silently discards stderr. interpretProcessByteString ::   InterpretersFor (ProcessIO ByteString ByteString) r@@ -29,7 +29,7 @@   interpretProcessOutputId @'Stdout .   interpretProcessInputId --- |Interpret 'ProcessIO' with 'ByteString's chunked as lines.+-- | Interpret 'ProcessIO' with 'ByteString's chunked as lines. -- Silently discards stderr. interpretProcessByteStringLines ::   InterpretersFor (ProcessIO ByteString ByteString) r@@ -38,7 +38,7 @@   interpretProcessOutputLines @'Stdout .   interpretProcessInputId --- |Interpret 'ProcessIO' with plain 'Text's without chunking.+-- | Interpret 'ProcessIO' with plain 'Text's without chunking. -- Silently discards stderr. interpretProcessText ::   InterpretersFor (ProcessIO Text Text) r@@ -47,7 +47,7 @@   interpretProcessOutputText @'Stdout .   interpretProcessInputText --- |Interpret 'ProcessIO' with 'Text's chunked as lines.+-- | Interpret 'ProcessIO' with 'Text's chunked as lines. -- Silently discards stderr. interpretProcessTextLines ::   InterpretersFor (ProcessIO Text Text) r
lib/Polysemy/Process/Interpreter/ProcessInput.hs view
@@ -1,11 +1,11 @@ {-# options_haddock prune #-} --- |Description: ProcessInput Interpreters, Internal+-- | Description: ProcessInput Interpreters, Internal module Polysemy.Process.Interpreter.ProcessInput where  import Polysemy.Process.Effect.ProcessInput (ProcessInput (Encode)) --- |Interpret 'ProcessInput' by passing 'ByteString' through.+-- | Interpret 'ProcessInput' by passing 'ByteString' through. interpretProcessInputId :: InterpreterFor (ProcessInput ByteString) r interpretProcessInputId =   interpret \case@@ -13,7 +13,7 @@       pure value {-# inline interpretProcessInputId #-} --- |Interpret 'ProcessInput' by UTF-8-encoding 'Text'.+-- | Interpret 'ProcessInput' by UTF-8-encoding 'Text'. interpretProcessInputText :: InterpreterFor (ProcessInput Text) r interpretProcessInputText =   interpret \case
lib/Polysemy/Process/Interpreter/ProcessOneshot.hs view
@@ -1,4 +1,4 @@--- |Description: Process Interpreters, Internal+-- | Description: Process Interpreters, Internal module Polysemy.Process.Interpreter.ProcessOneshot where  import Polysemy.Conc.Effect.Race (Race)@@ -13,7 +13,7 @@ import Polysemy.Process.Interpreter.ProcessIO (ProcessIO) import Polysemy.Process.Interpreter.SystemProcess (SysProcConf, interpretSystemProcessNative) --- |Interpret 'Process' with a system process resource whose file descriptors are connected to three+-- | Interpret 'Process' with a system process resource whose file descriptors are connected to three -- 'Control.Concurrent.STM.TBMQueue.TBMQueue's, deferring decoding of stdout and stderr to the interpreters of two -- 'Polysemy.Process.ProcessOutput' effects. -- Unlike 'Polysemy.Process.interpretProcess', this variant sends errors inside the scope to the individual 'Process'@@ -34,7 +34,7 @@   (\ p -> pscope @SystemProcessScopeError options (raiseUnder . proc) p)   (handleProcessWithQueues terminated) --- |Variant of 'interpretProcessOneshot' that takes a static 'SysProcConf'.+-- | Variant of 'interpretProcessOneshot' that takes a static 'SysProcConf'. interpretProcessOneshot_ ::   ∀ proc i o r .   Members (ProcessIO i o) r =>@@ -46,7 +46,7 @@ interpretProcessOneshot_ options proc =   interpretProcessOneshot options (const (pure proc)) --- |Interpret 'Process' as a native 'Polysemy.Process.SystemProcess'.+-- | Interpret 'Process' as a native 'Polysemy.Process.SystemProcess'. -- This variant is for parameterized scopes, meaning that a value of arbitrary type may be passed to -- 'Polysemy.Process.withProcessOneshotParam' which is then passed to the supplied function to produce a 'SysProcConf' -- for the native process.@@ -62,7 +62,7 @@   interpretProcessOneshot options (insertAt @0 . proc) .   raiseUnder --- |Interpret 'Process' as a native 'Polysemy.Process.SystemProcess'.+-- | Interpret 'Process' as a native 'Polysemy.Process.SystemProcess'. -- This variant takes a static 'SysProcConf'. interpretProcessOneshotNative_ ::   ∀ i o r .
lib/Polysemy/Process/Interpreter/ProcessOutput.hs view
@@ -1,6 +1,6 @@ {-# options_haddock prune #-} --- |Description: ProcessOutput Interpreters, Internal+-- | Description: ProcessOutput Interpreters, Internal module Polysemy.Process.Interpreter.ProcessOutput where  import qualified Data.ByteString as ByteString@@ -9,7 +9,7 @@ import Polysemy.Process.Effect.ProcessOutput (ProcessOutput (Chunk)) import qualified Polysemy.Process.Effect.ProcessOutput as ProcessOutput --- |Interpret 'ProcessOutput' by discarding any output.+-- | Interpret 'ProcessOutput' by discarding any output. interpretProcessOutputIgnore ::   ∀ p a r .   InterpreterFor (ProcessOutput p a) r@@ -19,7 +19,7 @@       pure ([], "") {-# inline interpretProcessOutputIgnore #-} --- |Interpret 'ProcessOutput' by immediately emitting raw 'ByteString's without accumulation.+-- | Interpret 'ProcessOutput' by immediately emitting raw 'ByteString's without accumulation. interpretProcessOutputId ::   ∀ p r .   InterpreterFor (ProcessOutput p ByteString) r@@ -29,7 +29,7 @@       pure ([buffer <> new], "") {-# inline interpretProcessOutputId #-} --- |Transformer for 'ProcessOutput' that lifts results into 'Left', creating 'ProcessOutput p (Either a b)' from+-- | Transformer for 'ProcessOutput' that lifts results into 'Left', creating 'ProcessOutput p (Either a b)' from -- 'ProcessOutput p a'. interpretProcessOutputLeft ::   ∀ p a b r .@@ -40,7 +40,7 @@     Chunk buf new ->       first (fmap Left) <$> ProcessOutput.chunk @p buf new --- |Transformer for 'ProcessOutput' that lifts results into 'Right', creating 'ProcessOutput p (Either a b)' from+-- | Transformer for 'ProcessOutput' that lifts results into 'Right', creating 'ProcessOutput p (Either a b)' from -- 'ProcessOutput p b'. interpretProcessOutputRight ::   ∀ p a b r .@@ -62,7 +62,7 @@     folder a (z, Just r) =       (a : z, Just r) --- |Interpret 'ProcessOutput' by emitting individual 'ByteString' lines of output.+-- | Interpret 'ProcessOutput' by emitting individual 'ByteString' lines of output. interpretProcessOutputLines ::   ∀ p r .   InterpreterFor (ProcessOutput p ByteString) r@@ -72,7 +72,7 @@       pure (splitLines buffer new) {-# inline interpretProcessOutputLines #-} --- |Interpret 'ProcessOutput' by immediately emitting 'Text' without accumulation.+-- | Interpret 'ProcessOutput' by immediately emitting 'Text' without accumulation. interpretProcessOutputText ::   ∀ p r .   InterpreterFor (ProcessOutput p Text) r@@ -82,7 +82,7 @@       pure ([decodeUtf8 (buffer <> new)], "") {-# inline interpretProcessOutputText #-} --- |Interpret 'ProcessOutput' by emitting individual 'Text' lines of output.+-- | Interpret 'ProcessOutput' by emitting individual 'Text' lines of output. interpretProcessOutputTextLines ::   ∀ p r .   InterpreterFor (ProcessOutput p Text) r@@ -95,7 +95,7 @@ type Parser a =   ByteString -> ProcessOutputParseResult a --- |Internal helper for 'interpretProcessOutputIncremental' that repeatedly parses elements from a chunk until the+-- | Internal helper for 'interpretProcessOutputIncremental' that repeatedly parses elements from a chunk until the -- parser returns a failure or a partial result. parseMany ::   Parser a ->@@ -117,7 +117,7 @@           Done a rest ->             spin (cons . (Right a :)) Nothing rest --- |Whenever a chunk of output arrives, call the supplied incremental parser whose result must be converted to+-- | Whenever a chunk of output arrives, call the supplied incremental parser whose result must be converted to -- 'ProcessOutputParseResult'. -- If a partial parse result is produced, it is stored in the state and resumed when the next chunk is available. -- If parsing an @a@ succeeds, the parser recurses until it fails.
lib/Polysemy/Process/Interpreter/Pty.hs view
@@ -1,6 +1,6 @@ {-# options_haddock prune #-} --- |Description: Pty Interpreters, Internal+-- | Description: Pty Interpreters, Internal module Polysemy.Process.Interpreter.Pty where  import Polysemy.Resume (Stop, interpretScopedResumable, stopEitherWith, stopNote, type (!!))@@ -42,7 +42,7 @@ withPty =   bracket acquirePty releasePty --- |Interpret Pty as a 'System.Posix.Pty'.+-- | Interpret Pty as a 'System.Posix.Pty'. interpretPty ::   Members [Resource, Embed IO] r =>   InterpreterFor (Scoped_ Pty !! PtyError) r
lib/Polysemy/Process/Interpreter/SystemProcess.hs view
@@ -1,6 +1,6 @@ {-# options_haddock prune #-} --- |Description: SystemProcess Interpreters, Internal+-- | Description: SystemProcess Interpreters, Internal module Polysemy.Process.Interpreter.SystemProcess where  import Data.ByteString (hGetSome, hPut)@@ -30,11 +30,11 @@ import qualified Polysemy.Process.Effect.SystemProcess as SystemProcess import Polysemy.Process.Effect.SystemProcess (SystemProcess) --- |Convenience alias for a vanilla 'ProcessConfig', which will usually be transformed by interpreters to use 'Handle's.+-- | Convenience alias for a vanilla 'ProcessConfig', which will usually be transformed by interpreters to use 'Handle's. type SysProcConf =   ProcessConfig () () () --- |Convenience alias for the 'Process' type used by native interpreters.+-- | Convenience alias for the 'Process' type used by native interpreters. type PipesProcess =   Process Handle Handle Handle @@ -109,7 +109,7 @@   b ->     pure b --- |Handle 'SystemProcess' with a concrete 'System.Process' with connected pipes.+-- | Handle 'SystemProcess' with a concrete 'System.Process' with connected pipes. handleSystemProcessWithProcess ::   ∀ r r0 a .   Members [Stop SystemProcessError, Embed IO] r =>@@ -131,7 +131,7 @@   SystemProcess.Wait ->     tryStop "wait failed" (waitExitCode process) --- |Interpret 'SystemProcess' with a concrete 'System.Process' with connected pipes.+-- | Interpret 'SystemProcess' with a concrete 'System.Process' with connected pipes. interpretSystemProcessWithProcess ::   ∀ r .   Member (Embed IO) r =>@@ -140,7 +140,7 @@ interpretSystemProcessWithProcess process =   interpretResumable (handleSystemProcessWithProcess process) --- |Interpret 'SystemProcess' as a single global 'System.Process' that's started immediately.+-- | Interpret 'SystemProcess' as a single global 'System.Process' that's started immediately. interpretSystemProcessNativeSingle ::   ∀ r .   Members [Stop SystemProcessScopeError, Resource, Embed IO] r =>@@ -162,7 +162,7 @@     stop (StartFailed err) {-# inline withProcConf #-} --- |Interpret 'SystemProcess' as a scoped 'System.Process' that's started wherever 'Polysemy.Process.withSystemProcess'+-- | Interpret 'SystemProcess' as a scoped 'System.Process' that's started wherever 'Polysemy.Process.withSystemProcess' -- is called and terminated when the wrapped action finishes. -- This variant is for parameterized scopes, allowing the consumer to supply a value of type @param@ to create the -- process config.@@ -174,7 +174,7 @@ interpretSystemProcessNative config =   interpretScopedR (\ p u -> raise (raise (config p)) >>= withProcConf u) handleSystemProcessWithProcess --- |Interpret 'SystemProcess' as a scoped 'System.Process' that's started wherever 'Polysemy.Process.withSystemProcess'+-- | Interpret 'SystemProcess' as a scoped 'System.Process' that's started wherever 'Polysemy.Process.withSystemProcess' -- is called and terminated when the wrapped action finishes. -- This variant takes a static 'SysProcConf'. interpretSystemProcessNative_ ::@@ -185,7 +185,7 @@ interpretSystemProcessNative_ config =   interpretScopedR (const (withProcess config)) handleSystemProcessWithProcess --- |Interpret 'SystemProcess' with a concrete 'System.Process' with no connection to stdio.+-- | Interpret 'SystemProcess' with a concrete 'System.Process' with no connection to stdio. interpretSystemProcessWithProcessOpaque ::   ∀ i o e r .   Member (Embed IO) r =>@@ -207,7 +207,7 @@     SystemProcess.Wait ->       tryStop "wait failed" (waitExitCode process) --- |Interpret 'SystemProcess' as a single global 'System.Process' that's started immediately.+-- | Interpret 'SystemProcess' as a single global 'System.Process' that's started immediately. interpretSystemProcessNativeOpaqueSingle ::   ∀ i o e r .   Members [Resource, Embed IO] r =>@@ -217,7 +217,7 @@   withProcessOpaque config \ process ->     interpretSystemProcessWithProcessOpaque process sem --- |Interpret 'SystemProcess' as a scoped 'System.Process' that's started wherever 'Polysemy.Process.withSystemProcess'+-- | Interpret 'SystemProcess' as a scoped 'System.Process' that's started wherever 'Polysemy.Process.withSystemProcess' -- is called and terminated when the wrapped action finishes. interpretSystemProcessNativeOpaque ::   ∀ i o e r .
lib/Polysemy/Process/ProcessOutput.hs view
@@ -1,6 +1,6 @@ {-# options_haddock prune #-} --- |The utility effect 'ProcessOutput' takes care of decoding process output, getting called by the+-- | The utility effect 'ProcessOutput' takes care of decoding process output, getting called by the -- 'Polysemy.Process.Process' interpreters whenever a chunk was read, while accumulating chunks until they were decoded -- successfully. module Polysemy.Process.ProcessOutput (
lib/Polysemy/Process/Pty.hs view
@@ -1,6 +1,6 @@ {-# options_haddock prune #-} --- |The effect 'Pty' abstracts pseudo terminals.+-- | The effect 'Pty' abstracts pseudo terminals. module Polysemy.Process.Pty (   module Polysemy.Process.Effect.Pty,   module Polysemy.Process.Interpreter.Pty,
lib/Polysemy/Process/SysProcConf.hs view
@@ -1,4 +1,4 @@--- |Constructors for 'SysProcConf', Internal+-- | Constructors for 'SysProcConf', Internal module Polysemy.Process.SysProcConf where  import Path (Abs, File, Path, Rel, toFilePath)@@ -7,19 +7,19 @@  import Polysemy.Process.Interpreter.SystemProcess (SysProcConf) --- |Create a 'SysProcConf' from an executable path and a list of arguments.+-- | Create a 'SysProcConf' from an executable path and a list of arguments. processConfig :: Path Abs File -> [Text] -> SysProcConf processConfig exe args =   proc (toFilePath exe) (toString <$> args) {-# inline processConfig #-} --- |Create a 'SysProcConf' from an shell command line.+-- | Create a 'SysProcConf' from an shell command line. shellConfig :: Text -> SysProcConf shellConfig cmd =   shell (toString cmd) {-# inline shellConfig #-} --- |Create a 'SysProcConf' by looking up an executable in the path, and using the supplied arguments.+-- | Create a 'SysProcConf' by looking up an executable in the path, and using the supplied arguments. which ::   Member (Embed IO) r =>   Path Rel File ->
lib/Polysemy/Process/SystemProcess.hs view
@@ -1,6 +1,6 @@ {-# options_haddock prune #-} --- |The effect 'SystemProcess' is a low-level abstraction of a native system process.+-- | The effect 'SystemProcess' is a low-level abstraction of a native system process. module Polysemy.Process.SystemProcess (   module Polysemy.Process.Effect.SystemProcess,   module Polysemy.Process.Interpreter.SystemProcess,@@ -30,7 +30,7 @@   ) import Polysemy.Process.SysProcConf --- |Obtain the current process's 'Pid'.+-- | Obtain the current process's 'Pid'. currentPid ::   Member (Embed IO) r =>   Sem r Pid
polysemy-process.cabal view
@@ -1,11 +1,11 @@ cabal-version: 2.2 --- This file has been generated from package.yaml by hpack version 0.35.2.+-- This file has been generated from package.yaml by hpack version 0.36.0. -- -- see: https://github.com/sol/hpack  name:           polysemy-process-version:        0.13.0.1+version:        0.14.0.0 synopsis:       Polysemy effects for system processes description:    See https://hackage.haskell.org/package/polysemy-process/docs/Polysemy-Process.html category:       Process@@ -36,12 +36,14 @@       Polysemy.Process.Data.PtyError       Polysemy.Process.Data.PtyResources       Polysemy.Process.Data.SystemProcessError+      Polysemy.Process.Effect.Interrupt       Polysemy.Process.Effect.Process       Polysemy.Process.Effect.ProcessInput       Polysemy.Process.Effect.ProcessOutput       Polysemy.Process.Effect.Pty       Polysemy.Process.Effect.SystemProcess       Polysemy.Process.Executable+      Polysemy.Process.Interpreter.Interrupt       Polysemy.Process.Interpreter.Process       Polysemy.Process.Interpreter.ProcessInput       Polysemy.Process.Interpreter.ProcessIO@@ -70,6 +72,7 @@       GADTs       LambdaCase       LiberalTypeSynonyms+      MonadComprehensions       MultiWayIf       OverloadedLabels       OverloadedLists@@ -92,19 +95,20 @@       NoFieldSelectors   ghc-options: -Wall -Widentities -Wincomplete-uni-patterns -Wmissing-deriving-strategies -Wredundant-constraints -Wunused-type-patterns -Wunused-packages   build-depends:-      base ==4.*-    , incipit-core ==0.5.*-    , path ==0.9.*-    , path-io >=1.7 && <1.9-    , polysemy ==1.9.*-    , polysemy-conc >=0.13.0.1 && <0.14-    , polysemy-resume >=0.7 && <0.9-    , polysemy-time ==0.6.*-    , posix-pty ==0.2.*-    , process-    , stm-chans >=3 && <3.1-    , typed-process >=0.2.6 && <0.3-    , unix+      async >=2.2.5 && <2.3+    , base >=4.17.2.1 && <4.20+    , incipit-core >=0.4.1.0 && <0.7+    , path >=0.9.1 && <0.10+    , path-io >=0.2.0 && <1.9+    , polysemy >=1.9.0.0 && <1.10+    , polysemy-conc >=0.14.0.0 && <0.15+    , polysemy-resume >=0.7.0.0 && <0.10+    , polysemy-time >=0.5.1.0 && <0.8+    , posix-pty >=0.2.2 && <0.3+    , process >=1.6.18.0 && <1.7+    , stm-chans >=2.0.0 && <3.1+    , typed-process >=0.2.4.1 && <0.3+    , unix >=2.7.3 && <2.9   mixins:       base hiding (Prelude)     , incipit-core (IncipitCore as Prelude)@@ -115,6 +119,7 @@   type: exitcode-stdio-1.0   main-is: Main.hs   other-modules:+      Polysemy.Process.Test.InterruptTest       Polysemy.Process.Test.ProcessTest   hs-source-dirs:       test@@ -133,6 +138,7 @@       GADTs       LambdaCase       LiberalTypeSynonyms+      MonadComprehensions       MultiWayIf       OverloadedLabels       OverloadedLists@@ -155,18 +161,20 @@       NoFieldSelectors   ghc-options: -threaded -rtsopts -with-rtsopts=-N -Wall -Widentities -Wincomplete-uni-patterns -Wmissing-deriving-strategies -Wredundant-constraints -Wunused-type-patterns -Wunused-packages   build-depends:-      base ==4.*-    , incipit-core ==0.5.*-    , polysemy-    , polysemy-conc >=0.13.0.1 && <0.14-    , polysemy-plugin >=0.4.4 && <0.5+      async >=2.2.5 && <2.3+    , base >=4.17.2.1 && <4.20+    , incipit-core >=0.4.1.0 && <0.7+    , polysemy >=1.9.0.0 && <1.10+    , polysemy-conc >=0.14.0.0 && <0.15+    , polysemy-plugin >=0.4.4.0 && <0.5     , polysemy-process-    , polysemy-resume >=0.7 && <0.9-    , polysemy-test >=0.6 && <0.10-    , polysemy-time ==0.6.*-    , tasty ==1.4.*-    , tasty-expected-failure ==0.12.*-    , typed-process+    , polysemy-resume >=0.7.0.0 && <0.10+    , polysemy-test >=0.3.1.6 && <0.11+    , polysemy-time >=0.5.1.0 && <0.8+    , tasty >=1.4.2 && <1.5+    , tasty-expected-failure >=0.11.1.2 && <0.13+    , typed-process >=0.2.4.1 && <0.3+    , unix >=2.7.3 && <2.9   mixins:       base hiding (Prelude)     , incipit-core (IncipitCore as Prelude)
readme.md view
@@ -1,6 +1,6 @@ # About -This library provides [Polysemy] effects for system processes and pseudo terminals.+This library provides [Polysemy] effects for system processes, signal handling and pseudo terminals.  Please visit [Hackage] for documentation. 
test/Main.hs view
@@ -1,12 +1,17 @@ module Main where +import Polysemy.Process.Test.InterruptTest (test_interrupt) import Polysemy.Process.Test.ProcessTest (test_processAll)+import Polysemy.Test (unitTest) import Test.Tasty (TestTree, defaultMain, testGroup)  tests :: TestTree tests =   testGroup "main" [-    test_processAll+    test_processAll,+    testGroup "interrupt" [+      unitTest "interrupt" test_interrupt+    ]   ]  main :: IO ()
+ test/Polysemy/Process/Test/InterruptTest.hs view
@@ -0,0 +1,31 @@+module Polysemy.Process.Test.InterruptTest where++import Control.Concurrent.STM (TVar, atomically, modifyTVar, newTVarIO, readTVarIO)+import Polysemy.Conc.Interpreter.Critical (interpretCritical)+import Polysemy.Conc.Interpreter.Race (interpretRace)+import Polysemy.Test (UnitTest, assertEq, runTestAuto)+import System.Posix (Handler (CatchInfoOnce), SignalInfo, installHandler, keyboardSignal, raiseSignal)++import qualified Polysemy.Process.Effect.Interrupt as Interrupt+import Polysemy.Process.Interpreter.Interrupt (interpretInterrupt)++handler :: MVar () -> TVar Int -> SignalInfo -> IO ()+handler mv tv _ = do+  atomically (modifyTVar tv (5 +))+  putMVar mv ()++test_interrupt :: UnitTest+test_interrupt = do+  runTestAuto do+    tv <- embed (newTVarIO 0)+    mv <- embed newEmptyMVar+    embed (installHandler keyboardSignal (CatchInfoOnce (handler mv tv)) Nothing)+    asyncToIOFinal $ interpretCritical $ interpretRace $ interpretInterrupt do+      Interrupt.register "test 1" do+        atomically (modifyTVar tv (3 +))+      Interrupt.register "test 2" do+        atomically (modifyTVar tv (9 +))+      embed (raiseSignal keyboardSignal)+    embed (takeMVar mv)+    result <- embed (readTVarIO tv)+    assertEq @_ @IO 17 result