Skip to content

Commit cd2951a

Browse files
committed
Clarify the situation when generated extension property causes ClassCast or NPE exceptions
1 parent b116126 commit cd2951a

File tree

4 files changed

+45
-1
lines changed

4 files changed

+45
-1
lines changed

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;
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/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/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
}

0 commit comments

Comments
 (0)