packages feed

pandoc-plot 0.7.0.0 → 0.7.1.0

raw patch · 15 files changed

+160/−139 lines, 15 filesdep +lifted-asyncdep −asyncdep −timedep ~criteriondep ~mtlPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependencies added: lifted-async

Dependencies removed: async, time

Dependency ranges changed: criterion, mtl

API changes (from Hackage documentation)

+ Text.Pandoc.Filter.Plot.Internal: RuntimeEnv :: Configuration -> Logger -> RuntimeEnv
+ Text.Pandoc.Filter.Plot.Internal: [envConfig] :: RuntimeEnv -> Configuration
+ Text.Pandoc.Filter.Plot.Internal: [envLogger] :: RuntimeEnv -> Logger
+ Text.Pandoc.Filter.Plot.Internal: asksConfig :: (Configuration -> a) -> PlotM a
+ Text.Pandoc.Filter.Plot.Internal: data RuntimeEnv
- Text.Pandoc.Filter.Plot: make :: Configuration -> Block -> IO Block
+ Text.Pandoc.Filter.Plot: make :: Block -> PlotM Block
- Text.Pandoc.Filter.Plot: makeEither :: Configuration -> Block -> IO (Either PandocPlotError Block)
+ Text.Pandoc.Filter.Plot: makeEither :: Block -> PlotM (Either PandocPlotError Block)
- Text.Pandoc.Filter.Plot.Internal: type PlotM a = ReaderT Configuration LoggingM a
+ Text.Pandoc.Filter.Plot.Internal: type PlotM a = ReaderT RuntimeEnv IO a

Files

