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

org.jetbrains.kotlinx.jupyter.libraries.LibraryDescriptor.kt Maven / Gradle / Ivy

Go to download

Implementation of REPL compiler and preprocessor for Jupyter dialect of Kotlin (IDE-compatible)

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

import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import org.jetbrains.kotlinx.jupyter.api.ExactRendererTypeHandler
import org.jetbrains.kotlinx.jupyter.api.KotlinKernelVersion
import org.jetbrains.kotlinx.jupyter.api.libraries.CodeExecution
import org.jetbrains.kotlinx.jupyter.api.libraries.DescriptorVariables
import org.jetbrains.kotlinx.jupyter.api.libraries.DescriptorVariablesSerializer
import org.jetbrains.kotlinx.jupyter.api.libraries.KernelRepository
import org.jetbrains.kotlinx.jupyter.api.libraries.LibraryDefinition
import org.jetbrains.kotlinx.jupyter.api.libraries.LibraryResource
import org.jetbrains.kotlinx.jupyter.api.libraries.Variable
import org.jetbrains.kotlinx.jupyter.api.libraries.filter
import org.jetbrains.kotlinx.jupyter.api.libraries.libraryDefinition
import org.jetbrains.kotlinx.jupyter.config.currentKernelVersion
import org.jetbrains.kotlinx.jupyter.exceptions.ReplPreprocessingException
import org.jetbrains.kotlinx.jupyter.util.KotlinKernelVersionSerializer
import org.jetbrains.kotlinx.jupyter.util.PatternNameAcceptanceRule
import org.jetbrains.kotlinx.jupyter.util.RenderersSerializer
import org.jetbrains.kotlinx.jupyter.util.replaceVariables

@Serializable
data class LibraryDescriptor(
    val dependencies: List = emptyList(),
    @Serializable(DescriptorVariablesSerializer::class)
    @SerialName("properties")
    val variables: DescriptorVariables = DescriptorVariables(),
    val initCell: List = emptyList(),
    val imports: List = emptyList(),
    val repositories: List = emptyList(),
    val init: List = emptyList(),
    val shutdown: List = emptyList(),
    @Serializable(RenderersSerializer::class)
    val renderers: List = emptyList(),
    val resources: List = emptyList(),
    val link: String? = null,
    val description: String? = null,
    @Serializable(KotlinKernelVersionSerializer::class)
    val minKernelVersion: KotlinKernelVersion? = null,
    val formatVersion: LibraryDescriptorFormatVersion? = null,
    val integrationTypeNameRules: List = emptyList(),
) {
    fun convertToDefinition(
        arguments: List,
        options: LibraryDescriptorGlobalOptions = DefaultLibraryDescriptorGlobalOptions,
    ): LibraryDefinition {
        assertDescriptorFormatVersionCompatible(this)
        val newVariables = variables.filter { !options.isPropertyIgnored(it.name) }
        val descriptor = if (variables != newVariables) this.copy(variables = newVariables) else this

        val mapping = substituteArguments(descriptor.variables, arguments)
        return descriptor.processDescriptor(mapping)
    }

    /**
     * Matches a list of actual library arguments with declared library parameters
     * Arguments can be named or not. Named arguments should be placed after unnamed
     * Parameters may have default value
     *
     * @return A name-to-value map of library arguments
     */
    private fun substituteArguments(
        descriptorVariables: DescriptorVariables,
        arguments: List,
    ): Map {
        val result = mutableMapOf()

        fun addResult(
            name: String,
            value: String,
        ) {
            result[name] = substituteKernelVars(value)
        }

        val parameters = descriptorVariables.properties

        if (descriptorVariables.hasOrder) {
            var namedPart = false
            for ((i, arg) in arguments.withIndex()) {
                val isNamed = arg.name.isNotEmpty()
                if (namedPart) {
                    if (!isNamed) throw ReplPreprocessingException("Unnamed argument is not allowed after named one")
                } else {
                    if (isNamed) namedPart = true
                }

                assert(namedPart == isNamed) { "Named argument might be only in the named part of call" }

                if (isNamed) {
                    addResult(arg.name, arg.value)
                } else {
                    if (parameters.lastIndex < i) {
                        throw ReplPreprocessingException(
                            "Number of unnamed arguments cannot be more than the number of parameters",
                        )
                    }
                    addResult(parameters[i].name, arg.value)
                }
            }
        } else {
            if (arguments.any { it.name.isEmpty() }) {
                if (parameters.count() != 1) {
                    throw ReplPreprocessingException("Unnamed argument is allowed only if library has a single property")
                }
                if (arguments.count() != 1) {
                    throw ReplPreprocessingException("Too many unnamed arguments")
                }
                addResult(parameters[0].name, arguments[0].value)
                return result
            }

            arguments.forEach {
                addResult(it.name, it.value)
            }
        }

        parameters.forEach {
            if (!result.containsKey(it.name)) {
                addResult(it.name, it.value)
            }
        }
        return result
    }

    private fun processDescriptor(mapping: Map): LibraryDefinition {
        return libraryDefinition {
            it.options = mapping
            it.description = description
            it.website = link
            it.dependencies = dependencies.replaceVariables(mapping)
            it.repositories = repositories.replaceVariables(mapping)
            it.imports = imports.replaceVariables(mapping)
            it.init = init.replaceVariables(mapping)
            it.shutdown = shutdown.replaceVariables(mapping)
            it.initCell = initCell.replaceVariables(mapping)
            it.renderers = renderers.replaceVariables(mapping)
            it.resources = resources.replaceVariables(mapping)
            it.minKernelVersion = minKernelVersion
            it.integrationTypeNameRules = integrationTypeNameRules.replaceVariables(mapping)
            it.originalDescriptorText = Json.encodeToString(this)
        }
    }

    companion object {
        private val kernelVariables =
            mapOf(
                "kernelVersion" to currentKernelVersion.toString(),
                "kernelMavenVersion" to currentKernelVersion.toMavenVersion(),
            )

        fun substituteKernelVars(value: String): String {
            return replaceVariables(value, kernelVariables)
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy