From b4f54c190b3d1b0042d472f3440b8e069e4181f6 Mon Sep 17 00:00:00 2001 From: David Raymond Christiansen Date: Mon, 12 Jan 2015 17:39:29 -0800 Subject: [PATCH] Support installing profile Now, EasyKernel supports installing a .tar file containing it's ipython profile, which includes information about how to launch the executable. --- ipython-kernel/example-data/calc_profile.tar | Bin 0 -> 168448 bytes ipython-kernel/examples/Calc.hs | 35 +++++++++---- ipython-kernel/ipython-kernel.cabal | 14 ++++- .../src/IHaskell/IPython/EasyKernel.hs | 48 ++++++++++++++++-- 4 files changed, 81 insertions(+), 16 deletions(-) create mode 100644 ipython-kernel/example-data/calc_profile.tar diff --git a/ipython-kernel/example-data/calc_profile.tar b/ipython-kernel/example-data/calc_profile.tar new file mode 100644 index 0000000000000000000000000000000000000000..65c10475c3045c9dfc4cd98bde29a47687685c60 GIT binary patch literal 168448 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 literal 0 HcmV?d00001 diff --git a/ipython-kernel/examples/Calc.hs b/ipython-kernel/examples/Calc.hs index ba4cf1f8..a7bd11e2 100644 --- a/ipython-kernel/examples/Calc.hs +++ b/ipython-kernel/examples/Calc.hs @@ -15,13 +15,16 @@ import Data.Monoid ((<>)) import qualified Data.Text as T import IHaskell.IPython.Kernel -import IHaskell.IPython.EasyKernel (easyKernel, KernelConfig(..)) +import IHaskell.IPython.EasyKernel (installProfile, easyKernel, KernelConfig(..)) import System.Environment (getArgs) +import System.FilePath (()) import Text.Parsec (Parsec, ParseError, alphaNum, char, letter, oneOf, optionMaybe, runParser, ()) import qualified Text.Parsec.Token as P +import qualified Paths_ipython_kernel as Paths + --------------------------------------------------------- -- Hutton's Razor, plus time delays, plus a global state --------------------------------------------------------- @@ -203,14 +206,15 @@ execRazor val Count clear send = do mkConfig :: MVar Integer -- ^ The internal state of the execution -> KernelConfig IO [IntermediateEvalRes] (Either ParseError Integer) mkConfig var = KernelConfig - { languageName = "Hutton's Razor + extra" - , languageVersion = [0,1,0] - , displayResult = displayRes - , displayOutput = displayOut - , completion = langCompletion - , objectInfo = langInfo - , run = parseAndRun - , debug = False + { languageName = "expanded_huttons_razor" + , languageVersion = [0,1,0] + , profileSource = Just . ( "calc_profile.tar") <$> Paths.getDataDir + , displayResult = displayRes + , displayOutput = displayOut + , completion = langCompletion + , objectInfo = langInfo + , run = parseAndRun + , debug = False } where displayRes (Left err) = @@ -231,6 +235,15 @@ mkConfig var = KernelConfig return (Right res, Ok, T.unpack pager) main :: IO () -main = do ["kernel", profileFile] <- getArgs +main = do args <- getArgs val <- newMVar 1 - easyKernel profileFile (mkConfig val) + case args of + ["kernel", profileFile] -> + easyKernel profileFile (mkConfig val) + ["setup"] -> do + putStrLn "Installing profile..." + installProfile (mkConfig val) + _ -> do + putStrLn "Usage:" + putStrLn "simple-calc-example setup -- set up the profile" + putStrLn "simple-calc-example kernel FILE -- run a kernel with FILE for communication with the frontend" diff --git a/ipython-kernel/ipython-kernel.cabal b/ipython-kernel/ipython-kernel.cabal index 7ca7ccfb..19f3dbe3 100644 --- a/ipython-kernel/ipython-kernel.cabal +++ b/ipython-kernel/ipython-kernel.cabal @@ -14,10 +14,15 @@ build-type: Simple cabal-version: >=1.16 +data-dir: example-data +data-files: calc_profile.tar + + flag examples description: Build example programs default: False + library exposed-modules: IHaskell.IPython.Kernel IHaskell.IPython.Types @@ -36,22 +41,27 @@ library bytestring >=0.10, cereal >=0.3, containers >=0.5, + directory >=1.1, + filepath >=1.2, mtl >=2.1, + tar >=0.4.0.1, text >=0.11, transformers >=0.3, unix >=2.6, uuid >=1.3, zeromq4-haskell >=0.1 + -- Example program executable simple-calc-example hs-source-dirs: examples main-is: Calc.hs build-depends: ipython-kernel, - base >=4.6 && < 4.8, + base >=4.6 && <4.8, + filepath >=1.2, mtl >=2.1, parsec >=3.1, - text >= 0.11, + text >=0.11, transformers >=0.3 if !flag(examples) diff --git a/ipython-kernel/src/IHaskell/IPython/EasyKernel.hs b/ipython-kernel/src/IHaskell/IPython/EasyKernel.hs index b5b50f70..e17df8dd 100644 --- a/ipython-kernel/src/IHaskell/IPython/EasyKernel.hs +++ b/ipython-kernel/src/IHaskell/IPython/EasyKernel.hs @@ -45,12 +45,14 @@ -- source code for further configuration of the frontend, including -- syntax highlighting, logos, help text, and so forth. -module IHaskell.IPython.EasyKernel (easyKernel, KernelConfig(..)) where +module IHaskell.IPython.EasyKernel (easyKernel, installProfile, KernelConfig(..)) where import Data.Aeson (decode) import qualified Data.ByteString.Lazy as BL +import qualified Codec.Archive.Tar as Tar + import Control.Concurrent (MVar, readChan, writeChan, newMVar, readMVar, modifyMVar_) import Control.Monad.IO.Class (MonadIO(..)) import Control.Monad (forever, when) @@ -62,7 +64,8 @@ import qualified Data.Text as T import IHaskell.IPython.Kernel import IHaskell.IPython.Message.UUID as UUID - +import System.Directory (createDirectoryIfMissing, doesDirectoryExist, doesFileExist, getHomeDirectory) +import System.FilePath (()) import System.Exit (exitSuccess) import System.IO (openFile, IOMode(ReadMode)) @@ -71,8 +74,18 @@ import System.IO (openFile, IOMode(ReadMode)) -- your kernel will run, the type of intermediate outputs from running -- cells, and the type of final results of cells, respectively. data KernelConfig m output result = KernelConfig - { languageName :: String -- ^ The name of the language + { languageName :: String + -- ^ The name of the language. This field is used to calculate + -- the name of the profile, so it should contain characters that + -- are reasonable to have in file names. , languageVersion :: [Int] -- ^ The version of the language + , profileSource :: IO (Maybe FilePath) + -- ^ Determine the source of a profile to install using + -- 'installProfile'. The source should be a tarball whose contents + -- will be unpacked directly into the profile directory. For + -- example, the file whose name is @ipython_config.py@ in the + -- tar file for a language named @lang@ will end up in + -- @~/.ipython/profile_lang/ipython_config.py@. , displayOutput :: output -> [DisplayData] -- ^ How to render intermediate output , displayResult :: result -> [DisplayData] -- ^ How to render final cell results , completion :: T.Text -> T.Text -> Int -> Maybe ([T.Text], T.Text, T.Text) @@ -97,6 +110,35 @@ data KernelConfig m output result = KernelConfig -- the console } +-- | Attempt to install the IPython profile from the .tar file +-- indicated by the 'profileSource' field of the configuration, if it +-- is not already installed. +installProfile :: MonadIO m => KernelConfig m output result -> m () +installProfile config = do + installed <- isInstalled + when (not installed) $ do + profSrc <- liftIO $ profileSource config + case profSrc of + Nothing -> liftIO (putStrLn "No IPython profile is installed or specified") + Just tar -> do + profExists <- liftIO $ doesFileExist tar + profTgt <- profDir + if profExists + then do liftIO $ createDirectoryIfMissing True profTgt + liftIO $ Tar.extract profTgt tar + else liftIO . putStrLn $ + "The supplied profile source '" ++ tar ++ "' does not exist" + + where + profDir = do + home <- liftIO getHomeDirectory + return $ home ".ipython" ("profile_" ++ languageName config) + isInstalled = do + prof <- profDir + dirThere <- liftIO $ doesDirectoryExist prof + isProf <- liftIO . doesFileExist $ prof "ipython_config.py" + return $ dirThere && isProf + getProfile :: FilePath -> IO Profile getProfile fn = do profData <- openFile fn ReadMode >>= BL.hGetContents