Skip to content

Commit 8f27978

Browse files
author
Alejandro Gómez
committed
Do IO in the Task context
1 parent d198f6d commit 8f27978

File tree

3 files changed

+53
-55
lines changed

3 files changed

+53
-55
lines changed

src/main/scala/Evaluation.scala

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -206,15 +206,13 @@ class Eval(target: Option[File] = None, jars: List[File] = Nil) {
206206
* where unique is computed from the jvmID (a random number)
207207
* and a digest of code
208208
*/
209-
def applyProcessed[T](code: String, resetState: Boolean, jars: Seq[File]): T = {
209+
def execute[T](code: String, resetState: Boolean, jars: Seq[File]): T = {
210210
val id = uniqueId(code)
211211
val className = "Evaluator__" + id
212-
applyProcessed(className, code, resetState, jars)
212+
execute(className, code, resetState, jars)
213213
}
214214

215-
/**
216-
*/
217-
def applyProcessed[T](className: String, code: String, resetState: Boolean, jars: Seq[File]): T = {
215+
def execute[T](className: String, code: String, resetState: Boolean, jars: Seq[File]): T = {
218216
val jarUrls = jars.map(jar => new java.net.URL(s"file://${jar.getAbsolutePath}")).toArray
219217
val urlClassLoader = new URLClassLoader(jarUrls , compiler.getClass.getClassLoader)
220218
val classLoader = new AbstractFileClassLoader(compilerOutputDir, urlClassLoader)

src/main/scala/Evaluator.scala

Lines changed: 40 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,9 @@ import scala.util.Try
2222
import scala.util.control.NonFatal
2323
import scala.concurrent._
2424
import scala.concurrent.duration._
25-
import scalaz.concurrent.{ Task => ZTask }
25+
import scalaz.concurrent.Task
2626

27-
import monix.eval.Task
28-
import monix.execution._
27+
import monix.execution.Scheduler
2928

3029
import com.twitter.util.Eval; import Eval._
3130

@@ -48,14 +47,15 @@ object EvalResult {
4847
case object Timeout extends EvalResult[Nothing]
4948
case class Success[T](complilationInfos: CI, result: T, consoleOutput: String) extends EvalResult[T]
5049
case class Timeout[T]() extends EvalResult[T]
50+
case class UnresolvedDependency(error: coursier.FileError) extends EvalResult[Nothing]
5151
case class EvalRuntimeError(complilationInfos: CI, runtimeError: Option[RuntimeError]) extends EvalResult[Nothing]
5252
case class CompilationError(complilationInfos: CI) extends EvalResult[Nothing]
5353
case class GeneralError(stack: Throwable) extends EvalResult[Nothing]
5454
}
5555

56-
class Evaluator(timeout: FiniteDuration = 20.seconds) {
57-
implicit val scheduler: Scheduler = Scheduler.io("evaluation-scheduler")
58-
56+
class Evaluator(timeout: FiniteDuration = 20.seconds)(
57+
implicit S: Scheduler
58+
) {
5959
type Dependency = (String, String, String)
6060
type Remote = String
6161

@@ -69,28 +69,27 @@ class Evaluator(timeout: FiniteDuration = 20.seconds) {
6969
(sev, CompilationInfo(msg, Some(RangePosition(pos.start, pos.point, pos.end))) :: Nil)
7070
}
7171

72-
def resolveArtifacts(remotes: Seq[Remote], dependencies: Seq[Dependency]): Resolution = {
73-
val resolution = Resolution(
74-
dependencies.map(d => {
75-
Dependency(
76-
Module(d._1, d._2), d._3
77-
)
78-
}).toSet
72+
def remoteToRepository(remote: Remote): Repository =
73+
MavenRepository(remote)
74+
75+
def dependencyToModule(dependency: Dependency): coursier.Dependency =
76+
coursier.Dependency(
77+
Module(dependency._1, dependency._2), dependency._3
7978
)
80-
val repositories: Seq[Repository] = remotes.map(url => MavenRepository(url))
8179

80+
def resolveArtifacts(remotes: Seq[Remote], dependencies: Seq[Dependency]): Task[Resolution] = {
81+
val resolution = Resolution(dependencies.map(dependencyToModule).toSet)
82+
val repositories: Seq[Repository] = remotes.map(remoteToRepository)
8283
val fetch = Fetch.from(repositories, Cache.fetch())
83-
resolution.process.run(fetch).run
84+
resolution.process.run(fetch)
8485
}
8586

86-
def fetchArtifacts(resolution: Resolution): List[coursier.FileError \/ File] = {
87-
if (resolution.isDone)
88-
ZTask.gatherUnordered(
89-
resolution.artifacts.map(Cache.file(_).run)
90-
).run
91-
else
92-
Nil
93-
}
87+
def fetchArtifacts(remotes: Seq[Remote], dependencies: Seq[Dependency]): Task[coursier.FileError \/ List[File]] = for {
88+
resolution <- resolveArtifacts(remotes, dependencies)
89+
artifacts <- Task.gatherUnordered(
90+
resolution.artifacts.map(Cache.file(_).run)
91+
)
92+
} yield artifacts.sequenceU
9493

9594
def createEval(jars: Seq[File]) = {
9695
new Eval(jars = jars.toList) {
@@ -117,12 +116,12 @@ class Evaluator(timeout: FiniteDuration = 20.seconds) {
117116
}
118117
}
119118

120-
private[this] def performEval[T](code: String, jars: Seq[File]): EvalResult[T] = {
119+
private[this] def evaluate[T](code: String, jars: Seq[File]): EvalResult[T] = {
121120
val eval = createEval(jars)
122121

123122
val result = for {
124123
_ Try(eval.check(code))
125-
result Try(eval.applyProcessed[T](code, resetState = true, jars = jars))
124+
result Try(eval.execute[T](code, resetState = true, jars = jars))
126125
} yield result
127126

128127
val errors: Map[Severity, List[CompilationInfo]] = eval.errors.toMap
@@ -137,22 +136,21 @@ class Evaluator(timeout: FiniteDuration = 20.seconds) {
137136
}
138137
}
139138

140-
def eval[T](code: String, remotes: Seq[Remote] = Nil, dependencies: Seq[Dependency] = Nil): EvalResult[T] = {
141-
// todo: don't take into account dependency resolution time in timeout
142-
val resolution = resolveArtifacts(remotes, dependencies)
143-
val allJars = fetchArtifacts(resolution).sequenceU
144-
145-
val jars: Seq[File] = allJars match {
146-
case \/-(jars) => jars
147-
case -\/(fileError) => Nil // todo: handle errors
148-
}
149-
150-
val evaluation = Task({
151-
performEval(code, jars)
152-
}) // todo
153-
154-
Try(
155-
Await.result(evaluation.runAsync, timeout)
156-
).getOrElse(EvalResult.Timeout())
139+
def eval[T](
140+
code: String,
141+
remotes: Seq[Remote] = Nil,
142+
dependencies: Seq[Dependency] = Nil
143+
): Task[EvalResult[T]] = {
144+
for {
145+
allJars <- fetchArtifacts(remotes, dependencies)
146+
result <- allJars match {
147+
case \/-(jars) => Task({
148+
evaluate(code, jars)
149+
}).timed(timeout).handle({
150+
case err: TimeoutException => EvalResult.Timeout[T]()
151+
})
152+
case -\/(fileError) => Task.now(EvalResult.UnresolvedDependency(fileError))
153+
}
154+
} yield result
157155
}
158156
}

src/test/scala/EvaluatorSpec.scala

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,22 +6,24 @@
66
package org.scalaexercises.evaluator
77

88
import scala.concurrent.duration._
9+
import monix.execution.Scheduler
910
import org.scalatest._
1011

1112
class EvaluatorSpec extends FunSpec with Matchers {
12-
val evaluator = new Evaluator(10 seconds)
13+
implicit val scheduler: Scheduler = Scheduler.io("exercises-spec")
14+
val evaluator = new Evaluator(20 seconds)
1315

1416
describe("evaluation") {
1517
it("can evaluate simple expressions") {
16-
val result: EvalResult[Int] = evaluator.eval("{ 41 + 1 }")
18+
val result: EvalResult[Int] = evaluator.eval("{ 41 + 1 }").run
1719

1820
result should matchPattern {
1921
case EvalResult.Success(_, 42, _)
2022
}
2123
}
2224

2325
it("fails with a timeout when takes longer than the configured timeout") {
24-
val result: EvalResult[Int] = evaluator.eval("{ while(true) {}; 123 }")
26+
val result: EvalResult[Int] = evaluator.eval("{ while(true) {}; 123 }").run
2527

2628
result should matchPattern {
2729
case t: EvalResult.Timeout[_]
@@ -43,7 +45,7 @@ Eval.now(42).value
4345
code,
4446
remotes = remotes,
4547
dependencies = dependencies
46-
)
48+
).run
4749

4850
result should matchPattern {
4951
case EvalResult.Success(_, 42, _) =>
@@ -67,12 +69,12 @@ Eval.now(42).value
6769
code,
6870
remotes = remotes,
6971
dependencies = dependencies1
70-
)
72+
).run
7173
val result2: EvalResult[Int] = evaluator.eval(
7274
code,
7375
remotes = remotes,
7476
dependencies = dependencies2
75-
)
77+
).run
7678

7779
result1 should matchPattern {
7880
case EvalResult.Success(_, 42, _) =>
@@ -96,7 +98,7 @@ Asserts.scalaTestAsserts(true)
9698
code,
9799
remotes = remotes,
98100
dependencies = dependencies
99-
)
101+
).run
100102

101103
result should matchPattern {
102104
case EvalResult.Success(_, (), _) =>
@@ -117,7 +119,7 @@ Asserts.scalaTestAsserts(false)
117119
code,
118120
remotes = remotes,
119121
dependencies = dependencies
120-
)
122+
).run
121123

122124
result should matchPattern {
123125
case EvalResult.EvalRuntimeError(_, Some(RuntimeError(err: TestFailedException, _))) =>

0 commit comments

Comments
 (0)