packages feed

ipython-kernel 0.7.0.0 → 0.8.0.0

raw patch · 4 files changed

+266/−91 lines, 4 filesdep +processdep +temporarydep −tarnew-component:exe:fun-calc-example

Dependencies added: process, temporary

Dependencies removed: tar

Files

examples/Calc.hs view
@@ -16,7 +16,7 @@ import qualified Data.Text as T  import           IHaskell.IPython.Kernel-import           IHaskell.IPython.EasyKernel (installProfile, easyKernel, KernelConfig(..))+import           IHaskell.IPython.EasyKernel (installKernelspec, easyKernel, KernelConfig(..))  import           System.Environment (getArgs) import           System.FilePath ((</>))@@ -106,12 +106,15 @@ parse :: String -> Either ParseError Razor parse = runParser expr () "(input)" --- -------------------- Language operations -------------------- | Completion-langCompletion :: T.Text -> T.Text -> Int -> Maybe ([T.Text], T.Text, T.Text)-langCompletion _code line col =-  let (before, _) = T.splitAt col line-  in fmap (\word -> (map T.pack . matchesFor $ T.unpack word, word, word))-       (lastMaybe (T.words before))+-------------------- Language operations -------------------- +--+-- | Completion+langCompletion :: Monad m => T.Text -> Int -> m (T.Text, [T.Text])+langCompletion code pos = return $+  let (before, _) = T.splitAt pos code+  in case lastMaybe (T.words before) of+    Nothing   -> ("", [])+    Just word -> (word, map T.pack . matchesFor $ T.unpack word)   where     lastMaybe :: [a] -> Maybe a     lastMaybe [] = Nothing@@ -122,8 +125,8 @@     available = ["sleep", "then", "end", "count"] ++ map show [(-1000 :: Int) .. 1000]  -- | Documentation lookup-langInfo :: T.Text -> Maybe (T.Text, T.Text, T.Text)-langInfo obj =+langInfo :: Monad m => T.Text -> Int -> m (Maybe [DisplayData])+langInfo code pos = return $ toDisplay $   if | any (T.isPrefixOf obj) ["sleep", "then", "end"] -> Just (obj, sleepDocs, sleepType)      | T.isPrefixOf obj "count" -> Just (obj, countDocs, countType)      | obj == "+" -> Just (obj, plusDocs, plusType)@@ -133,6 +136,12 @@       , T.all isDigit y -> Just (obj, floatDocs obj, floatType)      | otherwise -> Nothing   where+    (before, _) = T.splitAt pos code+    obj = last $ T.words before++    toDisplay Nothing = Nothing+    toDisplay (Just (x, y, z)) = Just [DisplayData PlainText $ T.unlines [x, y, z]]+     sleepDocs = "sleep DURATION then VALUE end: sleep DURATION seconds, then eval VALUE"     sleepType = "sleep FLOAT then INT end"     plusDocs = "Perform addition"@@ -207,9 +216,17 @@ mkConfig :: MVar Integer -- ^ The internal state of the execution          -> KernelConfig IO [IntermediateEvalRes] (Either ParseError Integer) mkConfig var = KernelConfig-  { languageName = "expanded_huttons_razor"-  , languageVersion = [0, 1, 0]-  , profileSource = Just . (</> "calc_profile.tar") <$> Paths.getDataDir+  { kernelLanguageInfo = LanguageInfo+    { languageName = "expanded_huttons_razor"+    , languageVersion = "1.0.0"+    , languageFileExtension = ".txt"+    , languageCodeMirrorMode = "null"+    }+  , writeKernelspec = const $ return $ KernelSpec+    { kernelDisplayName = "Hutton's Razor"+    , kernelLanguage = "hutton"+    , kernelCommand = ["simple-calc-example", "kernel", "{connection_file}"]+    }   , displayResult = displayRes   , displayOutput = displayOut   , completion = langCompletion@@ -242,11 +259,10 @@   case args of     ["kernel", profileFile] ->       easyKernel profileFile (mkConfig val)-    ["setup"] -> do-      putStrLn "Installing profile..."-      installProfile (mkConfig val)+    ["install"] -> do+      putStrLn "Installing kernelspec..."+      installKernelspec (mkConfig val) False Nothing     _ -> 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"+      putStrLn "simple-calc-example install      -- set up the kernelspec"+      putStrLn "simple-calc-example kernel FILE  -- run a kernel with FILE for communication with the frontend"
+ examples/Simple.hs view
@@ -0,0 +1,148 @@+module Main where++import qualified Data.Text as T++import System.Environment (getArgs)++import Text.Parsec+import Text.Parsec.String++import IHaskell.IPython.EasyKernel (easyKernel, installKernelspec, KernelConfig(..))+import IHaskell.IPython.Types++-- Define the actual language!+data Expr = Plus Expr Expr+          | Minus Expr Expr+          | Times Expr Expr+          | Div Expr Expr+          | Exp Expr Expr+          | Val Int+  deriving (Show, Eq)++eval :: Expr -> Int+eval (Val i) = i+eval (Plus x y) = eval x + eval y+eval (Minus x y) = eval x - eval y+eval (Times x y) = eval x * eval y+eval (Div x y) = eval x `div` eval y+eval (Exp x y) = eval x ^ eval y++parseExpr :: String -> Either String Expr+parseExpr str =+  case parse expr "interactive" (filter (/= ' ') str) of+    Left err -> Left (show err)+    Right e  -> Right e+  where+    expr :: Parser Expr+    expr = val <|> op++    val :: Parser Expr+    val = Val <$> read <$> many1 (oneOf "0123456789")++    op :: Parser Expr+    op = do+      func <- choice $ map string $ ["plus", "minus", "times", "div", "exp"]+      char '('+      x <- expr+      char ','+      y <- expr+      char ')'+      return $ case func of+        "plus"  -> Plus x y+        "minus" -> Minus x y+        "times" -> Times x y+        "div"   -> Div x y+        "exp"   -> Exp x y++languageConfig :: LanguageInfo+languageConfig = LanguageInfo+  { languageName = "funcalc"+  , languageVersion = "1.0.0"+  , languageFileExtension = ".txt"+  , languageCodeMirrorMode = "null"+  }++languageKernelspec :: KernelSpec+languageKernelspec = KernelSpec+  { kernelDisplayName = "Calculator"+  , kernelLanguage = "calc"+  , kernelCommand = ["fun-calc-example", "kernel", "{connection_file}"]+  }++displayString :: String -> [DisplayData]+displayString str = [DisplayData PlainText (T.pack str)]++languageCompletion :: Monad m => T.Text -> Int -> m (T.Text, [T.Text])+languageCompletion code pos = return $ +  let (before, _) = T.splitAt pos code+      word = last $ T.words $ T.map replace before+  in (word, map T.pack $ matches $ T.unpack word)++  where+    matches :: String -> [String]+    matches word =+      case head word of+        'p' -> ["plus"]+        'm' -> ["minus"]+        'e' -> ["exp"]+        'd' -> ["div"]+        't' -> ["times"]++    replace :: Char -> Char+    replace '(' = ' '+    replace ')' = ' '+    replace ',' = ' '+    replace  x = x++languageInspect :: Monad m => T.Text -> Int -> m (Maybe [DisplayData])+languageInspect _ _ = return $+  Just+    [ DisplayData PlainText $ T.pack $+      unlines+        [ "We support five arithmetic functions:"+        , "   - plus  +"+        , "   - minus -"+        , "   - div   /"+        , "   - times *"+        , "   - exp   ^"+        , "Expressions are written as f(exp, exp)"+        ]+    ]++languageRun :: T.Text -> IO () -> (String -> IO ()) -> IO (String, ExecuteReplyStatus, String)+languageRun code init intermediate = do+  init+  let expr = parseExpr $ T.unpack code+  intermediate (show expr)++  return+    (case expr of+       Left err   -> err+       Right expr -> show (eval expr), IHaskell.IPython.Types.Ok, "")+++simpleConfig :: KernelConfig IO String String+simpleConfig = KernelConfig+  { kernelLanguageInfo = languageConfig+  , writeKernelspec = const $ return languageKernelspec+  , displayOutput = displayString+  , displayResult = displayString+  , completion = languageCompletion+  , inspectInfo = languageInspect+  , run = languageRun+  , debug = False+  }++main :: IO ()+main = do+  args <- getArgs+  case args of+    ["kernel", profileFile] ->+      easyKernel profileFile simpleConfig+    ["install"] -> do+      putStrLn "Installing kernelspec..."+      installKernelspec simpleConfig False Nothing+    _ -> do+      putStrLn "Usage:"+      putStrLn "fun-calc-example install      -- set up the kernelspec"+      putStrLn "fun-calc-example kernel FILE  -- run a kernel with FILE for communication with the frontend"
ipython-kernel.cabal view
@@ -1,5 +1,5 @@ name:                ipython-kernel-version:             0.7.0.0+version:             0.8.0.0 synopsis:            A library for creating kernels for IPython frontends  description:         ipython-kernel is a library for communicating with frontends for the interactive IPython framework. It is used extensively in IHaskell, the interactive Haskell environment.@@ -40,9 +40,10 @@                        cereal          >=0.3,                        containers      >=0.5,                        directory       >=1.1,+                       temporary       >=1.2,                        filepath        >=1.2,+                       process         >=1.1,                        mtl             >=2.1,-                       tar             >=0.4.0.1,                        text            >=0.11,                        transformers    >=0.3,                        uuid            >=1.3,@@ -53,6 +54,20 @@ executable simple-calc-example   hs-source-dirs: examples   main-is:        Calc.hs+  build-depends:  ipython-kernel,+                  base           >=4.6 && <4.9,+                  filepath       >=1.2,+                  mtl            >=2.1,+                  parsec         >=3.1,+                  text           >=0.11,+                  transformers   >=0.3++  if !flag(examples)+    buildable: False++executable fun-calc-example+  hs-source-dirs: examples+  main-is:        Simple.hs   build-depends:  ipython-kernel,                   base           >=4.6 && <4.9,                   filepath       >=1.2,
src/IHaskell/IPython/EasyKernel.hs view
@@ -9,46 +9,31 @@ -- a simple language that nevertheless has side effects, global state, and timing effects is -- included in the examples directory. ----- = Profiles To run your kernel, you will need an IPython profile that causes the frontend to run--- it. To generate a fresh profile, run the command------ > ipython profile create NAME------ This will create a fresh IPython profile in @~\/.ipython\/profile_NAME@. This profile must be--- modified in two ways:------ 1. It needs to run your kernel instead of the default ipython 2. It must have message signing--- turned off, because 'easyKernel' doesn't support it------ == Setting the executable To set the executable, modify the configuration object's--- @KernelManager.kernel_cmd@ property. For example:------ > c.KernelManager.kernel_cmd = ['my_kernel', '{connection_file}']+-- = Kernel Specs ----- Your own main should arrange to parse command line arguments such--- that the connection file is passed to easyKernel.+-- To run your kernel, you will need to install the kernelspec into the Jupyter namespace.+-- If your kernel name is `kernel`, you will need to run the command: ----- == Message signing--- To turn off message signing, use the following snippet:+-- > kernel install ----- > c.Session.key = b''--- > c.Session.keyfile = b''+-- This will inform Jupyter of the kernel so that it may be used. -- -- == Further profile improvements -- Consult the IPython documentation along with the generated profile -- source code for further configuration of the frontend, including -- syntax highlighting, logos, help text, and so forth.-module IHaskell.IPython.EasyKernel (easyKernel, installProfile, KernelConfig(..)) where+module IHaskell.IPython.EasyKernel (easyKernel, installKernelspec, KernelConfig(..)) where -import           Data.Aeson (decode)+import           Data.Aeson (decode, encode)  import qualified Data.ByteString.Lazy as BL -import qualified Codec.Archive.Tar as Tar+import           System.IO.Temp (withTempDirectory)+import           System.Process (rawSystem)  import           Control.Concurrent (MVar, readChan, writeChan, newMVar, readMVar, modifyMVar_) import           Control.Monad.IO.Class (MonadIO(..))-import           Control.Monad (forever, when, unless)+import           Control.Monad (forever, when, unless, void)  import qualified Data.Map as Map import           Data.Maybe (fromMaybe)@@ -59,7 +44,7 @@ import           IHaskell.IPython.Types  import           System.Directory (createDirectoryIfMissing, doesDirectoryExist, doesFileExist,-                                   getHomeDirectory)+                                   getHomeDirectory, getTemporaryDirectory) import           System.FilePath ((</>)) import           System.Exit (exitSuccess) import           System.IO (openFile, IOMode(ReadMode))@@ -72,22 +57,20 @@          {           -- | Info on the language of the kernel.          kernelLanguageInfo :: LanguageInfo-         -- | 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@.-         , profileSource :: IO (Maybe FilePath)+         -- | Write all the files into the kernel directory, including `kernel.js`, `logo-64x64.png`, and any+         -- other required files. The directory to write to will be passed to this function, and the return+         -- value should be the kernelspec to be written to `kernel.json`.+         , writeKernelspec :: FilePath -> IO KernelSpec          -- | How to render intermediate output          , displayOutput :: output -> [DisplayData]          -- | How to render final cell results          , displayResult :: result -> [DisplayData]-         -- | Perform completion. The returned tuple consists of the matches, the matched text, and the-         -- completion text. The arguments are the code in the cell, the current line as text, and the column-         -- at which the cursor is placed.-         , completion :: T.Text -> T.Text -> Int -> Maybe ([T.Text], T.Text, T.Text)-         -- | Return the information or documentation for its argument. The returned tuple consists of the-         -- name, the documentation, and the type, respectively.-         , inspectInfo :: T.Text -> Maybe (T.Text, T.Text, T.Text)+         -- | Perform completion. The returned tuple consists of the matched text and completions. The+         -- arguments are the code in the cell and the position of the cursor in the cell.+         , completion :: T.Text -> Int -> m (T.Text, [T.Text])+         -- | Return the information or documentation for its argument, described by the cell contents and+         -- cursor position. The returned value is simply the data to display.+         , inspectInfo :: T.Text -> Int -> m (Maybe [DisplayData])          -- | Execute a cell. The arguments are the contents of the cell, an IO action that will clear the          -- current intermediate output, and an IO action that will add a new item to the intermediate          -- output. The result consists of the actual result, the status to be sent to IPython, and the@@ -97,34 +80,30 @@          , debug :: Bool -- ^ Whether to print extra debugging information to          } --- 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-  unless 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"+-- Install the kernelspec, using the `writeKernelspec` field of the kernel configuration.+installKernelspec :: MonadIO m+                  => KernelConfig m output result -- ^ Kernel configuration to install+                  -> Bool                         -- ^ Whether to use Jupyter `--replace`+                  -> Maybe FilePath               -- ^ (Optional) prefix to install into for Jupyter `--prefix`+                  -> m ()+installKernelspec config replace installPrefixMay =+  liftIO $ withTmpDir $ \tmp -> do+    let kernelDir = tmp </> languageName (kernelLanguageInfo config)+    createDirectoryIfMissing True kernelDir+    kernelSpec <- writeKernelspec config kernelDir +    let filename = kernelDir </> "kernel.json"+    BL.writeFile filename $ encode $ toJSON kernelSpec++    let replaceFlag = ["--replace" | replace]+        installPrefixFlag = maybe ["--user"] (\prefix -> ["--prefix", prefix]) installPrefixMay+        cmd = concat [["kernelspec", "install"], installPrefixFlag, [kernelDir], replaceFlag]+    void $ rawSystem "ipython" cmd+   where-    profDir = do-      home <- liftIO getHomeDirectory-      return $ home </> ".ipython" </> ("profile_" ++ languageName (kernelLanguageInfo config))-    isInstalled = do-      prof <- profDir-      dirThere <- liftIO $ doesDirectoryExist prof-      isProf <- liftIO . doesFileExist $ prof </> "ipython_config.py"-      return $ dirThere && isProf+    withTmpDir act = do+      tmp <- getTemporaryDirectory+      withTempDirectory tmp "easyKernel" act  getProfile :: FilePath -> IO Profile getProfile fn = do@@ -204,10 +183,12 @@                                       sendOutput x =                                                       send $ PublishDisplayData                                                                outputHeader-                                                               (languageName $ kernelLanguageInfo config)+                                                               (languageName $ kernelLanguageInfo+                                                                                 config)                                                                (displayOutput config x)                                   in run config code clearOutput sendOutput-  liftIO . send $ PublishDisplayData outputHeader (languageName $ kernelLanguageInfo config) (displayResult config res)+  liftIO . send $ PublishDisplayData outputHeader (languageName $ kernelLanguageInfo config)+                    (displayResult config res)     idleHeader <- dupHeader replyHeader StatusMessage@@ -224,12 +205,27 @@       , status = replyStatus       } -replyTo config _ _ req@CompleteRequest{} replyHeader =-  -- TODO: FIX-  error "Completion: Unimplemented for IPython 3.0"+replyTo config _ _ req@CompleteRequest{} replyHeader = do+  let code = getCode req+      pos = getCursorPos req+  (matchedText, completions) <- completion config code pos -replyTo _ _ _ InspectRequest{} _ =-  error "Inspection: Unimplemented for IPython 3.0"+  let start = pos - T.length matchedText+      end = pos+      reply = CompleteReply replyHeader completions start end Map.empty True+  return reply++replyTo config _ _ req@InspectRequest{} replyHeader = do+  result <- inspectInfo config (inspectCode req) (inspectCursorPos req)+  let reply =+        case result of+          Just datas -> InspectReply+            { header = replyHeader+            , inspectStatus = True+            , inspectData = datas+            }+          _ -> InspectReply { header = replyHeader, inspectStatus = False, inspectData = [] }+  return reply  replyTo _ _ _ msg _ = do   liftIO $ putStrLn "Unknown message: "