JetBrains / lets-plot

Multiplatform plotting library based on the Grammar of Graphics
https://lets-plot.org
MIT License
1.56k stars 51 forks source link

Kandy `toPNG` reports NullPointerException #1228

Open gmitch215 opened 1 week ago

gmitch215 commented 1 week ago

Hello,

I'm using Kandy for creating graphing data using Kotlin. Occasionally, the toPNG function throws this error:

[DefaultDispatcher-worker-5] ERROR SpecTransformBackendUtil - Internal error: NullPointerException : <no message>
java.lang.NullPointerException
        at org.jetbrains.letsPlot.core.plot.base.GeomMeta.renders(GeomMeta.kt:24)
        at org.jetbrains.letsPlot.core.plot.base.GeomMeta.renders(GeomMeta.kt:33)
        at org.jetbrains.letsPlot.core.plot.base.GeomMeta.renders$default(GeomMeta.kt:27)
        at org.jetbrains.letsPlot.core.spec.config.LayerConfig.<init>(LayerConfig.kt:74)
        at org.jetbrains.letsPlot.core.spec.config.PlotConfig.createLayerConfig(PlotConfig.kt:160)
        at org.jetbrains.letsPlot.core.spec.config.PlotConfig.createLayerConfigs(PlotConfig.kt:133)
        at org.jetbrains.letsPlot.core.spec.config.PlotConfig.<init>(PlotConfig.kt:80)
        at org.jetbrains.letsPlot.core.spec.back.PlotConfigBackend.<init>(PlotConfigBackend.kt:40)
        at org.jetbrains.letsPlot.core.spec.back.SpecTransformBackendUtil.processTransformIntern2(SpecTransformBackendUtil.kt:154)
        at org.jetbrains.letsPlot.core.spec.back.SpecTransformBackendUtil.processTransformIntern(SpecTransformBackendUtil.kt:136)
        at org.jetbrains.letsPlot.core.spec.back.SpecTransformBackendUtil.processTransform$plot_stem(SpecTransformBackendUtil.kt:42)
        at org.jetbrains.letsPlot.core.spec.back.SpecTransformBackendUtil.processTransform(SpecTransformBackendUtil.kt:22)
        at org.jetbrains.letsPlot.core.util.MonolithicCommon.processRawSpecs(MonolithicCommon.kt:326)
        at org.jetbrains.letsPlot.core.util.MonolithicCommon.buildSvgImagesFromRawSpecs(MonolithicCommon.kt:37)
        at org.jetbrains.letsPlot.core.util.PlotSvgExportCommon.buildSvgImageFromRawSpecs(PlotSvgExportCommon.kt:35)
        at org.jetbrains.letsPlot.awt.plot.PlotSvgExport.buildSvgImageFromRawSpecs(PlotSvgExport.kt:24)
        at org.jetbrains.letsPlot.awt.plot.PlotSvgExport.buildSvgImageFromRawSpecs$default(PlotSvgExport.kt:19)
        at org.jetbrains.letsPlot.core.plot.export.PlotImageExport.buildImageFromRawSpecs(PlotImageExport.kt:90)
        at org.jetbrains.kotlinx.kandy.letsplot.export.ToBufferedImageKt.toBufferedImage(toBufferedImage.kt:28)
        at org.jetbrains.kotlinx.kandy.letsplot.export.ToBufferedImageKt.toPNG(toBufferedImage.kt:99)
        at org.jetbrains.kotlinx.kandy.letsplot.export.ToBufferedImageKt.toPNG$default(toBufferedImage.kt:95)

It seems to be directly a Lets-Plot bug instead of a Kandy bug, and is linked to this line of code in GeomMeta:

@ThreadLocal
object GeomMeta {
    private val renderedAesByGeom = HashMap<GeomKind, List<Aes<*>>>()

    private fun renders(geomKind: GeomKind): List<Aes<*>> {
        if (!renderedAesByGeom.containsKey(geomKind)) {
            renderedAesByGeom[geomKind] =
                renderedAesList(geomKind)
        }
        return renderedAesByGeom[geomKind]!! // thrown here
    }
alshan commented 3 days ago

Hi, in what environment did you get this error? I.e., Jupyter notebook/lab, Kotlin notebook plugin in IntelliJ IDEA, or other?

gmitch215 commented 3 days ago

Using Kandy as a gradle dependency on Kotlin/JVM.

dependencies {
    // ...
    implementation("org.jetbrains.kotlinx:kandy-lets-plot:0.7.1")
alshan commented 3 days ago

Uh-oh, apparently @ThreadLocal only has an effect on the Kotlin/Native platform.

I understand you are calling toPNG() in your own mulithreded app?

gmitch215 commented 3 days ago

Yes, I'm using coroutines on the JVM.