2020# cython: embedsignature = True
2121# cython: language_level = 3
2222
23+ from cython.operator cimport dereference as deref
2324
2425from pyarrow.includes.common cimport *
2526from pyarrow.includes.libarrow cimport *
@@ -69,7 +70,7 @@ cdef class ReadOptions:
6970 # Avoid mistakingly creating attributes
7071 __slots__ = ()
7172
72- def __init__ (self , use_threads = None , block_size = None , skip_rows = None ,
73+ def __init__ (self , *, use_threads = None , block_size = None , skip_rows = None ,
7374 column_names = None , autogenerate_column_names = None ):
7475 self .options = CCSVReadOptions.Defaults()
7576 if use_threads is not None :
@@ -176,7 +177,7 @@ cdef class ParseOptions:
176177 """
177178 __slots__ = ()
178179
179- def __init__ (self , delimiter = None , quote_char = None , double_quote = None ,
180+ def __init__ (self , *, delimiter = None , quote_char = None , double_quote = None ,
180181 escape_char = None , newlines_in_values = None ,
181182 ignore_empty_lines = None ):
182183 self .options = CCSVParseOptions.Defaults()
@@ -296,10 +297,31 @@ cdef class ParseOptions:
296297 out.options = options
297298 return out
298299
299- def __reduce__ (self ):
300- return ParseOptions, (self .delimiter, self .quote_char,
301- self .double_quote, self .escape_char,
302- self .newlines_in_values, self .ignore_empty_lines)
300+ def __getstate__ (self ):
301+ return (self .delimiter, self .quote_char, self .double_quote,
302+ self .escape_char, self .newlines_in_values,
303+ self .ignore_empty_lines)
304+
305+ def __setstate__ (self , state ):
306+ (self .delimiter, self .quote_char, self .double_quote,
307+ self .escape_char, self .newlines_in_values,
308+ self .ignore_empty_lines) = state
309+
310+
311+ cdef class _ISO8601:
312+ """
313+ A special object indicating ISO-8601 parsing.
314+ """
315+ __slots__ = ()
316+
317+ def __str__ (self ):
318+ return ' ISO8601'
319+
320+ def __eq__ (self , other ):
321+ return isinstance (other, _ISO8601)
322+
323+
324+ ISO8601 = _ISO8601()
303325
304326
305327cdef class ConvertOptions:
@@ -324,6 +346,11 @@ cdef class ConvertOptions:
324346 false_values: list, optional
325347 A sequence of strings that denote false booleans in the data
326348 (defaults are appropriate in most cases).
349+ timestamp_parsers: list, optional
350+ A sequence of strptime()-compatible format strings, tried in order
351+ when attempting to infer or convert timestamp values (the special
352+ value ISO8601() can also be given). By default, a fast built-in
353+ ISO-8601 parser is used.
327354 strings_can_be_null: bool, optional (default False)
328355 Whether string / binary columns can have null values.
329356 If true, then strings in null_values are considered null for
@@ -359,11 +386,11 @@ cdef class ConvertOptions:
359386 # Avoid mistakingly creating attributes
360387 __slots__ = ()
361388
362- def __init__ (self , check_utf8 = None , column_types = None , null_values = None ,
389+ def __init__ (self , *, check_utf8 = None , column_types = None , null_values = None ,
363390 true_values = None , false_values = None ,
364391 strings_can_be_null = None , include_columns = None ,
365392 include_missing_columns = None , auto_dict_encode = None ,
366- auto_dict_max_cardinality = None ):
393+ auto_dict_max_cardinality = None , timestamp_parsers = None ):
367394 self .options = CCSVConvertOptions.Defaults()
368395 if check_utf8 is not None :
369396 self .check_utf8 = check_utf8
@@ -385,6 +412,8 @@ cdef class ConvertOptions:
385412 self .auto_dict_encode = auto_dict_encode
386413 if auto_dict_max_cardinality is not None :
387414 self .auto_dict_max_cardinality = auto_dict_max_cardinality
415+ if timestamp_parsers is not None :
416+ self .timestamp_parsers = timestamp_parsers
388417
389418 @property
390419 def check_utf8 (self ):
@@ -526,6 +555,44 @@ cdef class ConvertOptions:
526555 def include_missing_columns (self , value ):
527556 self .options.include_missing_columns = value
528557
558+ @property
559+ def timestamp_parsers (self ):
560+ """
561+ A sequence of strptime()-compatible format strings, tried in order
562+ when attempting to infer or convert timestamp values (the special
563+ value ISO8601() can also be given). By default, a fast built-in
564+ ISO-8601 parser is used.
565+ """
566+ cdef:
567+ shared_ptr[CTimestampParser] c_parser
568+ c_string kind
569+
570+ parsers = []
571+ for c_parser in self .options.timestamp_parsers:
572+ kind = deref(c_parser).kind()
573+ if kind == b' strptime' :
574+ parsers.append(frombytes(deref(c_parser).format()))
575+ else :
576+ assert kind == b' iso8601'
577+ parsers.append(ISO8601)
578+
579+ return parsers
580+
581+ @timestamp_parsers.setter
582+ def timestamp_parsers (self , value ):
583+ cdef:
584+ vector[shared_ptr[CTimestampParser]] c_parsers
585+
586+ for v in value:
587+ if isinstance (v, str ):
588+ c_parsers.push_back(CTimestampParser.MakeStrptime(tobytes(v)))
589+ elif v == ISO8601:
590+ c_parsers.push_back(CTimestampParser.MakeISO8601())
591+ else :
592+ raise TypeError (" Expected list of str or ISO8601 objects" )
593+
594+ self .options.timestamp_parsers = move(c_parsers)
595+
529596
530597cdef _get_reader(input_file, shared_ptr[CInputStream]* out):
531598 use_memory_map = False
0 commit comments