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

org.jetbrains.kotlinx.jupyter.api.libraries.JupyterIntegration.kt Maven / Gradle / Ivy

There is a newer version: 0.12.0-335
Show newest version
package org.jetbrains.kotlinx.jupyter.api.libraries

import org.jetbrains.kotlinx.jupyter.api.AfterCellExecutionCallback
import org.jetbrains.kotlinx.jupyter.api.ClassAnnotationHandler
import org.jetbrains.kotlinx.jupyter.api.ClassDeclarationsCallback
import org.jetbrains.kotlinx.jupyter.api.Code
import org.jetbrains.kotlinx.jupyter.api.CodeCell
import org.jetbrains.kotlinx.jupyter.api.CodePreprocessor
import org.jetbrains.kotlinx.jupyter.api.ExecutionCallback
import org.jetbrains.kotlinx.jupyter.api.FieldHandler
import org.jetbrains.kotlinx.jupyter.api.FieldValue
import org.jetbrains.kotlinx.jupyter.api.FileAnnotationCallback
import org.jetbrains.kotlinx.jupyter.api.FileAnnotationHandler
import org.jetbrains.kotlinx.jupyter.api.InternalVariablesMarker
import org.jetbrains.kotlinx.jupyter.api.InterruptionCallback
import org.jetbrains.kotlinx.jupyter.api.KotlinKernelHost
import org.jetbrains.kotlinx.jupyter.api.KotlinKernelVersion
import org.jetbrains.kotlinx.jupyter.api.Notebook
import org.jetbrains.kotlinx.jupyter.api.RendererFieldHandler
import org.jetbrains.kotlinx.jupyter.api.RendererHandler
import org.jetbrains.kotlinx.jupyter.api.RendererTypeHandler
import org.jetbrains.kotlinx.jupyter.api.ResultHandlerExecution
import org.jetbrains.kotlinx.jupyter.api.SubtypeRendererTypeHandler
import org.jetbrains.kotlinx.jupyter.api.SubtypeThrowableRenderer
import org.jetbrains.kotlinx.jupyter.api.TextRenderer
import org.jetbrains.kotlinx.jupyter.api.TextRendererWithPriority
import org.jetbrains.kotlinx.jupyter.api.ThrowableRenderer
import org.jetbrains.kotlinx.jupyter.api.TypeName
import org.jetbrains.kotlinx.jupyter.api.VariableDeclarationCallback
import org.jetbrains.kotlinx.jupyter.api.VariableUpdateCallback
import org.jetbrains.kotlinx.jupyter.util.AcceptanceRule
import org.jetbrains.kotlinx.jupyter.util.NameAcceptanceRule
import kotlin.reflect.KProperty

/**
 * Base class for library integration with Jupyter Kernel via DSL
 * Derive from this class and pass registration callback into constructor
 */
abstract class JupyterIntegration : LibraryDefinitionProducer {
    abstract fun Builder.onLoaded()

    class Builder(val notebook: Notebook) {
        private val renderers = mutableListOf()

        private val textRenderers = mutableListOf()

        private val throwableRenderers = mutableListOf()

        private val init = mutableListOf>()

        private val beforeCellExecution = mutableListOf>()

        private val afterCellExecution = mutableListOf()

        private val shutdownCallbacks = mutableListOf>()

        private val converters = mutableListOf()

        private val classAnnotations = mutableListOf()

        private val fileAnnotations = mutableListOf()

        private val resources = mutableListOf()

        private val imports = mutableListOf()

        private val dependencies = mutableListOf()

        private val repositories = mutableListOf()

        private val codePreprocessors = mutableListOf()

        private val internalVariablesMarkers = mutableListOf()

        private val integrationTypeNameRules = mutableListOf>()

        private val interruptionCallbacks = mutableListOf()

        private val colorSchemeChangedCallbacks = mutableListOf()

        private var minKernelVersion: KotlinKernelVersion? = null

        private val options: MutableMap = mutableMapOf()

        private var website: String? = null
        private var description: String? = null

        fun addRenderer(handler: RendererFieldHandler) {
            renderers.add(handler)
        }

        // Left for ABI compatibility
        fun addRenderer(handler: RendererHandler) {
            renderers.add(handler)
        }

        // Left for ABI compatibility
        fun addRenderer(handler: RendererTypeHandler) {
            renderers.add(handler)
        }

        fun addTextRenderer(renderer: TextRenderer) {
            textRenderers.add(TextRendererWithPriority(renderer))
        }

        fun addThrowableRenderer(renderer: ThrowableRenderer) {
            throwableRenderers.add(renderer)
        }

        fun addTypeConverter(handler: FieldHandler) {
            converters.add(handler)
        }

        fun addClassAnnotationHandler(handler: ClassAnnotationHandler) {
            classAnnotations.add(handler)
        }

        fun addFileAnnotationHanlder(handler: FileAnnotationHandler) {
            fileAnnotations.add(handler)
        }

        fun addCodePreprocessor(preprocessor: CodePreprocessor) {
            codePreprocessors.add(preprocessor)
        }

        fun markVariableInternal(marker: InternalVariablesMarker) {
            internalVariablesMarkers.add(marker)
        }

        inline fun  render(noinline renderer: CodeCell.(T) -> Any) {
            return renderWithHost { _, value: T -> renderer(this, value) }
        }

        inline fun  renderWithHost(noinline renderer: CodeCell.(ExecutionHost, T) -> Any) {
            val execution =
                ResultHandlerExecution { host, property ->
                    val currentCell =
                        notebook.currentCell
                            ?: throw IllegalStateException("Current cell should not be null on renderer invocation")
                    FieldValue(renderer(currentCell, host, property.value as T), null)
                }
            addRenderer(SubtypeRendererTypeHandler(T::class, execution))
        }

        inline fun  renderThrowable(noinline renderer: (E) -> Any) {
            addThrowableRenderer(SubtypeThrowableRenderer(E::class, renderer))
        }

        fun resource(resource: LibraryResource) {
            resources.add(resource)
        }

        fun import(vararg paths: String) {
            imports.addAll(paths)
        }

        inline fun  import() {
            import(T::class.qualifiedName!!)
        }

        inline fun  importPackage() {
            val name = T::class.qualifiedName!!
            val lastDot = name.lastIndexOf(".")
            if (lastDot != -1) {
                import(name.substring(0, lastDot + 1) + "*")
            }
        }

        fun dependencies(vararg paths: String) {
            dependencies.addAll(paths)
        }

        fun repositories(vararg paths: String) {
            for (path in paths) {
                repository(path)
            }
        }

        fun addRepository(repository: KernelRepository) {
            repositories.add(repository)
        }

        fun repository(
            path: String,
            username: String? = null,
            password: String? = null,
        ) {
            addRepository(KernelRepository(path, username, password))
        }

        fun onLoaded(callback: KotlinKernelHost.() -> Unit) {
            init.add(callback)
        }

        fun onShutdown(callback: KotlinKernelHost.() -> Unit) {
            shutdownCallbacks.add(callback)
        }

        fun beforeCellExecution(callback: KotlinKernelHost.() -> Unit) {
            beforeCellExecution.add(callback)
        }

        fun afterCellExecution(callback: AfterCellExecutionCallback) {
            afterCellExecution.add(callback)
        }

        /**
         * Runs [callback] for every snippet property of compile-time subtype of type [T]
         *
         * [callback] gives access to both runtime value of the property and its [KProperty] object
         */
        inline fun  onVariable(noinline callback: VariableDeclarationCallback) {
            addTypeConverter(FieldHandlerFactory.createDeclareHandler(TypeDetection.COMPILE_TIME, callback))
        }

        /**
         * Runs [callback] for every snippet property of compile-time subtype of type [T]
         *
         * [callback] gives access to both runtime value of the property and its [KProperty] object
         *
         * [callback] should usually execute some code that:
         * - has non-Unit result and return the name of result field
         * - defines some variable and return its name
         *
         * Original variable will then be **reassigned** to this new name.
         *
         * For example:
         *
         * ```
         * updateVariable { value, kProperty ->
         *     // MyWrapper class should be previously defined in the notebook
         *     execute("MyWrapper(${kProperty.name})").name
         * }
         * ```
         * or
         * ```
         * updateVariable { value, kProperty ->
         *     // MyWrapper class should be previously defined in the notebook
         *     execute("val wrapper = MyWrapper(${kProperty.name})")
         *     return "wrapper"
         * }
         * ```
         */
        inline fun  updateVariable(noinline callback: VariableUpdateCallback) {
            addTypeConverter(FieldHandlerFactory.createUpdateHandler(TypeDetection.COMPILE_TIME, callback))
        }

