All Downloads are FREE. Search and download functionalities are using the official Maven repository.

polynote.kernel.interpreter.Interpreter.scala Maven / Gradle / Ivy

The newest version!
package polynote.kernel
package interpreter

import java.util.ServiceLoader

import scala.collection.JavaConverters._
import cats.syntax.semigroup._
import cats.instances.map._
import cats.instances.list._
import polynote.messages.CellID
import polynote.kernel.environment.{Config, CurrentNotebook, CurrentTask}
import polynote.kernel.task.TaskManager
import zio.blocking.{Blocking, effectBlocking}
import zio.{Has, Layer, RIO, Task, ZIO, ZLayer}

trait Interpreter {

  /**
    * Run the given code in the given [[State]], returning an updated [[State]].
    *
    * @param code  The code string to be executed
    * @param state The given [[State]] will have the Cell ID of the cell containing the given code, and it will
    *              initially have empty values. Its `prev` will point to the [[State]] returned by the closes prior
    *              executed cell, or to [[State.Root]] if there is no such cell.
    */
  def run(code: String, state: State): RIO[InterpreterEnv, State]

  /**
    * Ask for completions (if applicable) at the given position in the given code string.
    *
    * @param code  The code string in which completions are requested
    * @param pos   The position within the code string at which completions are requested
    * @param state The given [[State]] will have the Cell ID of the cell containing the given code, and it will
    *              initially have empty values. Its `prev` will point to the [[State]] returned by the closes prior
    *              executed cell, or to [[State.Root]] if there is no such cell.
    */
  def completionsAt(code: String, pos: Int, state: State): RIO[Blocking, List[Completion]]

  /**
    * Ask for parameter hints (if applicable) at the given position in the given code string.
    *
    * @param code  The code string in which parameter hints are requested
    * @param pos   The position within the code string at which parameter hints are requested
    * @param state The given [[State]] will have the Cell ID of the cell containing the given code, and it will
    *              initially have empty values. Its `prev` will point to the [[State]] returned by the closes prior
    *              executed cell, or to [[State.Root]] if there is no such cell.
    */
  def parametersAt(code: String, pos: Int, state: State): RIO[Blocking, Option[Signatures]]

  /**
    * Initialize the interpreter, running any predef code and setting up an initial state.
    * @param state A [[State]] which is the current state of the notebook execution.
    * @return An initial state for this interpreter
    */
  def init(state: State): RIO[InterpreterEnv, State]

  /**
    * Shut down this interpreter, releasing its resources and ending any internally managed tasks or processes
    */
  def shutdown(): Task[Unit]
}

object Interpreter {

  /**
    * An interpreter factory gets a Scala compiler and access to the current notebook, and constructs an interpreter.
    * It's also responsible for fetching/installing/configuring whatever dependencies are needed based on the
    * notebook configuration, or propagating any necessary notebook configuration state to the interpreter (as the
    * interpreter methods won't have access to the notebook)
    */
  trait Factory {
    def languageName: String
    def apply(): RIO[BaseEnv with GlobalEnv with ScalaCompiler.Provider with CurrentNotebook with CurrentTask with TaskManager, Interpreter]
    def requireSpark: Boolean = false
    def priority: Int = 0
    def sparkConfig(config: Map[String, String]): RIO[BaseEnv with GlobalEnv, Map[String, String]] = ZIO.succeed(config)
  }

  type Factories = Has[Map[String, List[Interpreter.Factory]]]

  object Factories {
    def layer(factories: Map[String, List[Interpreter.Factory]]): Layer[Nothing, Factories] = ZLayer.succeed(factories)
    def load: ZLayer[Blocking, Throwable, Factories] = ZLayer.fromEffect(Loader.load)
    def access: ZIO[Factories, Nothing, Map[String, List[Interpreter.Factory]]] = ZIO.access[Factories](_.get)
  }

  def availableFactories(language: String): RIO[Factories, List[Interpreter.Factory]] = for {
    allFactories <- ZIO.access[Factories](_.get)
    factories    <- ZIO.fromOption(allFactories.get(language)).mapError(_ => new IllegalArgumentException(s"No interpreter for $language"))
  } yield factories

}

trait Loader {
  def factories: Map[String, Interpreter.Factory]
}

object Loader {
  private lazy val unsafeLoad = ServiceLoader.load(classOf[Loader]).iterator.asScala.toList
  def load: RIO[Blocking, Map[String, List[Interpreter.Factory]]] = effectBlocking(unsafeLoad).map {
    loaders =>
      loaders.map(_.factories.mapValues(List(_)).toMap).foldLeft(Map.empty[String, List[Interpreter.Factory]])(_ |+| _).toMap.mapValues(_.sortBy(f => (-f.priority, !f.getClass.getName.startsWith("polynote"), f.getClass.getName))).toMap
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy