Commit 9d7149c4 authored by Andrew Gibiansky's avatar Andrew Gibiansky

adding --ipython flag which you point to an executable

parent 2de9734b
...@@ -33,23 +33,8 @@ ...@@ -33,23 +33,8 @@
], ],
"language": "python", "language": "python",
"metadata": {}, "metadata": {},
"outputs": [ "outputs": [],
{ "prompt_number": "*"
"metadata": {},
"output_type": "display_data",
"text": [
"8"
]
},
{
"metadata": {},
"output_type": "display_data",
"text": [
"\"Hello, World!\""
]
}
],
"prompt_number": 1
}, },
{ {
"cell_type": "markdown", "cell_type": "markdown",
......
...@@ -16,11 +16,13 @@ import IHaskell.Types ...@@ -16,11 +16,13 @@ import IHaskell.Types
-- Command line arguments to IHaskell. A set of aruments is annotated with -- Command line arguments to IHaskell. A set of aruments is annotated with
-- the mode being invoked. -- the mode being invoked.
data Args = Args IHaskellMode [Argument] data Args = Args IHaskellMode [Argument]
deriving Show
data Argument data Argument
= ServeFrom String -- ^ Which directory to serve notebooks from. = ServeFrom String -- ^ Which directory to serve notebooks from.
| Extension String -- ^ An extension to load at startup. | Extension String -- ^ An extension to load at startup.
| ConfFile String -- ^ A file with commands to load at startup. | ConfFile String -- ^ A file with commands to load at startup.
| IPythonFrom String -- ^ Which executable to use for IPython.
| Help -- ^ Display help text. | Help -- ^ Display help text.
deriving (Eq, Show) deriving (Eq, Show)
...@@ -50,6 +52,10 @@ help mode = ...@@ -50,6 +52,10 @@ help mode =
chooseMode (Kernel _) = kernel chooseMode (Kernel _) = kernel
chooseMode UpdateIPython = update chooseMode UpdateIPython = update
ipythonFlag :: Flag Args
ipythonFlag =
flagReq ["ipython", "i"] (store IPythonFrom) "<path>" "Executable for IPython."
universalFlags :: [Flag Args] universalFlags :: [Flag Args]
universalFlags = [ universalFlags = [
flagReq ["extension","e", "X"] (store Extension) "<ghc-extension>" "Extension to enable at start.", flagReq ["extension","e", "X"] (store Extension) "<ghc-extension>" "Extension to enable at start.",
...@@ -65,10 +71,11 @@ store constructor str (Args mode prev) = Right $ Args mode $ constructor str : p ...@@ -65,10 +71,11 @@ store constructor str (Args mode prev) = Right $ Args mode $ constructor str : p
notebook :: Mode Args notebook :: Mode Args
notebook = mode "notebook" (Args Notebook []) "Browser-based notebook interface." noArgs $ notebook = mode "notebook" (Args Notebook []) "Browser-based notebook interface." noArgs $
flagReq ["serve","s"] (store ServeFrom) "<dir>" "Directory to serve notebooks from.": flagReq ["serve","s"] (store ServeFrom) "<dir>" "Directory to serve notebooks from.":
ipythonFlag:
universalFlags universalFlags
console :: Mode Args console :: Mode Args
console = mode "console" (Args Console []) "Console-based interactive repl." noArgs universalFlags console = mode "console" (Args Console []) "Console-based interactive repl." noArgs $ ipythonFlag:universalFlags
kernel = mode "kernel" (Args (Kernel Nothing) []) "Invoke the IHaskell kernel." kernelArg [] kernel = mode "kernel" (Args (Kernel Nothing) []) "Invoke the IHaskell kernel." kernelArg []
where where
...@@ -79,7 +86,9 @@ update :: Mode Args ...@@ -79,7 +86,9 @@ update :: Mode Args
update = mode "update" (Args UpdateIPython []) "Update IPython frontends." noArgs [] update = mode "update" (Args UpdateIPython []) "Update IPython frontends." noArgs []
view :: Mode Args view :: Mode Args
view = (modeEmpty $ Args (View Nothing Nothing) []) { view =
let empty = mode "view" (Args (View Nothing Nothing) []) "View IHaskell notebook." noArgs [ipythonFlag] in
empty {
modeNames = ["view"], modeNames = ["view"],
modeCheck = modeCheck =
\a@(Args (View fmt file) _) -> \a@(Args (View fmt file) _) ->
...@@ -93,7 +102,8 @@ view = (modeEmpty $ Args (View Nothing Nothing) []) { ...@@ -93,7 +102,8 @@ view = (modeEmpty $ Args (View Nothing Nothing) []) {
["pdf", "html", "ipynb", "markdown", "latex"]), ["pdf", "html", "ipynb", "markdown", "latex"]),
"." "."
], ],
modeArgs = ([formatArg, filenameArg], Nothing) modeArgs = ([formatArg, filenameArg] ++ fst (modeArgs empty),
snd $ modeArgs empty)
} }
where where
...@@ -105,7 +115,6 @@ view = (modeEmpty $ Args (View Nothing Nothing) []) { ...@@ -105,7 +115,6 @@ view = (modeEmpty $ Args (View Nothing Nothing) []) {
Nothing -> Left $ "Invalid format '" ++ fmtStr ++ "'." Nothing -> Left $ "Invalid format '" ++ fmtStr ++ "'."
updateFile name (Args (View f _) flags) = Right $ Args (View f (Just name)) flags updateFile name (Args (View f _) flags) = Right $ Args (View f (Just name)) flags
ihaskellArgs :: Mode Args ihaskellArgs :: Mode Args
ihaskellArgs = ihaskellArgs =
let descr = "Haskell for Interactive Computing." let descr = "Haskell for Interactive Computing."
......
...@@ -14,7 +14,9 @@ module IHaskell.IPython ( ...@@ -14,7 +14,9 @@ module IHaskell.IPython (
getIHaskellDir, getIHaskellDir,
getSandboxPackageConf, getSandboxPackageConf,
nbconvert, nbconvert,
subHome,
ViewFormat(..), ViewFormat(..),
WhichIPython(..),
) where ) where
import ClassyPrelude import ClassyPrelude
...@@ -33,6 +35,11 @@ import qualified Codec.Archive.Tar as Tar ...@@ -33,6 +35,11 @@ import qualified Codec.Archive.Tar as Tar
import IHaskell.Types import IHaskell.Types
-- | Which IPython to use.
data WhichIPython
= DefaultIPython -- ^ Use the one that IHaskell tries to install.
| ExplicitIPython String -- ^ Use the command-line flag provided one.
-- | Which commit of IPython we are on. -- | Which commit of IPython we are on.
ipythonCommit :: Text ipythonCommit :: Text
ipythonCommit = "9c922f54af799704f4000aeee94ec7c74cada194" ipythonCommit = "9c922f54af799704f4000aeee94ec7c74cada194"
...@@ -42,11 +49,12 @@ ipythonProfile :: String ...@@ -42,11 +49,12 @@ ipythonProfile :: String
ipythonProfile = "haskell" ipythonProfile = "haskell"
-- | Run IPython with any arguments. -- | Run IPython with any arguments.
ipython :: Bool -- ^ Whether to suppress output. ipython :: WhichIPython -- ^ Which IPython to use (user-provided or IHaskell-installed).
-> Bool -- ^ Whether to suppress output.
-> [Text] -- ^ IPython command line arguments. -> [Text] -- ^ IPython command line arguments.
-> Sh String -- ^ IPython output. -> Sh String -- ^ IPython output.
ipython suppress args = do ipython which suppress args = do
ipythonPath <- ipythonExePath ipythonPath <- ipythonExePath which
runHandles ipythonPath args handles doNothing runHandles ipythonPath args handles doNothing
where handles = [InHandle Inherit, outHandle suppress, errorHandle suppress] where handles = [InHandle Inherit, outHandle suppress, errorHandle suppress]
outHandle True = OutHandle CreatePipe outHandle True = OutHandle CreatePipe
...@@ -79,8 +87,11 @@ ihaskellDir = do ...@@ -79,8 +87,11 @@ ihaskellDir = do
ipythonDir :: Sh FilePath ipythonDir :: Sh FilePath
ipythonDir = ensure $ (</> "ipython") <$> ihaskellDir ipythonDir = ensure $ (</> "ipython") <$> ihaskellDir
ipythonExePath :: Sh FilePath ipythonExePath :: WhichIPython -> Sh FilePath
ipythonExePath = (</> ("bin" </> "ipython")) <$> ipythonDir ipythonExePath which =
case which of
DefaultIPython -> (</> ("bin" </> "ipython")) <$> ipythonDir
ExplicitIPython path -> return $ fromString path
notebookDir :: Sh FilePath notebookDir :: Sh FilePath
notebookDir = ensure $ (</> "notebooks") <$> ihaskellDir notebookDir = ensure $ (</> "notebooks") <$> ihaskellDir
...@@ -102,8 +113,8 @@ defaultConfFile = shellyNoDir $ do ...@@ -102,8 +113,8 @@ defaultConfFile = shellyNoDir $ do
-- | Find a notebook and then convert it into the provided format. -- | Find a notebook and then convert it into the provided format.
-- Notebooks are searched in the current directory as well as the IHaskell -- Notebooks are searched in the current directory as well as the IHaskell
-- notebook directory (in that order). -- notebook directory (in that order).
nbconvert :: ViewFormat -> String -> IO () nbconvert :: WhichIPython -> ViewFormat -> String -> IO ()
nbconvert fmt name = void . shellyNoDir $ do nbconvert which fmt name = void . shellyNoDir $ do
curdir <- pwd curdir <- pwd
nbdir <- notebookDir nbdir <- notebookDir
-- Find which of the options is available. -- Find which of the options is available.
...@@ -125,18 +136,30 @@ nbconvert fmt name = void . shellyNoDir $ do ...@@ -125,18 +136,30 @@ nbconvert fmt name = void . shellyNoDir $ do
Pdf -> ["--to=latex", "--post=pdf"] Pdf -> ["--to=latex", "--post=pdf"]
Html -> ["--to=html", "--template=ihaskell"] Html -> ["--to=html", "--template=ihaskell"]
fmt -> ["--to=" ++ show fmt] in fmt -> ["--to=" ++ show fmt] in
void $ runIHaskell ipythonProfile "nbconvert" $ viewArgs ++ [fpToString notebook] void $ runIHaskell which ipythonProfile "nbconvert" $ viewArgs ++ [fpToString notebook]
-- | Set up IPython properly. -- | Set up IPython properly.
setupIPython :: IO () setupIPython :: WhichIPython -> IO ()
setupIPython = do
setupIPython (ExplicitIPython path) = do
exists <- shellyNoDir $
test_f $ fromString path
unless exists $
fail $ "Cannot find IPython at " ++ path
setupIPython DefaultIPython = do
installed <- ipythonInstalled installed <- ipythonInstalled
if installed if installed
then updateIPython then updateIPython
else installIPython else installIPython
-- | Replace "~" with $HOME if $HOME is defined.
-- Otherwise, do nothing.
subHome :: String -> IO String
subHome path = shellyNoDir $ do
home <- unpack <$> fromMaybe "~" <$> get_env "HOME"
return $ replace "~" home path
-- | Update the IPython source tree and rebuild. -- | Update the IPython source tree and rebuild.
updateIPython :: IO () updateIPython :: IO ()
updateIPython = void . shellyNoDir $ do updateIPython = void . shellyNoDir $ do
...@@ -176,12 +199,11 @@ installPipDependencies = withTmpDir $ \tmpDir -> ...@@ -176,12 +199,11 @@ installPipDependencies = withTmpDir $ \tmpDir ->
[ [
("pyzmq", "14.0.1") ("pyzmq", "14.0.1")
, ("tornado","3.1.1") , ("tornado","3.1.1")
, ("jinja2","2.7.1") , ("Jinja2","2.7.1")
-- The following cannot go first in the dependency list, because -- The following cannot go first in the dependency list, because
-- their setup.py are broken and require the directory to exist -- their setup.py are broken and require the directory to exist
-- already. -- already.
, ("MarkupSafe", "0.18") , ("MarkupSafe", "0.18")
--, ("setuptools", "2.0.2")
] ]
where where
installDependency :: FilePath -> (Text, Text) -> Sh () installDependency :: FilePath -> (Text, Text) -> Sh ()
...@@ -195,6 +217,7 @@ installPipDependencies = withTmpDir $ \tmpDir -> ...@@ -195,6 +217,7 @@ installPipDependencies = withTmpDir $ \tmpDir ->
-- Download the package. -- Download the package.
let downloadOpt = "--download=" ++ fpToText tmpDir let downloadOpt = "--download=" ++ fpToText tmpDir
rm $ fpFromText $ versioned ++ ".tar.gz"
run_ pipPath ["install", downloadOpt, dep ++ "==" ++ version] run_ pipPath ["install", downloadOpt, dep ++ "==" ++ version]
-- Extract it. -- Extract it.
...@@ -232,14 +255,14 @@ buildIPython = do ...@@ -232,14 +255,14 @@ buildIPython = do
, "import sys" , "import sys"
, "sys.path = [\"" ++ fpToText ipyDir ++ , "sys.path = [\"" ++ fpToText ipyDir ++
"/lib/" ++ pythonVer ++ "/site-packages\"] + sys.path"] "/lib/" ++ pythonVer ++ "/site-packages\"] + sys.path"]
ipythonPath <- ipythonExePath ipythonPath <- ipythonExePath DefaultIPython
contents <- readFile ipythonPath contents <- readFile ipythonPath
writeFile ipythonPath $ unlines patchLines ++ "\n" ++ contents writeFile ipythonPath $ unlines patchLines ++ "\n" ++ contents
-- Remove the old IPython profile so that we write a new one in its -- Remove the old IPython profile so that we write a new one in its
-- place. Users are not expected to fiddle with the profile, so we give -- place. Users are not expected to fiddle with the profile, so we give
-- no warning whatsoever. This may be changed eventually. -- no warning whatsoever. This may be changed eventually.
removeIPythonProfile ipythonProfile removeIPythonProfile DefaultIPython ipythonProfile
-- | Attempt to guess the Python version. -- | Attempt to guess the Python version.
pythonVersion :: Sh Text pythonVersion :: Sh Text
...@@ -255,7 +278,7 @@ pythonVersion = do ...@@ -255,7 +278,7 @@ pythonVersion = do
-- | Check whether IPython is properly installed. -- | Check whether IPython is properly installed.
ipythonInstalled :: IO Bool ipythonInstalled :: IO Bool
ipythonInstalled = shellyNoDir $ do ipythonInstalled = shellyNoDir $ do
ipythonPath <- ipythonExePath ipythonPath <- ipythonExePath DefaultIPython
test_f ipythonPath test_f ipythonPath
-- | Get the path to an executable. If it doensn't exist, fail with an -- | Get the path to an executable. If it doensn't exist, fail with an
...@@ -269,13 +292,6 @@ path exe = do ...@@ -269,13 +292,6 @@ path exe = do
fail $ "`" ++ unpack exe ++ "` not on $PATH." fail $ "`" ++ unpack exe ++ "` not on $PATH."
Just exePath -> return exePath Just exePath -> return exePath
-- | Use the `ipython --version` command to figure out the version.
-- Return a tuple with (major, minor, patch).
ipythonVersion :: IO (Int, Int, Int)
ipythonVersion = shellyNoDir $ do
[major, minor, patch] <- parseVersion <$> ipython True ["--version"]
return (major, minor, patch)
-- | Parse an IPython version string into a list of integers. -- | Parse an IPython version string into a list of integers.
parseVersion :: String -> [Int] parseVersion :: String -> [Int]
parseVersion versionStr = map read' $ split "." versionStr parseVersion versionStr = map read' $ split "." versionStr
...@@ -284,13 +300,14 @@ parseVersion versionStr = map read' $ split "." versionStr ...@@ -284,13 +300,14 @@ parseVersion versionStr = map read' $ split "." versionStr
_ -> error $ "cannot parse version: "++ versionStr _ -> error $ "cannot parse version: "++ versionStr
-- | Run an IHaskell application using the given profile. -- | Run an IHaskell application using the given profile.
runIHaskell :: String -- ^ IHaskell profile name. runIHaskell :: WhichIPython
-> String -- ^ IHaskell profile name.
-> String -- ^ IPython app name. -> String -- ^ IPython app name.
-> [String] -- ^ Arguments to IPython. -> [String] -- ^ Arguments to IPython.
-> Sh () -> Sh ()
runIHaskell profile app args = void $ do runIHaskell which profile app args = void $ do
-- Try to locate the profile. Do not die if it doesn't exist. -- Try to locate the profile. Do not die if it doesn't exist.
errExit False $ ipython True ["locate", "profile", pack profile] errExit False $ ipython which True ["locate", "profile", pack profile]
-- If the profile doesn't exist, create it. -- If the profile doesn't exist, create it.
-- We have an ugly hack that removes the profile whenever the IPython -- We have an ugly hack that removes the profile whenever the IPython
...@@ -298,25 +315,25 @@ runIHaskell profile app args = void $ do ...@@ -298,25 +315,25 @@ runIHaskell profile app args = void $ do
exitCode <- lastExitCode exitCode <- lastExitCode
when (exitCode /= 0) $ liftIO $ do when (exitCode /= 0) $ liftIO $ do
putStrLn "Creating IPython profile." putStrLn "Creating IPython profile."
setupIPythonProfile profile setupIPythonProfile which profile
-- Run the IHaskell command. -- Run the IHaskell command.
ipython False $ map pack $ [app, "--profile", profile] ++ args ipython which False $ map pack $ [app, "--profile", profile] ++ args
runConsole :: InitInfo -> IO () runConsole :: WhichIPython -> InitInfo -> IO ()
runConsole initInfo = void . shellyNoDir $ do runConsole which initInfo = void . shellyNoDir $ do
writeInitInfo initInfo writeInitInfo initInfo
runIHaskell ipythonProfile "console" [] runIHaskell which ipythonProfile "console" []
runNotebook :: InitInfo -> Maybe String -> IO () runNotebook :: WhichIPython -> InitInfo -> Maybe String -> IO ()
runNotebook initInfo maybeServeDir = void . shellyNoDir $ do runNotebook which initInfo maybeServeDir = void . shellyNoDir $ do
notebookDirStr <- fpToString <$> notebookDir notebookDirStr <- fpToString <$> notebookDir
let args = case maybeServeDir of let args = case maybeServeDir of
Nothing -> ["--notebook-dir", unpack notebookDirStr] Nothing -> ["--notebook-dir", unpack notebookDirStr]
Just dir -> ["--notebook-dir", dir] Just dir -> ["--notebook-dir", dir]
writeInitInfo initInfo writeInitInfo initInfo
runIHaskell ipythonProfile "notebook" args runIHaskell which ipythonProfile "notebook" args
writeInitInfo :: InitInfo -> Sh () writeInitInfo :: InitInfo -> Sh ()
writeInitInfo info = do writeInitInfo info = do
...@@ -329,28 +346,29 @@ readInitInfo = shellyNoDir $ do ...@@ -329,28 +346,29 @@ readInitInfo = shellyNoDir $ do
read <$> liftIO (readFile filename) read <$> liftIO (readFile filename)
-- | Create the IPython profile. -- | Create the IPython profile.
setupIPythonProfile :: String -- ^ IHaskell profile name. setupIPythonProfile :: WhichIPython
-> String -- ^ IHaskell profile name.
-> IO () -> IO ()
setupIPythonProfile profile = shellyNoDir $ do setupIPythonProfile which profile = shellyNoDir $ do
-- Create the IPython profile. -- Create the IPython profile.
void $ ipython True ["profile", "create", pack profile] void $ ipython which True ["profile", "create", pack profile]
-- Find the IPython profile directory. Make sure to get rid of trailing -- Find the IPython profile directory. Make sure to get rid of trailing
-- newlines from the output of the `ipython locate` call. -- newlines from the output of the `ipython locate` call.
ipythonDir <- pack <$> rstrip <$> ipython True ["locate"] ipythonDir <- pack <$> rstrip <$> ipython which True ["locate"]
let profileDir = ipythonDir ++ "/profile_" ++ pack profile ++ "/" let profileDir = ipythonDir ++ "/profile_" ++ pack profile ++ "/"
liftIO $ copyProfile profileDir liftIO $ copyProfile profileDir
insertIHaskellPath profileDir insertIHaskellPath profileDir
removeIPythonProfile :: String -> Sh () removeIPythonProfile :: WhichIPython -> String -> Sh ()
removeIPythonProfile profile = do removeIPythonProfile which profile = do
-- Try to locate the profile. Do not die if it doesn't exist. -- Try to locate the profile. Do not die if it doesn't exist.
errExit False $ ipython True ["locate", "profile", pack profile] errExit False $ ipython which True ["locate", "profile", pack profile]
-- If the profile exists, delete it. -- If the profile exists, delete it.
exitCode <- lastExitCode exitCode <- lastExitCode
dir <- pack <$> rstrip <$> ipython True ["locate"] dir <- pack <$> rstrip <$> ipython which True ["locate"]
when (exitCode == 0 && dir /= "") $ do when (exitCode == 0 && dir /= "") $ do
putStrLn "Updating IPython profile." putStrLn "Updating IPython profile."
let profileDir = dir ++ "/profile_" ++ pack profile ++ "/" let profileDir = dir ++ "/profile_" ++ pack profile ++ "/"
......
...@@ -43,6 +43,11 @@ main = do ...@@ -43,6 +43,11 @@ main = do
Right args -> Right args ->
ihaskell args ihaskell args
chooseIPython [] = return DefaultIPython
chooseIPython (IPythonFrom path:_) =
ExplicitIPython <$> subHome path
chooseIPython (_:xs) = chooseIPython xs
ihaskell :: Args -> IO () ihaskell :: Args -> IO ()
-- If no mode is specified, print help text. -- If no mode is specified, print help text.
ihaskell (Args (ShowHelp help) _) = ihaskell (Args (ShowHelp help) _) =
...@@ -53,21 +58,24 @@ ihaskell (Args (ShowHelp help) _) = ...@@ -53,21 +58,24 @@ ihaskell (Args (ShowHelp help) _) =
-- isn't updated. This is hard to detect since versions of IPython might -- isn't updated. This is hard to detect since versions of IPython might
-- not change! -- not change!
ihaskell (Args UpdateIPython _) = do ihaskell (Args UpdateIPython _) = do
setupIPython setupIPython DefaultIPython
putStrLn "IPython updated." putStrLn "IPython updated."
ihaskell (Args Console flags) = showingHelp Console flags $ do ihaskell (Args Console flags) = showingHelp Console flags $ do
setupIPython ipython <- chooseIPython flags
setupIPython ipython
flags <- addDefaultConfFile flags flags <- addDefaultConfFile flags
info <- initInfo IPythonConsole flags info <- initInfo IPythonConsole flags
runConsole info runConsole ipython info
ihaskell (Args (View (Just fmt) (Just name)) []) = ihaskell (Args (View (Just fmt) (Just name)) args) = do
nbconvert fmt name ipython <- chooseIPython args
nbconvert ipython fmt name
ihaskell (Args Notebook flags) = showingHelp Notebook flags $ do ihaskell (Args Notebook flags) = showingHelp Notebook flags $ do
setupIPython ipython <- chooseIPython flags
setupIPython ipython
let server = case mapMaybe serveDir flags of let server = case mapMaybe serveDir flags of
[] -> Nothing [] -> Nothing
...@@ -79,7 +87,7 @@ ihaskell (Args Notebook flags) = showingHelp Notebook flags $ do ...@@ -79,7 +87,7 @@ ihaskell (Args Notebook flags) = showingHelp Notebook flags $ do
curdir <- getCurrentDirectory curdir <- getCurrentDirectory
let info = undirInfo { initDir = curdir } let info = undirInfo { initDir = curdir }
runNotebook info server runNotebook ipython info server
where where
serveDir (ServeFrom dir) = Just dir serveDir (ServeFrom dir) = Just dir
serveDir _ = Nothing serveDir _ = Nothing
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment