Skip to content

Commit 7eaf070

Browse files
committed
Clarify the situation when generated extension property causes ClassCast or NPE exceptions
1 parent c7b1004 commit 7eaf070

File tree

3 files changed

+40
-1
lines changed

3 files changed

+40
-1
lines changed
Lines changed: 16 additions & 0 deletions
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 ColumnValuesMismatchColumnTypeException(public val column: AnyCol, cause: Throwable) :
16+
RuntimeException("Failed to convert column '${column.name()}'", cause)

core/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
@@ -31,6 +31,7 @@ import org.jetbrains.kotlinx.dataframe.columns.values
3131
import org.jetbrains.kotlinx.dataframe.dataTypes.IFRAME
3232
import org.jetbrains.kotlinx.dataframe.dataTypes.IMG
3333
import org.jetbrains.kotlinx.dataframe.exceptions.CellConversionException
34+
import org.jetbrains.kotlinx.dataframe.exceptions.ColumnValuesMismatchColumnTypeException
3435
import org.jetbrains.kotlinx.dataframe.exceptions.TypeConversionException
3536
import org.jetbrains.kotlinx.dataframe.exceptions.TypeConverterNotFoundException
3637
import org.jetbrains.kotlinx.dataframe.impl.columns.DataColumnInternal
@@ -60,7 +61,16 @@ internal fun <T, C, R> Convert<T, C>.withRowCellImpl(
6061
type: KType,
6162
infer: Infer,
6263
rowConverter: RowValueExpression<T, C, R>,
63-
): DataFrame<T> = to { col -> df.newColumn(type, col.name, infer) { rowConverter(it, it[col]) } }
64+
): DataFrame<T> =
65+
to { col ->
66+
try {
67+
df.newColumn(type, col.name, infer) { rowConverter(it, it[col]) }
68+
} catch (e: ClassCastException) {
69+
throw ColumnValuesMismatchColumnTypeException(col, e)
70+
} catch (e: NullPointerException) {
71+
throw ColumnValuesMismatchColumnTypeException(col, e)
72+
}
73+
}
6474

6575
@PublishedApi
6676
internal fun <T, C, R> Convert<T, C>.convertRowColumnImpl(

core/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.ColumnValuesMismatchColumnTypeException
1315
import org.jetbrains.kotlinx.dataframe.exceptions.TypeConversionException
1416
import org.jetbrains.kotlinx.dataframe.exceptions.TypeConverterNotFoundException
1517
import org.jetbrains.kotlinx.dataframe.hasNulls
@@ -172,4 +174,15 @@ class ConvertTests {
172174
val res = col.convertTo<String>()
173175
res.print()
174176
}
177+
178+
private interface Marker
179+
180+
private val ColumnsContainer<Marker>.a get() = this["a"] as DataColumn<String>
181+
182+
@Test
183+
fun `convert with buggy extension property`() {
184+
shouldThrow<ColumnValuesMismatchColumnTypeException> {
185+
dataFrameOf("a")(1, 2, 3).cast<Marker>().convert { a }.with { it }
186+
}
187+
}
175188
}

0 commit comments

Comments
 (0)