Skip to content

Commit ca87728

Browse files
committed
Feedback
1 parent acda57f commit ca87728

File tree

2 files changed

+24
-8
lines changed

2 files changed

+24
-8
lines changed

python/pydantic_core/_pydantic_core.pyi

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -364,7 +364,7 @@ class SchemaSerializer:
364364
value: The Python object to serialize.
365365
indent: If `None`, the JSON will be compact, otherwise it will be pretty-printed with the indent provided.
366366
ensure_ascii: If `True`, the output is guaranteed to have all incoming non-ASCII characters escaped.
367-
If `False` (the default), these characters will be outputted as-is.
367+
If `False` (the default), these characters will be output as-is.
368368
include: A set of fields to include, if `None` all fields are included.
369369
exclude: A set of fields to exclude, if `None` no fields are excluded.
370370
by_alias: Whether to use the alias names of fields.
@@ -418,7 +418,7 @@ def to_json(
418418
value: The Python object to serialize.
419419
indent: If `None`, the JSON will be compact, otherwise it will be pretty-printed with the indent provided.
420420
ensure_ascii: If `True`, the output is guaranteed to have all incoming non-ASCII characters escaped.
421-
If `False` (the default), these characters will be outputted as-is.
421+
If `False` (the default), these characters will be output as-is.
422422
include: A set of fields to include, if `None` all fields are included.
423423
exclude: A set of fields to exclude, if `None` no fields are excluded.
424424
by_alias: Whether to use the alias names of fields.

src/serializers/shared.rs

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -437,15 +437,31 @@ struct EscapeNonAsciiFormatter;
437437

438438
impl Formatter for EscapeNonAsciiFormatter {
439439
fn write_string_fragment<W: ?Sized + Write>(&mut self, writer: &mut W, fragment: &str) -> io::Result<()> {
440-
for ch in fragment.chars() {
441-
if ch.is_ascii() {
442-
writer.write_all(ch.encode_utf8(&mut [0; 4]).as_bytes())?;
440+
let mut input = fragment;
441+
442+
while let Some((idx, non_ascii_char)) = input.chars().enumerate().find(|(_, c)| !c.is_ascii()) {
443+
if idx > 0 {
444+
// write all ascii characters before the non-ascii one
445+
let ascii_run = &input[..idx];
446+
writer.write_all(ascii_run.as_bytes()).unwrap();
447+
}
448+
449+
let codepoint = non_ascii_char as u32;
450+
if codepoint < 0xFFFF {
451+
// write basic codepoint as single escape
452+
write!(writer, "\\u{codepoint:04x}").unwrap();
443453
} else {
444-
for escape in ch.encode_utf16(&mut [0; 2]) {
445-
write!(writer, "\\u{escape:04x}")?;
454+
// encode extended plane character as utf16 pair
455+
for escape in non_ascii_char.encode_utf16(&mut [0; 2]) {
456+
write!(writer, "\\u{escape:04x}").unwrap();
446457
}
447458
}
459+
460+
input = &input[(idx + non_ascii_char.len_utf8())..];
448461
}
462+
463+
// write any ascii trailer
464+
writer.write_all(fragment.as_bytes())?;
449465
Ok(())
450466
}
451467
}
@@ -484,7 +500,7 @@ macro_rules! defer {
484500
}
485501

486502
#[allow(clippy::needless_lifetimes)]
487-
impl<'a> Formatter for EscapeNonAsciiPrettyFormatter<'a> {
503+
impl Formatter for EscapeNonAsciiPrettyFormatter<'_> {
488504
defer!(escape_non_ascii, write_string_fragment, &str);
489505
defer!(pretty, begin_array);
490506
defer!(pretty, end_array);

0 commit comments

Comments
 (0)