        /**
         * Same as [onVariable], but based on runtime type that is figured out by reflection
         */
        inline fun  onVariableByRuntimeType(noinline callback: VariableDeclarationCallback) {
            addTypeConverter(FieldHandlerFactory.createDeclareHandler(TypeDetection.RUNTIME, callback))
        }

        /**
         * Same as [updateVariable], but based on runtime type that is figured out by reflection
         */
        inline fun  updateVariableByRuntimeType(noinline callback: VariableUpdateCallback) {
            addTypeConverter(FieldHandlerFactory.createUpdateHandler(TypeDetection.RUNTIME, callback))
        }

        inline fun  onClassAnnotation(noinline callback: ClassDeclarationsCallback) {
            addClassAnnotationHandler(ClassAnnotationHandler(T::class, callback))
        }

        inline fun  onFileAnnotation(noinline callback: FileAnnotationCallback) {
            addFileAnnotationHanlder(FileAnnotationHandler(T::class, callback))
        }

        fun preprocessCodeWithLibraries(callback: KotlinKernelHost.(Code) -> CodePreprocessor.Result) {
            addCodePreprocessor(
                object : CodePreprocessor {
                    override fun process(
                        code: String,
                        host: KotlinKernelHost,
                    ): CodePreprocessor.Result {
                        return host.callback(code)
                    }
                },
            )
        }

        fun preprocessCode(callback: KotlinKernelHost.(Code) -> Code) {
            preprocessCodeWithLibraries { CodePreprocessor.Result(this.callback(it)) }
        }

        /**
         * All integrations transitively loaded by this integration will be tested against
         * passed acceptance rule and won't be loaded if the rule returned `false`.
         * If there were no acceptance rules that returned not-null values, integration
         * **will be loaded**. If there are several acceptance rules that returned not-null values,
         * the latest one will be taken into account.
         */
        fun addIntegrationTypeNameRule(rule: AcceptanceRule) {
            integrationTypeNameRules.add(rule)
        }

        /**
         * See [addIntegrationTypeNameRule]
         */
        fun acceptIntegrationTypeNameIf(predicate: (TypeName) -> Boolean) {
            addIntegrationTypeNameRule(NameAcceptanceRule(true, predicate))
        }

        /**
         * See [addIntegrationTypeNameRule]
         */
        fun discardIntegrationTypeNameIf(predicate: (TypeName) -> Boolean) {
            addIntegrationTypeNameRule(NameAcceptanceRule(false, predicate))
        }

        fun onInterrupt(action: InterruptionCallback) {
            interruptionCallbacks.add(action)
        }

        fun onColorSchemeChange(action: ColorSchemeChangedCallback) {
            colorSchemeChangedCallbacks.add(action)
        }

        fun setMinimalKernelVersion(version: KotlinKernelVersion) {
            minKernelVersion = version
        }

        fun setMinimalKernelVersion(version: String) {
            setMinimalKernelVersion(KotlinKernelVersion.from(version) ?: error("Wrong kernel version format: $version"))
        }

        fun addOption(
            name: String,
            value: String,
        ) {
            options[name] = value
        }

        fun addOptions(options: Map) {
            this.options.putAll(options)
        }

        fun setDescription(description: String) {
            this.description = description
        }

        fun setWebsite(website: String) {
            this.website = website
        }

        internal fun getDefinition() =
            libraryDefinition {
                it.description = description
                it.website = website
                it.options = options
                it.init = init
                it.renderers = renderers
                it.textRenderers = textRenderers
                it.throwableRenderers = throwableRenderers
                it.converters = converters
                it.imports = imports
                it.dependencies = dependencies
                it.repositories = repositories
                it.initCell = beforeCellExecution
                it.afterCellExecution = afterCellExecution
                it.shutdown = shutdownCallbacks
                it.classAnnotations = classAnnotations
                it.fileAnnotations = fileAnnotations
                it.resources = resources
                it.codePreprocessors = codePreprocessors
                it.internalVariablesMarkers = internalVariablesMarkers
                it.integrationTypeNameRules = integrationTypeNameRules
                it.interruptionCallbacks = interruptionCallbacks
                it.colorSchemeChangedCallbacks = colorSchemeChangedCallbacks
                it.minKernelVersion = minKernelVersion
            }
    }

    override fun getDefinitions(notebook: Notebook): List {
        val builder = Builder(notebook)
        builder.onLoaded()
        return listOf(builder.getDefinition())
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy