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

org.jetbrains.kotlinx.jupyter.libraries.LibraryResolvers.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 org.jetbrains.kotlinx.jupyter.api.KernelLoggerFactory
import org.jetbrains.kotlinx.jupyter.api.getLogger
import org.jetbrains.kotlinx.jupyter.api.libraries.LibraryDefinition
import org.jetbrains.kotlinx.jupyter.api.libraries.LibraryReference
import org.jetbrains.kotlinx.jupyter.api.libraries.LibraryResolutionInfo
import org.jetbrains.kotlinx.jupyter.api.libraries.Variable
import org.jetbrains.kotlinx.jupyter.common.HttpClient
import org.jetbrains.kotlinx.jupyter.common.LibraryDescriptorsManager
import org.jetbrains.kotlinx.jupyter.common.getHttp
import org.jetbrains.kotlinx.jupyter.config.KernelStreams
import org.jetbrains.kotlinx.jupyter.exceptions.ReplLibraryLoadingException
import java.io.File
import java.io.IOException
import kotlin.reflect.KClass
import kotlin.reflect.cast

class DefaultInfoLibraryResolver(
    private val parent: LibraryResolver,
    private val infoProvider: ResolutionInfoProvider,
    libraryDescriptorsManager: LibraryDescriptorsManager,
    paths: List = emptyList(),
) : LibraryResolver {
    private val resolutionInfos = paths.map { AbstractLibraryResolutionInfo.ByDir(it) }
    private val byDirResolver = getByDirResolver(libraryDescriptorsManager)

    override fun resolve(
        reference: LibraryReference,
        arguments: List,
    ): LibraryDefinition? {
        val referenceInfo = reference.info
        if (referenceInfo !is AbstractLibraryResolutionInfo.Default) return parent.resolve(reference, arguments)

        val result = resolutionInfos.asSequence().map { byDirResolver.resolveRaw(it, reference.name) }.firstOrNull()
        if (result != null) {
            val (definitionText, options) = result
            if (definitionText != null) return parseLibraryDescriptor(definitionText).convertToDefinition(arguments, options)
        }

        val newReference = transformReference(referenceInfo, reference.name)
        return parent.resolve(newReference, arguments)
    }

    private fun transformReference(
        referenceInfo: AbstractLibraryResolutionInfo.Default,
        referenceName: String?,
    ): LibraryReference {
        val transformedInfo = infoProvider.get(referenceInfo.string)
        return LibraryReference(transformedInfo, referenceName)
    }
}

class LocalLibraryResolver(
    parent: LibraryResolver?,
    loggerFactory: KernelLoggerFactory,
    private val libraryDescriptorsManager: LibraryDescriptorsManager,
    mainLibrariesDir: File?,
) : ChainedLibraryResolver(parent) {
    private val logger = loggerFactory.getLogger(this::class)
    private val pathsToCheck: List

    init {
        val paths =
            mutableListOf(
                libraryDescriptorsManager.userCacheDir,
                libraryDescriptorsManager.userLibrariesDir,
            )
        mainLibrariesDir?.let { paths.add(it) }

        pathsToCheck = paths
    }

    override fun shouldResolve(reference: LibraryReference): Boolean {
        return reference.shouldBeCachedLocally
    }

    override fun tryResolve(
        reference: LibraryReference,
        arguments: List,
    ): LibraryDefinition? {
        val files =
            pathsToCheck.mapNotNull { dir ->
                val file = reference.getFile(dir)
                if (file.exists()) file else null
            }

        if (files.size > 1) {
            logger.warn("More than one file for library $reference found in local cache directories")
        }

        val jsonFile = files.firstOrNull() ?: return null
        return parseLibraryDescriptor(jsonFile.readText()).convertToDefinition(arguments)
    }

    override fun save(
        reference: LibraryReference,
        definition: LibraryDefinition,
    ) {
        val text = definition.originalDescriptorText ?: return
        val dir = pathsToCheck.first()
        val file = reference.getFile(dir)
        file.parentFile.mkdirs()
        file.writeText(text)
    }

    private fun LibraryReference.getFile(dir: File) = dir.resolve(libraryDescriptorsManager.descriptorFileName(key))
}

open class ResourcesLibraryResolver(
    parent: LibraryResolver?,
    private val libraryDescriptorsManager: LibraryDescriptorsManager,
    private val classLoader: ClassLoader,
) : ChainedLibraryResolver(parent) {
    override fun tryResolve(
        reference: LibraryReference,
        arguments: List,
    ): LibraryDefinition? {
        val text = resolveDescriptorFromResources(reference.name) ?: return null
        return parseLibraryDescriptor(text).convertToDefinition(arguments)
    }

    fun resolveDescriptorFromResources(libraryName: String?): String? {
        libraryName ?: return null
        val url = classLoader.getResource(libraryDescriptorsManager.resourceLibraryPath(libraryName)) ?: return null
        return url.readText()
    }

    fun resolveDescriptorOptionsFromResources(): LibraryDescriptorGlobalOptions {
        val url = classLoader.getResource(libraryDescriptorsManager.resourceOptionsPath()) ?: return DefaultLibraryDescriptorGlobalOptions
        return parseLibraryDescriptorGlobalOptions(url.readText())
    }

    override fun shouldResolve(reference: LibraryReference): Boolean {
        return when (reference.info) {
            is AbstractLibraryResolutionInfo.Default -> true
            is AbstractLibraryResolutionInfo.ByClasspath -> true
            else -> false
        }
    }
}

