@@ -151,6 +151,7 @@ def _schedule_results_render(
151151 rows : list [tuple ],
152152 * ,
153153 escape : bool ,
154+ coerce_to_str_columns : set [int ] | None = None ,
154155 start_index : int ,
155156 row_limit : int ,
156157 render_token : int ,
@@ -171,6 +172,17 @@ def add_batch() -> None:
171172 if end <= index :
172173 return
173174 batch = rows [index :end ]
175+ if coerce_to_str_columns :
176+ coerced : list [tuple ] = []
177+ for row in batch :
178+ new_row = []
179+ for col_idx , value in enumerate (row ):
180+ if col_idx in coerce_to_str_columns and value is not None :
181+ new_row .append (str (value ))
182+ else :
183+ new_row .append (value )
184+ coerced .append (tuple (new_row ))
185+ batch = coerced
174186 try :
175187 table .add_rows (batch )
176188 except Exception as exc :
@@ -218,26 +230,46 @@ def _render_results_table_incremental(
218230 has_decimal_in_initial = any (
219231 isinstance (value , Decimal ) for row in initial_rows for value in row
220232 )
221- if has_decimal_in_initial :
222- decimal_types = self ._get_decimal_column_types (rows )
223- if decimal_types :
224- from textual_fastdatatable .backend import ArrowBackend
225- import pyarrow as pa
226-
227- arrays = []
228- for idx , _name in enumerate (columns ):
229- values = [row [idx ] for row in initial_rows ] if initial_rows else []
230- col_type = decimal_types .get (idx )
231- if col_type is not None :
232- arrays .append (pa .array (values , type = col_type ))
233- else :
234- arrays .append (pa .array (values ))
235- table_backend = ArrowBackend (pa .Table .from_arrays (arrays , names = columns ))
236- table = self ._build_results_table (columns , initial_rows , escape = escape , backend = table_backend )
233+ coerce_to_str_columns : set [int ] | None = None
234+ try :
235+ if has_decimal_in_initial :
236+ decimal_types = self ._get_decimal_column_types (rows )
237+ if decimal_types :
238+ from textual_fastdatatable .backend import ArrowBackend
239+ import pyarrow as pa
240+
241+ arrays = []
242+ coerce_to_str_columns = set ()
243+ for idx , _name in enumerate (columns ):
244+ values = [row [idx ] for row in initial_rows ] if initial_rows else []
245+ col_type = decimal_types .get (idx )
246+ try :
247+ if col_type is not None :
248+ arrays .append (pa .array (values , type = col_type ))
249+ else :
250+ arrays .append (pa .array (values ))
251+ except (TypeError , ValueError , pa .ArrowInvalid , pa .ArrowTypeError ):
252+ coerce_to_str_columns .add (idx )
253+ arrays .append (
254+ pa .array (
255+ [str (value ) if value is not None else None for value in values ],
256+ type = pa .string (),
257+ )
258+ )
259+ table_backend = ArrowBackend (pa .Table .from_arrays (arrays , names = columns ))
260+ table = self ._build_results_table (columns , initial_rows , escape = escape , backend = table_backend )
261+ else :
262+ table = self ._build_results_table (columns , initial_rows , escape = escape )
237263 else :
238264 table = self ._build_results_table (columns , initial_rows , escape = escape )
239- else :
240- table = self ._build_results_table (columns , initial_rows , escape = escape )
265+ except Exception as exc :
266+ try :
267+ self .log .error (f"Results table build failed; falling back to full render: { exc } " )
268+ except Exception :
269+ pass
270+ if render_token == getattr (self , "_results_render_token" , 0 ):
271+ self ._replace_results_table_with_data (columns , rows , escape = escape )
272+ return
241273 if render_token != getattr (self , "_results_render_token" , 0 ):
242274 return
243275 self ._replace_results_table_with_table (table )
@@ -246,6 +278,7 @@ def _render_results_table_incremental(
246278 columns ,
247279 rows ,
248280 escape = escape ,
281+ coerce_to_str_columns = coerce_to_str_columns ,
249282 start_index = initial_count ,
250283 row_limit = row_limit ,
251284 render_token = render_token ,
0 commit comments