Skip to content

Commit 1fa0ebb

Browse files
committed
Merge remote-tracking branch 'origin/master' into 0.15-notebook
2 parents 9ba8c62 + b1f92dc commit 1fa0ebb

File tree

32 files changed

+421
-93
lines changed

32 files changed

+421
-93
lines changed

build.gradle.kts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,8 @@ val dependencyUpdateExclusions = listOf(
9898
libs.android.gradle.api.get().group,
9999
// Directly dependent on the Gradle version
100100
"org.gradle.kotlin.kotlin-dsl",
101+
// Can't be updated to 2.1.0+ due to Java 8 compatibility
102+
libs.plugins.simpleGit.get().pluginId,
101103
)
102104

103105
// run `./gradlew dependencyUpdates` to check for updates

core/api/core.api

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9918,6 +9918,11 @@ public final class org/jetbrains/kotlinx/dataframe/exceptions/ColumnNotFoundExce
99189918
public fun getMessage ()Ljava/lang/String;
99199919
}
99209920

9921+
public final class org/jetbrains/kotlinx/dataframe/exceptions/ColumnTypeMismatchesColumnValuesException : java/lang/RuntimeException {
9922+
public fun <init> (Lorg/jetbrains/kotlinx/dataframe/DataColumn;Ljava/lang/Throwable;)V
9923+
public final fun getColumn ()Lorg/jetbrains/kotlinx/dataframe/DataColumn;
9924+
}
9925+
99219926
public final class org/jetbrains/kotlinx/dataframe/exceptions/DuplicateColumnNamesException : java/lang/IllegalArgumentException {
99229927
public fun <init> (Ljava/util/List;)V
99239928
public final fun getAllColumnNames ()Ljava/util/List;

core/generated-sources/src/main/kotlin/org/jetbrains/kotlinx/dataframe/dataTypes/IMG.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,6 @@ public data class IMG(
2323
if (height != null) style.append("height:${height}px;")
2424
if (maxWidth != null) style.append("max-width:${maxWidth}px;")
2525
if (maxHeight != null) style.append("max-height:${maxHeight}px;")
26-
return """<img src="$src" style="$style"}/>"""
26+
return """<img src="$src" style="$style"/>"""
2727
}
2828
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package org.jetbrains.kotlinx.dataframe.exceptions
2+
3+
import org.jetbrains.kotlinx.dataframe.AnyCol
4+
import org.jetbrains.kotlinx.dataframe.DataColumn
5+
import org.jetbrains.kotlinx.dataframe.columns.values
6+
import org.jetbrains.kotlinx.dataframe.type
7+
8+
/**
9+
* Extension properties are generated according to [DataColumn.type] property
10+
* [DataColumn.type] must match types of [DataColumn.values], but it can fail to do so.
11+
* This causes [ClassCastException] or [NullPointerException] when you use extension property and actual value is of different type or is null.
12+
* If generated extension property causes this exception, this is a bug in the library
13+
* You can work around this problem by referring to [column] using String API
14+
*/
15+
public class ColumnTypeMismatchesColumnValuesException(public val column: AnyCol, cause: Throwable) :
16+
RuntimeException("Failed to convert column '${column.name()}'", cause)

core/generated-sources/src/main/kotlin/org/jetbrains/kotlinx/dataframe/impl/api/convert.kt

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import org.jetbrains.kotlinx.dataframe.columns.values
3333
import org.jetbrains.kotlinx.dataframe.dataTypes.IFRAME
3434
import org.jetbrains.kotlinx.dataframe.dataTypes.IMG
3535
import org.jetbrains.kotlinx.dataframe.exceptions.CellConversionException
36+
import org.jetbrains.kotlinx.dataframe.exceptions.ColumnTypeMismatchesColumnValuesException
3637
import org.jetbrains.kotlinx.dataframe.exceptions.TypeConversionException
3738
import org.jetbrains.kotlinx.dataframe.exceptions.TypeConverterNotFoundException
3839
import org.jetbrains.kotlinx.dataframe.impl.columns.DataColumnInternal
@@ -68,7 +69,16 @@ internal fun <T, C, R> Convert<T, C>.withRowCellImpl(
6869
type: KType,
6970
infer: Infer,
7071
rowConverter: RowValueExpression<T, C, R>,
71-
): DataFrame<T> = to { col -> df.newColumn(type, col.name, infer) { rowConverter(it, it[col]) } }
72+
): DataFrame<T> =
73+
to { col ->
74+
try {
75+
df.newColumn(type, col.name, infer) { rowConverter(it, it[col]) }
76+
} catch (e: ClassCastException) {
77+
throw ColumnTypeMismatchesColumnValuesException(col, e)
78+
} catch (e: NullPointerException) {
79+
throw ColumnTypeMismatchesColumnValuesException(col, e)
80+
}
81+
}
7282

7383
@PublishedApi
7484
internal fun <T, C, R> Convert<T, C>.convertRowColumnImpl(

core/generated-sources/src/main/kotlin/org/jetbrains/kotlinx/dataframe/io/html.kt

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ import org.jetbrains.kotlinx.dataframe.columns.BaseColumn
2020
import org.jetbrains.kotlinx.dataframe.columns.ColumnGroup
2121
import org.jetbrains.kotlinx.dataframe.columns.ColumnWithPath
2222
import org.jetbrains.kotlinx.dataframe.columns.FrameColumn
23+
import org.jetbrains.kotlinx.dataframe.dataTypes.IFRAME
24+
import org.jetbrains.kotlinx.dataframe.dataTypes.IMG
2325
import org.jetbrains.kotlinx.dataframe.impl.DataFrameSize
2426
import org.jetbrains.kotlinx.dataframe.impl.columns.addPath
2527
import org.jetbrains.kotlinx.dataframe.impl.io.resizeKeepingAspectRatio
@@ -514,6 +516,8 @@ private fun AnyFrame.getColumnsHeaderGrid(): List<List<ColumnWithPathWithBorder<
514516
internal fun DataFrameHtmlData.print() = println(this)
515517

516518
/**
519+
* By default, cell content is formatted as text
520+
* Use [RenderedContent.media] or [IMG], [IFRAME] if you need custom HTML inside a cell.
517521
* @return DataFrameHtmlData with table script and css definitions. Can be saved as an *.html file and displayed in the browser
518522
*/
519523
public fun <T> DataFrame<T>.toStandaloneHTML(
@@ -523,6 +527,8 @@ public fun <T> DataFrame<T>.toStandaloneHTML(
523527
): DataFrameHtmlData = toHTML(configuration, cellRenderer, getFooter).withTableDefinitions()
524528

525529
/**
530+
* By default, cell content is formatted as text
531+
* Use [RenderedContent.media] or [IMG], [IFRAME] if you need custom HTML inside a cell.
526532
* @return DataFrameHtmlData without additional definitions. Can be rendered in Jupyter kernel environments
527533
*/
528534
public fun <T> DataFrame<T>.toHTML(
@@ -834,7 +840,7 @@ internal class DataFrameFormatter(
834840
return sb.result()
835841
}
836842

837-
val result = when (value) {
843+
val result: RenderedContent? = when (value) {
838844
null -> "null".addCss(nullClass)
839845

840846
is AnyRow -> {
@@ -899,6 +905,12 @@ internal class DataFrameFormatter(
899905

900906
is DataFrameHtmlData -> RenderedContent.text(value.body)
901907

908+
is IMG -> RenderedContent.media(value.toString())
909+
910+
is IFRAME -> RenderedContent.media(value.toString())
911+
912+
is RenderedContent -> value
913+
902914
else -> renderer.content(value, configuration)
903915
}
904916
if (result != null && result.textLength > configuration.cellContentLimit) return null

core/generated-sources/src/main/kotlin/org/jetbrains/kotlinx/dataframe/jupyter/CellRenderer.kt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package org.jetbrains.kotlinx.dataframe.jupyter
22

3+
import org.intellij.lang.annotations.Language
34
import org.jetbrains.kotlinx.dataframe.io.DisplayConfiguration
45
import org.jetbrains.kotlinx.dataframe.io.renderValueForHtml
56
import org.jetbrains.kotlinx.dataframe.io.tooltipLimit
@@ -12,7 +13,9 @@ public data class RenderedContent(
1213
) {
1314
public companion object {
1415

15-
public fun media(html: String): RenderedContent = RenderedContent(html, 0, null, false)
16+
public fun media(
17+
@Language("HTML") html: String,
18+
): RenderedContent = RenderedContent(html, 0, null, false)
1619

1720
public fun textWithLength(str: String, len: Int): RenderedContent = RenderedContent(str, len, null, false)
1821

core/generated-sources/src/test/kotlin/org/jetbrains/kotlinx/dataframe/api/convert.kt

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,12 @@ import io.kotest.matchers.shouldBe
66
import kotlinx.datetime.Clock
77
import kotlinx.datetime.Instant
88
import kotlinx.datetime.LocalTime
9+
import org.jetbrains.kotlinx.dataframe.ColumnsContainer
910
import org.jetbrains.kotlinx.dataframe.DataColumn
1011
import org.jetbrains.kotlinx.dataframe.DataFrame
1112
import org.jetbrains.kotlinx.dataframe.annotations.DataSchema
1213
import org.jetbrains.kotlinx.dataframe.exceptions.CellConversionException
14+
import org.jetbrains.kotlinx.dataframe.exceptions.ColumnTypeMismatchesColumnValuesException
1315
import org.jetbrains.kotlinx.dataframe.exceptions.TypeConversionException
1416
import org.jetbrains.kotlinx.dataframe.exceptions.TypeConverterNotFoundException
1517
import org.jetbrains.kotlinx.dataframe.hasNulls
@@ -315,4 +317,15 @@ class ConvertTests {
315317
}
316318
}
317319
}
320+
321+
private interface Marker
322+
323+
private val ColumnsContainer<Marker>.a get() = this["a"] as DataColumn<String>
324+
325+
@Test
326+
fun `convert with buggy extension property`() {
327+
shouldThrow<ColumnTypeMismatchesColumnValuesException> {
328+
dataFrameOf("a")(1, 2, 3).cast<Marker>().convert { a }.with { it }
329+
}
330+
}
318331
}

core/generated-sources/src/test/kotlin/org/jetbrains/kotlinx/dataframe/rendering/RenderingTests.kt

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ import org.jetbrains.kotlinx.dataframe.api.parse
2020
import org.jetbrains.kotlinx.dataframe.api.schema
2121
import org.jetbrains.kotlinx.dataframe.api.toDataFrame
2222
import org.jetbrains.kotlinx.dataframe.api.with
23+
import org.jetbrains.kotlinx.dataframe.dataTypes.IFRAME
24+
import org.jetbrains.kotlinx.dataframe.dataTypes.IMG
2325
import org.jetbrains.kotlinx.dataframe.io.DisplayConfiguration
2426
import org.jetbrains.kotlinx.dataframe.io.escapeHTML
2527
import org.jetbrains.kotlinx.dataframe.io.formatter
@@ -227,4 +229,26 @@ class RenderingTests : TestBase() {
227229
val html = df.toStandaloneHTML()
228230
html.toString() shouldInclude "style: \"background-color"
229231
}
232+
233+
@Test
234+
fun `render img`() {
235+
val src = "https://github.com/Kotlin/dataframe/blob/master/docs/StardustDocs/images/gettingStarted.png?raw=true"
236+
val df = dataFrameOf("img")(IMG(src))
237+
df.toStandaloneHTML().toString() shouldInclude
238+
"""values: ["<img src=\"https://github.com/Kotlin/dataframe/blob/master/docs/StardustDocs/images/gettingStarted.png?raw=true\" style=\"\"/>"]"""
239+
}
240+
241+
@Test
242+
fun `render custom content`() {
243+
val df = dataFrameOf("customUrl")(RenderedContent.media("""<a href="http://example.com">Click me!</a>"""))
244+
df.toStandaloneHTML().toString() shouldInclude
245+
"""values: ["<a href=\"http://example.com\">Click me!</a>"]"""
246+
}
247+
248+
@Test
249+
fun `render iframe content`() {
250+
val df = dataFrameOf("iframe")(IFRAME("http://example.com"))
251+
df.toStandaloneHTML().toString() shouldInclude
252+
"""values: ["<iframe src=\"http://example.com\"""
253+
}
230254
}

core/src/main/kotlin/org/jetbrains/kotlinx/dataframe/dataTypes/IMG.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,6 @@ public data class IMG(
2323
if (height != null) style.append("height:${height}px;")
2424
if (maxWidth != null) style.append("max-width:${maxWidth}px;")
2525
if (maxHeight != null) style.append("max-height:${maxHeight}px;")
26-
return """<img src="$src" style="$style"}/>"""
26+
return """<img src="$src" style="$style"/>"""
2727
}
2828
}

0 commit comments

Comments
 (0)