private fun LibraryDescriptorsManager.getDescriptorOptions(sha: String): LibraryDescriptorGlobalOptions {
    val optionsText = downloadGlobalDescriptorOptions(sha) ?: return DefaultLibraryDescriptorGlobalOptions
    return parseLibraryDescriptorGlobalOptions(optionsText)
}

class FallbackLibraryResolver(
    private val httpClient: HttpClient,
    private val libraryDescriptorsManager: LibraryDescriptorsManager,
) : ChainedLibraryResolver() {
    private val resourcesResolver =
        ResourcesLibraryResolver(
            null,
            libraryDescriptorsManager,
            ResourcesLibraryResolver::class.java.classLoader,
        )

    private val standardResolvers =
        listOf(
            getByDirResolver(libraryDescriptorsManager),
            resolverWithOptions { name ->
                if (name == null) throw ReplLibraryLoadingException(message = "Reference library resolver needs name to be specified")

                val descriptorText =
                    try {
                        libraryDescriptorsManager.downloadLibraryDescriptor(sha, name)
                    } catch (e: IOException) {
                        KernelStreams.err.println(
                            "WARNING: Can't resolve library $name from the given reference. " +
                                "Using classpath version of this library. Error: $e",
                        )
                        KernelStreams.err.flush()
                        resourcesResolver.resolveDescriptorFromResources(name)
                    }

                descriptorText to libraryDescriptorsManager.getDescriptorOptions(sha)
            },
            resolverWithOptions { name ->
                if (name == null) throw ReplLibraryLoadingException(message = "Reference library resolver needs name to be specified")
                val descriptorText = libraryDescriptorsManager.downloadLibraryDescriptor(sha, name)

                descriptorText to libraryDescriptorsManager.getDescriptorOptions(sha)
            },
            resolver {
                file.readText()
            },
            resolver {
                val response = httpClient.getHttp(url.toString())
                response.text
            },
            resolver { "{}" },
            resolverWithOptions { name ->
                resourcesResolver.resolveDescriptorFromResources(name) to resourcesResolver.resolveDescriptorOptionsFromResources()
            },
            resolver { null },
        )

    override fun tryResolve(
        reference: LibraryReference,
        arguments: List,
    ): LibraryDefinition? {
        return standardResolvers.firstOrNull { it.accepts(reference) }?.resolve(reference, arguments)
    }
}

class SpecificLibraryResolver(
    private val kClass: KClass,
    val resolveRaw: (T, String?) -> Pair,
) : LibraryResolver {
    fun accepts(reference: LibraryReference): Boolean {
        return kClass.isInstance(reference.info)
    }

    override fun resolve(
        reference: LibraryReference,
        arguments: List,
    ): LibraryDefinition? {
        if (!accepts(reference)) return null
        val (text, options) = resolveRaw(kClass.cast(reference.info), reference.name)
        text ?: return null
        return parseLibraryDescriptor(text).convertToDefinition(arguments, options)
    }
}

private inline fun  resolverWithOptions(
    noinline resolverFun: T.(String?) -> Pair,
) = SpecificLibraryResolver(
    T::class,
    resolverFun,
)

private inline fun  resolver(noinline resolverFun: T.(String?) -> String?) =
    SpecificLibraryResolver(
        T::class,
    ) { info, descriptorText -> resolverFun(info, descriptorText) to DefaultLibraryDescriptorGlobalOptions }

private fun getByDirResolver(
    libraryDescriptorsManager: LibraryDescriptorsManager,
): SpecificLibraryResolver {
    return resolverWithOptions { name ->
        if (name == null) throw ReplLibraryLoadingException(null, "Directory library resolver needs library name to be specified")

        val jsonFile = librariesDir.resolve(libraryDescriptorsManager.descriptorFileName(name))
        if (jsonFile.exists() && jsonFile.isFile) {
            val descriptorText = jsonFile.readText()

            val optionsFile = librariesDir.resolve(libraryDescriptorsManager.optionsFileName())
            val options =
                if (optionsFile.exists() && optionsFile.isFile) {
                    parseLibraryDescriptorGlobalOptions(optionsFile.readText())
                } else {
                    DefaultLibraryDescriptorGlobalOptions
                }
            descriptorText to options
        } else {
            null to DefaultLibraryDescriptorGlobalOptions
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy