1
0
Fork 0
mirror of https://github.com/ultrajson/ultrajson.git synced 2024-05-11 22:06:04 +02:00

Compare commits

...

7 Commits

Author SHA1 Message Date
Peter Varo 52c8437cba
Merge 6491c1839b into 04daf02b94 2024-04-09 18:06:46 +02:00
Hugo van Kemenade 04daf02b94
[pre-commit.ci] pre-commit autoupdate (#624) 2024-04-01 21:02:10 +03:00
Hugo van Kemenade 362c88a8b5
Use Black mirror 2024-04-01 11:58:55 -06:00
pre-commit-ci[bot] e96c8cfee3
[pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/asottile/pyupgrade: v3.15.0 → v3.15.2](https://github.com/asottile/pyupgrade/compare/v3.15.0...v3.15.2)
- [github.com/psf/black: 23.12.1 → 24.3.0](https://github.com/psf/black/compare/23.12.1...24.3.0)
- [github.com/PyCQA/flake8: 6.1.0 → 7.0.0](https://github.com/PyCQA/flake8/compare/6.1.0...7.0.0)
2024-04-01 17:55:50 +00:00
Hugo van Kemenade fbf7afff00
Update pypa/cibuildwheel action to v2.17.0 (#623) 2024-04-01 08:47:36 +03:00
renovate[bot] 08f14e5938
Update pypa/cibuildwheel action to v2.17.0 2024-04-01 01:26:13 +00:00
peter-varo-hx 6491c1839b Support Python's Negative Exponent Padding Idiosyncrasy 2024-02-05 21:36:32 +00:00
9 changed files with 58 additions and 14 deletions

View File

@ -37,7 +37,7 @@ jobs:
# https://github.com/pypa/cibuildwheel
- name: Build wheels
uses: pypa/cibuildwheel@v2.16.5
uses: pypa/cibuildwheel@v2.17.0
with:
output-dir: dist
# Options are supplied via environment variables:
@ -89,7 +89,7 @@ jobs:
# https://github.com/pypa/cibuildwheel
- name: Build wheels
uses: pypa/cibuildwheel@v2.16.5
uses: pypa/cibuildwheel@v2.17.0
with:
output-dir: dist
# Options are supplied via environment variables:

View File

@ -1,12 +1,12 @@
repos:
- repo: https://github.com/asottile/pyupgrade
rev: v3.15.0
rev: v3.15.2
hooks:
- id: pyupgrade
args: [--py38-plus]
- repo: https://github.com/psf/black
rev: 23.12.1
- repo: https://github.com/psf/black-pre-commit-mirror
rev: 24.3.0
hooks:
- id: black
args: [--target-version=py38]
@ -17,7 +17,7 @@ repos:
- id: isort
- repo: https://github.com/PyCQA/flake8
rev: 6.1.0
rev: 7.0.0
hooks:
- id: flake8
additional_dependencies: [flake8-2020, flake8-implicit-str-concat]

View File

@ -78,6 +78,19 @@ Controls whether indentation ("pretty output") is enabled. Default is `0` (disab
}
```
#### zero_pad_negative_9_to_5_exponent
If true, adds a single `0` padding to the exponent in scientific notation if it
is between `-9` and `-5` both inclusive, which replicates the Python standard
library's `json` behavior. Default is `False`:
```pycon
>>> ujson.dumps([1e-10, 1e-9, 1e-5, 1e-4])
'[1e-10,1e-9,1e-5,0.0001]'
>>> ujson.dumps([1e-10, 1e-9, 1e-5, 1e-4], zero_pad_negative_9_to_5_exponent=True)
'[1e-10,1e-09,1e-05,0.0001]'
```
## Benchmarks
*UltraJSON* calls/sec compared to other popular JSON parsers with performance gain

View File

@ -12,12 +12,13 @@ namespace double_conversion
int decimal_in_shortest_low,
int decimal_in_shortest_high,
int max_leading_padding_zeroes_in_precision_mode,
int max_trailing_padding_zeroes_in_precision_mode)
int max_trailing_padding_zeroes_in_precision_mode,
int min_exponent_width)
{
*d2s = new DoubleToStringConverter(flags, infinity_symbol, nan_symbol,
exponent_character, decimal_in_shortest_low,
decimal_in_shortest_high, max_leading_padding_zeroes_in_precision_mode,
max_trailing_padding_zeroes_in_precision_mode);
max_trailing_padding_zeroes_in_precision_mode, min_exponent_width);
}
int dconv_d2s(void *d2s, double value, char* buf, int buflen, int* strlength)

View File

@ -268,6 +268,12 @@ typedef struct __JSONObjectEncoder
If true, bytes are rejected. */
int rejectBytes;
/*
If true, adds a single 0 padding to the exponent in scientific notation if it
is between -9 and -5 both inclusive, which replicates the Python standard
library's JSON behavior, e.g. 1e-5 will become 1e-05. */
int zeroPadNegative9to5Exponent;
/*
Configuration for item and key separators, e.g. "," and ":" for a compact representation or ", " and ": " to match the Python standard library's defaults. */
size_t itemSeparatorLength;
@ -382,7 +388,8 @@ void dconv_d2s_init(void **d2s,
int decimal_in_shortest_low,
int decimal_in_shortest_high,
int max_leading_padding_zeroes_in_precision_mode,
int max_trailing_padding_zeroes_in_precision_mode);
int max_trailing_padding_zeroes_in_precision_mode,
int min_exponent_width);
int dconv_d2s(void *d2s, double value, char* buf, int buflen, int* strlength);
void dconv_d2s_free(void **d2s);

View File

@ -659,7 +659,7 @@ static char *Object_iterGetName(JSOBJ obj, JSONTypeContext *tc, size_t *outLen)
PyObject* objToJSON(PyObject* self, PyObject *args, PyObject *kwargs)
{
static char *kwlist[] = { "obj", "ensure_ascii", "encode_html_chars", "escape_forward_slashes", "sort_keys", "indent", "allow_nan", "reject_bytes", "default", "separators", NULL };
static char *kwlist[] = { "obj", "ensure_ascii", "encode_html_chars", "escape_forward_slashes", "sort_keys", "indent", "allow_nan", "reject_bytes", "default", "separators", "zero_pad_negative_9_to_5_exponent", NULL };
char buffer[65536];
char *ret;
@ -676,9 +676,11 @@ PyObject* objToJSON(PyObject* self, PyObject *args, PyObject *kwargs)
PyObject *separatorsItemBytes = NULL;
PyObject *oseparatorsKey = NULL;
PyObject *separatorsKeyBytes = NULL;
PyObject *ozero_pad_negative_9_to_5_exponent = NULL;
int allowNan = -1;
int orejectBytes = -1;
size_t retLen;
int minExponentWidth = 0;
JSONObjectEncoder encoder =
{
@ -704,6 +706,7 @@ PyObject* objToJSON(PyObject* self, PyObject *args, PyObject *kwargs)
0, //indent
1, //allowNan
1, //rejectBytes
0, //zeroPadNegative9to5Exponent
0, //itemSeparatorLength
NULL, //itemSeparatorChars
0, //keySeparatorLength
@ -714,7 +717,7 @@ PyObject* objToJSON(PyObject* self, PyObject *args, PyObject *kwargs)
PRINTMARK();
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|OOOOiiiOO", kwlist, &oinput, &oensureAscii, &oencodeHTMLChars, &oescapeForwardSlashes, &osortKeys, &encoder.indent, &allowNan, &orejectBytes, &odefaultFn, &oseparators))
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O|OOOOiiiOOO", kwlist, &oinput, &oensureAscii, &oencodeHTMLChars, &oescapeForwardSlashes, &osortKeys, &encoder.indent, &allowNan, &orejectBytes, &odefaultFn, &oseparators, &ozero_pad_negative_9_to_5_exponent))
{
return NULL;
}
@ -816,9 +819,14 @@ PyObject* objToJSON(PyObject* self, PyObject *args, PyObject *kwargs)
}
}
if (ozero_pad_negative_9_to_5_exponent != NULL && PyObject_IsTrue(ozero_pad_negative_9_to_5_exponent))
{
minExponentWidth = 2;
}
encoder.d2s = NULL;
dconv_d2s_init(&encoder.d2s, DCONV_D2S_EMIT_TRAILING_DECIMAL_POINT | DCONV_D2S_EMIT_TRAILING_ZERO_AFTER_POINT | DCONV_D2S_EMIT_POSITIVE_EXPONENT_SIGN,
csInf, csNan, 'e', DCONV_DECIMAL_IN_SHORTEST_LOW, DCONV_DECIMAL_IN_SHORTEST_HIGH, 0, 0);
csInf, csNan, 'e', DCONV_DECIMAL_IN_SHORTEST_LOW, DCONV_DECIMAL_IN_SHORTEST_HIGH, 0, 0, minExponentWidth);
PRINTMARK();
ret = JSON_EncodeObject (oinput, &encoder, buffer, sizeof (buffer), &retLen);

View File

@ -59,7 +59,8 @@ PyObject* JSONDecodeError;
"Set encode_html_chars=True to encode < > & as unicode escape sequences. "\
"Set escape_forward_slashes=False to prevent escaping / characters." \
"Set allow_nan=False to raise an exception when NaN or Infinity would be serialized." \
"Set reject_bytes=True to raise TypeError on bytes."
"Set reject_bytes=True to raise TypeError on bytes." \
"Set zero_pad_negative_9_to_5_exponent=True to add 0-pad for exponents -9 to -5."
static PyMethodDef ujsonMethods[] = {
{"encode", (PyCFunction) objToJSON, METH_VARARGS | METH_KEYWORDS, "Converts arbitrary object recursively into JSON. " ENCODER_HELP_TEXT},

View File

@ -133,6 +133,13 @@ parser.add_argument(
help="Sets the escape_forward_slashes option to ujson.dumps(). "
"May be 0 or 1 or 0,1 to test both.",
)
parser.add_argument(
"--zero_pad_negative_9_to_5_exponent",
default=(0, 1),
action=ListOption,
help="Sets the zero_pad_negative_9_to_5_exponent option to ujson.dumps(). "
"May be 0 or 1 or 0,1 to test both.",
)
parser.add_argument(
"--dump-python",
action="store_true",

View File

@ -84,12 +84,19 @@ def test_double_long_decimal_issue():
assert sut == decoded
# NOTE: can't match exponents -9 to -5; Python 0-pads
# NOTE: The default behaviour can't match exponents -9 to -5; Python 0-pads
@pytest.mark.parametrize("val", [1e-10, 1e-4, 1e10, 1e15, 1e16, 1e30])
def test_encode_float_string_rep(val):
assert ujson.dumps(val) == json.dumps(val)
@pytest.mark.parametrize(
"val", [1e-10, 1e-9, 1e-8, 1e-6, 1e-5, 1e-4, 1e10, 1e15, 1e16, 1e30]
)
def test_encode_float_string_replicate_python(val):
assert ujson.dumps(val, zero_pad_negative_9_to_5_exponent=True) == json.dumps(val)
def test_encode_decode_long_decimal():
sut = {"a": -528656961.4399388}
encoded = ujson.dumps(sut)