summaryrefslogtreecommitdiff
path: root/src/Text/Pandoc/Filter/Pyplot/Scripting.hs
blob: d867363246cc85748ce732bae009ea17f1b3a5ad (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
{-# LANGUAGE OverloadedStrings #-}

{-|
Module      : Text.Pandoc.Filter.Pyplot.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.Pyplot.Scripting
    ( runTempPythonScript
    , hasBlockingShowCall
    ) where

import           Data.Hashable        (hash)
import           Data.Monoid          (Any(..))
import qualified Data.Text            as T
import qualified Data.Text.IO         as T

import           System.Exit          (ExitCode (..))
import           System.FilePath      ((</>))
import           System.IO.Temp       (getCanonicalTemporaryDirectory)
import           System.Process.Typed (runProcess, shell)

import           Text.Pandoc.Filter.Pyplot.Types

-- | Take a python script in string form, write it in a temporary directory,
-- then execute it.
runTempPythonScript :: String          -- ^ Interpreter (e.g. "python" or "python35")
                    -> PythonScript    -- ^ Content of the script
                    -> IO ScriptResult -- ^ Result with exit code.
runTempPythonScript interpreter' script' = do
    -- We involve the script hash as a temporary filename
    -- so that there is never any collision
    scriptPath <- (</> hashedPath) <$> getCanonicalTemporaryDirectory
    T.writeFile scriptPath script'

    ec <- runProcess $ shell $ mconcat [interpreter', " ", 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