Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add predef file support #268

Merged
merged 2 commits into from
Dec 6, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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