From f25df05bcac927f07872674dfa970bfac1f81e76 Mon Sep 17 00:00:00 2001 From: Scala Steward Date: Wed, 15 May 2024 14:00:55 +0000 Subject: [PATCH 1/8] Update ammonite-compiler, ammonite-repl, ... to 3.0.0-M2-1-3763a1d4 --- project/deps.sc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/deps.sc b/project/deps.sc index dca4304b3..c6db455d7 100644 --- a/project/deps.sc +++ b/project/deps.sc @@ -2,7 +2,7 @@ import mill._ import mill.scalalib._ object Versions { - def ammonite = "3.0.0-M0-58-9ccdff7c" + def ammonite = "3.0.0-M2-1-3763a1d4" def caseApp = "2.1.0-M26" def coursier = "2.1.10" def jsoniterScala = "2.13.5" From 705d3f3aebfc03f1b5977b17a389ceadd6b46716 Mon Sep 17 00:00:00 2001 From: Alexandre Archambault Date: Fri, 17 May 2024 15:36:24 +0200 Subject: [PATCH 2/8] Tweak tests output --- .../almond/integration/AlmondFunSuite.scala | 31 +++++++++++++++---- 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/modules/scala/integration/src/main/scala/almond/integration/AlmondFunSuite.scala b/modules/scala/integration/src/main/scala/almond/integration/AlmondFunSuite.scala index cdb7fff12..c4c9a2738 100644 --- a/modules/scala/integration/src/main/scala/almond/integration/AlmondFunSuite.scala +++ b/modules/scala/integration/src/main/scala/almond/integration/AlmondFunSuite.scala @@ -12,7 +12,12 @@ abstract class AlmondFunSuite extends munit.FunSuite { def mightRetry: Boolean = false override def munitTimeout = 5.minutes - override def test(options: TestOptions)(body: => Any)(implicit loc: Location): Unit = + override def test(options: TestOptions)(body: => Any)(implicit loc: Location): Unit = { + val className = getClass.getName + val (classNameInit, classNameLast) = { + val a = className.split('.') + (a.init, a.last) + } super.test(options) { def runBody(attempt: Int): Any = { @@ -33,7 +38,9 @@ abstract class AlmondFunSuite extends munit.FunSuite { if (attempt == 1) AlmondFunSuite.retriedTestsCount.incrementAndGet() System.err.println( - s"Attempt $attempt of ${Console.RED}${options.name}${Console.RESET} failed, trying again" + s"Attempt $attempt of ${Console.RED}${classNameInit.mkString(".")}" + "." + + s"${Console.BOLD}$classNameLast${Console.RESET}${Console.RED}" + "." + + s"${Console.BOLD}${options.name}${Console.RESET} failed, trying again" ) e.printStackTrace(System.err) runBody(attempt + 1) @@ -44,7 +51,11 @@ abstract class AlmondFunSuite extends munit.FunSuite { } System.err.println() - System.err.println(s"Running ${Console.BLUE}${options.name}${Console.RESET}") + System.err.println( + s"${Console.BLUE}Running ${classNameInit.mkString(".")}" + "." + + s"${Console.BOLD}$classNameLast${Console.RESET}${Console.BLUE}" + "." + + s"${Console.BOLD}${options.name}${Console.RESET}" + ) var success = false var exOpt = Option.empty[Throwable] try { @@ -58,15 +69,23 @@ abstract class AlmondFunSuite extends munit.FunSuite { } finally { if (success) - System.err.println(s"Done: ${Console.CYAN}${options.name}${Console.RESET}") + System.err.println( + s"${Console.CYAN}Done: ${classNameInit.mkString(".")}" + "." + + s"${Console.BOLD}$classNameLast${Console.RESET}${Console.CYAN}" + "." + + s"${Console.BOLD}${options.name}${Console.RESET}" + ) else { - System.err.println(s"Failed: ${Console.RED}${options.name}${Console.RESET}") + System.err.println( + s"${Console.RED}Failed: ${classNameInit.mkString(".")}" + "." + + s"${Console.BOLD}$classNameLast${Console.RESET}${Console.RED}" + "." + + s"${Console.BOLD}${options.name}${Console.RESET}" + ) exOpt.foreach(_.printStackTrace(System.err)) } System.err.println() } }(loc) - + } } object AlmondFunSuite { From 690447e651ce87b28712b44d70d0b49779b663fb Mon Sep 17 00:00:00 2001 From: Alexandre Archambault Date: Tue, 28 May 2024 16:40:37 +0200 Subject: [PATCH 3/8] Set linger to a finite value before closing ZeroMQ channels --- .../main/scala/almond/echo/EchoKernel.scala | 9 +++- .../src/main/scala/almond/echo/Options.scala | 18 +++++++- .../almond/integration/KernelLauncher.scala | 8 ++-- .../main/scala/almond/launcher/Launcher.scala | 8 +++- .../almond/launcher/LauncherOptions.scala | 12 ++++- .../src/main/scala/almond/Options.scala | 12 ++++- .../src/main/scala/almond/ScalaKernel.scala | 3 +- .../scala/almond/channels/Connection.scala | 9 ++-- .../channels/zeromq/ZeromqConnection.scala | 9 ++-- .../almond/channels/zeromq/ZeromqSocket.scala | 2 +- .../channels/zeromq/ZeromqSocketImpl.scala | 8 +++- .../zeromq/ZeromqConnectionTests.scala | 4 +- .../channels/zeromq/ZeromqSocketTests.scala | 9 ++-- .../src/main/scala/almond/kernel/Kernel.scala | 46 +++++++++++++------ 14 files changed, 117 insertions(+), 40 deletions(-) diff --git a/modules/echo/src/main/scala/almond/echo/EchoKernel.scala b/modules/echo/src/main/scala/almond/echo/EchoKernel.scala index 035af382c..8fc02e1bc 100644 --- a/modules/echo/src/main/scala/almond/echo/EchoKernel.scala +++ b/modules/echo/src/main/scala/almond/echo/EchoKernel.scala @@ -53,7 +53,14 @@ object EchoKernel extends CaseApp[Options] { log.debug("Running kernel") Kernel.create(new EchoInterpreter, interpreterEc, kernelThreads, logCtx) - .flatMap(_.runOnConnectionFile(connectionFile, "echo", zeromqThreads, Nil, autoClose = true)) + .flatMap(_.runOnConnectionFile( + connectionFile, + "echo", + zeromqThreads, + Nil, + autoClose = true, + lingerDuration = options.lingerDuration + )) .unsafeRunSync()(IORuntime.global) } } diff --git a/modules/echo/src/main/scala/almond/echo/Options.scala b/modules/echo/src/main/scala/almond/echo/Options.scala index 11724fbdd..4ec4c7bf0 100644 --- a/modules/echo/src/main/scala/almond/echo/Options.scala +++ b/modules/echo/src/main/scala/almond/echo/Options.scala @@ -4,6 +4,8 @@ import almond.kernel.install.{Options => InstallOptions} import caseapp.{HelpMessage, Recurse} import caseapp.core.help.Help import caseapp.core.parser.Parser +import caseapp.Hidden +import scala.concurrent.duration.{Duration, DurationInt} final case class Options( connectionFile: Option[String] = None, @@ -11,8 +13,20 @@ final case class Options( log: String = "warn", install: Boolean = false, @Recurse - installOptions: InstallOptions = InstallOptions() -) + installOptions: InstallOptions = InstallOptions(), + @HelpMessage( + """Time given to the client to accept ZeroMQ messages before exiting. Parsed with scala.concurrent.duration.Duration, this accepts things like "Inf" or "5 seconds"""" + ) + @Hidden + linger: Option[String] = None +) { + + lazy val lingerDuration = linger + .map(_.trim) + .filter(_.nonEmpty) + .map(Duration(_)) + .getOrElse(5.seconds) +} object Options { implicit lazy val parser: Parser[Options] = Parser.derive diff --git a/modules/scala/integration/src/main/scala/almond/integration/KernelLauncher.scala b/modules/scala/integration/src/main/scala/almond/integration/KernelLauncher.scala index 586c5e2f6..bf6bc22e5 100644 --- a/modules/scala/integration/src/main/scala/almond/integration/KernelLauncher.scala +++ b/modules/scala/integration/src/main/scala/almond/integration/KernelLauncher.scala @@ -275,9 +275,11 @@ class KernelLauncher( } def close(): Unit = { - conn.close(partial = false).unsafeRunTimed(2.minutes)(IORuntime.global).getOrElse { - sys.error("Timeout when closing ZeroMQ connections") - } + conn.close(partial = false, lingerDuration = 30.seconds) + .unsafeRunTimed(2.minutes)(IORuntime.global) + .getOrElse { + sys.error("Timeout when closing ZeroMQ connections") + } if (perTestZeroMqContext) { val t = stackTracePrinterThread(output) diff --git a/modules/scala/launcher/src/main/scala/almond/launcher/Launcher.scala b/modules/scala/launcher/src/main/scala/almond/launcher/Launcher.scala index 2bb9deb9d..3a1148e58 100644 --- a/modules/scala/launcher/src/main/scala/almond/launcher/Launcher.scala +++ b/modules/scala/launcher/src/main/scala/almond/launcher/Launcher.scala @@ -26,6 +26,7 @@ import java.nio.channels.ClosedSelectorException import scala.concurrent.duration.DurationInt import scala.jdk.CollectionConverters._ import scala.util.control.NonFatal +import scala.concurrent.duration.Duration object Launcher extends CaseApp[LauncherOptions] { @@ -325,7 +326,8 @@ object Launcher extends CaseApp[LauncherOptions] { "scala", zeromqThreads, Nil, - autoClose = false + autoClose = false, + lingerDuration = Duration.Inf // unused here )) .unsafeRunSync()(IORuntime.global) val leftoverMessages: Seq[(Channel, RawMessage)] = run.unsafeRunSync()(IORuntime.global) @@ -410,7 +412,9 @@ object Launcher extends CaseApp[LauncherOptions] { for (outputHandler <- outputHandlerOpt) outputHandler.done() - try conn.close(partial = false).unsafeRunSync()(IORuntime.global) + try + conn.close(partial = false, lingerDuration = options.lingerDuration) + .unsafeRunSync()(IORuntime.global) catch { case NonFatal(e) => throw new Exception(e) diff --git a/modules/scala/launcher/src/main/scala/almond/launcher/LauncherOptions.scala b/modules/scala/launcher/src/main/scala/almond/launcher/LauncherOptions.scala index 460531a9f..bcf0d4bdd 100644 --- a/modules/scala/launcher/src/main/scala/almond/launcher/LauncherOptions.scala +++ b/modules/scala/launcher/src/main/scala/almond/launcher/LauncherOptions.scala @@ -6,6 +6,7 @@ import caseapp._ import scala.cli.directivehandler.EitherSequence._ import scala.collection.mutable +import scala.concurrent.duration.{Duration, DurationInt} // format: off final case class LauncherOptions( @@ -34,7 +35,10 @@ final case class LauncherOptions( quiet: Option[Boolean] = None, silentImports: Option[Boolean] = None, useNotebookCoursierLogger: Option[Boolean] = None, - customDirectiveGroup: List[String] = Nil + customDirectiveGroup: List[String] = Nil, + @HelpMessage("Time given to the client to accept ZeroMQ messages before handing over the connections to the kernel. Parsed with scala.concurrent.duration.Duration, this accepts things like \"Inf\" or \"5 seconds\"") + @Hidden + linger: Option[String] = None ) { // format: on @@ -91,6 +95,12 @@ final case class LauncherOptions( groups } } + + lazy val lingerDuration = linger + .map(_.trim) + .filter(_.nonEmpty) + .map(Duration(_)) + .getOrElse(5.seconds) } object LauncherOptions { diff --git a/modules/scala/scala-kernel/src/main/scala/almond/Options.scala b/modules/scala/scala-kernel/src/main/scala/almond/Options.scala index 014f0f8ef..165fb4dd8 100644 --- a/modules/scala/scala-kernel/src/main/scala/almond/Options.scala +++ b/modules/scala/scala-kernel/src/main/scala/almond/Options.scala @@ -17,6 +17,7 @@ import coursierapi.{Dependency, Module} import coursier.parse.{DependencyParser, ModuleParser} import scala.collection.compat._ +import scala.concurrent.duration.{Duration, DurationInt} import scala.jdk.CollectionConverters._ // format: off @@ -129,7 +130,11 @@ final case class Options( @HelpMessage("Pass launcher directive groups with this option. These directives will be either ignored (see --ignore-launcher-directives-in), or trigger an unused directive warning") @Hidden - launcherDirectiveGroup: List[String] = Nil + launcherDirectiveGroup: List[String] = Nil, + + @HelpMessage("""Time given to the client to accept ZeroMQ messages before exiting. Parsed with scala.concurrent.duration.Duration, this accepts things like "Inf" or "5 seconds"""") + @Hidden + linger: Option[String] = None ) { // format: on @@ -300,6 +305,11 @@ final case class Options( readFromArray(bytes)(KernelOptions.AsJson.codec) } + lazy val lingerDuration = linger + .map(_.trim) + .filter(_.nonEmpty) + .map(Duration(_)) + .getOrElse(5.seconds) } object Options { diff --git a/modules/scala/scala-kernel/src/main/scala/almond/ScalaKernel.scala b/modules/scala/scala-kernel/src/main/scala/almond/ScalaKernel.scala index 1ee01aac3..e24f0c55b 100644 --- a/modules/scala/scala-kernel/src/main/scala/almond/ScalaKernel.scala +++ b/modules/scala/scala-kernel/src/main/scala/almond/ScalaKernel.scala @@ -252,7 +252,8 @@ object ScalaKernel extends CaseApp[Options] { "scala", zeromqThreads, options.leftoverMessages0(), - autoClose = true + autoClose = true, + lingerDuration = options.lingerDuration )) .unsafeRunSync()(IORuntime.global) finally diff --git a/modules/shared/channels/src/main/scala/almond/channels/Connection.scala b/modules/shared/channels/src/main/scala/almond/channels/Connection.scala index 9abbfe44d..aa05349ab 100644 --- a/modules/shared/channels/src/main/scala/almond/channels/Connection.scala +++ b/modules/shared/channels/src/main/scala/almond/channels/Connection.scala @@ -38,7 +38,7 @@ abstract class Connection { * * Can be run multiple times. Only the first call will actually close the channels. */ - def close(partial: Boolean): IO[Unit] + def close(partial: Boolean, lingerDuration: Duration): IO[Unit] /** Try to read a message from the specified [[Channel]]. * @@ -77,7 +77,10 @@ abstract class Connection { final def sink: Pipe[IO, (Channel, Message), Unit] = _.evalMap((send _).tupled) - final def autoCloseSink(partial: Boolean): Pipe[IO, (Channel, Message), Unit] = - s => Stream.bracket(IO.unit)(_ => close(partial)).flatMap(_ => sink(s)) + final def autoCloseSink( + partial: Boolean, + lingerDuration: Duration + ): Pipe[IO, (Channel, Message), Unit] = + s => Stream.bracket(IO.unit)(_ => close(partial, lingerDuration)).flatMap(_ => sink(s)) } diff --git a/modules/shared/channels/src/main/scala/almond/channels/zeromq/ZeromqConnection.scala b/modules/shared/channels/src/main/scala/almond/channels/zeromq/ZeromqConnection.scala index afc0fd8b6..29346efbf 100644 --- a/modules/shared/channels/src/main/scala/almond/channels/zeromq/ZeromqConnection.scala +++ b/modules/shared/channels/src/main/scala/almond/channels/zeromq/ZeromqConnection.scala @@ -12,6 +12,7 @@ import org.zeromq.ZMQ.{PollItem, Poller} import zmq.ZError import scala.concurrent.duration.Duration +import cats.Parallel final class ZeromqConnection( params: ConnectionParameters, @@ -203,17 +204,17 @@ final class ZeromqConnection( .getOrElse(IO.pure(None)) }.evalOn(threads.pollingEc).flatMap(identity) - def close(partial: Boolean): IO[Unit] = { + def close(partial: Boolean, lingerDuration: Duration): IO[Unit] = { val log0 = IO(log.debug(s"Closing channels for $params")) - val channels = Seq( + val channels = List( requests0, control0, stdin0 - ) ++ (if (partial) Nil else Seq(publish0)) + ) ::: (if (partial) Nil else List(publish0)) - val t = channels.foldLeft(IO.unit)((acc, c) => acc *> c.close) + val t = Parallel.parTraverse(channels)(_.close(lingerDuration)) val other = IO { log.debug(s"Closing things for $params" + (if (partial) " (partial)" else "")) diff --git a/modules/shared/channels/src/main/scala/almond/channels/zeromq/ZeromqSocket.scala b/modules/shared/channels/src/main/scala/almond/channels/zeromq/ZeromqSocket.scala index e4abcfb9c..565751a69 100644 --- a/modules/shared/channels/src/main/scala/almond/channels/zeromq/ZeromqSocket.scala +++ b/modules/shared/channels/src/main/scala/almond/channels/zeromq/ZeromqSocket.scala @@ -13,7 +13,7 @@ trait ZeromqSocket { def open: IO[Unit] def read: IO[Option[Message]] def send(message: Message): IO[Unit] - def close: IO[Unit] + def close(lingerDuration: Duration): IO[Unit] def channel: ZMQ.Socket } diff --git a/modules/shared/channels/src/main/scala/almond/channels/zeromq/ZeromqSocketImpl.scala b/modules/shared/channels/src/main/scala/almond/channels/zeromq/ZeromqSocketImpl.scala index 8139ad08e..2ff96a128 100644 --- a/modules/shared/channels/src/main/scala/almond/channels/zeromq/ZeromqSocketImpl.scala +++ b/modules/shared/channels/src/main/scala/almond/channels/zeromq/ZeromqSocketImpl.scala @@ -218,10 +218,16 @@ final class ZeromqSocketImpl( }.evalOn(ec) ) - val close: IO[Unit] = { + def close(lingerDuration: Duration): IO[Unit] = { val t = IO { if (!closed) { + val linger = lingerDuration match { + case d: FiniteDuration => d.toMillis.toInt + case _ => -1 + } + if (channel.getLinger != linger) + channel.setLinger(linger) channel.close() closed = true } diff --git a/modules/shared/channels/src/test/scala/almond/channels/zeromq/ZeromqConnectionTests.scala b/modules/shared/channels/src/test/scala/almond/channels/zeromq/ZeromqConnectionTests.scala index d6ffaff35..190511f39 100644 --- a/modules/shared/channels/src/test/scala/almond/channels/zeromq/ZeromqConnectionTests.scala +++ b/modules/shared/channels/src/test/scala/almond/channels/zeromq/ZeromqConnectionTests.scala @@ -43,8 +43,8 @@ object ZeromqConnectionTests extends TestSuite { _ = assert(resp._1 == Channel.Requests) _ = assert(resp._2.copy(idents = Nil) == msg0) // TODO Enforce this is run via bracketing - _ <- kernel.close(partial = false) - _ <- server.close(partial = false) + _ <- kernel.close(partial = false, lingerDuration = 2.seconds) + _ <- server.close(partial = false, lingerDuration = 2.seconds) } yield () t.unsafeRunSync()(IORuntime.global) diff --git a/modules/shared/channels/src/test/scala/almond/channels/zeromq/ZeromqSocketTests.scala b/modules/shared/channels/src/test/scala/almond/channels/zeromq/ZeromqSocketTests.scala index ce92c989b..cda9d48ce 100644 --- a/modules/shared/channels/src/test/scala/almond/channels/zeromq/ZeromqSocketTests.scala +++ b/modules/shared/channels/src/test/scala/almond/channels/zeromq/ZeromqSocketTests.scala @@ -10,6 +10,7 @@ import org.zeromq.{SocketType, ZMQ} import utest._ import scala.concurrent.ExecutionContext +import scala.concurrent.duration.DurationInt import java.nio.charset.StandardCharsets object ZeromqSocketTests extends TestSuite { @@ -86,8 +87,8 @@ object ZeromqSocketTests extends TestSuite { readOpt <- rep.read _ = assert(readOpt.contains(msg)) // FIXME Closing should be enforced via bracketing - _ <- req.close - _ <- rep.close + _ <- req.close(lingerDuration = 5.seconds) + _ <- rep.close(lingerDuration = 5.seconds) } yield () t.unsafeRunSync()(IORuntime.global) @@ -151,8 +152,8 @@ object ZeromqSocketTests extends TestSuite { readOpt <- rep.read _ = assert(readOpt.contains(msg)) // FIXME Closing should be enforced via bracketing - _ <- req.close - _ <- rep.close + _ <- req.close(lingerDuration = 5.seconds) + _ <- rep.close(lingerDuration = 5.seconds) } yield () t.unsafeRunSync()(IORuntime.global) diff --git a/modules/shared/kernel/src/main/scala/almond/kernel/Kernel.scala b/modules/shared/kernel/src/main/scala/almond/kernel/Kernel.scala index a18e898df..691a8a80d 100644 --- a/modules/shared/kernel/src/main/scala/almond/kernel/Kernel.scala +++ b/modules/shared/kernel/src/main/scala/almond/kernel/Kernel.scala @@ -24,6 +24,7 @@ import fs2.{Pipe, Stream} import scala.concurrent.ExecutionContext import scala.concurrent.duration.DurationInt +import scala.concurrent.duration.Duration final case class Kernel( interpreter: IOInterpreter, @@ -186,7 +187,8 @@ final case class Kernel( connection: ConnectionParameters, kernelId: String, zeromqThreads: ZeromqThreads, - leftoverMessages: Seq[(Channel, RawMessage)] + leftoverMessages: Seq[(Channel, RawMessage)], + lingerDuration: Duration ): IO[Unit] = for { t <- runOnConnectionAllowClose0( @@ -194,7 +196,8 @@ final case class Kernel( kernelId, zeromqThreads, leftoverMessages, - autoClose = true + autoClose = true, + lingerDuration = lingerDuration ) (run, _) = t _ <- run @@ -205,7 +208,8 @@ final case class Kernel( kernelId: String, zeromqThreads: ZeromqThreads, leftoverMessages: Seq[(Channel, RawMessage)], - autoClose: Boolean + autoClose: Boolean, + lingerDuration: Duration ): IO[(IO[Unit], Connection)] = for { c <- connection.channels( @@ -219,7 +223,11 @@ final case class Kernel( val run0 = for { _ <- c.open - _ <- run(c.stream(), c.autoCloseSink(partial = !autoClose), leftoverMessages) + _ <- run( + c.stream(), + c.autoCloseSink(partial = !autoClose, lingerDuration = lingerDuration), + leftoverMessages + ) } yield () (run0, c) } @@ -236,14 +244,16 @@ final case class Kernel( kernelId: String, zeromqThreads: ZeromqThreads, leftoverMessages: Seq[(Channel, RawMessage)], - autoClose: Boolean + autoClose: Boolean, + lingerDuration: Duration ): IO[(IO[Seq[(Channel, RawMessage)]], Connection)] = runOnConnectionAllowClose0( connection, kernelId, zeromqThreads, leftoverMessages, - autoClose + autoClose, + lingerDuration ).map { case (run, conn) => val run0 = run.attempt.flatMap { @@ -264,7 +274,8 @@ final case class Kernel( kernelId: String, zeromqThreads: ZeromqThreads, leftoverMessages: Seq[(Channel, RawMessage)], - autoClose: Boolean + autoClose: Boolean, + lingerDuration: Duration ): IO[(IO[Seq[(Channel, RawMessage)]], Connection)] = for { _ <- { @@ -285,7 +296,8 @@ final case class Kernel( kernelId, zeromqThreads, leftoverMessages, - autoClose + autoClose, + lingerDuration ) } yield value @@ -294,7 +306,8 @@ final case class Kernel( kernelId: String, zeromqThreads: ZeromqThreads, leftoverMessages: Seq[(Channel, RawMessage)], - autoClose: Boolean + autoClose: Boolean, + lingerDuration: Duration ): IO[Unit] = for { t <- runOnConnectionFileAllowClose( @@ -302,7 +315,8 @@ final case class Kernel( kernelId, zeromqThreads, leftoverMessages, - autoClose + autoClose, + lingerDuration ) (run, _) = t _ <- run @@ -313,7 +327,8 @@ final case class Kernel( kernelId: String, zeromqThreads: ZeromqThreads, leftoverMessages: Seq[(Channel, RawMessage)], - autoClose: Boolean + autoClose: Boolean, + lingerDuration: Duration ): IO[Unit] = for { t <- runOnConnectionFileAllowClose( @@ -321,7 +336,8 @@ final case class Kernel( kernelId, zeromqThreads, leftoverMessages, - autoClose + autoClose, + lingerDuration ) (run, _) = t _ <- run @@ -332,14 +348,16 @@ final case class Kernel( kernelId: String, zeromqThreads: ZeromqThreads, leftoverMessages: Seq[(Channel, RawMessage)], - autoClose: Boolean + autoClose: Boolean, + lingerDuration: Duration ): IO[(IO[Seq[(Channel, RawMessage)]], Connection)] = runOnConnectionFileAllowClose( Paths.get(connectionPath), kernelId, zeromqThreads, leftoverMessages, - autoClose + autoClose, + lingerDuration ) } From 9ca73eb8bc8a2688d8b365705d7b7fdf7b4fa2cb Mon Sep 17 00:00:00 2001 From: Alexandre Archambault Date: Tue, 28 May 2024 17:08:52 +0200 Subject: [PATCH 4/8] Update scalameta to 4.9.4 --- project/deps.sc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/deps.sc b/project/deps.sc index c6db455d7..5eddc3b31 100644 --- a/project/deps.sc +++ b/project/deps.sc @@ -60,7 +60,7 @@ object Deps { def osLib = ivy"com.lihaoyi::os-lib:0.10.1" def pprint = ivy"com.lihaoyi::pprint:0.9.0" def scalafmtDynamic = ivy"org.scalameta::scalafmt-dynamic:${Versions.scalafmt}" - def scalameta = ivy"org.scalameta::scalameta:4.8.3" + def scalameta = ivy"org.scalameta::scalameta:4.9.4" def scalaparse = ivy"com.lihaoyi::scalaparse:3.1.0" def scalapy = ivy"me.shadaj::scalapy-core:0.5.2" def scalaReflect(sv: String) = ivy"org.scala-lang:scala-reflect:$sv" From c39fa0ebde9e2882fb9f5c5462fe9be8e566a3bb Mon Sep 17 00:00:00 2001 From: Alexandre Archambault Date: Tue, 28 May 2024 17:20:50 +0200 Subject: [PATCH 5/8] NIT Formatting --- .../src/main/scala/almond/echo/Options.scala | 9 +++-- .../src/main/scala/almond/Options.scala | 34 +++++++++---------- 2 files changed, 23 insertions(+), 20 deletions(-) diff --git a/modules/echo/src/main/scala/almond/echo/Options.scala b/modules/echo/src/main/scala/almond/echo/Options.scala index 4ec4c7bf0..7ac8dc223 100644 --- a/modules/echo/src/main/scala/almond/echo/Options.scala +++ b/modules/echo/src/main/scala/almond/echo/Options.scala @@ -7,19 +7,22 @@ import caseapp.core.parser.Parser import caseapp.Hidden import scala.concurrent.duration.{Duration, DurationInt} +// format: off final case class Options( connectionFile: Option[String] = None, @HelpMessage("Log level (one of none, error, warn, info, or debug)") - log: String = "warn", + log: String = "warn", install: Boolean = false, @Recurse - installOptions: InstallOptions = InstallOptions(), + installOptions: InstallOptions = InstallOptions(), + @HelpMessage( """Time given to the client to accept ZeroMQ messages before exiting. Parsed with scala.concurrent.duration.Duration, this accepts things like "Inf" or "5 seconds"""" ) @Hidden - linger: Option[String] = None + linger: Option[String] = None ) { + // format: on lazy val lingerDuration = linger .map(_.trim) diff --git a/modules/scala/scala-kernel/src/main/scala/almond/Options.scala b/modules/scala/scala-kernel/src/main/scala/almond/Options.scala index 165fb4dd8..57576186b 100644 --- a/modules/scala/scala-kernel/src/main/scala/almond/Options.scala +++ b/modules/scala/scala-kernel/src/main/scala/almond/Options.scala @@ -25,7 +25,7 @@ import scala.jdk.CollectionConverters._ final case class Options( install: Boolean = false, @Recurse - installOptions: InstallOptions = InstallOptions(), + installOptions: InstallOptions = InstallOptions(), extraRepository: List[String] = Nil, banner: Option[String] = None, link: List[String] = Nil, @@ -36,50 +36,50 @@ final case class Options( defaultAutoDependencies: Boolean = true, defaultAutoVersions: Boolean = true, @HelpMessage("Default almond-spark version to load when Spark is loaded as a library") - defaultAlmondSparkVersion: Option[String] = None, + defaultAlmondSparkVersion: Option[String] = None, @HelpMessage("Default almond-scalapy version to load when ScalaPy is loaded as a library") - defaultAlmondScalapyVersion: Option[String] = None, + defaultAlmondScalapyVersion: Option[String] = None, @HelpMessage("Force Maven properties during dependency resolution") - forceProperty: List[String] = Nil, + forceProperty: List[String] = Nil, @HelpMessage("Enable Maven profile (start with ! to disable)") - profile: List[String] = Nil, + profile: List[String] = Nil, @HelpMessage("Log level (one of none, error, warn, info, debug)") - log: Option[String] = None, + log: Option[String] = None, @HelpMessage("Send log to a file rather than stderr") @ValueDescription("/path/to/log-file") - logTo: Option[String] = None, + logTo: Option[String] = None, connectionFile: Option[String] = None, // For class loader isolation, the user code is loaded from the classloader of the api module. // If the right -i / -I options are passed to coursier bootstrap when generating a launcher, that loader // only sees the api module and its dependencies, rather than the full classpath of almond. @HelpMessage("Use class loader that loaded the api module rather than the context class loader") - specificLoader: Boolean = true, + specificLoader: Boolean = true, @HelpMessage( "Start a metabrowse server for go to source navigation (linked from Jupyter inspections, server is started upon first inspection)" ) - metabrowse: Boolean = false, + metabrowse: Boolean = false, @HelpMessage("Trap what user code sends to stdout and stderr") - trapOutput: Boolean = false, + trapOutput: Boolean = false, @HelpMessage("If false, duplicates stdout/stderr to console, similar to IPKernelApp.quiet") - quiet: Boolean = true, + quiet: Boolean = true, @HelpMessage("Disable ammonite compilation cache") - disableCache: Boolean = false, + disableCache: Boolean = false, @HelpMessage("Whether to automatically update lazy val-s upon computation") - autoUpdateLazyVals: Boolean = true, + autoUpdateLazyVals: Boolean = true, @HelpMessage("Whether to automatically update var-s upon change") - autoUpdateVars: Boolean = true, + autoUpdateVars: Boolean = true, @HelpMessage("Whether to silence imports (not printing them back in output)") silentImports: Boolean = false, @HelpMessage("Whether to use a notebook-specific coursier logger") useNotebookCoursierLogger: Boolean = false, @HelpMessage("Whether to enable variable inspector") - variableInspector: Option[Boolean] = None, + variableInspector: Option[Boolean] = None, @HelpMessage("Whether to process format requests with scalafmt") - scalafmt: Boolean = true, + scalafmt: Boolean = true, @HelpMessage( "Whether to use 'Thread.interrupt' method or deprecated 'Thread.stop' method (default) when interrupting kernel." ) - useThreadInterrupt: Boolean = false, + useThreadInterrupt: Boolean = false, @ExtraName("outputDir") outputDirectory: Option[String] = None, From 623215c048e0844cc8fb733ca7cf09ebaa079476 Mon Sep 17 00:00:00 2001 From: Alexandre Archambault Date: Wed, 29 May 2024 15:47:55 +0200 Subject: [PATCH 6/8] Drop support for Scala 2.12.15 / 2.13.9 These are not supported anymore by Metals, that we used indirectly here via mtags --- project/deps.sc | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/project/deps.sc b/project/deps.sc index 5eddc3b31..b8e6cb7f3 100644 --- a/project/deps.sc +++ b/project/deps.sc @@ -87,11 +87,9 @@ object ScalaVersions { scala213, "2.13.11", "2.13.10", - "2.13.9", scala212, "2.12.17", - "2.12.16", - "2.12.15" + "2.12.16" ).distinct def binary(sv: String) = From 6cb6c607a314d8ae938c19d811473d4f17aaf414 Mon Sep 17 00:00:00 2001 From: Alexandre Archambault Date: Wed, 29 May 2024 15:54:30 +0200 Subject: [PATCH 7/8] Lazily initialize inspections stuff MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Seems it's only going to work with Java >= 11 from now on… --- .../integration/KernelTestsDefinitions.scala | 9 +++++- .../main/scala/almond/ScalaInterpreter.scala | 30 +++++++++++-------- 2 files changed, 26 insertions(+), 13 deletions(-) diff --git a/modules/scala/integration/src/main/scala/almond/integration/KernelTestsDefinitions.scala b/modules/scala/integration/src/main/scala/almond/integration/KernelTestsDefinitions.scala index 030e492d7..da5ee4bd8 100644 --- a/modules/scala/integration/src/main/scala/almond/integration/KernelTestsDefinitions.scala +++ b/modules/scala/integration/src/main/scala/almond/integration/KernelTestsDefinitions.scala @@ -8,6 +8,13 @@ abstract class KernelTestsDefinitions extends AlmondFunSuite { override def mightRetry = true + lazy val javaMajorVersion = { + val versionString = + sys.props.getOrElse("java.version", sys.error("java.version property not set")) + .stripPrefix("1.") // for Java 8 and below + versionString.takeWhile(_ != '.').toInt + } + test("jvm-repr") { kernelLauncher.withKernel { implicit runner => implicit val sessionId: SessionId = SessionId() @@ -102,7 +109,7 @@ abstract class KernelTestsDefinitions extends AlmondFunSuite { } } - if (kernelLauncher.defaultScalaVersion.startsWith("2.")) + if (kernelLauncher.defaultScalaVersion.startsWith("2.") && javaMajorVersion >= 11) test("inspections") { kernelLauncher.withKernel { implicit runner => implicit val sessionId: SessionId = SessionId() diff --git a/modules/scala/scala-interpreter/src/main/scala/almond/ScalaInterpreter.scala b/modules/scala/scala-interpreter/src/main/scala/almond/ScalaInterpreter.scala index e2e793782..fb976d757 100644 --- a/modules/scala/scala-interpreter/src/main/scala/almond/ScalaInterpreter.scala +++ b/modules/scala/scala-interpreter/src/main/scala/almond/ScalaInterpreter.scala @@ -34,17 +34,22 @@ final class ScalaInterpreter( if (params.extraClassPath.nonEmpty) frames0().head.addClasspath(params.extraClassPath.map(_.toNIO.toUri.toURL)) - private val inspections = new ScalaInterpreterInspections( - logCtx, - params.metabrowse, - params.metabrowseHost, - params.metabrowsePort, - ammonite.compiler.CompilerBuilder.scalaVersion, - ammInterp - .compilerManager - .asInstanceOf[ammonite.compiler.CompilerLifecycleManager], - frames0() - ) + private var inspectionsInitialized = false + private lazy val inspections = { + val value = new ScalaInterpreterInspections( + logCtx, + params.metabrowse, + params.metabrowseHost, + params.metabrowsePort, + ammonite.compiler.CompilerBuilder.scalaVersion, + ammInterp + .compilerManager + .asInstanceOf[ammonite.compiler.CompilerLifecycleManager], + frames0() + ) + inspectionsInitialized = true + value + } private val colors0: Ref[Colors] = Ref(params.initialColors) @@ -306,7 +311,8 @@ final class ScalaInterpreter( case NonFatal(e) => log.warn("Caught exception while trying to run exit hooks", e) } - inspections.shutdown() + if (inspectionsInitialized) + inspections.shutdown() } } From 1023fdab0b2bff05b6b557157b57875e04a3bca1 Mon Sep 17 00:00:00 2001 From: Alexandre Archambault Date: Tue, 28 May 2024 21:02:52 +0200 Subject: [PATCH 8/8] Update metabrowse to 0.2.13 --- .../scala-2/almond/internals/ScalaInterpreterInspections.scala | 3 ++- project/deps.sc | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/modules/scala/scala-interpreter/src/main/scala-2/almond/internals/ScalaInterpreterInspections.scala b/modules/scala/scala-interpreter/src/main/scala-2/almond/internals/ScalaInterpreterInspections.scala index 4f7b57744..af7552fa2 100644 --- a/modules/scala/scala-interpreter/src/main/scala-2/almond/internals/ScalaInterpreterInspections.scala +++ b/modules/scala/scala-interpreter/src/main/scala-2/almond/internals/ScalaInterpreterInspections.scala @@ -141,7 +141,8 @@ final class ScalaInterpreterInspections( try docs.documentation( sym, - () => symbol.allOverriddenSymbols.map(_.toSemantic).toList.asJava + () => symbol.allOverriddenSymbols.map(_.toSemantic).toList.asJava, + scala.meta.pc.ContentType.MARKDOWN ) catch { case e: IndexingExceptions.InvalidSymbolException => diff --git a/project/deps.sc b/project/deps.sc index b8e6cb7f3..47c2562e7 100644 --- a/project/deps.sc +++ b/project/deps.sc @@ -56,7 +56,7 @@ object Deps { def jvmRepr = ivy"com.github.jupyter:jvm-repr:0.4.0" def mdoc = ivy"org.scalameta::mdoc:2.5.2" def munit = ivy"org.scalameta::munit:1.0.0" - def metabrowseServer = ivy"org.scalameta:::metabrowse-server:0.2.12" + def metabrowseServer = ivy"org.scalameta:::metabrowse-server:0.2.13" def osLib = ivy"com.lihaoyi::os-lib:0.10.1" def pprint = ivy"com.lihaoyi::pprint:0.9.0" def scalafmtDynamic = ivy"org.scalameta::scalafmt-dynamic:${Versions.scalafmt}"