Options is picklable, _skip_single_line does not drop exceptions
diff --git a/src/_decoder.pyx b/src/_decoder.pyx
index ee2708a..c519380 100644
--- a/src/_decoder.pyx
+++ b/src/_decoder.pyx
@@ -2,13 +2,15 @@
NO_EXTRA_DATA = 0x0011_0000
-cdef void _skip_single_line(ReaderRef reader):
+cdef boolean _skip_single_line(ReaderRef reader) except False:
cdef uint32_t c0
while _reader_good(reader):
c0 = _reader_get(reader)
if _is_line_terminator(c0):
break
+ return True
+
cdef boolean _skip_multiline_comment(ReaderRef reader) except False:
cdef uint32_t c0
diff --git a/src/_encoder.pyx b/src/_encoder.pyx
index 7e074aa..96eabb0 100644
--- a/src/_encoder.pyx
+++ b/src/_encoder.pyx
@@ -289,14 +289,10 @@
if expect(formatter_string is None, False):
_raise_unstringifiable(data)
- formatter_string = formatter_string % data
- if PyUnicode_IS_ASCII(formatter_string):
- string = <const char*> PyUnicode_1BYTE_DATA(formatter_string)
- length = PyUnicode_GET_LENGTH(formatter_string)
- else:
- string = PyUnicode_AsUTF8AndSize(formatter_string, &length)
-
+ formatter_string = PyUnicode_Format(formatter_string, data)
+ string = PyUnicode_AsUTF8AndSize(formatter_string, &length)
writer.append_s(writer, string, length)
+
return True
diff --git a/src/_encoder_options.pyx b/src/_encoder_options.pyx
index daff41e..b96b599 100644
--- a/src/_encoder_options.pyx
+++ b/src/_encoder_options.pyx
@@ -24,6 +24,14 @@
return datum
+cdef object _options_reduce_arg(object key, object value, object default):
+ if value != default:
+ if value is not None:
+ return key, value
+ else:
+ return key, False
+
+
cdef object _option_from_ascii(object name, object value, object default):
if value == default:
return
@@ -47,6 +55,8 @@
@final
+@no_gc
+@freelist(8)
@auto_pickle(False)
cdef class Options:
'''
@@ -130,16 +140,17 @@
'''
def __reduce__(self):
- '''
- Pickling is not supported (yet).
- '''
- raise NotImplementedError
-
- def __reduce_ex__(self, protocol):
- '''
- Pickling is not supported (yet).
- '''
- raise NotImplementedError
+ cdef object args = tuple(filter(bool, (
+ _options_reduce_arg('quotationmark', self.quotationmark, DEFAULT_QUOTATIONMARK),
+ _options_reduce_arg('tojson', self.tojson, None),
+ _options_reduce_arg('posinfinity', self.posinfinity, DEFAULT_POSINFINITY),
+ _options_reduce_arg('neginfinity', self.neginfinity, DEFAULT_NEGINFINITY),
+ _options_reduce_arg('intformat', self.intformat, DEFAULT_INTFORMAT),
+ _options_reduce_arg('floatformat', self.floatformat, DEFAULT_FLOATFORMAT),
+ _options_reduce_arg('decimalformat', self.decimalformat, DEFAULT_DECIMALFORMAT),
+ _options_reduce_arg('nan', self.nan, DEFAULT_NAN),
+ )))
+ return (_UnpickleOptions if args else Options), args
def __repr__(self):
cdef object repr_options = _options_from_ascii(self)
@@ -198,19 +209,19 @@
if intformat is not None:
try:
- intformat % TEST_INT
+ PyUnicode_Format(intformat, TEST_INT)
except Exception as ex:
raise ValueError('intformat is not a valid format string') from ex
if floatformat is not None:
try:
- floatformat % TEST_FLOAT
+ PyUnicode_Format(floatformat, TEST_FLOAT)
except Exception as ex:
raise ValueError('floatformat is not a valid format string') from ex
if decimalformat is not None:
try:
- decimalformat % TEST_DECIMAL
+ PyUnicode_Format(decimalformat, TEST_DECIMAL)
except Exception as ex:
raise ValueError('decimalformat is not a valid format string') from ex
@@ -223,16 +234,26 @@
raise TypeError('mappingtypes must be a sequence of types '
'or False')
- def update(self, **kw):
+ def update(self, *args, **kw):
'''
Creates a new Options instance by modifying some members.
'''
- return _to_options(self, kw)
+ if kw:
+ return _to_options(self, kw)
+ else:
+ return self
cdef Options DEFAULT_OPTIONS_OBJECT = Options()
+def _UnpickleOptions(*args):
+ if args:
+ return _to_options(None, dict(args))
+ else:
+ return DEFAULT_OPTIONS_OBJECT
+
+
cdef object _to_options(Options arg, dict kw):
if arg is None:
if not kw:
diff --git a/src/_imports.pyx b/src/_imports.pyx
index c70ea17..6c6571c 100644
--- a/src/_imports.pyx
+++ b/src/_imports.pyx
@@ -1,4 +1,4 @@
-from cython import final, no_gc, auto_pickle
+from cython import final, no_gc, auto_pickle, freelist
from cpython cimport dict, int, list, long, tuple, type
from cpython.bool cimport PyBool_Check
from cpython.buffer cimport (
@@ -15,7 +15,7 @@
from cpython.long cimport PyLong_FromString, PyLong_Check
from cpython.object cimport PyObject
from cpython.type cimport PyType_Check
-from cpython.unicode cimport PyUnicode_Check, PyUnicode_FromEncodedObject
+from cpython.unicode cimport PyUnicode_Check, PyUnicode_FromEncodedObject, PyUnicode_Format
from libcpp cimport bool as boolean
diff --git a/src/_legacy.pyx b/src/_legacy.pyx
index 218ff83..337dc84 100644
--- a/src/_legacy.pyx
+++ b/src/_legacy.pyx
@@ -24,7 +24,7 @@
see ``decode(...)``
'''
if not isinstance(s, unicode):
- s = unicode(s, encoding, 'strict')
+ s = PyUnicode_FromEncodedObject(s, encoding, 'strict')
return decode(s)