diff options
author | Bob Ippolito <bob@redivi.com> | 2013-05-01 13:00:06 -0700 |
---|---|---|
committer | Bob Ippolito <bob@redivi.com> | 2013-05-01 13:00:06 -0700 |
commit | 236c8a174c0e333608e992f6f7ec7826c2fd1cd8 (patch) | |
tree | 3a75f4ab4e8a7d6a1af1dd793a0cea99bf0a6fe0 | |
parent | 928421f68ff2128c5397553a57f3d76a75e39ca4 (diff) | |
download | simplejson-236c8a174c0e333608e992f6f7ec7826c2fd1cd8.tar.gz |
ignore_nan #63ecma-262-63
-rw-r--r-- | CHANGES.txt | 4 | ||||
-rw-r--r-- | conf.py | 4 | ||||
-rw-r--r-- | index.rst | 41 | ||||
-rw-r--r-- | simplejson/__init__.py | 49 | ||||
-rw-r--r-- | simplejson/_speedups.c | 35 | ||||
-rw-r--r-- | simplejson/encoder.py | 16 | ||||
-rw-r--r-- | simplejson/tests/test_float.py | 10 |
7 files changed, 112 insertions, 47 deletions
diff --git a/CHANGES.txt b/CHANGES.txt index 7c30cb1..de6f269 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,5 +1,9 @@ Version 3.2.0 released 2013-05-XX +* New ignore_nan kwarg in encoder that serializes out + of range floats (Infinity, -Infinity, NaN) as null for ECMA-262 + compliance. + https://github.com/simplejson/simplejson/pull/63 * New for_json kwarg in encoder to make it possible to for subclasses of dict and list to be specialized. https://github.com/simplejson/simplejson/pull/69 @@ -42,9 +42,9 @@ copyright = '2013, Bob Ippolito' # other places throughout the built documents. # # The short X.Y version. -version = '3.1' +version = '3.2' # The full version, including alpha/beta/rc tags. -release = '3.1.3' +release = '3.2.0' # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: @@ -129,7 +129,7 @@ Using :mod:`simplejson.tool` from the shell to validate and pretty-print:: Basic Usage ----------- -.. function:: dump(obj, fp[, skipkeys[, ensure_ascii[, check_circular[, allow_nan[, cls[, indent[, separators[, encoding[, default[, use_decimal[, namedtuple_as_object[, tuple_as_array[, bigint_as_string[, sort_keys[, item_sort_key[, **kw]]]]]]]]]]]]]]]]) +.. function:: dump(obj, fp[, skipkeys[, ensure_ascii[, check_circular[, allow_nan[, cls[, indent[, separators[, encoding[, default[, use_decimal[, namedtuple_as_object[, tuple_as_array[, bigint_as_string[, sort_keys[, item_sort_key[, [for_json[, ignore_nan[, **kw]]]]]]]]]]]]]]]]]]]) Serialize *obj* as a JSON formatted stream to *fp* (a ``.write()``-supporting file-like object). @@ -152,9 +152,10 @@ Basic Usage If *allow_nan* is false (default: ``True``), then it will be a :exc:`ValueError` to serialize out of range :class:`float` values (``nan``, - ``inf``, ``-inf``) in strict compliance of the JSON specification. + ``inf``, ``-inf``) in strict compliance of the original JSON specification. If *allow_nan* is true, their JavaScript equivalents will be used - (``NaN``, ``Infinity``, ``-Infinity``). + (``NaN``, ``Infinity``, ``-Infinity``). See also *ignore_nan* for ECMA-262 + compliant behavior. If *indent* is a string, then JSON array elements and object members will be pretty-printed with a newline followed by that string repeated @@ -187,7 +188,7 @@ Basic Usage .. note:: Subclassing is not recommended. Use the *default* kwarg - instead. This is faster and more portable. + or *for_json* instead. This is faster and more portable. If *use_decimal* is true (default: ``True``) then :class:`decimal.Decimal` will be natively serialized to JSON with full precision. @@ -252,14 +253,22 @@ Basic Usage .. versionchanged:: 3.2.0 *for_json* is new in 3.2.0. - .. note:: + If *ignore_nan* is true (default: ``False``), then out of range + :class:`float` values (``nan``, ``inf``, ``-inf``) will be serialized as + ``null`` in compliance with the ECMA-262 specification. If true, this will + override *allow_nan*. + + .. versionchanged:: 3.2.0 + *ignore_nan* is new in 3.2.0. + + .. note:: JSON is not a framed protocol so unlike :mod:`pickle` or :mod:`marshal` it does not make sense to serialize more than one JSON document without some container protocol to delimit them. -.. function:: dumps(obj[, skipkeys[, ensure_ascii[, check_circular[, allow_nan[, cls[, indent[, separators[, encoding[, default[, use_decimal[, namedtuple_as_object[, tuple_as_array[, bigint_as_string[, sort_keys[, item_sort_key[, for_json[, **kw]]]]]]]]]]]]]]]]]) +.. function:: dumps(obj[, skipkeys[, ensure_ascii[, check_circular[, allow_nan[, cls[, indent[, separators[, encoding[, default[, use_decimal[, namedtuple_as_object[, tuple_as_array[, bigint_as_string[, sort_keys[, item_sort_key[, for_json[, ignore_nan[, **kw]]]]]]]]]]]]]]]]]]) Serialize *obj* to a JSON formatted :class:`str`. @@ -268,6 +277,8 @@ Basic Usage :func:`dump`. Note that the default *ensure_ascii* setting has much better performance. + The other options have the same meaning as in :func:`dump`. + .. function:: load(fp[, encoding[, cls[, object_hook[, parse_float[, parse_int[, parse_constant[, object_pairs_hook[, use_decimal[, **kw]]]]]]]]]) @@ -463,7 +474,7 @@ Encoders and decoders :exc:`JSONDecodeError` will be raised if the given JSON document is not valid. -.. class:: JSONEncoder([skipkeys[, ensure_ascii[, check_circular[, allow_nan[, sort_keys[, indent[, separators[, encoding[, default[, use_decimal[, namedtuple_as_object[, tuple_as_array[, bigint_as_string[, item_sort_key[, for_json]]]]]]]]]]]]]]) +.. class:: JSONEncoder([skipkeys[, ensure_ascii[, check_circular[, allow_nan[, sort_keys[, indent[, separators[, encoding[, default[, use_decimal[, namedtuple_as_object[, tuple_as_array[, bigint_as_string[, item_sort_key[, for_json[, ignore_nan]]]]]]]]]]]]]]]]) Extensible JSON encoder for Python data structures. @@ -498,7 +509,7 @@ Encoders and decoders .. note:: Subclassing is not recommended. You should use the *default* - kwarg. This is faster and more portable than subclassing. + or *for_json* kwarg. This is faster and more portable than subclassing. If *skipkeys* is false (the default), then it is a :exc:`TypeError` to attempt encoding of keys that are not str, int, long, float or None. If @@ -514,10 +525,10 @@ Encoders and decoders Otherwise, no such check takes place. If *allow_nan* is true (the default), then ``NaN``, ``Infinity``, and - ``-Infinity`` will be encoded as such. This behavior is not JSON + ``-Infinity`` will be encoded as such. This behavior is not JSON specification compliant, but is consistent with most JavaScript based encoders and decoders. Otherwise, it will be a :exc:`ValueError` to encode - such floats. + such floats. See also *ignore_nan* for ECMA-262 compliant behavior. If *sort_keys* is true (not the default), then the output of dictionaries will be sorted by key; this is useful for regression tests to ensure that @@ -601,6 +612,14 @@ Encoders and decoders .. versionchanged:: 3.2.0 *for_json* is new in 3.2.0. + If *ignore_nan* is true (default: ``False``), then out of range + :class:`float` values (``nan``, ``inf``, ``-inf``) will be serialized as + ``null`` in compliance with the ECMA-262 specification. If true, this will + override *allow_nan*. + + .. versionchanged:: 3.2.0 + *ignore_nan* is new in 3.2.0. + .. method:: default(o) Implement this method in a subclass such that it returns a serializable @@ -648,7 +667,7 @@ Encoders and decoders Note that :meth:`encode` has much better performance than :meth:`iterencode`. -.. class:: JSONEncoderForHTML([skipkeys[, ensure_ascii[, check_circular[, allow_nan[, sort_keys[, indent[, separators[, encoding[, default[, use_decimal[, namedtuple_as_object[, tuple_as_array[, bigint_as_string[, item_sort_key]]]]]]]]]]]]]) +.. class:: JSONEncoderForHTML([skipkeys[, ensure_ascii[, check_circular[, allow_nan[, sort_keys[, indent[, separators[, encoding[, default[, use_decimal[, namedtuple_as_object[, tuple_as_array[, bigint_as_string[, item_sort_key[, for_json[, ignore_nan]]]]]]]]]]]]]]]]) Subclass of :class:`JSONEncoder` that escapes &, <, and > for embedding in HTML. diff --git a/simplejson/__init__.py b/simplejson/__init__.py index a1095c5..37a9e52 100644 --- a/simplejson/__init__.py +++ b/simplejson/__init__.py @@ -98,7 +98,7 @@ Using simplejson.tool from the shell to validate and pretty-print:: Expecting property name: line 1 column 3 (char 2) """ from __future__ import absolute_import -__version__ = '3.1.3' +__version__ = '3.2.0' __all__ = [ 'dump', 'dumps', 'load', 'loads', 'JSONDecoder', 'JSONDecodeError', 'JSONEncoder', @@ -143,6 +143,7 @@ _default_encoder = JSONEncoder( bigint_as_string=False, item_sort_key=None, for_json=False, + ignore_nan=False, ) def dump(obj, fp, skipkeys=False, ensure_ascii=True, check_circular=True, @@ -150,28 +151,29 @@ def dump(obj, fp, skipkeys=False, ensure_ascii=True, check_circular=True, encoding='utf-8', default=None, use_decimal=True, namedtuple_as_object=True, tuple_as_array=True, bigint_as_string=False, sort_keys=False, item_sort_key=None, - for_json=False, **kw): + for_json=False, ignore_nan=False, **kw): """Serialize ``obj`` as a JSON formatted stream to ``fp`` (a ``.write()``-supporting file-like object). - If ``skipkeys`` is true then ``dict`` keys that are not basic types + If *skipkeys* is true then ``dict`` keys that are not basic types (``str``, ``unicode``, ``int``, ``long``, ``float``, ``bool``, ``None``) will be skipped instead of raising a ``TypeError``. - If ``ensure_ascii`` is false, then the some chunks written to ``fp`` + If *ensure_ascii* is false, then the some chunks written to ``fp`` may be ``unicode`` instances, subject to normal Python ``str`` to ``unicode`` coercion rules. Unless ``fp.write()`` explicitly understands ``unicode`` (as in ``codecs.getwriter()``) this is likely to cause an error. - If ``check_circular`` is false, then the circular reference check + If *check_circular* is false, then the circular reference check for container types will be skipped and a circular reference will result in an ``OverflowError`` (or worse). - If ``allow_nan`` is false, then it will be a ``ValueError`` to + If *allow_nan* is false, then it will be a ``ValueError`` to serialize out of range ``float`` values (``nan``, ``inf``, ``-inf``) - in strict compliance of the JSON specification, instead of using the - JavaScript equivalents (``NaN``, ``Infinity``, ``-Infinity``). + in strict compliance of the original JSON specification, instead of using + the JavaScript equivalents (``NaN``, ``Infinity``, ``-Infinity``). See + *ignore_nan* for ECMA-262 compliant behavior. If *indent* is a string, then JSON array elements and object members will be pretty-printed with a newline followed by that string repeated @@ -180,16 +182,16 @@ def dump(obj, fp, skipkeys=False, ensure_ascii=True, check_circular=True, versions of simplejson earlier than 2.1.0, an integer is also accepted and is converted to a string with that many spaces. - If specified, ``separators`` should be an + If specified, *separators* should be an ``(item_separator, key_separator)`` tuple. The default is ``(', ', ': ')`` if *indent* is ``None`` and ``(',', ': ')`` otherwise. To get the most compact JSON representation, you should specify ``(',', ':')`` to eliminate whitespace. - ``encoding`` is the character encoding for str instances, default is UTF-8. + *encoding* is the character encoding for str instances, default is UTF-8. - ``default(obj)`` is a function that should return a serializable version - of obj or raise TypeError. The default simply raises TypeError. + *default(obj)* is a function that should return a serializable version + of obj or raise ``TypeError``. The default simply raises ``TypeError``. If *use_decimal* is true (default: ``True``) then decimal.Decimal will be natively serialized to JSON with full precision. @@ -219,10 +221,15 @@ def dump(obj, fp, skipkeys=False, ensure_ascii=True, check_circular=True, method will use the return value of that method for encoding as JSON instead of the object. + If *ignore_nan* is true (default: ``False``), then out of range + :class:`float` values (``nan``, ``inf``, ``-inf``) will be serialized as + ``null`` in compliance with the ECMA-262 specification. If true, this will + override *allow_nan*. + To use a custom ``JSONEncoder`` subclass (e.g. one that overrides the ``.default()`` method to serialize additional types), specify it with - the ``cls`` kwarg. NOTE: You should use *default* instead of subclassing - whenever possible. + the ``cls`` kwarg. NOTE: You should use *default* or *for_json* instead + of subclassing whenever possible. """ # cached encoder @@ -232,7 +239,7 @@ def dump(obj, fp, skipkeys=False, ensure_ascii=True, check_circular=True, encoding == 'utf-8' and default is None and use_decimal and namedtuple_as_object and tuple_as_array and not bigint_as_string and not item_sort_key - and not for_json and not kw): + and not for_json and not ignore_nan and not kw): iterable = _default_encoder.iterencode(obj) else: if cls is None: @@ -247,6 +254,7 @@ def dump(obj, fp, skipkeys=False, ensure_ascii=True, check_circular=True, sort_keys=sort_keys, item_sort_key=item_sort_key, for_json=for_json, + ignore_nan=ignore_nan, **kw).iterencode(obj) # could accelerate with writelines in some versions of Python, at # a debuggability cost @@ -259,7 +267,7 @@ def dumps(obj, skipkeys=False, ensure_ascii=True, check_circular=True, encoding='utf-8', default=None, use_decimal=True, namedtuple_as_object=True, tuple_as_array=True, bigint_as_string=False, sort_keys=False, item_sort_key=None, - for_json=False, **kw): + for_json=False, ignore_nan=False, **kw): """Serialize ``obj`` to a JSON formatted ``str``. If ``skipkeys`` is false then ``dict`` keys that are not basic types @@ -323,6 +331,11 @@ def dumps(obj, skipkeys=False, ensure_ascii=True, check_circular=True, method will use the return value of that method for encoding as JSON instead of the object. + If *ignore_nan* is true (default: ``False``), then out of range + :class:`float` values (``nan``, ``inf``, ``-inf``) will be serialized as + ``null`` in compliance with the ECMA-262 specification. If true, this will + override *allow_nan*. + To use a custom ``JSONEncoder`` subclass (e.g. one that overrides the ``.default()`` method to serialize additional types), specify it with the ``cls`` kwarg. NOTE: You should use *default* instead of subclassing @@ -336,7 +349,8 @@ def dumps(obj, skipkeys=False, ensure_ascii=True, check_circular=True, encoding == 'utf-8' and default is None and use_decimal and namedtuple_as_object and tuple_as_array and not bigint_as_string and not sort_keys - and not item_sort_key and not for_json and not kw): + and not item_sort_key and not for_json + and not ignore_nan and not kw): return _default_encoder.encode(obj) if cls is None: cls = JSONEncoder @@ -351,6 +365,7 @@ def dumps(obj, skipkeys=False, ensure_ascii=True, check_circular=True, sort_keys=sort_keys, item_sort_key=item_sort_key, for_json=for_json, + ignore_nan=ignore_nan, **kw).encode(obj) diff --git a/simplejson/_speedups.c b/simplejson/_speedups.c index c698130..93f136c 100644 --- a/simplejson/_speedups.c +++ b/simplejson/_speedups.c @@ -93,6 +93,9 @@ typedef int Py_ssize_t; #define PyEncoder_Check(op) PyObject_TypeCheck(op, &PyEncoderType) #define PyEncoder_CheckExact(op) (Py_TYPE(op) == &PyEncoderType) +#define JSON_ALLOW_NAN 1 +#define JSON_IGNORE_NAN 2 + static PyTypeObject PyScannerType; static PyTypeObject PyEncoderType; @@ -163,7 +166,8 @@ typedef struct _PyEncoderObject { PyObject *skipkeys_bool; int skipkeys; int fast_encode; - int allow_nan; + /* 0, JSON_ALLOW_NAN, JSON_IGNORE_NAN */ + int allow_or_ignore_nan; int use_decimal; int namedtuple_as_object; int tuple_as_array; @@ -725,11 +729,11 @@ encoder_dict_iteritems(PyEncoderObject *s, PyObject *dct) goto bail; #if PY_MAJOR_VERSION < 3 else if (PyString_Check(key)) { - // item can be added as-is + /* item can be added as-is */ } #endif /* PY_MAJOR_VERSION < 3 */ else if (PyUnicode_Check(key)) { - // item can be added as-is + /* item can be added as-is */ } else { PyObject *tpl; @@ -737,7 +741,7 @@ encoder_dict_iteritems(PyEncoderObject *s, PyObject *dct) if (kstr == NULL) goto bail; else if (kstr == Py_None) { - // skipkeys + /* skipkeys */ Py_DECREF(kstr); continue; } @@ -2588,22 +2592,23 @@ static int encoder_init(PyObject *self, PyObject *args, PyObject *kwds) { /* initialize Encoder object */ - static char *kwlist[] = {"markers", "default", "encoder", "indent", "key_separator", "item_separator", "sort_keys", "skipkeys", "allow_nan", "key_memo", "use_decimal", "namedtuple_as_object", "tuple_as_array", "bigint_as_string", "item_sort_key", "encoding", "for_json", "Decimal", NULL}; + static char *kwlist[] = {"markers", "default", "encoder", "indent", "key_separator", "item_separator", "sort_keys", "skipkeys", "allow_nan", "key_memo", "use_decimal", "namedtuple_as_object", "tuple_as_array", "bigint_as_string", "item_sort_key", "encoding", "for_json", "ignore_nan", "Decimal", NULL}; PyEncoderObject *s; PyObject *markers, *defaultfn, *encoder, *indent, *key_separator; PyObject *item_separator, *sort_keys, *skipkeys, *allow_nan, *key_memo; PyObject *use_decimal, *namedtuple_as_object, *tuple_as_array; - PyObject *bigint_as_string, *item_sort_key, *encoding, *Decimal, *for_json; + PyObject *bigint_as_string, *item_sort_key, *encoding, *for_json; + PyObject *ignore_nan, *Decimal; assert(PyEncoder_Check(self)); s = (PyEncoderObject *)self; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "OOOOOOOOOOOOOOOOOO:make_encoder", kwlist, + if (!PyArg_ParseTupleAndKeywords(args, kwds, "OOOOOOOOOOOOOOOOOOO:make_encoder", kwlist, &markers, &defaultfn, &encoder, &indent, &key_separator, &item_separator, &sort_keys, &skipkeys, &allow_nan, &key_memo, &use_decimal, &namedtuple_as_object, &tuple_as_array, &bigint_as_string, - &item_sort_key, &encoding, &for_json, &Decimal)) + &item_sort_key, &encoding, &for_json, &ignore_nan, &Decimal)) return -1; s->markers = markers; @@ -2619,7 +2624,9 @@ encoder_init(PyObject *self, PyObject *args, PyObject *kwds) s->skipkeys = PyObject_IsTrue(skipkeys); s->key_memo = key_memo; s->fast_encode = (PyCFunction_Check(s->encoder) && PyCFunction_GetFunction(s->encoder) == (PyCFunction)py_encode_basestring_ascii); - s->allow_nan = PyObject_IsTrue(allow_nan); + s->allow_or_ignore_nan = ( + (PyObject_IsTrue(ignore_nan) ? JSON_IGNORE_NAN : 0) | + (PyObject_IsTrue(allow_nan) ? JSON_ALLOW_NAN : 0)); s->use_decimal = PyObject_IsTrue(use_decimal); s->namedtuple_as_object = PyObject_IsTrue(namedtuple_as_object); s->tuple_as_array = PyObject_IsTrue(tuple_as_array); @@ -2734,11 +2741,15 @@ encoder_encode_float(PyEncoderObject *s, PyObject *obj) /* Return the JSON representation of a PyFloat */ double i = PyFloat_AS_DOUBLE(obj); if (!Py_IS_FINITE(i)) { - if (!s->allow_nan) { + if (!s->allow_or_ignore_nan) { PyErr_SetString(PyExc_ValueError, "Out of range float values are not JSON compliant"); return NULL; } - if (i > 0) { + if (s->allow_or_ignore_nan & JSON_IGNORE_NAN) { + return _encoded_const(Py_None); + } + /* JSON_ALLOW_NAN is set */ + else if (i > 0) { static PyObject *sInfinity = NULL; if (sInfinity == NULL) sInfinity = JSON_InternFromString("Infinity"); @@ -2985,7 +2996,7 @@ encoder_listencode_dict(PyEncoderObject *s, JSON_Accu *rval, PyObject *dct, Py_s if (kstr == NULL) goto bail; else if (kstr == Py_None) { - // skipkeys + /* skipkeys */ Py_DECREF(item); Py_DECREF(kstr); continue; diff --git a/simplejson/encoder.py b/simplejson/encoder.py index f2acdd3..9815ee5 100644 --- a/simplejson/encoder.py +++ b/simplejson/encoder.py @@ -121,7 +121,7 @@ class JSONEncoder(object): indent=None, separators=None, encoding='utf-8', default=None, use_decimal=True, namedtuple_as_object=True, tuple_as_array=True, bigint_as_string=False, - item_sort_key=None, for_json=False): + item_sort_key=None, for_json=False, ignore_nan=False): """Constructor for JSONEncoder, with sensible defaults. If skipkeys is false, then it is a TypeError to attempt @@ -188,6 +188,11 @@ class JSONEncoder(object): method will use the return value of that method for encoding as JSON instead of the object. + If *ignore_nan* is true (default: ``False``), then out of range + :class:`float` values (``nan``, ``inf``, ``-inf``) will be serialized + as ``null`` in compliance with the ECMA-262 specification. If true, + this will override *allow_nan*. + """ self.skipkeys = skipkeys @@ -201,6 +206,7 @@ class JSONEncoder(object): self.bigint_as_string = bigint_as_string self.item_sort_key = item_sort_key self.for_json = for_json + self.ignore_nan = ignore_nan if indent is not None and not isinstance(indent, string_types): indent = indent * ' ' self.indent = indent @@ -285,7 +291,7 @@ class JSONEncoder(object): o = o.decode(_encoding) return _orig_encoder(o) - def floatstr(o, allow_nan=self.allow_nan, + def floatstr(o, allow_nan=self.allow_nan, ignore_nan=self.ignore_nan, _repr=FLOAT_REPR, _inf=PosInf, _neginf=-PosInf): # Check for specials. Note that this type of test is processor # and/or platform-specific, so do tests which don't depend on @@ -300,7 +306,9 @@ class JSONEncoder(object): else: return _repr(o) - if not allow_nan: + if ignore_nan: + text = 'null' + elif not allow_nan: raise ValueError( "Out of range float values are not JSON compliant: " + repr(o)) @@ -317,7 +325,7 @@ class JSONEncoder(object): self.skipkeys, self.allow_nan, key_memo, self.use_decimal, self.namedtuple_as_object, self.tuple_as_array, self.bigint_as_string, self.item_sort_key, - self.encoding, self.for_json, + self.encoding, self.for_json, self.ignore_nan, Decimal) else: _iterencode = _make_iterencode( diff --git a/simplejson/tests/test_float.py b/simplejson/tests/test_float.py index f61ed68..e382ec2 100644 --- a/simplejson/tests/test_float.py +++ b/simplejson/tests/test_float.py @@ -5,13 +5,21 @@ import simplejson as json from simplejson.decoder import NaN, PosInf, NegInf class TestFloat(TestCase): - def test_degenerates(self): + def test_degenerates_allow(self): for inf in (PosInf, NegInf): self.assertEqual(json.loads(json.dumps(inf)), inf) # Python 2.5 doesn't have math.isnan nan = json.loads(json.dumps(NaN)) self.assertTrue((0 + nan) != nan) + def test_degenerates_ignore(self): + for f in (PosInf, NegInf, NaN): + self.assertEqual(json.loads(json.dumps(f, ignore_nan=True)), None) + + def test_degenerates_deny(self): + for f in (PosInf, NegInf, NaN): + self.assertRaises(ValueError, json.dumps, f, allow_nan=False) + def test_floats(self): for num in [1617161771.7650001, math.pi, math.pi**100, math.pi**-100, 3.1]: |