{-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE Unsafe #-} {-| Module : Text.Pandoc.Filter.Scripting Copyright : (c) Laurent P René de Cotret, 2019 License : MIT Maintainer : laurent.decotret@outlook.com Stability : internal Portability : portable This module defines types and functions that help with running Python scripts. -} module Text.Pandoc.Filter.Scripting ( runTempPythonScript , hasBlockingShowCall , PythonScript , ScriptResult(..) ) where import Data.Text (Text) import qualified Data.Text as T import qualified Data.Text.IO as T import Data.Hashable (hash) import System.Exit (ExitCode (..)) import System.FilePath (()) import System.IO.Temp (getCanonicalTemporaryDirectory) import System.Process.Typed (runProcess, shell) import Data.Monoid (Any (..), (<>)) -- | String representation of a Python script type PythonScript = Text -- | Possible result of running a Python script data ScriptResult = ScriptSuccess | ScriptFailure Int -- | Take a python script in string form, write it in a temporary directory, -- then execute it. runTempPythonScript :: PythonScript -- ^ Content of the script -> IO ScriptResult -- ^ Result with exit code. runTempPythonScript script -- Write script to temporary directory -- We involve the script hash as a temporary filename -- so that there is never any collision = do scriptPath <- ( hashedPath) <$> getCanonicalTemporaryDirectory T.writeFile scriptPath script -- Execute script ec <- runProcess $ shell $ "python " <> (show scriptPath) case ec of ExitSuccess -> return ScriptSuccess ExitFailure code -> return $ ScriptFailure code where hashedPath = show . hash $ script -- | Detect the presence of a blocking show call, for example "plt.show()" hasBlockingShowCall :: PythonScript -> Bool hasBlockingShowCall script = anyOf [ "plt.show()" `elem` scriptLines , "pyplot.show()" `elem` scriptLines , "matplotlib.pyplot.show()" `elem` scriptLines ] where scriptLines = T.lines script anyOf xs = getAny $ mconcat $ Any <$> xs