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 +34/−18
- examples/Simple.hs +148/−0
- ipython-kernel.cabal +17/−2
- src/IHaskell/IPython/EasyKernel.hs +67/−71
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: "