CHANGELOG.md view
@@ -2,8 +2,14 @@ 
 pandoc-plot uses [Semantic Versioning](http://semver.org/spec/v2.0.0.html)
 
+Release 0.7.1.0
+---------------
+
+* Better multi-threaded logging. Only one thread (the logging thread) performs IO on the log file. This prevents hang-ups when working on large documents. 
+
 Release 0.7.0.0
 ---------------
+
 * Added documentation on using `pandoc-plot` with LaTeX documents as well.
 * Added preliminary support for logging to `pandoc-plot`. You can turn on this feature in the configuration as follows:
 
README.md view
@@ -1,5 +1,5 @@ <!--
-The file README.md is automatically generated by the tools/mkreadme script 
+The file README.md is automatically generated by the mkreadme.ps1 script 
 Do not edit manually
 -->
 
benchmark/MatplotlibGallery.hs view
@@ -19,8 +19,8 @@         strToExp :: String -> Q Exp
         strToExp s = return $ VarE 'fromString `AppE` LitE (StringL s)
 
-galleryItem1 :: Q Exp
+galleryItem1, galleryItem2, galleryItem3, galleryItem4 :: Q Exp
 galleryItem1 = galleryItem "benchmark/gallery_item_1.py"
-
-galleryItem2 :: Q Exp
-galleryItem2 = galleryItem "benchmark/gallery_item_2.py"+galleryItem2 = galleryItem "benchmark/gallery_item_2.py"
+galleryItem3 = galleryItem "benchmark/gallery_item_3.py"
+galleryItem4 = galleryItem "benchmark/gallery_item_4.py"
benchmark/bench.hs view
@@ -6,19 +6,25 @@ import Text.Pandoc.Filter.Plot
 import Text.Pandoc.Filter.Plot.Internal
 
-import MatplotlibGallery (galleryItem1, galleryItem2)
+import MatplotlibGallery ( galleryItem1, galleryItem2
+                         , galleryItem3, galleryItem4
+                         )
 
 main :: IO ()
 main = 
     defaultMain [
         envWithCleanup (return ()) (\_ -> cleanupEnv) $ \_ -> 
             bgroup "main" [
-                  bench "filter" $ nfIO (plotTransform defaultConfiguration benchDoc)
+                    bench "filter-async" $ nfIO (plotTransform plotConfig benchDoc)
+                  , bench "filter"       $ nfIO (makePlot plotConfig benchDoc)
                 ]
     ]
 
+plotConfig :: Configuration
+plotConfig = defaultConfiguration {logVerbosity=Silent, logSink =StdErr}
+
 cleanupEnv :: IO ()
-cleanupEnv = cleanOutputDirs defaultConfiguration benchDoc >> return ()
+cleanupEnv = cleanOutputDirs plotConfig benchDoc >> return ()
 
 
 codeBlock :: Script -> Block
@@ -28,4 +34,6 @@ benchDoc :: Pandoc
 benchDoc = Pandoc mempty [ codeBlock $(galleryItem1)
                          , codeBlock $(galleryItem2)
+                         , codeBlock $(galleryItem3)
+                         , codeBlock $(galleryItem4)
                          ]
pandoc-plot.cabal view
@@ -1,5 +1,5 @@ name:           pandoc-plot
-version:        0.7.0.0
+version:        0.7.1.0
 cabal-version:  >= 1.12
 synopsis:       A Pandoc filter to include figures generated from code blocks using your plotting toolkit of choice.
 description:    A Pandoc filter to include figures generated from code blocks. Keep the document and code in the same location. Output is captured and included as a figure.
@@ -12,6 +12,7 @@ license:        GPL-2
 license-file:   LICENSE
 build-type:     Simple
+tested-with:    GHC == 8.6.5, GHC == 8.8.3
 extra-source-files:
     CHANGELOG.md
     LICENSE
@@ -61,13 +62,12 @@         , hashable           >= 1     && < 2
         , pandoc             >= 2.8   && < 3
         , pandoc-types       >= 1.20  && < 2
-        , async              >= 2     && < 3
+        , lifted-async       >= 0.10  && < 1
         , shakespeare        >= 2.0   && < 3
         , text               >= 1     && < 2
-        , time               >= 1     && < 2
         , typed-process      >= 0.2.1 && < 1
         , yaml               >= 0.8   && < 1
-        , mtl                >= 2.2   && < 2.3
+        , mtl                >= 2.2   && < 3
     default-language: Haskell2010
 
 executable pandoc-plot
@@ -122,7 +122,7 @@     build-depends:   base
                    , pandoc-plot
                    , pandoc-types
-                   , criterion          >= 1.0 && < 1.6
+                   , criterion          >= 1.0 && < 2
                    , template-haskell   > 2.7 && < 3
                    , text
     ghc-options:   -Wall -Wcompat -rtsopts -O2 -threaded -with-rtsopts=-N
src/Text/Pandoc/Filter/Plot.hs view
@@ -89,8 +89,7 @@     , readDoc
     ) where
 
-import Control.Concurrent.Async          (mapConcurrently)
-
+import Control.Concurrent.Async.Lifted   (mapConcurrently)
 import Data.Text                         (Text, unpack)
 
 import Text.Pandoc.Definition            (Pandoc(..), Block)
@@ -108,8 +107,8 @@ plotTransform :: Configuration -- ^ Configuration for default values
               -> Pandoc        -- ^ Input document
               -> IO Pandoc
-plotTransform conf (Pandoc meta blocks) =
-    mapConcurrently (makePlot conf) blocks >>= return . Pandoc meta
+plotTransform conf (Pandoc meta blocks) = 
+    runPlotM conf $ mapConcurrently make blocks >>= return . Pandoc meta
 
 
 -- | Highest-level function that can be walked over a Pandoc tree.
@@ -127,33 +126,29 @@          => Configuration -- ^ Configuration for default values
          -> a             -- ^ Input block or document
          -> IO a
-makePlot conf = walkM (make conf)
+makePlot conf = runPlotM conf . walkM make
 
 
-make :: Configuration
-     -> Block 
-     -> IO Block
-make conf blk = 
-    either (const (return blk) ) return =<< makeEither conf blk
+-- | Try to process the block with `pandoc-plot`. If a failure happens (or the block)
+-- was not meant to become a figure, return the block as-is.
+make :: Block -> PlotM Block
+make blk = either (const (return blk) ) return =<< makeEither blk
 
 
-makeEither :: Configuration 
-           -> Block 
-           -> IO (Either PandocPlotError Block)
-makeEither conf block = runPlotM conf (go block)
+-- | Try to process the block with `pandoc-plot`, documenting the error.
+makeEither :: Block -> PlotM (Either PandocPlotError Block)
+makeEither block = 
+    parseFigureSpec block 
+        >>= maybe 
+                (return $ Right block)
+                (\s -> runScriptIfNecessary s >>= handleResult s)
     where
-        go :: Block -> PlotM (Either PandocPlotError Block)
-        go blk = parseFigureSpec blk
-                    >>= maybe
-                        (return $ Right blk)
-                        (\s -> runScriptIfNecessary s >>= handleResult s)
-            where
-                -- Logging of errors has been taken care of in @runScriptIfNecessary@ 
-                handleResult :: FigureSpec -> ScriptResult -> PlotM (Either PandocPlotError Block)
-                handleResult spec ScriptSuccess          = return $ Right $ toImage (captionFormat conf) spec
-                handleResult _ (ScriptFailure msg code)  = return $ Left (ScriptRuntimeError msg code) 
-                handleResult _ (ScriptChecksFailed msg)  = return $ Left (ScriptChecksFailedError msg)
-                handleResult _ (ToolkitNotInstalled tk') = return $ Left (ToolkitNotInstalledError tk') 
+        -- Logging of errors has been taken care of in @runScriptIfNecessary@ 
+        handleResult :: FigureSpec -> ScriptResult -> PlotM (Either PandocPlotError Block)
+        handleResult _ (ScriptFailure msg code)       = return $ Left (ScriptRuntimeError msg code) 
+        handleResult _ (ScriptChecksFailed msg)       = return $ Left (ScriptChecksFailedError msg)
+        handleResult _ (ToolkitNotInstalled tk')      = return $ Left (ToolkitNotInstalledError tk') 
+        handleResult spec ScriptSuccess = asks envConfig >>= \c -> return $ Right $ toImage (captionFormat c) spec
 
 
 data PandocPlotError
src/Text/Pandoc/Filter/Plot/Monad.hs view
@@ -13,6 +13,7 @@ module Text.Pandoc.Filter.Plot.Monad (
       Configuration(..)
     , PlotM
+    , RuntimeEnv(..)
     , runPlotM
     -- * Running external commands
     , runCommand
@@ -27,14 +28,19 @@     , liftIO
     , ask
     , asks
+    , asksConfig
     -- * Base types
     , module Text.Pandoc.Filter.Plot.Monad.Types
 ) where
 
+
+import           Control.Concurrent.Chan     (writeChan)
+
 import           Control.Monad.Reader
 
 import           Data.ByteString.Lazy        (toStrict)
 import           Data.Text                   (Text, pack, unpack)
+import qualified Data.Text                   as T
 import           Data.Text.Encoding          (decodeUtf8With)
 import           Data.Text.Encoding.Error    (lenientDecode)
 
@@ -47,39 +53,56 @@ 
 import           Prelude                     hiding (log, fst, snd)
 
-import Text.Pandoc.Filter.Plot.Monad.Logging
+import Text.Pandoc.Filter.Plot.Monad.Logging as Log
 import Text.Pandoc.Filter.Plot.Monad.Types
 
+
 -- | pandoc-plot monad
-type PlotM a = ReaderT Configuration LoggingM a
+type PlotM a = ReaderT RuntimeEnv IO a
 
 
--- | Evaluate a @PlotM@ action 
+data RuntimeEnv = 
+    RuntimeEnv { envConfig :: Configuration
+               , envLogger :: Logger
+               }
+
+-- | Get access to configuration within the @PlotM@ monad.
+asksConfig :: (Configuration -> a) -> PlotM a
+asksConfig f = asks (f . envConfig)
+
+
+-- | Evaluate a @PlotM@ action.
 runPlotM :: Configuration -> PlotM a -> IO a
 runPlotM conf v = 
     let verbosity = logVerbosity conf
         sink      = logSink conf 
-    in runLoggingM verbosity sink (runReaderT v conf)
-
-
-debug :: Text -> PlotM ()
-debug t = lift $ log "DEBUG| " Debug t
-
-
-err :: Text -> PlotM ()
-err t = lift $ log "ERROR| " Error t
+    in withLogger verbosity sink $ 
+        \logger -> runReaderT v (RuntimeEnv conf logger)
 
 
-warning :: Text -> PlotM ()
-warning t = lift $ log "WARN | " Warning t
+debug, err, warning, info :: Text -> PlotM ()
+debug     = log "DEBUG| " Debug 
+err       = log "ERROR| " Error 
+warning   = log "WARN | " Warning 
+info      = log "INFO | " Info 
 
 
-info :: Text -> PlotM ()
-info t = lift $ log "INFO | " Info t
+-- | General purpose logging. 
+log :: Text      -- ^ Header.
+    -> Verbosity -- ^ Verbosity of the message.
+    -> Text      -- ^ Message (can be multiple lines).
+    -> PlotM ()
+log h v t = do
+    logger <- asks envLogger
+    when (v >= lVerbosity logger) $ 
+        liftIO $ do
+            let lines' = [l' | l' <- T.lines t]
+            forM_ lines' $ \l -> writeChan (lChannel logger) (Just (h <> l <> "\n"))
 
 
--- | Run a command within the @PlotM@ monad. Stdout and Stderr
--- are read and decoded. Logging happens at the debug level.
+-- | Run a command within the @PlotM@ monad. Stderr stream
+-- is read and decoded, while Stdout is ignored. 
+-- Logging happens at the debug level.
 runCommand :: Text -> PlotM (ExitCode, Text)
 runCommand command = do
     (ec, processOutput') <- liftIO 
@@ -108,7 +131,7 @@ --     ---
 --     title: My document
 --     author: John Doe
---     plot-configuration: /path/to/file.yml
+--     plot-configuration: path\to\file.yml
 --     ---     
 -- @
 --
src/Text/Pandoc/Filter/Plot/Monad/Logging.hs view
@@ -15,27 +15,25 @@ module Text.Pandoc.Filter.Plot.Monad.Logging 
     ( Verbosity(..)
     , LogSink(..)
-    , LoggingM
-    , runLoggingM
-    , log
+    , Logger(..)
+    , withLogger
     ) where
 
 
-import           Control.Monad.Trans         (liftIO)
-import           Control.Monad.Writer.Strict (WriterT, runWriterT, tell)
+import           Control.Concurrent           (forkIO)
+import           Control.Concurrent.Chan      (Chan, newChan, readChan, writeChan)
+import           Control.Concurrent.MVar      (MVar, newEmptyMVar, putMVar, takeMVar)
 
-import           Data.Char                   (toLower)
-import           Data.List                   (sortOn)
-import           Data.String                 (IsString(..))
-import           Data.Text                   (Text, unpack)
-import qualified Data.Text                   as T
-import           Data.Text.IO                (hPutStr)
-import           Data.Time.Clock.System      (getSystemTime, SystemTime(..))
+import           Control.Monad                (forever)
+
+import           Data.Char                    (toLower)
+import           Data.String                  (IsString(..))
+import           Data.Text                    (Text, unpack)
+import           Data.Text.IO                 (hPutStr)
 import           Data.Yaml
 
-import           System.IO                   (stderr, withFile, IOMode (AppendMode) )
+import           System.IO                    (stderr, withFile, IOMode (AppendMode) )
 
-import           Prelude                     hiding (log, fst, snd)
 
 
 -- | Verbosity of the logger.
@@ -52,40 +50,45 @@              | LogFile FilePath -- ^ Appended to file.
              deriving (Eq, Show)
 
-type LogMessage = (Verbosity, SystemTime, Text)
 
-type LoggingM = WriterT [LogMessage] IO
+-- | The logging implementation is very similar to Hakyll's.
+data Logger = Logger
+    { lVerbosity :: Verbosity
+    , lChannel   :: Chan (Maybe Text)
+    , lSink      :: Text -> IO ()
+    , lSync      :: MVar ()
+    }
 
 
-runLoggingM :: Verbosity -> LogSink -> LoggingM a -> IO a
-runLoggingM Silent _       = runLoggingM' Silent $ mapM_ (return . trd)
-runLoggingM v StdErr       = runLoggingM' v $ mapM_ (hPutStr stderr . trd)
-runLoggingM v (LogFile fp) = runLoggingM' v $ mapM_ (\m -> withFile fp AppendMode $ \h -> hPutStr h (trd m))
+-- | Perform an IO action with a logger. Using this function
+-- ensures that logging will be gracefully shut down.
+withLogger :: Verbosity -> LogSink -> (Logger -> IO a) -> IO a
+withLogger v s f = do
+    logger <- Logger <$> pure v
+                     <*> newChan
+                     <*> pure (sink s)
+                     <*> newEmptyMVar 
 
+    -- The logger either logs messages (if Just "message"),
+    -- or stops working on Nothing.
+    _ <- forkIO $ forever $ 
+            readChan (lChannel logger) 
+                >>= maybe (putMVar (lSync logger) ()) (lSink logger)
 
-runLoggingM' :: Verbosity                -- ^ Minimum verbosity to keep
-             -> ([LogMessage] -> IO ())  -- ^ Log sink
-             -> LoggingM a
-             -> IO a
-runLoggingM' v f m = do
-    (r, t) <- runWriterT m
-    -- Messages with lower level than minimum are discarded
-    -- We also re-order messages to be chronological
-    let t' = sortOn snd $ filter (\message -> fst message >= v) t
-    liftIO $ f t'
-    return r
+    result <- f logger
+    
+    -- Flushing the logger
+    -- To signal to the logger that logging duties are over,
+    -- we append Nothing to the channel, and wait for it to finish
+    -- dealing with all items in the channel.
+    writeChan (lChannel logger) Nothing
+    () <- takeMVar (lSync logger)
 
+    return result
 
--- | General logging function.
--- Input text will be decomposed into lines, with each
--- line becoming a log line.    
-log :: Text       -- ^ Header  
-    -> Verbosity  
-    -> Text       -- ^ Message
-    -> LoggingM ()
-log h v t = do
-    timestamp <- liftIO $ getSystemTime
-    tell [(v, timestamp, h <> l <> "\n") | l <- T.lines t]
+    where
+        sink StdErr       = hPutStr stderr
+        sink (LogFile fp) = \t -> withFile fp AppendMode $ \h -> hPutStr h t
 
 
 instance IsString Verbosity where
@@ -101,16 +104,4 @@ 
 instance FromJSON Verbosity where
     parseJSON (String t) = pure $ fromString . unpack $ t
-    parseJSON _ = fail $ "Could not parse the logging verbosity."
-
-
-fst :: (a,b,c) -> a
-fst (a,_,_) = a
-
-
-snd :: (a,b,c) -> b
-snd (_,b,_) = b
-
-
-trd :: (a,b,c) -> c
-trd (_,_,c) = c+    parseJSON _ = fail $ "Could not parse the logging verbosity."
src/Text/Pandoc/Filter/Plot/Monad/Types.hs view
@@ -33,7 +33,6 @@ import           Data.Char              (toLower)
 import           Data.Hashable          (hash)
 import           Data.List              (intersperse)
-import           Data.Semigroup         (Semigroup (..))
 import           Data.String            (IsString (..))
 import           Data.Text              (Text)
 import           Data.Yaml
src/Text/Pandoc/Filter/Plot/Parse.hs view
@@ -69,7 +69,7 @@ 
         figureSpec :: Toolkit -> PlotM FigureSpec
         figureSpec toolkit = do
-            conf <- ask
+            conf <- asks envConfig
             let extraAttrs' = parseExtraAttrs toolkit attrs'
                 header = comment toolkit $ "Generated by pandoc-plot " <> ((pack . showVersion) version)
                 defaultPreamble = preambleSelector toolkit conf
src/Text/Pandoc/Filter/Plot/Renderers.hs view
@@ -30,7 +30,7 @@     , OutputSpec(..)
 ) where
 
-import           Control.Concurrent.Async                      (forConcurrently)
+import           Control.Concurrent.Async.Lifted               (forConcurrently)
 
 import           Data.List                                     ((\\))
 import           Data.Map.Strict                               (Map)
@@ -175,9 +175,8 @@ -- | Monadic version of @availableToolkits@.
 availableToolkitsM :: PlotM [Toolkit]
 availableToolkitsM = do
-    conf <- ask
-    mtks <- liftIO $ forConcurrently toolkits $  \tk -> do
-        available <- runPlotM conf $ toolkitAvailable tk
+    mtks <- forConcurrently toolkits $  \tk -> do
+        available <- toolkitAvailable tk
         if available
             then return $ Just tk
             else return Nothing
src/Text/Pandoc/Filter/Plot/Renderers/Matlab.hs view
@@ -40,7 +40,7 @@ -- Therefore, we cannot rely on this behavior to know if matlab is present, 
 -- like other toolkits.
 matlabAvailable :: PlotM Bool
-matlabAvailable = asks matlabExe >>= (\exe -> liftIO $ existsOnPath (exe <> exeExtension))
+matlabAvailable = asksConfig matlabExe >>= (\exe -> liftIO $ existsOnPath (exe <> exeExtension))
 
 
 matlabCapture :: FigureSpec -> FilePath -> Script
src/Text/Pandoc/Filter/Plot/Renderers/Prelude.hs view
@@ -56,15 +56,15 @@ -- | Path to the executable of a toolkit. If the executable can
 -- be found, then it will be the full path to it.
 executable :: Toolkit -> PlotM FilePath
-executable Matplotlib   = asks matplotlibExe   >>= liftIO . tryToFindExe
-executable PlotlyPython = asks plotlyPythonExe >>= liftIO . tryToFindExe
-executable PlotlyR      = asks plotlyRExe      >>= liftIO . tryToFindExe
-executable Matlab       = asks matlabExe       >>= liftIO . tryToFindExe
-executable Mathematica  = asks mathematicaExe  >>= liftIO . tryToFindExe
-executable Octave       = asks octaveExe       >>= liftIO . tryToFindExe
-executable GGPlot2      = asks ggplot2Exe      >>= liftIO . tryToFindExe
-executable GNUPlot      = asks gnuplotExe      >>= liftIO . tryToFindExe
-executable Graphviz     = asks graphvizExe     >>= liftIO . tryToFindExe
+executable Matplotlib   = asksConfig matplotlibExe   >>= liftIO . tryToFindExe
+executable PlotlyPython = asksConfig plotlyPythonExe >>= liftIO . tryToFindExe
+executable PlotlyR      = asksConfig plotlyRExe      >>= liftIO . tryToFindExe
+executable Matlab       = asksConfig matlabExe       >>= liftIO . tryToFindExe
+executable Mathematica  = asksConfig mathematicaExe  >>= liftIO . tryToFindExe
+executable Octave       = asksConfig octaveExe       >>= liftIO . tryToFindExe
+executable GGPlot2      = asksConfig ggplot2Exe      >>= liftIO . tryToFindExe
+executable GNUPlot      = asksConfig gnuplotExe      >>= liftIO . tryToFindExe
+executable Graphviz     = asksConfig graphvizExe     >>= liftIO . tryToFindExe
 
 
 -- | Internal description of all information 
stack.yaml view
@@ -1,4 +1,4 @@-resolver: lts-15.16 # GHC 8.8.3
+resolver: lts-16.0 # GHC 8.8.3
 
 packages:
 - .
tests/Common.hs view
@@ -43,7 +43,7 @@         ensureDirectoryExistsAndEmpty tempDir
 
         let cb = (addDirectory tempDir $ codeBlock tk (trivialContent tk))
-        _ <- make defaultTestConfig cb
+        _ <- runPlotM defaultTestConfig $ make cb
         filesCreated <- length <$> listDirectory tempDir
         assertEqual "" 2 filesCreated
 
@@ -58,7 +58,7 @@ 
         let cb = (addPreamble (include tk) $
                     addDirectory tempDir $ codeBlock tk (trivialContent tk))
-        _ <- make defaultTestConfig cb
+        _ <- runPlotM defaultTestConfig $ make cb
         inclusion <- readFile (include tk)
         sourcePath <- head . filter (isExtensionOf "txt") <$> listDirectory tempDir
         src <- readFile (tempDir </> sourcePath)
@@ -84,7 +84,7 @@         let fmt = head (supportedSaveFormats tk)
             cb = (addSaveFormat fmt $
                  addDirectory tempDir $ codeBlock tk (trivialContent tk))
-        _ <- make defaultTestConfig cb
+        _ <- runPlotM defaultTestConfig $ make cb
         numberjpgFiles <-
             length <$> filter (isExtensionOf (extension fmt)) <$>
             listDirectory tempDir
@@ -108,8 +108,8 @@                       $ addDirectory tempDir 
                       $ addCaption expected 
                       $ codeBlock tk (trivialContent tk)
-        blockNoSource   <- make defaultTestConfig noSource
-        blockWithSource <- make defaultTestConfig withSource
+        blockNoSource   <- runPlotM defaultTestConfig $ make noSource
+        blockWithSource <- runPlotM defaultTestConfig $ make withSource
 
         -- In the case where source=false, the caption is used verbatim.
         -- Otherwise, links will be appended to the caption; hence, the caption
@@ -148,7 +148,7 @@             let cb = addDirectory tempDir 
                         $ addSaveFormat PNG
                         $ codeBlock tk (trivialContent tk)
-            _ <- make config cb
+            _ <- runPlotM config $ make cb
 
             numberPngFiles <-
                 length <$> filter (isExtensionOf (extension PNG)) <$>
@@ -175,7 +175,7 @@                     $ addCaption "**caption**" 
                     $ codeBlock tk (trivialContent tk)
             fmt = B.Format "markdown"
-        result <- make (defaultTestConfig {captionFormat=fmt}) cb
+        result <- runPlotM (defaultTestConfig {captionFormat=fmt}) $ make cb
         assertIsInfix expected (extractCaption result)
     where
         extractCaption (B.Para blocks) = extractImageCaption . head $ blocks
@@ -200,7 +200,7 @@                     $ addCaption "[title](https://google.com)" 
                     $ codeBlock tk (trivialContent tk)
             fmt = B.Format "markdown"
-        result <- make (defaultTestConfig {captionFormat=fmt}) cb
+        result <- runPlotM (defaultTestConfig {captionFormat=fmt}) $ make cb
         assertIsInfix expected (extractCaption result)
     where
         extractCaption (B.Para blocks) = extractImageCaption . head $ blocks
@@ -222,7 +222,7 @@         let cb = addDirectory tempDir
                     $ codeBlock tk (trivialContent tk)
         
-        result <- make defaultTestConfig cb
+        result <- runPlotM defaultTestConfig $ make cb
         cleanedDirs <- cleanOutputDirs defaultTestConfig cb
 
         assertEqual "" [tempDir] cleanedDirs
@@ -244,7 +244,7 @@             ensureDirectoryExistsAndEmpty tempDir
 
             let cb = addDirectory tempDir $ codeBlock Matplotlib "plt.show()"
-            result <- makeEither defaultTestConfig cb
+            result <- runPlotM defaultTestConfig $ makeEither cb
             let expectedCheck :: Either PandocPlotError a -> Bool
                 expectedCheck (Left (ScriptChecksFailedError _)) = True
                 expectedCheck _ = False