ihaskell 0.2.0.3 → 0.2.0.4
raw patch · 8 files changed
+296/−85 lines, 8 filesdep +cmdargsdep −basic-preludedep ~MissingHdep ~aesondep ~bytestring
Dependencies added: cmdargs
Dependencies removed: basic-prelude
Dependency ranges changed: MissingH, aeson, bytestring, classy-prelude, containers, mtl, random, shelly, strict, text, zeromq3-haskell
Files
- IHaskell/Eval/Evaluate.hs +10/−3
- IHaskell/Eval/Parser.hs +1/−1
- IHaskell/IPython.hs +76/−14
- IHaskell/Types.hs +8/−0
- IHaskell/ZeroMQ.hs +3/−3
- Main.hs +162/−28
- ihaskell.cabal +36/−36
- profile/profile.tar binary
IHaskell/Eval/Evaluate.hs view
@@ -440,9 +440,16 @@ -- Do not display any output return [] -evalCommand _ (ParseError loc err) = wrapExecution $ do- write $ "Parse Error."- return $ displayError $ formatParseError loc err+evalCommand _ (TypeSignature sig) = wrapExecution $+ -- We purposefully treat this as a "success" because that way execution+ -- continues. Empty type signatures are likely due to a parse error later+ -- on, and we want that to be displayed.+ return $ displayError $ "The type signature " ++ sig +++ "\nlacks an accompanying binding."++evalCommand _ (ParseError loc err) = do+ write "Parse Error."+ return (Failure, displayError $ formatParseError loc err) capturedStatement :: (String -> IO ()) -- ^ Function used to publish intermediate output. -> String -- ^ Statement to evaluate.
IHaskell/Eval/Parser.hs view
@@ -124,7 +124,7 @@ failures (_:rest) = failures rest bestError :: [(ErrMsg, LineNumber, ColumnNumber)] -> CodeBlock- bestError errors = ParseError (Loc line col) msg+ bestError errors = ParseError (Loc (line + startLine - 1) col) msg where (msg, line, col) = maximumBy compareLoc errors compareLoc (_, line1, col1) (_, line2, col2) = compare line1 line2 <> compare col1 col2
IHaskell/IPython.hs view
@@ -3,16 +3,17 @@ -- | Description : Shell scripting wrapper using @Shelly@ for the @notebook@, @setup@, and -- @console@ commands. module IHaskell.IPython (- runIHaskell,- setupIPythonProfile,- ipythonVersion,- parseVersion, ipythonInstalled,- installIPython+ installIPython,+ removeIPython,+ runConsole,+ runNotebook,+ readInitInfo,+ defaultConfFile, ) where import ClassyPrelude-import Prelude (read, reads)+import Prelude (read, reads, init) import Shelly hiding (find, trace, path) import System.Argv0 import System.Directory@@ -22,20 +23,25 @@ import Text.Printf import qualified System.IO.Strict as StrictIO- import qualified Paths_ihaskell as Paths import qualified Codec.Archive.Tar as Tar +import IHaskell.Types+ -- | Which commit of IPython we are on. ipythonCommit :: Text ipythonCommit = "1faf2f6e77fa31f4533e3edbe101c38ddf8943d8" +-- | The IPython profile name.+ipythonProfile :: String+ipythonProfile = "haskell"+ -- | Run IPython with any arguments. ipython :: Bool -- ^ Whether to suppress output. -> [Text] -- ^ IPython command line arguments. -> Sh String -- ^ IPython output. ipython suppress args = do- (_, ipythonDir) <- ihaskellDirs+ (_, ipythonDir, _) <- ihaskellDirs let ipythonPath = fromText $ ipythonDir ++ "/bin/ipython" sub $ do setenv "PYTHONPATH" $ ipythonDir ++ "/lib/python2.7/site-packages"@@ -56,21 +62,39 @@ nothing _ _ _ = return () -- | Return the data directory for IHaskell and the IPython subdirectory. -ihaskellDirs :: Sh (Text, Text)+ihaskellDirs :: Sh (Text, Text, Text) ihaskellDirs = do home <- maybe (error "$HOME not defined.") id <$> get_env "HOME" :: Sh Text let ihaskellDir = home ++ "/.ihaskell" ipythonDir = ihaskellDir ++ "/ipython"+ notebookDir = ihaskellDir ++ "/notebooks" -- Make sure the directories exist. mkdir_p $ fromText ipythonDir+ mkdir_p $ fromText notebookDir - return (ihaskellDir, ipythonDir)+ return (ihaskellDir, ipythonDir, notebookDir) +defaultConfFile :: IO (Maybe String)+defaultConfFile = shellyNoDir $ do+ (ihaskellDir, _, _) <- ihaskellDirs+ let filename = ihaskellDir ++ "/rc.hs"+ exists <- test_f $ fromText filename+ return $ if exists+ then Just $ unpack filename+ else Nothing++-- | Remove IPython so it can be reinstalled.+removeIPython :: IO ()+removeIPython = void . shellyNoDir $ do+ (ihaskellDir, _, _) <- ihaskellDirs+ cd $ fromText ihaskellDir+ rm_rf "ipython-src"+ -- | Install IPython from source. installIPython :: IO () installIPython = void . shellyNoDir $ do- (ihaskellDir, ipythonDir) <- ihaskellDirs+ (ihaskellDir, ipythonDir, _) <- ihaskellDirs -- Install all Python dependencies. pipPath <- path "pip"@@ -98,7 +122,7 @@ -- | Check whether IPython is properly installed. ipythonInstalled :: IO Bool ipythonInstalled = shellyNoDir $ do- (_, ipythonDir) <- ihaskellDirs+ (_, ipythonDir, _) <- ihaskellDirs let ipythonPath = ipythonDir ++ "/bin/ipython" test_f $ fromText ipythonPath @@ -131,8 +155,8 @@ runIHaskell :: String -- ^ IHaskell profile name. -> String -- ^ IPython app name. -> [String] -- ^ Arguments to IPython.- -> IO ()-runIHaskell profile app args = void . shellyNoDir $ do+ -> Sh ()+runIHaskell profile app args = void $ do -- Try to locate the profile. Do not die if it doesn't exist. errExit False $ ipython True ["locate", "profile", pack profile] @@ -145,6 +169,33 @@ -- Run the IHaskell command. ipython False $ map pack $ [app, "--profile", profile] ++ args +runConsole :: InitInfo -> IO ()+runConsole initInfo = void . shellyNoDir $ do+ writeInitInfo initInfo+ runIHaskell ipythonProfile "console" []++runNotebook :: InitInfo -> Maybe String -> IO ()+runNotebook initInfo maybeServeDir = void . shellyNoDir $ do+ (_, _, notebookDir) <- ihaskellDirs+ let args = case maybeServeDir of + Nothing -> ["--notebook-dir", unpack notebookDir]+ Just dir -> ["--notebook-dir", dir]++ writeInitInfo initInfo+ runIHaskell ipythonProfile "notebook" args++writeInitInfo :: InitInfo -> Sh ()+writeInitInfo info = do+ (ihaskellDir, _, _) <- ihaskellDirs+ let filename = fromText $ ihaskellDir ++ "/last-arguments"+ liftIO $ writeFile filename $ show info++readInitInfo :: IO InitInfo+readInitInfo = shellyNoDir $ do+ (ihaskellDir, _, _) <- ihaskellDirs+ let filename = fromText $ ihaskellDir ++ "/last-arguments"+ read <$> liftIO (readFile filename)+ -- | Create the IPython profile. setupIPythonProfile :: String -- ^ IHaskell profile name. -> IO ()@@ -164,6 +215,17 @@ copyProfile :: Text -> IO () copyProfile profileDir = do profileTar <- Paths.getDataFileName "profile/profile.tar"+ {-+ -- Load profile from Resources directory of Mac *.app.+ ihaskellPath <- shellyNoDir getIHaskellPath+ profileTar <- if "IHaskell.app/Contents/MacOS" `isInfixOf` ihaskellPath+ then+ let pieces = split "/" ihaskellPath+ pathPieces = init pieces ++ ["..", "Resources", "profile.tar"] in+ return $ intercalate "/" pathPieces+ else Paths.getDataFileName "profile/profile.tar"+ -}+ putStrLn $ pack $ "Loading profile from " ++ profileTar Tar.extract (unpack profileDir) profileTar -- | Insert the IHaskell path into the IPython configuration.
IHaskell/Types.hs view
@@ -15,6 +15,7 @@ MimeType(..), DisplayData(..), ExecuteReplyStatus(..),+ InitInfo(..), ) where import ClassyPrelude@@ -64,6 +65,13 @@ "iopub_port" .= iopubPort profile, "key" .= key profile ]++-- | Initialization information for the kernel.+data InitInfo = InitInfo {+ extensions :: [String], -- ^ Extensions to enable at start.+ initCells :: [String] -- ^ Code blocks to run before start.+ }+ deriving (Show, Read) -- | A message header with some metadata. data MessageHeader = MessageHeader {
IHaskell/ZeroMQ.hs view
@@ -9,9 +9,9 @@ serveProfile ) where -import BasicPrelude+import ClassyPrelude hiding (stdin) import Control.Concurrent-import System.ZMQ3+import System.ZMQ3 hiding (stdin) import Data.Aeson (encode) import qualified Data.ByteString.Lazy as ByteString@@ -71,7 +71,7 @@ serveSocket :: SocketType a => Context -> a -> Port -> (Socket a -> IO b) -> IO () serveSocket context socketType port action = void $ withSocket context socketType $ \socket -> do- bind socket $ textToString $ "tcp://127.0.0.1:" ++ show port+ bind socket $ unpack $ "tcp://127.0.0.1:" ++ show port forever $ action socket -- | Listener on the heartbeat port. Echoes back any data it was sent.
Main.hs view
@@ -5,11 +5,14 @@ -- Chans to communicate with the ZeroMQ sockets. module Main where import ClassyPrelude hiding (liftIO)+import Prelude (last) import Control.Concurrent.Chan+import Control.Concurrent (threadDelay) import Data.Aeson import Text.Printf import System.Exit (exitSuccess) import System.Directory+import System.Console.CmdArgs.Explicit hiding (complete) import qualified Data.Map as Map @@ -22,40 +25,160 @@ import qualified Data.ByteString.Char8 as Chars import IHaskell.IPython -import GHC+import GHC hiding (extensions) import Outputable (showSDoc, ppr) +-- All state stored in the kernel between executions. data KernelState = KernelState { getExecutionCounter :: Int } +-- Command line arguments to IHaskell. A set of aruments is annotated with+-- the mode being invoked.+data Args = Args IHaskellMode [Argument]++data Argument+ = ServeFrom String -- ^ Which directory to serve notebooks from.+ | Extension String -- ^ An extension to load at startup.+ | ConfFile String -- ^ A file with commands to load at startup.+ | Help -- ^ Display help text.+ deriving (Eq, Show)++-- Which mode IHaskell is being invoked in.+-- `None` means no mode was specified.+data IHaskellMode+ = None+ | Notebook+ | Console+ | UpdateIPython+ | Kernel (Maybe String)+ deriving (Eq, Show)+ main :: IO () main = do+ stringArgs <- map unpack <$> getArgs+ writeFile "/users/silver/bloop" $ show stringArgs+ case process ihaskellArgs stringArgs of+ Left errmsg -> putStrLn $ pack errmsg+ Right args ->+ ihaskell args++universalFlags :: [Flag Args]+universalFlags = [+ flagReq ["extension","e", "X"] (store Extension) "<ghc-extension>" "Extension to enable at start.",+ flagReq ["conf","c"] (store ConfFile) "<file.hs>" "File with commands to execute at start.",+ flagHelpSimple (add Help)+ ]+ where + add flag (Args mode flags) = Args mode $ flag : flags++store :: (String -> Argument) -> String -> Args -> Either String Args+store constructor str (Args mode prev) = Right $ Args mode $ constructor str : prev++notebook :: Mode Args+notebook = mode "notebook" (Args Notebook []) "Browser-based notebook interface." noArgs $+ flagReq ["serve","s"] (store ServeFrom) "<dir>" "Directory to serve notebooks from.":+ universalFlags++console :: Mode Args+console = mode "console" (Args Console []) "Console-based interactive repl." noArgs universalFlags++kernel = mode "kernel" (Args (Kernel Nothing) []) "Invoke the IHaskell kernel." kernelArg []+ where+ kernelArg = flagArg update "<json-kernel-file>"+ update filename (Args _ flags) = Right $ Args (Kernel $ Just filename) flags++update :: Mode Args+update = mode "update" (Args UpdateIPython []) "Update IPython frontends." noArgs []++ihaskellArgs :: Mode Args+ihaskellArgs = (modeEmpty $ Args None []) { modeGroupModes = toGroup [console, notebook, update, kernel] }++noArgs = flagArg unexpected ""+ where+ unexpected a = error $ "Unexpected argument: " ++ a++ihaskell :: Args -> IO ()+-- If no mode is specified, print help text.+ihaskell (Args None _) = + print $ helpText [] HelpFormatAll ihaskellArgs++-- Update IPython: remove then reinstall.+-- This is in case cabal updates IHaskell but the corresponding IPython+-- isn't updated. This is hard to detect since versions of IPython might+-- not change!+ihaskell (Args UpdateIPython _) = do+ removeIPython+ installIPython+ putStrLn "IPython updated."+ +ihaskell (Args Console flags) = showingHelp Console flags $ do installed <- ipythonInstalled unless installed installIPython - args <- map unpack <$> getArgs- case args of- -- Create the "haskell" profile.- ["setup"] -> setupIPythonProfile "haskell"+ flags <- addDefaultConfFile flags+ info <- initInfo flags+ runConsole info - -- Run the ipython <cmd> --profile haskell <args> command.- "notebook":ipythonArgs -> runIHaskell "haskell" "notebook" ipythonArgs- "console":ipythonArgs -> runIHaskell "haskell" "console" ipythonArgs+ihaskell (Args Notebook flags) = showingHelp Notebook flags $ do+ installed <- ipythonInstalled+ unless installed installIPython - -- Read the profile JSON file from the argument list.- ["kernel", profileSrc] -> kernel profileSrc+ let server = case mapMaybe serveDir flags of+ [] -> Nothing+ xs -> Just $ last xs - -- Bad arguments.- [] -> putStrLn $ "Provide command to run ('setup', 'kernel <profile-file.json>', " ++- "'notebook [args]', 'console [args]')."- cmd:_ -> putStrLn $ "Unknown command: " ++ pack cmd+ flags <- addDefaultConfFile flags+ info <- initInfo flags+ runNotebook info server+ where+ serveDir (ServeFrom dir) = Just dir+ serveDir _ = Nothing +ihaskell (Args (Kernel (Just filename)) _) = do+ initInfo <- readInitInfo+ runKernel filename initInfo +-- | Add a conf file to the arguments if none exists.+addDefaultConfFile :: [Argument] -> IO [Argument]+addDefaultConfFile flags = do+ def <- defaultConfFile+ case (find isConfFile flags, def) of+ (Nothing, Just file) -> return $ ConfFile file : flags+ _ -> return flags+ where+ isConfFile (ConfFile _) = True+ isConfFile _ = False++showingHelp :: IHaskellMode -> [Argument] -> IO () -> IO ()+showingHelp mode flags act =+ case find (==Help) flags of+ Just _ ->+ print $ helpText [] HelpFormatAll $ chooseMode mode+ Nothing ->+ act+ where+ chooseMode Console = console+ chooseMode Notebook = notebook+ chooseMode (Kernel _) = kernel+ chooseMode UpdateIPython = update+ +-- | Parse initialization information from the flags.+initInfo :: [Argument] -> IO InitInfo+initInfo [] = return InitInfo { extensions = [], initCells = []}+initInfo (flag:flags) = do+ info <- initInfo flags+ case flag of+ Extension ext -> return info { extensions = ext:extensions info }+ ConfFile filename -> do+ cell <- readFile (fpFromText $ pack filename)+ return info { initCells = cell:initCells info }+ -- | Run the IHaskell language kernel.-kernel :: String -- ^ Filename of profile JSON file.- -> IO ()-kernel profileSrc = do+runKernel :: String -- ^ Filename of profile JSON file.+ -> InitInfo -- ^ Initialization information from the invocation.+ -> IO ()+runKernel profileSrc initInfo = do -- Switch to a temporary directory so that any files we create aren't -- visible. On Unix, this is usually /tmp. If there is no temporary -- directory available, just stay in the current one and ignore the@@ -71,20 +194,31 @@ state <- initialKernelState -- Receive and reply to all messages on the shell socket.- interpret $ forever $ do- -- Read the request from the request channel.- request <- liftIO $ readChan $ shellRequestChannel interface+ interpret $ do+ -- Initialize the context by evaluating everything we got from the + -- command line flags. This includes enabling some extensions and also+ -- running some code.+ let extLines = map (":extension " ++) $ extensions initInfo+ noPublish _ _ = return () + zero = 0 -- To please hlint+ evaluator line = evaluate zero line noPublish+ mapM_ evaluator extLines+ mapM_ evaluator $ initCells initInfo - -- Create a header for the reply.- replyHeader <- createReplyHeader (header request)+ forever $ do+ -- Read the request from the request channel.+ request <- liftIO $ readChan $ shellRequestChannel interface - -- Create the reply, possibly modifying kernel state.- oldState <- liftIO $ takeMVar state- (newState, reply) <- replyTo interface request replyHeader oldState - liftIO $ putMVar state newState+ -- Create a header for the reply.+ replyHeader <- createReplyHeader (header request) - -- Write the reply to the reply channel.- liftIO $ writeChan (shellReplyChannel interface) reply+ -- Create the reply, possibly modifying kernel state.+ oldState <- liftIO $ takeMVar state+ (newState, reply) <- replyTo interface request replyHeader oldState + liftIO $ putMVar state newState++ -- Write the reply to the reply channel.+ liftIO $ writeChan (shellReplyChannel interface) reply -- Initial kernel state. initialKernelState :: IO (MVar KernelState)
ihaskell.cabal view
@@ -7,7 +7,7 @@ -- PVP summary: +-+------- breaking API changes -- | | +----- non-breaking API additions -- | | | +--- code changes with no API change-version: 0.2.0.3+version: 0.2.0.4 -- A short (one-line) description of the package. synopsis: A Haskell backend kernel for the IPython project.@@ -47,32 +47,32 @@ library build-depends: base ==4.6.*,+ cmdargs >= 0.10, tar, ghc-parser, unix >= 2.6, hspec,- zeromq3-haskell ==0.5.*,- aeson ==0.6.*,- MissingH ==1.2.*,- basic-prelude ==0.3.*,+ zeromq3-haskell >=0.5,+ aeson >=0.6,+ MissingH >=1.2, classy-prelude >=0.6,- bytestring ==0.10.*,+ bytestring >=0.10, uuid >=1.2.6,- containers ==0.5.*,+ containers >=0.5, ghc ==7.6.*, ghc-paths ==0.1.*,- random ==1.0.*,+ random >=1.0, split >= 0.2, utf8-string,- strict ==0.3.*,- shelly ==1.3.*,+ strict >=0.3,+ shelly >=1.3, system-argv0, directory, here, system-filepath, cereal ==0.3.*,- text ==0.11.*,- mtl == 2.1.*,+ text >=0.11,+ mtl >= 2.1, template-haskell exposed-modules: IHaskell.Display, Paths_ihaskell,@@ -104,32 +104,32 @@ -- Other library packages from which modules are imported. build-depends: base ==4.6.*,+ cmdargs >= 0.10, tar, ghc-parser, unix >= 2.6, hspec,- zeromq3-haskell ==0.5.*,- aeson ==0.6.*,- MissingH ==1.2.*,- basic-prelude ==0.3.*,- classy-prelude ==0.6.*,- bytestring ==0.10.*,+ zeromq3-haskell >=0.5,+ aeson >=0.6,+ MissingH >=1.2,+ classy-prelude >=0.6,+ bytestring >=0.10, uuid >=1.2.6,- containers ==0.5.*,+ containers >=0.5, ghc ==7.6.*, ghc-paths ==0.1.*,- random ==1.0.*,+ random >=1.0, split >= 0.2, utf8-string,- strict ==0.3.*,- shelly ==1.3.*,+ strict >=0.3,+ shelly >=1.3, system-argv0, directory, here, system-filepath, cereal ==0.3.*,- text ==0.11.*,- mtl == 2.1.*,+ text >=0.11,+ mtl >= 2.1, template-haskell Test-Suite hspec@@ -137,32 +137,32 @@ Ghc-Options: -threaded Main-Is: Hspec.hs build-depends: base ==4.6.*,+ cmdargs >= 0.10, tar, ghc-parser, unix >= 2.6, hspec,- zeromq3-haskell ==0.5.*,- aeson ==0.6.*,- MissingH ==1.2.*,- basic-prelude ==0.3.*,- classy-prelude ==0.6.*,- bytestring ==0.10.*,+ zeromq3-haskell >=0.5,+ aeson >=0.6,+ MissingH >=1.2,+ classy-prelude >=0.6,+ bytestring >=0.10, uuid >=1.2.6,- containers ==0.5.*,+ containers >=0.5, ghc ==7.6.*, ghc-paths ==0.1.*,- random ==1.0.*,+ random >=1.0, split >= 0.2, utf8-string,- strict ==0.3.*,- shelly ==1.3.*,+ strict >=0.3,+ shelly >=1.3, system-argv0, directory, here, system-filepath, cereal ==0.3.*,- text ==0.11.*,- mtl == 2.1.*,+ text >=0.11,+ mtl >= 2.1, template-haskell source-repository head
profile/profile.tar view
binary file changed (83968 → 83968 bytes)