From d80c454bbfc30a9aa3ee6d3315a339caf3ef27f6 Mon Sep 17 00:00:00 2001 From: David Hewitt Date: Mon, 6 Nov 2023 18:24:51 +0000 Subject: [PATCH] fix: nan inf float (#1062) Co-authored-by: JeanArhancet --- python/pydantic_core/core_schema.py | 3 + src/errors/validation_exception.rs | 4 +- src/serializers/config.rs | 29 + src/serializers/errors.rs | 27 +- src/serializers/mod.rs | 1 + src/serializers/ser.rs | 1299 ++++++++++++++++++++ src/serializers/shared.rs | 7 +- src/serializers/type_serializers/float.rs | 102 ++ src/serializers/type_serializers/mod.rs | 1 + src/serializers/type_serializers/simple.rs | 1 - tests/serializers/test_simple.py | 27 + tests/test_errors.py | 2 +- tests/validators/test_float.py | 1 + 13 files changed, 1496 insertions(+), 8 deletions(-) create mode 100644 src/serializers/ser.rs create mode 100644 src/serializers/type_serializers/float.rs diff --git a/python/pydantic_core/core_schema.py b/python/pydantic_core/core_schema.py index 40d1eec77..fec3b9966 100644 --- a/python/pydantic_core/core_schema.py +++ b/python/pydantic_core/core_schema.py @@ -68,6 +68,8 @@ class CoreConfig(TypedDict, total=False): allow_inf_nan: Whether to allow infinity and NaN values for float fields. Default is `True`. ser_json_timedelta: The serialization option for `timedelta` values. Default is 'iso8601'. ser_json_bytes: The serialization option for `bytes` values. Default is 'utf8'. + ser_json_inf_nan: The serialization option for infinity and NaN values + in float fields. Default is 'null'. hide_input_in_errors: Whether to hide input data from `ValidationError` representation. validation_error_cause: Whether to add user-python excs to the __cause__ of a ValidationError. Requires exceptiongroup backport pre Python 3.11. @@ -102,6 +104,7 @@ class CoreConfig(TypedDict, total=False): # the config options are used to customise serialization to JSON ser_json_timedelta: Literal['iso8601', 'float'] # default: 'iso8601' ser_json_bytes: Literal['utf8', 'base64', 'hex'] # default: 'utf8' + ser_json_inf_nan: Literal['null', 'constants'] # default: 'null' # used to hide input data from ValidationError repr hide_input_in_errors: bool validation_error_cause: bool # default: False diff --git a/src/errors/validation_exception.rs b/src/errors/validation_exception.rs index cb3802d56..d616e3022 100644 --- a/src/errors/validation_exception.rs +++ b/src/errors/validation_exception.rs @@ -324,12 +324,12 @@ impl ValidationError { Some(indent) => { let indent = vec![b' '; indent]; let formatter = PrettyFormatter::with_indent(&indent); - let mut ser = serde_json::Serializer::with_formatter(writer, formatter); + let mut ser = crate::serializers::ser::PythonSerializer::with_formatter(writer, formatter); serializer.serialize(&mut ser).map_err(json_py_err)?; ser.into_inner() } None => { - let mut ser = serde_json::Serializer::new(writer); + let mut ser = crate::serializers::ser::PythonSerializer::new(writer); serializer.serialize(&mut ser).map_err(json_py_err)?; ser.into_inner() } diff --git a/src/serializers/config.rs b/src/serializers/config.rs index fb65623b7..e83497f64 100644 --- a/src/serializers/config.rs +++ b/src/serializers/config.rs @@ -189,3 +189,32 @@ pub fn utf8_py_error(py: Python, err: Utf8Error, data: &[u8]) -> PyErr { Err(err) => err, } } + +#[derive(Default, Debug, Clone, PartialEq, Eq)] +pub(crate) enum InfNanMode { + #[default] + Null, + Constants, +} + +impl FromStr for InfNanMode { + type Err = PyErr; + + fn from_str(s: &str) -> Result { + match s { + "null" => Ok(Self::Null), + "constants" => Ok(Self::Constants), + s => py_schema_err!( + "Invalid inf_nan serialization mode: `{}`, expected `null` or `constants`", + s + ), + } + } +} + +impl FromPyObject<'_> for InfNanMode { + fn extract(ob: &'_ PyAny) -> PyResult { + let s = ob.extract::<&str>()?; + Self::from_str(s) + } +} diff --git a/src/serializers/errors.rs b/src/serializers/errors.rs index ac4ea784f..71a0a024e 100644 --- a/src/serializers/errors.rs +++ b/src/serializers/errors.rs @@ -14,8 +14,33 @@ pub(super) fn py_err_se_err(py_error: E) -> T { T::custom(py_error.to_string()) } +#[pyclass(extends=PyValueError, module="pydantic_core._pydantic_core")] +#[derive(Debug, Clone)] +pub struct PythonSerializerError { + pub message: String, +} + +impl fmt::Display for PythonSerializerError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.message) + } +} + +impl std::error::Error for PythonSerializerError {} + +impl serde::ser::Error for PythonSerializerError { + fn custom(msg: T) -> Self + where + T: fmt::Display, + { + PythonSerializerError { + message: format!("{msg}"), + } + } +} + /// convert a serde serialization error into a `PyErr` -pub(super) fn se_err_py_err(error: serde_json::Error) -> PyErr { +pub(super) fn se_err_py_err(error: PythonSerializerError) -> PyErr { let s = error.to_string(); if let Some(msg) = s.strip_prefix(UNEXPECTED_TYPE_SER_MARKER) { if msg.is_empty() { diff --git a/src/serializers/mod.rs b/src/serializers/mod.rs index 00d70162f..e9208a510 100644 --- a/src/serializers/mod.rs +++ b/src/serializers/mod.rs @@ -23,6 +23,7 @@ mod fields; mod filter; mod infer; mod ob_type; +pub mod ser; mod shared; mod type_serializers; diff --git a/src/serializers/ser.rs b/src/serializers/ser.rs new file mode 100644 index 000000000..170cd1849 --- /dev/null +++ b/src/serializers/ser.rs @@ -0,0 +1,1299 @@ +use std::{io, num::FpCategory}; + +use serde::{ser::Impossible, serde_if_integer128, Serialize, Serializer}; +use serde_json::ser::{CompactFormatter, Formatter, PrettyFormatter, State}; + +use super::errors::PythonSerializerError; + +macro_rules! tri { + ($e:expr $(,)?) => { + match $e { + core::result::Result::Ok(val) => val, + core::result::Result::Err(err) => return core::result::Result::Err(err), + } + }; +} + +type Result = std::result::Result; +const TOKEN: &str = "$serde_json::private::Number"; +pub struct PythonSerializer { + writer: W, + formatter: F, +} + +impl PythonSerializer +where + W: io::Write, +{ + /// Creates a new JSON serializer. + #[inline] + pub fn new(writer: W) -> Self { + PythonSerializer::with_formatter(writer, CompactFormatter) + } +} + +impl<'a, W> PythonSerializer> +where + W: io::Write, +{ + /// Creates a new JSON pretty print serializer. + #[inline] + pub fn pretty(writer: W) -> Self { + PythonSerializer::with_formatter(writer, PrettyFormatter::new()) + } +} + +impl PythonSerializer +where + W: io::Write, + F: Formatter, +{ + /// Creates a new JSON visitor whose output will be written to the writer + /// specified. + #[inline] + pub fn with_formatter(writer: W, formatter: F) -> Self { + PythonSerializer { writer, formatter } + } + + /// Unwrap the `Writer` from the `Serializer`. + #[inline] + pub fn into_inner(self) -> W { + self.writer + } +} + +impl<'a, W, F> Serializer for &'a mut PythonSerializer +where + W: io::Write, + F: Formatter, +{ + type Ok = (); + type Error = PythonSerializerError; + + type SerializeSeq = Compound<'a, W, F>; + type SerializeTuple = Compound<'a, W, F>; + type SerializeTupleStruct = Compound<'a, W, F>; + type SerializeTupleVariant = Compound<'a, W, F>; + type SerializeMap = Compound<'a, W, F>; + type SerializeStruct = Compound<'a, W, F>; + type SerializeStructVariant = Compound<'a, W, F>; + + #[inline] + fn serialize_bool(self, value: bool) -> Result<()> { + self.formatter + .write_bool(&mut self.writer, value) + .map_err(|e| PythonSerializerError { message: e.to_string() }) + } + + #[inline] + fn serialize_i8(self, value: i8) -> Result<()> { + self.formatter + .write_i8(&mut self.writer, value) + .map_err(|e| PythonSerializerError { message: e.to_string() }) + } + + fn serialize_i16(self, value: i16) -> Result { + self.formatter + .write_i16(&mut self.writer, value) + .map_err(|e| PythonSerializerError { message: e.to_string() }) + } + + fn serialize_i32(self, value: i32) -> Result { + self.formatter + .write_i32(&mut self.writer, value) + .map_err(|e| PythonSerializerError { message: e.to_string() }) + } + + fn serialize_i64(self, value: i64) -> Result { + self.formatter + .write_i64(&mut self.writer, value) + .map_err(|e| PythonSerializerError { message: e.to_string() }) + } + + fn serialize_u8(self, value: u8) -> Result { + self.formatter + .write_u8(&mut self.writer, value) + .map_err(|e| PythonSerializerError { message: e.to_string() }) + } + + fn serialize_u16(self, value: u16) -> Result { + self.formatter + .write_u16(&mut self.writer, value) + .map_err(|e| PythonSerializerError { message: e.to_string() }) + } + + fn serialize_u32(self, value: u32) -> Result { + self.formatter + .write_u32(&mut self.writer, value) + .map_err(|e| PythonSerializerError { message: e.to_string() }) + } + + fn serialize_u64(self, value: u64) -> Result { + self.formatter + .write_u64(&mut self.writer, value) + .map_err(|e| PythonSerializerError { message: e.to_string() }) + } + + fn serialize_u128(self, value: u128) -> Result<()> { + self.formatter + .write_u128(&mut self.writer, value) + .map_err(|e| PythonSerializerError { message: e.to_string() }) + } + + #[inline] + fn serialize_f32(self, value: f32) -> Result<()> { + match value.classify() { + FpCategory::Nan => self + .formatter + .write_number_str(&mut self.writer, "NaN") + .map_err(|e| PythonSerializerError { message: e.to_string() }), + FpCategory::Infinite => { + let infinity = if value.is_sign_negative() { + "-Infinity" + } else { + "Infinity" + }; + self.formatter + .write_number_str(&mut self.writer, infinity) + .map_err(|e| PythonSerializerError { message: e.to_string() }) + } + _ => self + .formatter + .write_f32(&mut self.writer, value) + .map_err(|e| PythonSerializerError { message: e.to_string() }), + } + } + + fn serialize_f64(self, value: f64) -> Result { + match value.classify() { + FpCategory::Nan => self + .formatter + .write_number_str(&mut self.writer, "NaN") + .map_err(|e| PythonSerializerError { message: e.to_string() }), + FpCategory::Infinite => { + let infinity = if value.is_sign_negative() { + "-Infinity" + } else { + "Infinity" + }; + self.formatter + .write_number_str(&mut self.writer, infinity) + .map_err(|e| PythonSerializerError { message: e.to_string() }) + } + _ => self + .formatter + .write_f64(&mut self.writer, value) + .map_err(|e| PythonSerializerError { message: e.to_string() }), + } + } + + fn serialize_char(self, value: char) -> Result { + // A char encoded as UTF-8 takes 4 bytes at most. + let mut buf = [0; 4]; + self.serialize_str(value.encode_utf8(&mut buf)) + } + + fn serialize_str(self, value: &str) -> Result { + format_escaped_str(&mut self.writer, &mut self.formatter, value) + .map_err(|e| PythonSerializerError { message: e.to_string() }) + } + + fn serialize_bytes(self, value: &[u8]) -> Result<()> { + self.formatter + .write_byte_array(&mut self.writer, value) + .map_err(|e| PythonSerializerError { message: e.to_string() }) + } + + fn serialize_none(self) -> Result { + self.formatter + .write_null(&mut self.writer) + .map_err(|e| PythonSerializerError { message: e.to_string() }) + } + + fn serialize_some(self, value: &T) -> Result + where + T: ?Sized + Serialize, + { + value.serialize(self) + } + + fn serialize_unit(self) -> Result { + self.formatter + .write_null(&mut self.writer) + .map_err(|e| PythonSerializerError { message: e.to_string() }) + } + + fn serialize_unit_struct(self, _name: &'static str) -> Result { + self.serialize_unit() + } + + fn serialize_unit_variant( + self, + _name: &'static str, + _variant_index: u32, + variant: &'static str, + ) -> Result { + self.serialize_str(variant) + } + + fn serialize_newtype_struct(self, _name: &'static str, value: &T) -> Result + where + T: Serialize, + { + value.serialize(self) + } + + fn serialize_newtype_variant( + self, + _name: &'static str, + _variant_index: u32, + variant: &'static str, + value: &T, + ) -> Result + where + T: Serialize, + { + tri!(self + .formatter + .begin_object(&mut self.writer) + .map_err(|e| PythonSerializerError { message: e.to_string() })); + tri!(self + .formatter + .begin_object_key(&mut self.writer, true) + .map_err(|e| PythonSerializerError { message: e.to_string() })); + tri!(self.serialize_str(variant)); + tri!(self + .formatter + .end_object_key(&mut self.writer) + .map_err(|e| PythonSerializerError { message: e.to_string() })); + tri!(self + .formatter + .begin_object_value(&mut self.writer) + .map_err(|e| PythonSerializerError { message: e.to_string() })); + tri!(value.serialize(&mut *self)); + tri!(self + .formatter + .end_object_value(&mut self.writer) + .map_err(|e| PythonSerializerError { message: e.to_string() })); + self.formatter + .end_object(&mut self.writer) + .map_err(|e| PythonSerializerError { message: e.to_string() }) + } + + fn serialize_seq(self, len: Option) -> Result { + tri!(self + .formatter + .begin_array(&mut self.writer) + .map_err(|e| PythonSerializerError { message: e.to_string() })); + if len == Some(0) { + tri!(self + .formatter + .end_array(&mut self.writer) + .map_err(|e| PythonSerializerError { message: e.to_string() })); + Ok(Compound::Map { + ser: self, + state: State::Empty, + }) + } else { + Ok(Compound::Map { + ser: self, + state: State::First, + }) + } + } + + fn serialize_tuple(self, len: usize) -> Result { + self.serialize_seq(Some(len)) + } + + fn serialize_tuple_struct(self, _name: &'static str, len: usize) -> Result { + self.serialize_seq(Some(len)) + } + + fn serialize_tuple_variant( + self, + _name: &'static str, + _variant_index: u32, + variant: &'static str, + len: usize, + ) -> Result { + tri!(self + .formatter + .begin_object(&mut self.writer) + .map_err(|e| PythonSerializerError { message: e.to_string() })); + tri!(self + .formatter + .begin_object_key(&mut self.writer, true) + .map_err(|e| PythonSerializerError { message: e.to_string() })); + tri!(self.serialize_str(variant)); + tri!(self + .formatter + .end_object_key(&mut self.writer) + .map_err(|e| PythonSerializerError { message: e.to_string() })); + tri!(self + .formatter + .begin_object_value(&mut self.writer) + .map_err(|e| PythonSerializerError { message: e.to_string() })); + self.serialize_seq(Some(len)) + } + + fn serialize_map(self, len: Option) -> Result { + tri!(self + .formatter + .begin_object(&mut self.writer) + .map_err(|e| PythonSerializerError { message: e.to_string() })); + if len == Some(0) { + tri!(self + .formatter + .end_object(&mut self.writer) + .map_err(|e| PythonSerializerError { message: e.to_string() })); + Ok(Compound::Map { + ser: self, + state: State::Empty, + }) + } else { + Ok(Compound::Map { + ser: self, + state: State::First, + }) + } + } + + fn serialize_struct(self, name: &'static str, len: usize) -> Result { + match name { + TOKEN => Ok(Compound::Number { ser: self }), + _ => self.serialize_map(Some(len)), + } + } + + fn serialize_struct_variant( + self, + _name: &'static str, + _variant_index: u32, + variant: &'static str, + len: usize, + ) -> Result { + tri!(self + .formatter + .begin_object(&mut self.writer) + .map_err(|e| PythonSerializerError { message: e.to_string() })); + tri!(self + .formatter + .begin_object_key(&mut self.writer, true) + .map_err(|e| PythonSerializerError { message: e.to_string() })); + tri!(self.serialize_str(variant)); + tri!(self + .formatter + .end_object_key(&mut self.writer) + .map_err(|e| PythonSerializerError { message: e.to_string() })); + tri!(self + .formatter + .begin_object_value(&mut self.writer) + .map_err(|e| PythonSerializerError { message: e.to_string() })); + self.serialize_map(Some(len)) + } +} + +impl<'a, W, F> serde::ser::SerializeSeq for Compound<'a, W, F> +where + W: io::Write, + F: Formatter, +{ + type Ok = (); + type Error = PythonSerializerError; + + #[inline] + fn serialize_element(&mut self, value: &T) -> Result<()> + where + T: ?Sized + Serialize, + { + match self { + Compound::Map { ser, state } => { + tri!(ser + .formatter + .begin_array_value(&mut ser.writer, *state == State::First) + .map_err(|e| PythonSerializerError { message: e.to_string() })); + *state = State::Rest; + tri!(value.serialize(&mut **ser)); + tri!(ser + .formatter + .end_array_value(&mut ser.writer) + .map_err(|e| PythonSerializerError { message: e.to_string() })); + Ok(()) + } + Compound::Number { .. } => unreachable!(), + } + } + + fn end(self) -> Result<()> { + match self { + Compound::Map { ser, state } => { + match state { + State::Empty => {} + _ => tri!(ser + .formatter + .end_array(&mut ser.writer) + .map_err(|e| PythonSerializerError { message: e.to_string() })), + } + Ok(()) + } + Compound::Number { .. } => unreachable!(), + } + } +} + +impl<'a, W, F> serde::ser::SerializeTuple for Compound<'a, W, F> +where + W: io::Write, + F: Formatter, +{ + type Ok = (); + type Error = PythonSerializerError; + + #[inline] + fn serialize_element(&mut self, value: &T) -> Result<()> + where + T: ?Sized + Serialize, + { + serde::ser::SerializeSeq::serialize_element(self, value) + } + + #[inline] + fn end(self) -> Result<()> { + serde::ser::SerializeSeq::end(self) + } +} + +impl<'a, W, F> serde::ser::SerializeTupleStruct for Compound<'a, W, F> +where + W: io::Write, + F: Formatter, +{ + type Ok = (); + type Error = PythonSerializerError; + + #[inline] + fn serialize_field(&mut self, value: &T) -> Result<()> + where + T: ?Sized + Serialize, + { + serde::ser::SerializeSeq::serialize_element(self, value) + } + + #[inline] + fn end(self) -> Result<()> { + serde::ser::SerializeSeq::end(self) + } +} + +impl<'a, W, F> serde::ser::SerializeTupleVariant for Compound<'a, W, F> +where + W: io::Write, + F: Formatter, +{ + type Ok = (); + type Error = PythonSerializerError; + + #[inline] + fn serialize_field(&mut self, value: &T) -> Result<()> + where + T: ?Sized + Serialize, + { + serde::ser::SerializeSeq::serialize_element(self, value) + } + + #[inline] + fn end(self) -> Result<()> { + match self { + Compound::Map { ser, state } => { + match state { + State::Empty => {} + _ => tri!(ser + .formatter + .end_array(&mut ser.writer) + .map_err(|e| PythonSerializerError { message: e.to_string() })), + } + tri!(ser + .formatter + .end_object_value(&mut ser.writer) + .map_err(|e| PythonSerializerError { message: e.to_string() })); + tri!(ser + .formatter + .end_object(&mut ser.writer) + .map_err(|e| PythonSerializerError { message: e.to_string() })); + Ok(()) + } + Compound::Number { .. } => unreachable!(), + } + } +} + +impl<'a, W, F> serde::ser::SerializeMap for Compound<'a, W, F> +where + W: io::Write, + F: Formatter, +{ + type Ok = (); + type Error = PythonSerializerError; + + #[inline] + fn serialize_key(&mut self, key: &T) -> Result<()> + where + T: ?Sized + Serialize, + { + match self { + Compound::Map { ser, state } => { + tri!(ser + .formatter + .begin_object_key(&mut ser.writer, *state == State::First) + .map_err(|e| PythonSerializerError { message: e.to_string() })); + *state = State::Rest; + + tri!(key.serialize(MapKeySerializer { ser: *ser })); + + tri!(ser + .formatter + .end_object_key(&mut ser.writer) + .map_err(|e| PythonSerializerError { message: e.to_string() })); + Ok(()) + } + Compound::Number { .. } => unreachable!(), + } + } + + #[inline] + fn serialize_value(&mut self, value: &T) -> Result<()> + where + T: ?Sized + Serialize, + { + match self { + Compound::Map { ser, .. } => { + tri!(ser + .formatter + .begin_object_value(&mut ser.writer) + .map_err(|e| PythonSerializerError { message: e.to_string() })); + tri!(value.serialize(&mut **ser)); + tri!(ser + .formatter + .end_object_value(&mut ser.writer) + .map_err(|e| PythonSerializerError { message: e.to_string() })); + Ok(()) + } + Compound::Number { .. } => unreachable!(), + } + } + + #[inline] + fn end(self) -> Result<()> { + match self { + Compound::Map { ser, state } => { + match state { + State::Empty => {} + _ => tri!(ser + .formatter + .end_object(&mut ser.writer) + .map_err(|e| PythonSerializerError { message: e.to_string() })), + } + Ok(()) + } + Compound::Number { .. } => unreachable!(), + } + } +} + +impl<'a, W, F> serde::ser::SerializeStruct for Compound<'a, W, F> +where + W: io::Write, + F: Formatter, +{ + type Ok = (); + type Error = PythonSerializerError; + + #[inline] + fn serialize_field(&mut self, key: &'static str, value: &T) -> Result<()> + where + T: ?Sized + Serialize, + { + match self { + Compound::Map { .. } => serde::ser::SerializeMap::serialize_entry(self, key, value), + Compound::Number { ser, .. } => { + if key == TOKEN { + tri!(value.serialize(NumberStrEmitter(ser))); + Ok(()) + } else { + Err(invalid_number()) + } + } + } + } + + #[inline] + fn end(self) -> Result<()> { + match self { + Compound::Map { .. } => serde::ser::SerializeMap::end(self), + Compound::Number { .. } => Ok(()), + } + } +} + +impl<'a, W, F> serde::ser::SerializeStructVariant for Compound<'a, W, F> +where + W: io::Write, + F: Formatter, +{ + type Ok = (); + type Error = PythonSerializerError; + + #[inline] + fn serialize_field(&mut self, key: &'static str, value: &T) -> Result<()> + where + T: ?Sized + Serialize, + { + match *self { + Compound::Map { .. } => serde::ser::SerializeStruct::serialize_field(self, key, value), + Compound::Number { .. } => unreachable!(), + } + } + + #[inline] + fn end(self) -> Result<()> { + match self { + Compound::Map { ser, state } => { + match state { + State::Empty => {} + _ => tri!(ser + .formatter + .end_object(&mut ser.writer) + .map_err(|e| PythonSerializerError { message: e.to_string() })), + } + tri!(ser + .formatter + .end_object_value(&mut ser.writer) + .map_err(|e| PythonSerializerError { message: e.to_string() })); + tri!(ser + .formatter + .end_object(&mut ser.writer) + .map_err(|e| PythonSerializerError { message: e.to_string() })); + Ok(()) + } + Compound::Number { .. } => unreachable!(), + } + } +} + +fn format_escaped_str(writer: &mut W, formatter: &mut F, value: &str) -> io::Result<()> +where + W: ?Sized + io::Write, + F: ?Sized + Formatter, +{ + tri!(formatter.begin_string(writer)); + tri!(format_escaped_str_contents(writer, formatter, value)); + formatter.end_string(writer) +} + +fn format_escaped_str_contents(writer: &mut W, formatter: &mut F, value: &str) -> io::Result<()> +where + W: ?Sized + io::Write, + F: ?Sized + Formatter, +{ + let bytes = value.as_bytes(); + + let mut start = 0; + + for (i, &byte) in bytes.iter().enumerate() { + let escape = ESCAPE[byte as usize]; + if escape == 0 { + continue; + } + + if start < i { + tri!(formatter.write_string_fragment(writer, &value[start..i])); + } + + let char_escape = CharEscape::from_escape_table(escape, byte); + tri!(formatter.write_char_escape(writer, char_escape)); + + start = i + 1; + } + + if start == bytes.len() { + return Ok(()); + } + + formatter.write_string_fragment(writer, &value[start..]) +} + +const BB: u8 = b'b'; // \x08 +const TT: u8 = b't'; // \x09 +const NN: u8 = b'n'; // \x0A +const FF: u8 = b'f'; // \x0C +const RR: u8 = b'r'; // \x0D +const QU: u8 = b'"'; // \x22 +const BS: u8 = b'\\'; // \x5C +const UU: u8 = b'u'; // \x00...\x1F except the ones above +const __: u8 = 0; + +// Lookup table of escape sequences. A value of b'x' at index i means that byte +// i is escaped as "\x" in JSON. A value of 0 means that byte i is not escaped. +static ESCAPE: [u8; 256] = [ + // 1 2 3 4 5 6 7 8 9 A B C D E F + UU, UU, UU, UU, UU, UU, UU, UU, BB, TT, NN, UU, FF, RR, UU, UU, // 0 + UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, UU, // 1 + __, __, QU, __, __, __, __, __, __, __, __, __, __, __, __, __, // 2 + __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 3 + __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 4 + __, __, __, __, __, __, __, __, __, __, __, __, BS, __, __, __, // 5 + __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 6 + __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 7 + __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 8 + __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // 9 + __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // A + __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // B + __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // C + __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // D + __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // E + __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, __, // F +]; + +pub enum Compound<'a, W: 'a, F: 'a> { + Map { + ser: &'a mut PythonSerializer, + state: State, + }, + Number { + ser: &'a mut PythonSerializer, + }, +} + +/// Represents a character escape code in a type-safe manner. +pub enum CharEscape {} + +impl CharEscape { + #[inline] + fn from_escape_table(escape: u8, byte: u8) -> serde_json::ser::CharEscape { + match escape { + self::BB => serde_json::ser::CharEscape::Backspace, + self::TT => serde_json::ser::CharEscape::Tab, + self::NN => serde_json::ser::CharEscape::LineFeed, + self::FF => serde_json::ser::CharEscape::FormFeed, + self::RR => serde_json::ser::CharEscape::CarriageReturn, + self::QU => serde_json::ser::CharEscape::Quote, + self::BS => serde_json::ser::CharEscape::ReverseSolidus, + self::UU => serde_json::ser::CharEscape::AsciiControl(byte), + _ => unreachable!(), + } + } +} + +struct MapKeySerializer<'a, W: 'a, F: 'a> { + ser: &'a mut PythonSerializer, +} + +fn key_must_be_a_string() -> PythonSerializerError { + PythonSerializerError { + message: "Key must be a string".to_string(), + } +} +fn invalid_number() -> PythonSerializerError { + PythonSerializerError { + message: "Invalid Number".to_string(), + } +} + +impl<'a, W, F> serde::ser::Serializer for MapKeySerializer<'a, W, F> +where + W: io::Write, + F: Formatter, +{ + type Ok = (); + type Error = PythonSerializerError; + + #[inline] + fn serialize_str(self, value: &str) -> Result<()> { + self.ser.serialize_str(value) + } + + #[inline] + fn serialize_unit_variant(self, _name: &'static str, _variant_index: u32, variant: &'static str) -> Result<()> { + self.ser.serialize_str(variant) + } + + #[inline] + fn serialize_newtype_struct(self, _name: &'static str, value: &T) -> Result<()> + where + T: ?Sized + Serialize, + { + value.serialize(self) + } + + type SerializeSeq = Impossible<(), PythonSerializerError>; + type SerializeTuple = Impossible<(), PythonSerializerError>; + type SerializeTupleStruct = Impossible<(), PythonSerializerError>; + type SerializeTupleVariant = Impossible<(), PythonSerializerError>; + type SerializeMap = Impossible<(), PythonSerializerError>; + type SerializeStruct = Impossible<(), PythonSerializerError>; + type SerializeStructVariant = Impossible<(), PythonSerializerError>; + + fn serialize_bool(self, _value: bool) -> Result<()> { + Err(key_must_be_a_string()) + } + + fn serialize_i8(self, value: i8) -> Result<()> { + tri!(self + .ser + .formatter + .begin_string(&mut self.ser.writer) + .map_err(|e| PythonSerializerError { message: e.to_string() })); + tri!(self + .ser + .formatter + .write_i8(&mut self.ser.writer, value) + .map_err(|e| PythonSerializerError { message: e.to_string() })); + tri!(self + .ser + .formatter + .end_string(&mut self.ser.writer) + .map_err(|e| PythonSerializerError { message: e.to_string() })); + Ok(()) + } + + fn serialize_i16(self, value: i16) -> Result<()> { + tri!(self + .ser + .formatter + .begin_string(&mut self.ser.writer) + .map_err(|e| PythonSerializerError { message: e.to_string() })); + tri!(self + .ser + .formatter + .write_i16(&mut self.ser.writer, value) + .map_err(|e| PythonSerializerError { message: e.to_string() })); + tri!(self + .ser + .formatter + .end_string(&mut self.ser.writer) + .map_err(|e| PythonSerializerError { message: e.to_string() })); + Ok(()) + } + + fn serialize_i32(self, value: i32) -> Result<()> { + tri!(self + .ser + .formatter + .begin_string(&mut self.ser.writer) + .map_err(|e| PythonSerializerError { message: e.to_string() })); + tri!(self + .ser + .formatter + .write_i32(&mut self.ser.writer, value) + .map_err(|e| PythonSerializerError { message: e.to_string() })); + tri!(self + .ser + .formatter + .end_string(&mut self.ser.writer) + .map_err(|e| PythonSerializerError { message: e.to_string() })); + Ok(()) + } + + fn serialize_i64(self, value: i64) -> Result<()> { + tri!(self + .ser + .formatter + .begin_string(&mut self.ser.writer) + .map_err(|e| PythonSerializerError { message: e.to_string() })); + tri!(self + .ser + .formatter + .write_i64(&mut self.ser.writer, value) + .map_err(|e| PythonSerializerError { message: e.to_string() })); + tri!(self + .ser + .formatter + .end_string(&mut self.ser.writer) + .map_err(|e| PythonSerializerError { message: e.to_string() })); + Ok(()) + } + + serde_if_integer128! { + fn serialize_i128(self, value: i128) -> Result<()> { + tri!(self + .ser + .formatter + .begin_string(&mut self.ser.writer) + .map_err(|e| PythonSerializerError { message: e.to_string() })); + tri!(self + .ser + .formatter + .write_number_str(&mut self.ser.writer, &value.to_string()) + .map_err(|e| PythonSerializerError { message: e.to_string() })); + tri!(self + .ser + .formatter + .end_string(&mut self.ser.writer) + .map_err(|e| PythonSerializerError { message: e.to_string() })); + Ok(()) + } + } + + fn serialize_u8(self, value: u8) -> Result<()> { + tri!(self + .ser + .formatter + .begin_string(&mut self.ser.writer) + .map_err(|e| PythonSerializerError { message: e.to_string() })); + tri!(self + .ser + .formatter + .write_u8(&mut self.ser.writer, value) + .map_err(|e| PythonSerializerError { message: e.to_string() })); + tri!(self + .ser + .formatter + .end_string(&mut self.ser.writer) + .map_err(|e| PythonSerializerError { message: e.to_string() })); + Ok(()) + } + + fn serialize_u16(self, value: u16) -> Result<()> { + tri!(self + .ser + .formatter + .begin_string(&mut self.ser.writer) + .map_err(|e| PythonSerializerError { message: e.to_string() })); + tri!(self + .ser + .formatter + .write_u16(&mut self.ser.writer, value) + .map_err(|e| PythonSerializerError { message: e.to_string() })); + tri!(self + .ser + .formatter + .end_string(&mut self.ser.writer) + .map_err(|e| PythonSerializerError { message: e.to_string() })); + Ok(()) + } + + fn serialize_u32(self, value: u32) -> Result<()> { + tri!(self + .ser + .formatter + .begin_string(&mut self.ser.writer) + .map_err(|e| PythonSerializerError { message: e.to_string() })); + tri!(self + .ser + .formatter + .write_u32(&mut self.ser.writer, value) + .map_err(|e| PythonSerializerError { message: e.to_string() })); + tri!(self + .ser + .formatter + .end_string(&mut self.ser.writer) + .map_err(|e| PythonSerializerError { message: e.to_string() })); + Ok(()) + } + + fn serialize_u64(self, value: u64) -> Result<()> { + tri!(self + .ser + .formatter + .begin_string(&mut self.ser.writer) + .map_err(|e| PythonSerializerError { message: e.to_string() })); + tri!(self + .ser + .formatter + .write_u64(&mut self.ser.writer, value) + .map_err(|e| PythonSerializerError { message: e.to_string() })); + tri!(self + .ser + .formatter + .end_string(&mut self.ser.writer) + .map_err(|e| PythonSerializerError { message: e.to_string() })); + Ok(()) + } + + serde_if_integer128! { + fn serialize_u128(self, value: u128) -> Result<()> { + tri!(self + .ser + .formatter + .begin_string(&mut self.ser.writer) + .map_err(|e| PythonSerializerError { message: e.to_string() })); + tri!(self + .ser + .formatter + .write_number_str(&mut self.ser.writer, &value.to_string()) + .map_err(|e| PythonSerializerError { message: e.to_string() })); + tri!(self + .ser + .formatter + .end_string(&mut self.ser.writer) + .map_err(|e| PythonSerializerError { message: e.to_string() })); + Ok(()) + } + } + + fn serialize_f32(self, _value: f32) -> Result<()> { + Err(key_must_be_a_string()) + } + + fn serialize_f64(self, _value: f64) -> Result<()> { + Err(key_must_be_a_string()) + } + + fn serialize_char(self, value: char) -> Result<()> { + self.ser.serialize_str(&value.to_string()) + } + + fn serialize_bytes(self, _value: &[u8]) -> Result<()> { + Err(key_must_be_a_string()) + } + + fn serialize_unit(self) -> Result<()> { + Err(key_must_be_a_string()) + } + + fn serialize_unit_struct(self, _name: &'static str) -> Result<()> { + Err(key_must_be_a_string()) + } + + fn serialize_newtype_variant( + self, + _name: &'static str, + _variant_index: u32, + _variant: &'static str, + _value: &T, + ) -> Result<()> + where + T: ?Sized + Serialize, + { + Err(key_must_be_a_string()) + } + + fn serialize_none(self) -> Result<()> { + Err(key_must_be_a_string()) + } + + fn serialize_some(self, _value: &T) -> Result<()> + where + T: ?Sized + Serialize, + { + Err(key_must_be_a_string()) + } + + fn serialize_seq(self, _len: Option) -> Result { + Err(key_must_be_a_string()) + } + + fn serialize_tuple(self, _len: usize) -> Result { + Err(key_must_be_a_string()) + } + + fn serialize_tuple_struct(self, _name: &'static str, _len: usize) -> Result { + Err(key_must_be_a_string()) + } + + fn serialize_tuple_variant( + self, + _name: &'static str, + _variant_index: u32, + _variant: &'static str, + _len: usize, + ) -> Result { + Err(key_must_be_a_string()) + } + + fn serialize_map(self, _len: Option) -> Result { + Err(key_must_be_a_string()) + } + + fn serialize_struct(self, _name: &'static str, _len: usize) -> Result { + Err(key_must_be_a_string()) + } + + fn serialize_struct_variant( + self, + _name: &'static str, + _variant_index: u32, + _variant: &'static str, + _len: usize, + ) -> Result { + Err(key_must_be_a_string()) + } + + fn collect_str(self, value: &T) -> Result<()> + where + T: ?Sized + std::fmt::Display, + { + self.ser.collect_str(value) + } +} + +struct NumberStrEmitter<'a, W: 'a + io::Write, F: 'a + Formatter>(&'a mut PythonSerializer); + +impl<'a, W: io::Write, F: Formatter> serde::ser::Serializer for NumberStrEmitter<'a, W, F> { + type Ok = (); + type Error = PythonSerializerError; + + type SerializeSeq = Impossible<(), PythonSerializerError>; + type SerializeTuple = Impossible<(), PythonSerializerError>; + type SerializeTupleStruct = Impossible<(), PythonSerializerError>; + type SerializeTupleVariant = Impossible<(), PythonSerializerError>; + type SerializeMap = Impossible<(), PythonSerializerError>; + type SerializeStruct = Impossible<(), PythonSerializerError>; + type SerializeStructVariant = Impossible<(), PythonSerializerError>; + + fn serialize_bool(self, _v: bool) -> Result<()> { + Err(invalid_number()) + } + + fn serialize_i8(self, _v: i8) -> Result<()> { + Err(invalid_number()) + } + + fn serialize_i16(self, _v: i16) -> Result<()> { + Err(invalid_number()) + } + + fn serialize_i32(self, _v: i32) -> Result<()> { + Err(invalid_number()) + } + + fn serialize_i64(self, _v: i64) -> Result<()> { + Err(invalid_number()) + } + + serde_if_integer128! { + fn serialize_i128(self, _v: i128) -> Result<()> { + Err(invalid_number()) + } + } + + fn serialize_u8(self, _v: u8) -> Result<()> { + Err(invalid_number()) + } + + fn serialize_u16(self, _v: u16) -> Result<()> { + Err(invalid_number()) + } + + fn serialize_u32(self, _v: u32) -> Result<()> { + Err(invalid_number()) + } + + fn serialize_u64(self, _v: u64) -> Result<()> { + Err(invalid_number()) + } + + serde_if_integer128! { + fn serialize_u128(self, _v: u128) -> Result<()> { + Err(invalid_number()) + } + } + + fn serialize_f32(self, _v: f32) -> Result<()> { + Err(invalid_number()) + } + + fn serialize_f64(self, _v: f64) -> Result<()> { + Err(invalid_number()) + } + + fn serialize_char(self, _v: char) -> Result<()> { + Err(invalid_number()) + } + + fn serialize_str(self, value: &str) -> Result<()> { + let NumberStrEmitter(serializer) = self; + serializer + .formatter + .write_number_str(&mut serializer.writer, value) + .map_err(|e| PythonSerializerError { message: e.to_string() }) + } + + fn serialize_bytes(self, _value: &[u8]) -> Result<()> { + Err(invalid_number()) + } + + fn serialize_none(self) -> Result<()> { + Err(invalid_number()) + } + + fn serialize_some(self, _value: &T) -> Result<()> + where + T: ?Sized + Serialize, + { + Err(invalid_number()) + } + + fn serialize_unit(self) -> Result<()> { + Err(invalid_number()) + } + + fn serialize_unit_struct(self, _name: &'static str) -> Result<()> { + Err(invalid_number()) + } + + fn serialize_unit_variant(self, _name: &'static str, _variant_index: u32, _variant: &'static str) -> Result<()> { + Err(invalid_number()) + } + + fn serialize_newtype_struct(self, _name: &'static str, _value: &T) -> Result<()> + where + T: ?Sized + Serialize, + { + Err(invalid_number()) + } + + fn serialize_newtype_variant( + self, + _name: &'static str, + _variant_index: u32, + _variant: &'static str, + _value: &T, + ) -> Result<()> + where + T: ?Sized + Serialize, + { + Err(invalid_number()) + } + + fn serialize_seq(self, _len: Option) -> Result { + Err(invalid_number()) + } + + fn serialize_tuple(self, _len: usize) -> Result { + Err(invalid_number()) + } + + fn serialize_tuple_struct(self, _name: &'static str, _len: usize) -> Result { + Err(invalid_number()) + } + + fn serialize_tuple_variant( + self, + _name: &'static str, + _variant_index: u32, + _variant: &'static str, + _len: usize, + ) -> Result { + Err(invalid_number()) + } + + fn serialize_map(self, _len: Option) -> Result { + Err(invalid_number()) + } + + fn serialize_struct(self, _name: &'static str, _len: usize) -> Result { + Err(invalid_number()) + } + + fn serialize_struct_variant( + self, + _name: &'static str, + _variant_index: u32, + _variant: &'static str, + _len: usize, + ) -> Result { + Err(invalid_number()) + } +} diff --git a/src/serializers/shared.rs b/src/serializers/shared.rs index 7c24ff6db..cfccc748a 100644 --- a/src/serializers/shared.rs +++ b/src/serializers/shared.rs @@ -15,6 +15,7 @@ use crate::build_tools::py_schema_err; use crate::build_tools::py_schema_error_type; use crate::definitions::DefinitionsBuilder; use crate::py_gc::PyGcTraverse; +use crate::serializers::ser::PythonSerializer; use crate::tools::{py_err, SchemaDict}; use super::errors::se_err_py_err; @@ -112,7 +113,7 @@ combined_serializer! { Nullable: super::type_serializers::nullable::NullableSerializer; Int: super::type_serializers::simple::IntSerializer; Bool: super::type_serializers::simple::BoolSerializer; - Float: super::type_serializers::simple::FloatSerializer; + Float: super::type_serializers::float::FloatSerializer; Decimal: super::type_serializers::decimal::DecimalSerializer; Str: super::type_serializers::string::StrSerializer; Bytes: super::type_serializers::bytes::BytesSerializer; @@ -352,12 +353,12 @@ pub(crate) fn to_json_bytes( Some(indent) => { let indent = vec![b' '; indent]; let formatter = PrettyFormatter::with_indent(&indent); - let mut ser = serde_json::Serializer::with_formatter(writer, formatter); + let mut ser = PythonSerializer::with_formatter(writer, formatter); serializer.serialize(&mut ser).map_err(se_err_py_err)?; ser.into_inner() } None => { - let mut ser = serde_json::Serializer::new(writer); + let mut ser = PythonSerializer::new(writer); serializer.serialize(&mut ser).map_err(se_err_py_err)?; ser.into_inner() } diff --git a/src/serializers/type_serializers/float.rs b/src/serializers/type_serializers/float.rs new file mode 100644 index 000000000..23dcacf1a --- /dev/null +++ b/src/serializers/type_serializers/float.rs @@ -0,0 +1,102 @@ +use pyo3::types::PyDict; +use pyo3::{intern, prelude::*}; + +use std::borrow::Cow; + +use serde::Serializer; + +use crate::definitions::DefinitionsBuilder; +use crate::serializers::config::InfNanMode; +use crate::tools::SchemaDict; + +use super::simple::to_str_json_key; +use super::{ + infer_json_key, infer_serialize, infer_to_python, BuildSerializer, CombinedSerializer, Extra, IsType, ObType, + SerMode, TypeSerializer, +}; + +#[derive(Debug, Clone)] +pub struct FloatSerializer { + inf_nan_mode: InfNanMode, +} + +impl BuildSerializer for FloatSerializer { + const EXPECTED_TYPE: &'static str = "float"; + + fn build( + schema: &PyDict, + config: Option<&PyDict>, + _definitions: &mut DefinitionsBuilder, + ) -> PyResult { + let inf_nan_mode = config + .and_then(|c| c.get_as(intern!(schema.py(), "ser_json_inf_nan")).transpose()) + .transpose()? + .unwrap_or_default(); + Ok(Self { inf_nan_mode }.into()) + } +} + +impl_py_gc_traverse!(FloatSerializer {}); + +impl TypeSerializer for FloatSerializer { + fn to_python( + &self, + value: &PyAny, + include: Option<&PyAny>, + exclude: Option<&PyAny>, + extra: &Extra, + ) -> PyResult { + let py = value.py(); + match extra.ob_type_lookup.is_type(value, ObType::Float) { + IsType::Exact => Ok(value.into_py(py)), + IsType::Subclass => match extra.mode { + SerMode::Json => { + let rust_value = value.extract::()?; + Ok(rust_value.to_object(py)) + } + _ => infer_to_python(value, include, exclude, extra), + }, + IsType::False => { + extra.warnings.on_fallback_py(self.get_name(), value, extra)?; + infer_to_python(value, include, exclude, extra) + } + } + } + + fn json_key<'py>(&self, key: &'py PyAny, extra: &Extra) -> PyResult> { + match extra.ob_type_lookup.is_type(key, ObType::Float) { + IsType::Exact | IsType::Subclass => to_str_json_key(key), + IsType::False => { + extra.warnings.on_fallback_py(self.get_name(), key, extra)?; + infer_json_key(key, extra) + } + } + } + + fn serde_serialize( + &self, + value: &PyAny, + serializer: S, + include: Option<&PyAny>, + exclude: Option<&PyAny>, + extra: &Extra, + ) -> Result { + match value.extract::() { + Ok(v) => { + if (v.is_nan() || v.is_infinite()) && self.inf_nan_mode == InfNanMode::Null { + serializer.serialize_none() + } else { + serializer.serialize_f64(v) + } + } + Err(_) => { + extra.warnings.on_fallback_ser::(self.get_name(), value, extra)?; + infer_serialize(value, serializer, include, exclude, extra) + } + } + } + + fn get_name(&self) -> &str { + Self::EXPECTED_TYPE + } +} diff --git a/src/serializers/type_serializers/mod.rs b/src/serializers/type_serializers/mod.rs index b942b5b86..decb07aaf 100644 --- a/src/serializers/type_serializers/mod.rs +++ b/src/serializers/type_serializers/mod.rs @@ -5,6 +5,7 @@ pub mod datetime_etc; pub mod decimal; pub mod definitions; pub mod dict; +pub mod float; pub mod format; pub mod function; pub mod generator; diff --git a/src/serializers/type_serializers/simple.rs b/src/serializers/type_serializers/simple.rs index f0d90c2bf..dafb2b786 100644 --- a/src/serializers/type_serializers/simple.rs +++ b/src/serializers/type_serializers/simple.rs @@ -180,4 +180,3 @@ pub(crate) fn bool_json_key(key: &PyAny) -> PyResult> { } build_simple_serializer!(BoolSerializer, "bool", bool, ObType::Bool, bool_json_key); -build_simple_serializer!(FloatSerializer, "float", f64, ObType::Float, to_str_json_key); diff --git a/tests/serializers/test_simple.py b/tests/serializers/test_simple.py index 9bbaad05b..b63208c07 100644 --- a/tests/serializers/test_simple.py +++ b/tests/serializers/test_simple.py @@ -136,3 +136,30 @@ def test_numpy(): assert type(v) == float assert s.to_json(numpy.float64(1.0)) == b'1.0' + + +@pytest.mark.parametrize( + 'value,expected_json,config', + [ + # default values of ser_json_inf_nan + (float('inf'), 'null', {}), + (float('-inf'), 'null', {}), + (float('nan'), 'null', {}), + # explicit values of ser_json_inf_nan + (float('inf'), 'null', {'ser_json_inf_nan': 'null'}), + (float('-inf'), 'null', {'ser_json_inf_nan': 'null'}), + (float('nan'), 'null', {'ser_json_inf_nan': 'null'}), + (float('inf'), 'Infinity', {'ser_json_inf_nan': 'constants'}), + (float('-inf'), '-Infinity', {'ser_json_inf_nan': 'constants'}), + (float('nan'), 'NaN', {'ser_json_inf_nan': 'constants'}), + ], +) +def test_float_inf_and_nan_serializers(value, expected_json, config): + s = SchemaSerializer(core_schema.float_schema(), config) + + # Python can represent these values without needing any changes + assert s.to_python(value) is value + assert s.to_python(value, mode='json') is value + + # Serialized JSON value respects the ser_json_inf_nan setting + assert s.to_json(value).decode() == expected_json diff --git a/tests/test_errors.py b/tests/test_errors.py index 3683150ec..0d7a966e1 100644 --- a/tests/test_errors.py +++ b/tests/test_errors.py @@ -789,7 +789,7 @@ def raise_py_error(v: Any) -> Any: with pytest.raises(ValidationError) as exc_info: s.validate_python('anything') - exc = exc_info.value.errors()[0]['ctx']['error'] # type: ignore + exc = exc_info.value.errors()[0]['ctx']['error'] assert isinstance(exc, ValueError) assert isinstance(exc.__context__, AssertionError) diff --git a/tests/validators/test_float.py b/tests/validators/test_float.py index 35b04c3f9..4e3bda0c4 100644 --- a/tests/validators/test_float.py +++ b/tests/validators/test_float.py @@ -227,6 +227,7 @@ def test_float_key(py_and_json: PyAndJson): ('NaN', True, FunctionCheck(math.isnan)), ('NaN', False, Err("Input should be a finite number [type=finite_number, input_value='NaN', input_type=str]")), ('+inf', True, FunctionCheck(lambda x: math.isinf(x) and x > 0)), + ('inf', True, FunctionCheck(lambda x: math.isinf(x) and x > 0)), ( '+inf', False,