Skip to content

Commit

Permalink
Merge pull request #268 from alexarchambault/topic/predef
Browse files Browse the repository at this point in the history
Add predef file support
  • Loading branch information
alexarchambault authored Dec 6, 2018
2 parents 900b2d2 + f4720a8 commit 194675c
Show file tree
Hide file tree
Showing 8 changed files with 170 additions and 96 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package almond

import java.nio.charset.StandardCharsets.UTF_8
import java.nio.file.{Files, Path, Paths}
import java.util.UUID

import almond.api.JupyterApi
Expand All @@ -12,7 +13,7 @@ import almond.interpreter.input.InputManager
import almond.logger.LoggerContext
import almond.protocol.KernelInfo
import ammonite.interp.{Parsers, Preprocessor}
import ammonite.ops.{Path, read}
import ammonite.ops.read
import ammonite.repl._
import ammonite.runtime._
import ammonite.util._
Expand All @@ -29,7 +30,8 @@ final class ScalaInterpreter(
extraRepos: Seq[String] = Nil,
extraBannerOpt: Option[String] = None,
extraLinks: Seq[KernelInfo.Link] = Nil,
predef: String = "",
predefCode: String = "",
predefFiles: Seq[Path] = Nil,
automaticDependencies: Map[String, Seq[String]] = Map(),
forceMavenProperties: Map[String, String] = Map(),
mavenProfiles: Map[String, Boolean] = Map(),
Expand Down Expand Up @@ -146,22 +148,6 @@ final class ScalaInterpreter(
}


def runPredef(interp: ammonite.interp.Interpreter): Unit = {
val stmts = Parsers.split(predef).get.get.value
val predefRes = interp.processLine(predef, stmts, 9999999, silent = false, () => ())
Repl.handleOutput(interp, predefRes)
predefRes match {
case Res.Success(_) =>
case Res.Failure(msg) =>
throw new ScalaInterpreter.PredefException(msg, None)
case Res.Exception(t, msg) =>
throw new ScalaInterpreter.PredefException(msg, Some(t))
case Res.Skip =>
case Res.Exit(v) =>
log.warn(s"Ignoring exit request from predef (exit value: $v)")
}
}

lazy val ammInterp: ammonite.interp.Interpreter = {

val replApi: ReplApiImpl =
Expand Down Expand Up @@ -193,7 +179,7 @@ final class ScalaInterpreter(
case _ =>
}

def exec(file: Path): Unit = {
def exec(file: ammonite.ops.Path): Unit = {
ammInterp.watch(file)
apply(normalizeNewlines(read(file)))
}
Expand Down Expand Up @@ -245,6 +231,19 @@ final class ScalaInterpreter(
)
}

val predefFileInfos =
predefFiles.zipWithIndex.map {
case (path, idx) =>
val suffix = if (idx <= 0) "" else s"-$idx"
PredefInfo(
Name("FilePredef" + suffix),
// read with the local charset…
new String(Files.readAllBytes(path)),
false,
Some(os.Path(path))
)
}

try {

log.info("Creating Ammonite interpreter")
Expand All @@ -262,7 +261,9 @@ final class ScalaInterpreter(
None
)
),
customPredefs = Nil,
customPredefs = predefFileInfos ++ Seq(
PredefInfo(Name("CodePredef"), predefCode, false, None)
),
extraBridges = Seq(
(ammonite.repl.ReplBridge.getClass.getName.stripSuffix("$"), "repl", replApi),
(almond.api.JupyterAPIHolder.getClass.getName.stripSuffix("$"), "kernel", jupyterApi)
Expand All @@ -279,7 +280,16 @@ final class ScalaInterpreter(

log.info("Initializing interpreter predef")

ammInterp0.initializePredef()
for ((e, _) <- ammInterp0.initializePredef())
e match {
case Res.Failure(msg) =>
throw new ScalaInterpreter.PredefException(msg, None)
case Res.Exception(t, msg) =>
throw new ScalaInterpreter.PredefException(msg, Some(t))
case Res.Skip =>
case Res.Exit(v) =>
log.warn(s"Ignoring exit request from predef (exit value: $v)")
}

log.info("Loading base dependencies")

Expand All @@ -295,10 +305,6 @@ final class ScalaInterpreter(

ammInterp0.compilerManager.preConfigureCompiler(_.processArguments(Nil, processAll = true))

log.info(s"Warming up interpreter (predef: $predef)")

runPredef(ammInterp0)

log.info("Ammonite interpreter ok")

if (forceMavenProperties.nonEmpty)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
val n = 2z
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
val n: Int = sys.error("foo")
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
println("foo") // automatically generated: val res… = println("foo")
val n = 2
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
val n = 2
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package almond

import java.nio.file.{Path, Paths}

import almond.interpreter.api.DisplayData
import almond.interpreter.{Completion, ExecuteResult, Interpreter}
import ammonite.util.Colors
Expand All @@ -12,6 +14,107 @@ object ScalaInterpreterTests extends TestSuite {
initialColors = Colors.BlackWhite
)

private object Predef {
private def predefPath(name: String): Path =
Paths.get(getClass.getResource(s"/test-predefs/$name.sc").toURI)

def simple(fileBased: Boolean = false): Unit = {

val (predefCode, predefFiles) =
if (fileBased)
("", Seq(predefPath("simple")))
else
("val n = 2", Nil)

val interp = new ScalaInterpreter(
initialColors = Colors.BlackWhite,
predefCode = predefCode,
predefFiles = predefFiles
)

val res = interp.execute("val m = 2 * n")
val expectedRes = ExecuteResult.Success(DisplayData.text("m: Int = 4"))
assert(res == expectedRes)
}

def noVariableName(fileBased: Boolean = false): Unit = {

val (predefCode, predefFiles) =
if (fileBased)
("", Seq(predefPath("no-variable-name")))
else {
val code =
"""println("foo") // automatically generated: val res… = println("foo")
|val n = 2
""".stripMargin
(code, Nil)
}
val interp = new ScalaInterpreter(
initialColors = Colors.BlackWhite,
predefCode = predefCode,
predefFiles = predefFiles
)

val res = interp.execute("val m = 2 * n")
val expectedRes = ExecuteResult.Success(DisplayData.text("m: Int = 4"))
assert(res == expectedRes)
}

def compilationError(fileBased: Boolean = false): Unit = {

val (predefCode, predefFiles) =
if (fileBased)
("", Seq(predefPath("compilation-error")))
else
("val n = 2z", Nil)

val interp = new ScalaInterpreter(
initialColors = Colors.BlackWhite,
predefCode = predefCode,
predefFiles = predefFiles
)

val res =
try {
interp.execute("val m = 2 * n")
false
} catch {
case e: ScalaInterpreter.PredefException =>
assert(e.getCause == null)
true
}

assert(res)
}

def exception(fileBased: Boolean = false): Unit = {

val (predefCode, predefFiles) =
if (fileBased)
("", Seq(predefPath("exception")))
else
("""val n: Int = sys.error("foo")""", Nil)
val interp = new ScalaInterpreter(
initialColors = Colors.BlackWhite,
predefCode = predefCode,
predefFiles = predefFiles
)

val res =
try {
interp.execute("val m = 2 * n")
false
} catch {
case e: ScalaInterpreter.PredefException =>
val msgOpt = Option(e.getCause).flatMap(e0 => Option(e0.getMessage))
assert(msgOpt.contains("foo"))
true
}

assert(res)
}
}

val tests = Tests {

"execute" - {
Expand Down Expand Up @@ -66,76 +169,18 @@ object ScalaInterpreterTests extends TestSuite {

}

"predef" - {

"simple" - {
val predef = "val n = 2"
val interp = new ScalaInterpreter(
initialColors = Colors.BlackWhite,
predef = predef
)

val res = interp.execute("val m = 2 * n")
val expectedRes = ExecuteResult.Success(DisplayData.text("m: Int = 4"))
assert(res == expectedRes)
}

"no variable name" - {
val predef =
"""println("foo") // automatically generated: val res… = println("foo")
|val n = 2
""".stripMargin
val interp = new ScalaInterpreter(
initialColors = Colors.BlackWhite,
predef = predef
)

val res = interp.execute("val m = 2 * n")
val expectedRes = ExecuteResult.Success(DisplayData.text("m: Int = 4"))
assert(res == expectedRes)
}

"compilation error" - {
val predef = "val n = 2z"
val interp = new ScalaInterpreter(
initialColors = Colors.BlackWhite,
predef = predef
)

val res =
try {
interp.execute("val m = 2 * n")
false
} catch {
case e: ScalaInterpreter.PredefException =>
assert(e.getCause == null)
true
}

assert(res)
}

"exception" - {
val predef = """val n: Int = sys.error("foo")"""
val interp = new ScalaInterpreter(
initialColors = Colors.BlackWhite,
predef = predef
)

val res =
try {
interp.execute("val m = 2 * n")
false
} catch {
case e: ScalaInterpreter.PredefException =>
val msgOpt = Option(e.getCause).flatMap(e0 => Option(e0.getMessage))
assert(msgOpt.contains("foo"))
true
}

assert(res)
}
"predef code" - {
"simple" - Predef.simple()
"no variable name" - Predef.noVariableName()
"compilation error" - Predef.compilationError()
"exception" - Predef.exception()
}

"predef files" - {
"simple" - Predef.simple(fileBased = true)
"no variable name" - Predef.noVariableName(fileBased = true)
"compilation error" - Predef.compilationError(fileBased = true)
"exception" - Predef.exception(fileBased = true)
}

}
Expand Down
18 changes: 17 additions & 1 deletion modules/scala/scala-kernel/src/main/scala/almond/Options.scala
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package almond

import java.nio.file.{Files, Path, Paths}
import java.util.regex.Pattern

import almond.protocol.KernelInfo
Expand All @@ -15,7 +16,8 @@ final case class Options(
extraRepository: List[String] = Nil,
banner: Option[String] = None,
link: List[String] = Nil,
predef: String = "",
predefCode: String = "",
predef: List[String] = Nil,
autoDependency: List[String] = Nil,
@HelpMessage("Force Maven properties during dependency resolution")
forceProperty: List[String] = Nil,
Expand Down Expand Up @@ -81,6 +83,20 @@ final case class Options(
sys.error(s"Malformed link: $other")
}

def predefFiles(): Seq[Path] =
predef.map { p =>
val path = Paths.get(p)
if (!Files.exists(path)) {
System.err.println(s"Error: predef $p: not found")
sys.exit(1)
}
if (!Files.isRegularFile(path)) {
System.err.println(s"Error: predef $p: not a file")
sys.exit(1)
}
path
}

}

object Options {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ object ScalaKernel extends CaseApp[Options] {
val forceProperties = options.forceProperties()
val mavenProfiles = options.mavenProfiles()
val extraLinks = options.extraLinks()
val predefFiles = options.predefFiles()


log.info(
Expand Down Expand Up @@ -131,7 +132,8 @@ object ScalaKernel extends CaseApp[Options] {
extraRepos = options.extraRepository,
extraBannerOpt = options.banner,
extraLinks = extraLinks,
predef = options.predef,
predefCode = options.predefCode,
predefFiles = predefFiles,
automaticDependencies = autoDependencies,
forceMavenProperties = forceProperties,
mavenProfiles = mavenProfiles,
Expand Down

0 comments on commit 194675c

Please sign in to comment.