Sync selected Python 3.15 core library tests and repr type errors (#9321)

* Sync Python 3.15 core library tests and repr type errors

Synchronize ten selected library paths from CPython 3.15.0 at
d220409a13b77de7a6c30abe16ea90db01528edc using the canonical updater,
retaining existing RustPython adapters and failure markers.

Match generic __repr__ invalid-result errors to the qualified type-name
format required by the canonical ExceptionGroup regression, preserving
string results and exceptions raised by the implementation.

Implementation, source review, and validation were AI-assisted. The model
version identifier is unavailable; automated checks are not human
verification. Bounded validation covers 43 passes and one canonical skip,
not the full repository suites.

Assisted-by: OpenAI Codex:model version unavailable

* Preserve original escape decoder consumed length after callbacks

Keep the original input length as consumed after successful unicode-escape
and raw-unicode-escape decoding, including error-handler input replacement.
Only report the current cursor when a non-final decode stops on incomplete
input, matching CPython's codec wrappers and incomplete exits.

Fix the canonical Unicode escape mutation regression that failed CI on
Linux, macOS and Windows. Preserve all Lib tests and existing assertions.
Bounded Linux validation: 55 passes and one canonical skip, CLI build and
strict common/VM Clippy passed. Full repository suites were not run.

Implementation, source review, and validation were AI-assisted. The model
version identifier is unavailable; automated checks are not human
verification.

Assisted-by: OpenAI Codex:model version unavailable

---------

Co-authored-by: youknowdot <336763669+youknowdot@users.noreply.github.com>
This commit is contained in:
youknowdotandyouknowdot authored and GitHub committed 2026-10-23 18:01:14 +09:00
1 parent afe5d9030a
commit c70e9b8443
13 files changed
+198 -126

No files matched your search

+21 -90
View File
@@ -29,7 +29,7 @@
#
# History:
#
# <see CVS and SVN checkin messages for history>
# <see checkin messages for history>
#
# 1.0.9 - added invalidate_caches() function to invalidate cached values
# 1.0.8 - changed Windows support to read version from kernel32.dll
@@ -110,8 +110,6 @@ __copyright__ = """
"""
__version__ = '1.0.9'
import collections
import os
import re
@@ -129,7 +127,7 @@ except ImportError:
# Based on the description of the PHP's version_compare():
# http://php.net/manual/en/function.version-compare.php
_ver_stages = {
_ver_stages = frozendict({
# any string not found in this dict, will get 0 assigned
'dev': 10,
'alpha': 20, 'a': 20,
@@ -138,7 +136,7 @@ _ver_stages = {
'RC': 50, 'rc': 50,
# number, will get 100 assigned
'pl': 200, 'p': 200,
}
})
def _comparable_version(version):
@@ -307,8 +305,7 @@ def _syscmd_ver(system='', release='', version='',
text=True,
encoding="locale",
shell=True)
except (OSError, subprocess.CalledProcessError) as why:
#print('Command %s failed: %s' % (cmd, why))
except (OSError, subprocess.CalledProcessError):
continue
else:
break
@@ -539,53 +536,6 @@ def ios_ver(system="", release="", model="", is_simulator=False):
return IOSVersionInfo(system, release, model, is_simulator)
def _java_getprop(name, default):
"""This private helper is deprecated in 3.13 and will be removed in 3.15"""
from java.lang import System
try:
value = System.getProperty(name)
if value is None:
return default
return value
except AttributeError:
return default
def java_ver(release='', vendor='', vminfo=('', '', ''), osinfo=('', '', '')):
""" Version interface for Jython.
Returns a tuple (release, vendor, vminfo, osinfo) with vminfo being
a tuple (vm_name, vm_release, vm_vendor) and osinfo being a
tuple (os_name, os_version, os_arch).
Values which cannot be determined are set to the defaults
given as parameters (which all default to '').
"""
import warnings
warnings._deprecated('java_ver', remove=(3, 15))
# Import the needed APIs
try:
import java.lang # noqa: F401
except ImportError:
return release, vendor, vminfo, osinfo
vendor = _java_getprop('java.vendor', vendor)
release = _java_getprop('java.version', release)
vm_name, vm_release, vm_vendor = vminfo
vm_name = _java_getprop('java.vm.name', vm_name)
vm_vendor = _java_getprop('java.vm.vendor', vm_vendor)
vm_release = _java_getprop('java.vm.version', vm_release)
vminfo = vm_name, vm_release, vm_vendor
os_name, os_version, os_arch = osinfo
os_arch = _java_getprop('java.os.arch', os_arch)
os_name = _java_getprop('java.os.name', os_name)
os_version = _java_getprop('java.os.version', os_version)
osinfo = os_name, os_version, os_arch
return release, vendor, vminfo, osinfo
AndroidVer = collections.namedtuple(
"AndroidVer", "release api_level manufacturer model device is_emulator")
@@ -670,6 +620,9 @@ def system_alias(system, release, version):
### Various internal helpers
# Table for cleaning up characters in filenames.
_SIMPLE_SUBSTITUTIONS = str.maketrans(r' /\:;"()', r'_-------')
def _platform(*args):
""" Helper to format the platform string in a filename
@@ -679,28 +632,13 @@ def _platform(*args):
platform = '-'.join(x.strip() for x in filter(len, args))
# Cleanup some possible filename obstacles...
platform = platform.replace(' ', '_')
platform = platform.replace('/', '-')
platform = platform.replace('\\', '-')
platform = platform.replace(':', '-')
platform = platform.replace(';', '-')
platform = platform.replace('"', '-')
platform = platform.replace('(', '-')
platform = platform.replace(')', '-')
platform = platform.translate(_SIMPLE_SUBSTITUTIONS)
# No need to report 'unknown' information...
platform = platform.replace('unknown', '')
# Fold '--'s and remove trailing '-'
while True:
cleaned = platform.replace('--', '-')
if cleaned == platform:
break
platform = cleaned
while platform and platform[-1] == '-':
platform = platform[:-1]
return platform
return re.sub(r'-{2,}', '-', platform).rstrip('-')
def _node(default=''):
@@ -767,11 +705,11 @@ def _syscmd_file(target, default=''):
# Default values for architecture; non-empty strings override the
# defaults given as parameters
_default_architecture = {
_default_architecture = frozendict({
'win32': ('', 'WindowsPE'),
'win16': ('', 'Windows'),
'dos': ('', 'MSDOS'),
}
})
def architecture(executable=sys.executable, bits='', linkage=''):
@@ -1045,13 +983,6 @@ def uname():
version = '16bit'
system = 'Windows'
elif system[:4] == 'java':
release, vendor, vminfo, osinfo = java_ver()
system = 'Java'
version = ', '.join(vminfo)
if not version:
version = vendor
# System specific extensions
if system == 'OpenVMS':
# OpenVMS seems to have release and version mixed up
@@ -1386,15 +1317,6 @@ def platform(aliased=False, terse=False):
platform = _platform(system, release, machine, processor,
'with',
libcname+libcversion)
elif system == 'Java':
# Java platforms
r, v, vminfo, (os_name, os_version, os_arch) = java_ver()
if terse or not os_name:
platform = _platform(system, release, version)
else:
platform = _platform(system, release, version,
'on',
os_name, os_version, os_arch)
else:
# Generic handler
@@ -1483,7 +1405,7 @@ def invalidate_caches():
def _parse_args(args: list[str] | None):
import argparse
parser = argparse.ArgumentParser(color=True)
parser = argparse.ArgumentParser()
parser.add_argument("args", nargs="*", choices=["nonaliased", "terse"])
parser.add_argument(
"--terse",
@@ -1516,5 +1438,14 @@ def _main(args: list[str] | None = None):
print(platform(aliased, terse))
def __getattr__(name):
if name == "__version__":
from warnings import _deprecated
_deprecated("__version__", remove=(3, 20))
return "1.1.0" # Do not change
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
if __name__ == "__main__":
_main()
+66 -8
View File
@@ -1,3 +1,4 @@
from _codecs import _unregister_error as _codecs_unregister_error
import codecs
import html.entities
import itertools
@@ -754,8 +755,7 @@ class CodecCallbackTest(unittest.TestCase):
(s[:1], 1 + n)
)
# TODO: RUSTPYTHON
@unittest.expectedFailure
@unittest.expectedFailure # TODO: RUSTPYTHON
def test_badhandlerresults(self):
results = ( 42, "foo", (1,2,3), ("foo", 1, 3), ("foo", None), ("foo",), ("foo", 1, 3), ("foo", None), ("foo",) )
encs = ("ascii", "latin-1", "iso-8859-1", "iso-8859-15")
@@ -798,8 +798,7 @@ class CodecCallbackTest(unittest.TestCase):
codecs.lookup_error("namereplace")
)
# TODO: RUSTPYTHON
@unittest.expectedFailure
@unittest.expectedFailure # TODO: RUSTPYTHON
def test_encode_nonascii_replacement(self):
def handle(exc):
if isinstance(exc, UnicodeEncodeError):
@@ -1041,8 +1040,7 @@ class CodecCallbackTest(unittest.TestCase):
self.assertRaises(ValueError, codecs.charmap_encode, "\xff", err, D())
self.assertRaises(TypeError, codecs.charmap_encode, "\xff", err, {0xff: 300})
# TODO: RUSTPYTHON
@unittest.expectedFailure
@unittest.expectedFailure # TODO: RUSTPYTHON
def test_decodehelper_bug36819(self):
handler = RepeatedPosReturn("x")
codecs.register_error("test.bug36819", handler.handle)
@@ -1130,7 +1128,7 @@ class CodecCallbackTest(unittest.TestCase):
text = 'abc<def>ghi'*n
text.translate(charmap)
def test_mutatingdecodehandler(self):
def test_mutating_decode_handler(self):
baddata = [
("ascii", b"\xff"),
("utf-7", b"++"),
@@ -1165,6 +1163,42 @@ class CodecCallbackTest(unittest.TestCase):
for (encoding, data) in baddata:
self.assertEqual(data.decode(encoding, "test.mutating"), "\u4242")
def test_mutating_decode_handler_unicode_escape(self):
decode = codecs.unicode_escape_decode
def mutating(exc):
if isinstance(exc, UnicodeDecodeError):
r = data.get(exc.object[:exc.end])
if r is not None:
exc.object = r[0] + exc.object[exc.end:]
return ('\u0404', r[1])
raise AssertionError("don't know how to handle %r" % exc)
codecs.register_error('test.mutating2', mutating)
data = {
br'\x0': (b'\\', 0),
br'\x3': (b'xxx\\', 3),
br'\x5': (b'x\\', 1),
}
def check(input, expected, msg):
with self.assertWarns(DeprecationWarning) as cm:
self.assertEqual(decode(input, 'test.mutating2'), (expected, len(input)))
self.assertIn(msg, str(cm.warning))
check(br'\x0n\z', '\u0404\n\\z', r'"\z" is an invalid escape sequence')
check(br'\x0n\501', '\u0404\n\u0141', r'"\501" is an invalid octal escape sequence')
check(br'\x0z', '\u0404\\z', r'"\z" is an invalid escape sequence')
check(br'\x3n\zr', '\u0404\n\\zr', r'"\z" is an invalid escape sequence')
check(br'\x3zr', '\u0404\\zr', r'"\z" is an invalid escape sequence')
check(br'\x3z5', '\u0404\\z5', r'"\z" is an invalid escape sequence')
check(memoryview(br'\x3z5x')[:-1], '\u0404\\z5', r'"\z" is an invalid escape sequence')
check(memoryview(br'\x3z5xy')[:-2], '\u0404\\z5', r'"\z" is an invalid escape sequence')
check(br'\x5n\z', '\u0404\n\\z', r'"\z" is an invalid escape sequence')
check(br'\x5n\501', '\u0404\n\u0141', r'"\501" is an invalid octal escape sequence')
check(br'\x5z', '\u0404\\z', r'"\z" is an invalid escape sequence')
check(memoryview(br'\x5zy')[:-1], '\u0404\\z', r'"\z" is an invalid escape sequence')
# issue32583
def test_crashing_decode_handler(self):
# better generating one more character to fill the extra space slot
@@ -1216,7 +1250,6 @@ class CodecCallbackTest(unittest.TestCase):
'\ufffd\x00\x00'
)
def test_fake_error_class(self):
handlers = [
codecs.strict_errors,
@@ -1241,6 +1274,31 @@ class CodecCallbackTest(unittest.TestCase):
with self.assertRaises((TypeError, FakeUnicodeError)):
handler(FakeUnicodeError())
def test_reject_unregister_builtin_error_handler(self):
for name in [
'strict', 'ignore', 'replace', 'backslashreplace', 'namereplace',
'xmlcharrefreplace', 'surrogateescape', 'surrogatepass',
]:
with self.subTest(name):
self.assertRaises(ValueError, _codecs_unregister_error, name)
def test_unregister_custom_error_handler(self):
def custom_handler(exc):
raise exc
custom_name = 'test.test_unregister_custom_error_handler'
self.assertRaises(LookupError, codecs.lookup_error, custom_name)
codecs.register_error(custom_name, custom_handler)
self.assertIs(codecs.lookup_error(custom_name), custom_handler)
self.assertTrue(_codecs_unregister_error(custom_name))
self.assertRaises(LookupError, codecs.lookup_error, custom_name)
def test_unregister_custom_unknown_error_handler(self):
unknown_name = 'test.test_unregister_custom_unknown_error_handler'
self.assertRaises(LookupError, codecs.lookup_error, unknown_name)
self.assertFalse(_codecs_unregister_error(unknown_name))
self.assertRaises(LookupError, codecs.lookup_error, unknown_name)
if __name__ == "__main__":
unittest.main()
+18 -2
View File
@@ -1,7 +1,9 @@
import collections
import types
import unittest
from test.support import skip_emscripten_stack_overflow, skip_wasi_stack_overflow, exceeds_recursion_limit
from test.support import (skip_emscripten_stack_overflow,
skip_wasi_stack_overflow, skip_if_huge_c_stack,
exceeds_recursion_limit)
class TestExceptionGroupTypeHierarchy(unittest.TestCase):
def test_exception_group_types(self):
@@ -234,6 +236,18 @@ class StrAndReprTests(unittest.TestCase):
"ExceptionGroup('test', deque([ValueError(1), TypeError(2)]))"
)
def test_repr_small_size_args(self):
eg = ExceptionGroup("msg", [ValueError()])
eg.args = ()
# repr of the ExceptionGroup with empty args should not crash
self.assertEqual(repr(eg), "ExceptionGroup('msg', (ValueError(),))")
eg.args = (1,)
# repr of the ExceptionGroup with 1-size args should not crash
self.assertEqual(repr(eg), "ExceptionGroup('msg', (ValueError(),))")
def test_repr_raises(self):
class MySeq(collections.abc.Sequence):
def __init__(self, raises):
@@ -255,7 +269,7 @@ class StrAndReprTests(unittest.TestCase):
seq = MySeq(None)
with self.assertRaisesRegex(
TypeError,
r"__repr__ returned non-string \(type NoneType\)"
r".*MySeq\.__repr__\(\) must return a str, not NoneType"
):
ExceptionGroup("test", seq)
@@ -535,6 +549,7 @@ class DeepRecursionInSplitAndSubgroup(unittest.TestCase):
e = ExceptionGroup('eg', [e])
return e
@skip_if_huge_c_stack()
@skip_emscripten_stack_overflow()
@skip_wasi_stack_overflow()
def test_deep_split(self):
@@ -542,6 +557,7 @@ class DeepRecursionInSplitAndSubgroup(unittest.TestCase):
with self.assertRaises(RecursionError):
e.split(TypeError)
@skip_if_huge_c_stack()
@skip_emscripten_stack_overflow()
@skip_wasi_stack_overflow()
def test_deep_subgroup(self):
+18
View File
@@ -460,6 +460,24 @@ class BuiltinFunctionPropertiesTest(unittest.TestCase):
# XXX Not sure where this should really go since I can't find a
# test module specifically for builtin_function_or_method.
def test_builtin__module__(self):
import math
# builtin function:
self.assertEqual(len.__module__, 'builtins')
self.assertEqual(math.sin.__module__, 'math')
# instance method:
self.assertRaises(AttributeError, getattr, int.to_bytes, '__module__')
self.assertEqual(int.to_bytes.__objclass__.__module__, 'builtins')
# builtin classmethod:
self.assertEqual(int.from_bytes.__module__, None)
self.assertEqual(int.from_bytes.__self__.__module__, 'builtins')
# builtin staticmethod:
self.assertEqual(bytes.maketrans.__module__, 'builtins')
def test_builtin__qualname__(self):
import time
+10
View File
@@ -49,6 +49,16 @@ class TestCTest(CTest):
'_json')
class TestModule(unittest.TestCase):
def test_deprecated__version__(self):
with self.assertWarnsRegex(
DeprecationWarning,
"'__version__' is deprecated and slated for removal in Python 3.20",
) as cm:
getattr(json, "__version__")
self.assertEqual(cm.filename, __file__)
def load_tests(loader, _, pattern):
suite = unittest.TestSuite()
for mod in (json, json.encoder, json.decoder):
+7
View File
@@ -146,6 +146,13 @@ class TestFail:
('"', 'Unterminated string starting at', 0),
('"spam', 'Unterminated string starting at', 0),
]
# A complete \uXXXX escape at end of input leaves it unterminated.
test_cases += [
(r'"\u0041', 'Unterminated string starting at', 0),
(r'"\ud834', 'Unterminated string starting at', 0),
(r'"\ud834\udd1e', 'Unterminated string starting at', 0),
(r'{"a": "\u0041', 'Unterminated string starting at', 6),
]
for data, msg, idx in test_cases:
with self.assertRaises(self.JSONDecodeError) as cm:
self.loads(data)
+3
View File
@@ -139,6 +139,9 @@ class TestScanstring:
'"\\ud834\\u-123"',
'"\\ud834\\u+123"',
'"\\ud834\\u1_23"',
# Truncated or non-hex \uXXXX escape at end of input.
'"\\u004',
'"\\uXYZW',
]
for s in bad_escapes:
with self.assertRaises(self.JSONDecodeError, msg=s):
+22 -14
View File
@@ -11,7 +11,7 @@ import unittest
from unittest import mock
from test import support
from test.support import os_helper
from test.support import os_helper, warnings_helper
try:
# Some of the iOS tests need ctypes to operate.
@@ -133,6 +133,22 @@ class PlatformTest(unittest.TestCase):
for terse in (False, True):
res = platform.platform(aliased, terse)
def test__platform(self):
for src, res in [
('foo bar', 'foo_bar'),
(
'1/2\\3:4;5"6(7)8(7)6"5;4:3\\2/1',
'1-2-3-4-5-6-7-8-7-6-5-4-3-2-1'
),
('--', ''),
('-f', '-f'),
('-foo----', '-foo'),
('--foo---', '-foo'),
('---foo--', '-foo'),
]:
with self.subTest(src=src):
self.assertEqual(platform._platform(src), res)
def test_system(self):
res = platform.system()
@@ -383,15 +399,6 @@ class PlatformTest(unittest.TestCase):
finally:
platform._uname_cache = None
def test_java_ver(self):
import re
msg = re.escape(
"'java_ver' is deprecated and slated for removal in Python 3.15"
)
with self.assertWarnsRegex(DeprecationWarning, msg):
res = platform.java_ver()
self.assertEqual(len(res), 4)
@unittest.skipUnless(support.MS_WINDOWS, 'This test only makes sense on Windows')
def test_win32_ver(self):
release1, version1, csd1, ptype1 = 'a', 'b', 'c', 'd'
@@ -458,7 +465,7 @@ class PlatformTest(unittest.TestCase):
else:
self.assertEqual(res[2], 'PowerPC')
@warnings_helper.ignore_fork_in_thread_deprecation_warnings()
@unittest.skipUnless(sys.platform == 'darwin', "OSX only test")
def test_mac_ver_with_fork(self):
# Issue7895: platform.mac_ver() crashes when using fork without exec
@@ -527,7 +534,7 @@ class PlatformTest(unittest.TestCase):
def test_libc_ver(self):
if support.is_emscripten:
assert platform.libc_ver() == ("emscripten", "4.0.12")
assert platform.libc_ver() == ("emscripten", "4.0.19")
return
# check that libc_ver(executable) doesn't raise an exception
if os.path.isdir(sys.executable) and \
@@ -769,13 +776,14 @@ class CommandLineTest(unittest.TestCase):
platform._main(args=flags)
return output.getvalue()
@support.force_not_colorized
def test_unknown_flag(self):
output = io.StringIO()
with self.assertRaises(SystemExit):
output = io.StringIO()
# suppress argparse error message
with contextlib.redirect_stderr(output):
_ = self.invoke_platform('--unknown')
self.assertStartsWith(output, "usage: ")
self.assertStartsWith(output.getvalue(), "usage: ")
def test_invocation(self):
flags = (
+13 -2
View File
@@ -188,7 +188,10 @@ class TestJointOps:
self.assertEqual(type(i), self.basetype)
self.assertRaises(PassThru, self.s.symmetric_difference, check_pass_thru())
self.assertRaises(TypeError, self.s.symmetric_difference, [[]])
for C in set, frozenset, dict.fromkeys, str, list, tuple:
constructors = (set, frozenset,
dict.fromkeys, frozendict.fromkeys,
str, list, tuple)
for C in constructors:
self.assertEqual(self.thetype('abcba').symmetric_difference(C('cdc')), set('abd'))
self.assertEqual(self.thetype('abcba').symmetric_difference(C('efgfe')), set('abcefg'))
self.assertEqual(self.thetype('abcba').symmetric_difference(C('ccb')), set('a'))
@@ -670,7 +673,7 @@ class TestSet(TestJointOps, unittest.TestCase):
with check_unhashable_element():
myset.discard(elem)
# Only TypeError exception is overriden,
# Only TypeError exception is overridden,
# other exceptions are left unchanged.
class HashError:
def __hash__(self):
@@ -1596,6 +1599,14 @@ class TestOnlySetsDict(TestOnlySetsInBinaryOps, unittest.TestCase):
#------------------------------------------------------------------------------
class TestOnlySetsFrozenDict(TestOnlySetsInBinaryOps, unittest.TestCase):
def setUp(self):
self.set = set((1, 2, 3))
self.other = frozendict({1:2, 3:4})
self.otherIsIterable = True
#------------------------------------------------------------------------------
class TestOnlySetsOperator(TestOnlySetsInBinaryOps, unittest.TestCase):
def setUp(self):
self.set = set((1, 2, 3))
+2 -2
View File
@@ -245,7 +245,7 @@ class UserDictTest(mapping_tests.TestHashMappingProtocol):
test_repr_deep = mapping_tests.TestHashMappingProtocol.test_repr_deep
def test_mixed_or(self):
for t in UserDict, dict, types.MappingProxyType:
for t in UserDict, dict, frozendict, types.MappingProxyType:
with self.subTest(t.__name__):
u = UserDict({0: 'a', 1: 'b'}) | t({1: 'c', 2: 'd'})
self.assertEqual(u, {0: 'a', 1: 'c', 2: 'd'})
@@ -276,7 +276,7 @@ class UserDictTest(mapping_tests.TestHashMappingProtocol):
self.assertIs(type(u), UserDictSubclass)
def test_mixed_ior(self):
for t in UserDict, dict, types.MappingProxyType:
for t in UserDict, dict, frozendict, types.MappingProxyType:
with self.subTest(t.__name__):
u = u2 = UserDict({0: 'a', 1: 'b'})
u |= t({1: 'c', 2: 'd'})
@@ -41,6 +41,7 @@ where
E: DecodeErrorHandler<Ctx>,
{
let mut out = Wtf8Buf::new();
let mut consumed = ctx.full_data().len();
loop {
let rest = ctx.remaining_data();
if rest.is_empty() {
@@ -62,6 +63,7 @@ where
let escape_start = ctx.position();
let digits_start = 2;
if !final_decode && rest.len() < digits_start + want {
consumed = ctx.position();
break;
}
let available = (digits_start + want).min(rest.len());
@@ -99,6 +101,7 @@ where
continue;
}
if kind.is_none() && !final_decode {
consumed = ctx.position();
break;
}
out.push_char(b as char);
@@ -108,5 +111,5 @@ where
ctx.advance(1);
}
}
Ok((out, ctx.position()))
Ok((out, consumed))
}
@@ -68,6 +68,7 @@ where
{
let mut out = Wtf8Buf::new();
let mut warning = None;
let mut consumed = ctx.full_data().len();
loop {
let rest = ctx.remaining_data();
if rest.is_empty() {
@@ -84,6 +85,7 @@ where
let escape_start = ctx.position();
if rest.len() == 1 {
if !final_decode {
consumed = ctx.position();
break;
}
let end = ctx.full_data().len();
@@ -163,6 +165,7 @@ where
_ => (8usize, "truncated \\UXXXXXXXX escape"),
};
if !final_decode && rest.len() < 2 + digits {
consumed = ctx.position();
break;
}
ctx.advance(2);
@@ -194,6 +197,7 @@ where
}
b'N' => {
if rest.len() == 2 && !final_decode {
consumed = ctx.position();
break;
}
if rest.len() >= 3 && rest[2] == b'{' {
@@ -204,6 +208,7 @@ where
}
if look >= rest.len() {
if !final_decode {
consumed = ctx.position();
break;
}
let start = escape_start;
@@ -268,5 +273,5 @@ where
}
}
}
Ok((out, ctx.position(), warning))
Ok((out, consumed, warning))
}
+8 -6
View File
@@ -575,12 +575,14 @@ fn sequence_contains_wrapper(
fn repr_wrapper(zelf: &PyObject, vm: &VirtualMachine) -> PyResult<PyRef<PyStr>> {
let ret = vm.call_special_method(zelf, identifier!(vm, __repr__), ())?;
ret.downcast::<PyStr>().map_err(|obj| {
vm.new_type_error(format!(
"__repr__ returned non-string (type {})",
obj.class()
))
})
match ret.downcast::<PyStr>() {
Ok(value) => Ok(value),
Err(obj) => Err(vm.new_type_error(format!(
"{}.__repr__() must return a str, not {}",
zelf.class().fully_qualified_name(vm)?,
obj.class().fully_qualified_name(vm)?
))),
}
}
fn str_wrapper(zelf: &PyObject, vm: &VirtualMachine) -> PyResult<PyRef<PyStr>> {