Skip to content

[SPARK-20706][SPARK-SHELL] Spark-shell not overriding method/variable definition #19879

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

Closed
wants to merge 3 commits into from
Closed
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
Expand Up @@ -17,6 +17,7 @@

package org.apache.spark.repl

import scala.collection.mutable
import scala.tools.nsc.Settings
import scala.tools.nsc.interpreter._

Expand All @@ -30,7 +31,7 @@ class SparkILoopInterpreter(settings: Settings, out: JPrintWriter) extends IMain

override def chooseHandler(member: intp.global.Tree): MemberHandler = member match {
case member: Import => new SparkImportHandler(member)
case _ => super.chooseHandler (member)
case _ => super.chooseHandler(member)
}

class SparkImportHandler(imp: Import) extends ImportHandler(imp: Import) {
Expand Down Expand Up @@ -100,4 +101,139 @@ class SparkILoopInterpreter(settings: Settings, out: JPrintWriter) extends IMain
override def typeOfExpression(expr: String, silent: Boolean): global.Type =
expressionTyper.typeOfExpression(expr, silent)


import global.Name
override def importsCode(wanted: Set[Name], wrapper: Request#Wrapper,
definesClass: Boolean, generousImports: Boolean): ComputedImports = {

import global._
import definitions.{ ObjectClass, ScalaPackage, JavaLangPackage, PredefModule }
import memberHandlers._

val header, code, trailingBraces, accessPath = new StringBuilder
val currentImps = mutable.HashSet[Name]()
// only emit predef import header if name not resolved in history, loosely
var predefEscapes = false

/**
* Narrow down the list of requests from which imports
* should be taken. Removes requests which cannot contribute
* useful imports for the specified set of wanted names.
*/
case class ReqAndHandler(req: Request, handler: MemberHandler)

def reqsToUse: List[ReqAndHandler] = {
/**
* Loop through a list of MemberHandlers and select which ones to keep.
* 'wanted' is the set of names that need to be imported.
*/
def select(reqs: List[ReqAndHandler], wanted: Set[Name]): List[ReqAndHandler] = {
// Single symbol imports might be implicits! See bug #1752. Rather than
// try to finesse this, we will mimic all imports for now.
def keepHandler(handler: MemberHandler) = handler match {
// While defining classes in class based mode - implicits are not needed.
case h: ImportHandler if isClassBased && definesClass =>
h.importedNames.exists(x => wanted.contains(x))
case _: ImportHandler => true
case x if generousImports => x.definesImplicit ||
(x.definedNames exists (d => wanted.exists(w => d.startsWith(w))))
case x => x.definesImplicit ||
(x.definedNames exists wanted)
}

reqs match {
case Nil =>
predefEscapes = wanted contains PredefModule.name ; Nil
case rh :: rest if !keepHandler(rh.handler) => select(rest, wanted)
case rh :: rest =>
import rh.handler._
val augment = rh match {
case ReqAndHandler(_, _: ImportHandler) => referencedNames
case _ => Nil
}
val newWanted = wanted ++ augment -- definedNames -- importedNames
rh :: select(rest, newWanted)
}
}

/** Flatten the handlers out and pair each with the original request */
select(allReqAndHandlers reverseMap { case (r, h) => ReqAndHandler(r, h) }, wanted).reverse
}

// add code for a new object to hold some imports
def addWrapper() {
import nme.{ INTERPRETER_IMPORT_WRAPPER => iw }
code append (wrapper.prewrap format iw)
trailingBraces append wrapper.postwrap
accessPath append s".$iw"
currentImps.clear()
}

def maybeWrap(names: Name*) = if (names exists currentImps) addWrapper()

def wrapBeforeAndAfter[T](op: => T): T = {
addWrapper()
try op finally addWrapper()
}

// imports from Predef are relocated to the template header to allow hiding.
def checkHeader(h: ImportHandler) = h.referencedNames contains PredefModule.name

// loop through previous requests, adding imports for each one
wrapBeforeAndAfter {
// Reusing a single temporary value when import from a line with multiple definitions.
val tempValLines = mutable.Set[Int]()
for (ReqAndHandler(req, handler) <- reqsToUse) {
val objName = req.lineRep.readPathInstance
handler match {
case h: ImportHandler if checkHeader(h) =>
header.clear()
header append f"${h.member}%n"
// If the user entered an import, then just use it; add an import wrapping
// level if the import might conflict with some other import
case x: ImportHandler if x.importsWildcard =>
wrapBeforeAndAfter(code append (x.member + "\n"))
case x: ImportHandler =>
maybeWrap(x.importedNames: _*)
code append (x.member + "\n")
currentImps ++= x.importedNames

case x if isClassBased =>
for (sym <- x.definedSymbols) {
maybeWrap(sym.name)
x match {
case _: ClassHandler =>
code.append(s"import ${objName}${req.accessPath}.`${sym.name}`\n")
case _ =>
val valName = s"${req.lineRep.packageName}${req.lineRep.readName}"
if (!tempValLines.contains(req.lineRep.lineId)) {
code.append(s"val $valName: ${objName}.type = $objName\n")
tempValLines += req.lineRep.lineId
}
code.append(s"import ${valName}${req.accessPath}.`${sym.name}`\n")
}
currentImps += sym.name
}
// For other requests, import each defined name.
// import them explicitly instead of with _, so that
// ambiguity errors will not be generated. Also, quote
// the name of the variable, so that we don't need to
// handle quoting keywords separately.
case x =>
for (sym <- x.definedSymbols) {
maybeWrap(sym.name)
code append s"import ${x.path}\n"
currentImps += sym.name
}
}
}
}

val computedHeader = if (predefEscapes) header.toString else ""
ComputedImports(computedHeader, code.toString, trailingBraces.toString, accessPath.toString)
}

private def allReqAndHandlers =
prevRequestList flatMap (req => req.handlers map (req -> _))

}
39 changes: 30 additions & 9 deletions repl/src/test/scala/org/apache/spark/repl/ReplSuite.scala
Original file line number Diff line number Diff line change
Expand Up @@ -227,14 +227,35 @@ class ReplSuite extends SparkFunSuite {
assertDoesNotContain("error: not found: value sc", output)
}

test("spark-shell should find imported types in class constructors and extends clause") {
val output = runInterpreter("local",
"""
|import org.apache.spark.Partition
|class P(p: Partition)
|class P(val index: Int) extends Partition
""".stripMargin)
assertDoesNotContain("error: not found: type Partition", output)
}
test("spark-shell should find imported types in class constructors and extends clause") {
val output = runInterpreter("local",
"""
|import org.apache.spark.Partition
|class P(p: Partition)
|class P(val index: Int) extends Partition
""".stripMargin)
assertDoesNotContain("error: not found: type Partition", output)
}

test("spark-shell should shadow val/def definitions correctly") {
val output1 = runInterpreter("local",
"""
|def myMethod() = "first definition"
|val tmp = myMethod(); val out = tmp
|def myMethod() = "second definition"
|val tmp = myMethod(); val out = s"$tmp aabbcc"
""".stripMargin)
assertContains("second definition aabbcc", output1)

val output2 = runInterpreter("local",
"""
|val a = 1
|val b = a; val c = b;
|val a = 2
|val b = a; val c = b;
|s"!!$b!!"
""".stripMargin)
assertContains("!!2!!", output2)
}

}