packages feed

unbeliever 0.7.3.0 → 0.8.0.0

raw patch · 5 files changed

+193/−77 lines, 5 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

- Core.Program.Execute: write :: Rope -> Program τ ()
- Core.Program.Execute: writeR :: Render α => α -> Program τ ()
- Core.Program.Execute: writeS :: Show α => α -> Program τ ()
+ Core.Program.Logging: write :: Rope -> Program τ ()
+ Core.Program.Logging: writeR :: Render α => α -> Program τ ()
+ Core.Program.Logging: writeS :: Show α => α -> Program τ ()

Files

lib/Core/Encoding/Json.hs view
@@ -19,36 +19,29 @@ schema over time. For ease of exploration this module simply defines an easy to use intermediate type representing JSON as a format. -To use this module, you may find the following imports helpful:+Often you'll be working with literals directly in your code. While you can+write:  @-\{\-\# LANGUAGE OverloadedStrings \#\-\}-\{\-\# LANGUAGE OverloadedLists \#\-\}--import "Data.HashMap.Strict" ('HashMap')-import qualified "Data.HashMap.Strict" as 'HashMap'  -- from the __unordered-containers__ package.-import "Data.Scientific" ('Scientific')              -- from the __scientific__ package-import "Core.Encoding.Json"+    j = 'JsonObject' ('intoMap' [('JsonKey' "answer", 'JsonNumber' 42)]) @ -Often you'll be working with literals directly in your code. While you can-write:+and it would be correct, enabling:  @-    j = JsonObject (HashMap.fromList [(JsonKey "answer", JsonNumber 42)])+\{\-\# LANGUAGE OverloadedStrings \#\-\}+\{\-\# LANGUAGE OverloadedLists \#\-\} @ -and it would be correct, enabling @OverloadedStrings@ and @OverloadedLists@ allows you to write:  @-    j = JsonObject [("answer", 42)]+    j = 'JsonObject' [("answer", 42)] @ -which you is somewhat less cumbersome. You're certainly welcome to use the-constructors if you find it makes for more readable code or if you need-the type annotations.-+which you is somewhat less cumbersome in declaration-heavy code. You're+certainly welcome to use the constructors if you find it makes for more+readable code or if you need the type annotations. -} -- -- As currently implemented this module, in conjunction with
lib/Core/Program/Execute.hs view
@@ -70,9 +70,6 @@       , retrieve       , update         {-* Useful actions -}-      , write-      , writeS-      , writeR       , output         {-* Concurrency -}       , Thread@@ -93,9 +90,8 @@     , ExceptionInLinkedThread(..), AsyncCancelled, race_) import Control.Concurrent.MVar (readMVar, putMVar, modifyMVar_) import Control.Concurrent.STM (atomically, check)-import Control.Concurrent.STM.TQueue (TQueue, readTQueue-    , writeTQueue, isEmptyTQueue)-import qualified Control.Exception as Base (throwIO, evaluate)+import Control.Concurrent.STM.TQueue (TQueue, readTQueue, isEmptyTQueue)+import qualified Control.Exception as Base (throwIO) import Control.Exception.Safe (SomeException, Exception(displayException)) import qualified Control.Exception.Safe as Safe (throw, catchesAsync) import Control.Monad (when, forever)@@ -111,7 +107,6 @@ import Core.Data.Structures import Core.Text.Bytes import Core.Text.Rope-import Core.Text.Utilities import Core.System.Base import Core.Program.Context import Core.Program.Logging@@ -378,66 +373,23 @@ update = setApplicationState  {-|-Write the supplied text to @stdout@.--This is for normal program output.--@-     'write' "Beginning now"-@--}-write :: Rope -> Program τ ()-write text = do-    context <- ask-    liftIO $ do-        let out = outputChannelFrom context--        !text' <- Base.evaluate text-        atomically (writeTQueue out text')--{-|-Call 'show' on the supplied argument and write the resultant text to-@stdout@.--(This is the equivalent of 'print' from __base__)--}-writeS :: Show α => α -> Program τ ()-writeS = write . intoRope . show--{-|-Pretty print the supplied argument and write the resultant text to-@stdout@. This will pass the detected terminal width to the 'render'-function, resulting in appopriate line wrapping when rendering your value.--}-writeR :: Render α => α -> Program τ ()-writeR thing = do-    context <- ask-    liftIO $ do-        let out = outputChannelFrom context-        let columns = terminalWidthFrom context--        let text = render columns thing-        !text' <- Base.evaluate text-        atomically (writeTQueue out text')--{-| Write the supplied @Bytes@ to the given @Handle@. Note that in contrast to 'write' we don't output a trailing newline.  @-    output h b+    'output' h b @ -Do not use this to output to @stdout@ as that would bypass the mechanism-used by the @write*@ functions to sequence output correctly. If you wish to-write to the terminal use:+Do /not/ use this to output to @stdout@ as that would bypass the mechanism+used by the 'write'*, 'event', and 'debug'* functions to sequence output+correctly. If you wish to write to the terminal use:  @-    write (intoRope b)+    'write' ('intoRope' b) @  (which is not /unsafe/, but will lead to unexpected results if the binary-blob you pass in is not UTF-8 text).+blob you pass in is other than UTF-8 text). -} output :: Handle -> Bytes -> Program τ () output h b = liftIO $ do
lib/Core/Program/Logging.hs view
@@ -5,11 +5,127 @@ {-# LANGUAGE BangPatterns #-} {-# OPTIONS_HADDOCK prune #-} +{-|+Output and Logging from your program.++Broadly speaking, there are two kinds of program: console tools invoked for+a single purpose, and long-running daemons that effectively run forever.++Tools tend to be run to either have an effect (in which case they tend not+to say much of anything) or to report a result (which is usually printed to+your terminal). This tends to be written to \"standard output\",+traditionally abbreviated in code as @stdout@.++Daemons, on the other hand, don't write their output to file descriptor 1;+rather they tend to respond to requests by writing to files, replying over+network sockets, or sending up smoke signals (@ECPUTOOHOT@, in case you're+curious). What daemons /do/ output, however, is log messages.++While there are many sophisticated logging services around that you can+interact with directly, from the point of view of an individual /program/+these tend to have faded away and have become more an aspect of the+Infrastructure- or Platform-as-a-Service you're running on. Over the past+few years containerization mechanisms like __docker__, then more recently+container orchestration layers like __kubernetes__, have generally simply+captured programs' standard output /as if it were the program's log output/+and then sent that down external logging channels to whatever log analysis+system is available. Even programs running locally under __systemd__ or+similar tend to follow the same pattern; services write to @stdout@ and+that output, as "logs", ends up being fed to the system journal.++So with that in mind, in your program you will either be outputting results+to @stdout@ or not writing there at all, and you will either be describing+extensively what your application is up to, or not at all. ++There is also a \"standard error\" file descriptor available. We recommend+not using it. At best it is unclear what is written to @stderr@ and what+isn't; at worse it is lost as many environments in the wild discard+@stderr@ entirely. To avoid this most of the time people just combine them+in the invoking shell with @2>&1@, which inevitably results in @stderr@+text appearing in the middle of normal @stdout@ lines corrupting them.++The original idea of standard error was to provde a way to adverse+conditions without interrupting normal text output, but as we have just+observed if it happens without context or out of order there isn't much+point. Instead this library offers a mechanism which caters for the+different /kinds/ of output in a unified, safe manner.++== Three kinds of output/logging messages++/Standard output/++Your program's normal output to the terminal. This library provides the+'write' (and 'writeS' and 'writeR') functions to send output to @stdout@.++/Events/++When running a tool, you sometimes need to know /what it is doing/ as it is+carrying out its steps. The 'event' function allows you to emit descriptive+messages to the log channel tracing the activities of your program.++Ideally you would never need to turn this on in a command-line tool, but+sometimes a user or operations engineer needs to see what an application is+up to. These should be human readable status messages to convey a sense of+progress.++In the case of long-running daemons, 'event' can be used to describe+high-level lifecycle events, to document individual requests, or even+describing individual transitions in a request handler's state machine, all+depending on the nature of your program.++/Debugging/++Programmers, on the other hand, often need to see the internal state of+the program when /debugging/.++You almost always you want to know the value of some variable or parameter,+so the 'debug' (and 'debugS' and 'debugR') utility functions here send+messages to the log channel prefixed with a label that is, by convention,+the name of the value you are examining.++The important distinction here is that such internal values are almost+never useful for someone other than the person or team who wrote the code+emitting it. Operations engineers might be asked by developers to turn on+@--debug@ing and report back the results; but a user of your program is not+going to do that in and of themselves to solve a problem.++== Single output channel++It is the easy to make the mistake of having multiple subsystems attempting+to write to @stdout@ and these outputs corrupting each other, especially in+a multithreaded language like Haskell. The output actions described here+send all output to terminal down a single thread-safe channel. Output will+be written in the order it was executed, and (so long as you don't use the+@stdout@ Handle directly yourself) your terminal output will be sound.++Passing @--verbose@ on the command-line of your program will cause 'event'+to write its tracing messages to the terminal. This shares the same output+channel as the 'write'@*@ functions and will /not/ cause corruption of your+program's normal output.++Passing @--debug@ on the command-line of your program will cause the+'debug'@*@ actions to write their debug-level messages to the terminal.+This shares the same output channel as above and again will not cause+corruption of your program's normal output.++== Logging channel++/Event and debug messages are internally also sent to a "logging channel",/+/as distinct from the "output" one. This would allow us to send them/+/directly to a file, syslog, or network logging service, but this is/+/as-yet unimplemented./+-} module Core.Program.Logging     (         putMessage       , Verbosity(..)+        {-* Normal output -}+      , write+      , writeS+      , writeR+        {-* Event tracing -}       , event+        {-* Debugging -}       , debug       , debugS       , debugR@@ -94,6 +210,49 @@   where     pad = S.replicate len "0"     len = digits - length str++{-|+Write the supplied text to @stdout@.++This is for normal program output.++@+     'write' "Beginning now"+@+-}+write :: Rope -> Program τ ()+write text = do+    context <- ask+    liftIO $ do+        let out = outputChannelFrom context++        !text' <- evaluate text+        atomically (writeTQueue out text')++{-|+Call 'show' on the supplied argument and write the resultant text to+@stdout@.++(This is the equivalent of 'print' from __base__)+-}+writeS :: Show α => α -> Program τ ()+writeS = write . intoRope . show++{-|+Pretty print the supplied argument and write the resultant text to+@stdout@. This will pass the detected terminal width to the 'render'+function, resulting in appopriate line wrapping when rendering your value.+-}+writeR :: Render α => α -> Program τ ()+writeR thing = do+    context <- ask+    liftIO $ do+        let out = outputChannelFrom context+        let columns = terminalWidthFrom context++        let text = render columns thing+        !text' <- evaluate text+        atomically (writeTQueue out text')  {-| Note a significant event, state transition, status, or debugging
lib/Core/Text/Rope.hs view
@@ -80,10 +80,10 @@     , contains       {-* Interoperation and Output -}     , Textual(fromRope, intoRope, append)-    , unsafeIntoRope     , hWrite       {-* Internals -}     , unRope+    , unsafeIntoRope     , Width(..)     ) where 
unbeliever.cabal view
@@ -4,17 +4,25 @@ -- -- see: https://github.com/sol/hpack ----- hash: 1a286309370d68d11f651b386263a174b2744b09bf4f1919f38209b05542a639+-- hash: dc4af83e0e28d0d48d8e6550b7def407de3c5e3cc34d5c6380ecbfdc74229985  name:           unbeliever-version:        0.7.3.0+version:        0.8.0.0 synopsis:       Opinionated Haskell Interoperability description:    A library to help build command-line programs, both tools and                 longer-running daemons.                 .-                Useful starting points are "Core.Program.Execute" and "Core.Text.Rope".+                A description of this package, a list of features, and some background+                to its design is contained in the+                <https://github.com/oprdyn/unbeliever/blob/master/README.markdown README>+                on GitHub.+                .+                Useful starting points in the documentation are "Core.Program.Execute"+                and "Core.Text.Rope". category:       System stability:      experimental+homepage:       https://github.com/oprdyn/unbeliever#readme+bug-reports:    https://github.com/oprdyn/unbeliever/issues author:         Andrew Cowie <andrew@operationaldynamics.com> maintainer:     Andrew Cowie <andrew@operationaldynamics.com> copyright:      © 2018 Operational Dynamics Consulting Pty Ltd, and Others@@ -22,6 +30,10 @@ license-file:   LICENCE tested-with:    GHC == 8.4 build-type:     Simple++source-repository head+  type: git+  location: https://github.com/oprdyn/unbeliever  flag development   manual: True