mirror of
https://github.com/RustPython/RustPython.git
synced 2026-10-28 18:59:04 +08:00
Fix remaining tests
This commit is contained in:
19 files changed
+150
-106
No files matched your search
Vendored
-2
@@ -1066,8 +1066,6 @@ class CommonTest(BaseTest):
|
||||
hash(b)
|
||||
self.assertEqual(hash(a), hash(b))
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_capitalize_nonascii(self):
|
||||
# check that titlecased chars are lowered correctly
|
||||
# \u1ffc is the titlecased char
|
||||
|
||||
Vendored
+1
-2
@@ -574,8 +574,7 @@ class CmdLineTest(unittest.TestCase):
|
||||
self.assertTrue(text[1].startswith(' File '))
|
||||
self.assertTrue(text[3].startswith('NameError'))
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
@unittest.expectedFailureIf(sys.platform == "linux", "TODO: RUSTPYTHON")
|
||||
def test_non_ascii(self):
|
||||
# Mac OS X denies the creation of a file with an invalid UTF-8 name.
|
||||
# Windows allows creating a name with an arbitrary bytes name, but
|
||||
|
||||
Vendored
+2
@@ -1305,6 +1305,8 @@ class ImportTracebackTests(unittest.TestCase):
|
||||
else:
|
||||
importlib.SourceLoader.exec_module = old_exec_module
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
@unittest.skipUnless(TESTFN_UNENCODABLE, 'need TESTFN_UNENCODABLE')
|
||||
def test_unencodable_filename(self):
|
||||
# Issue #11619: The Python parser and the import machinery must not
|
||||
|
||||
-6
@@ -143,10 +143,4 @@ class TestScanstring:
|
||||
|
||||
|
||||
class TestPyScanstring(TestScanstring, PyTest): pass
|
||||
# TODO: RUSTPYTHON
|
||||
class TestPyScanstring(TestScanstring, PyTest):
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_bad_escapes(self):
|
||||
super().test_bad_escapes()
|
||||
class TestCScanstring(TestScanstring, CTest): pass
|
||||
Vendored
-6
@@ -1032,12 +1032,6 @@ class NtCommonTest(test_genericpath.CommonTest, unittest.TestCase):
|
||||
pathmodule = ntpath
|
||||
attributes = ['relpath']
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
if sys.platform == "linux":
|
||||
@unittest.expectedFailure
|
||||
def test_nonascii_abspath(self):
|
||||
super().test_nonascii_abspath()
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
if sys.platform == "win32":
|
||||
# TODO: RUSTPYTHON, ValueError: illegal environment variable name
|
||||
|
||||
Vendored
+1
@@ -2231,6 +2231,7 @@ class ReTests(unittest.TestCase):
|
||||
with self.assertRaisesRegex(TypeError, "got 'type'"):
|
||||
re.search("x*", type)
|
||||
|
||||
@unittest.skip("TODO: RUSTPYTHON: flaky, improve perf")
|
||||
@requires_resource('cpu')
|
||||
def test_search_anchor_at_beginning(self):
|
||||
s = 'x'*10**7
|
||||
|
||||
Vendored
+1
@@ -1578,6 +1578,7 @@ class GeneralModuleTests(unittest.TestCase):
|
||||
# only IP addresses are allowed
|
||||
self.assertRaises(OSError, socket.getnameinfo, ('mail.python.org',0), 0)
|
||||
|
||||
@unittest.skip("TODO: RUSTPYTHON: flaky on CI?")
|
||||
@unittest.skipUnless(support.is_resource_enabled('network'),
|
||||
'network is not enabled')
|
||||
def test_idna(self):
|
||||
|
||||
Vendored
-2
@@ -95,8 +95,6 @@ class SqliteTypeTests(unittest.TestCase):
|
||||
row = self.cur.fetchone()
|
||||
self.assertIsNone(row)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_string_with_surrogates(self):
|
||||
for value in 0xd8ff, 0xdcff:
|
||||
with self.assertRaises(UnicodeEncodeError):
|
||||
|
||||
Vendored
-2
@@ -102,8 +102,6 @@ class UnicodeNamesTest(unittest.TestCase):
|
||||
self.checkletter("CJK UNIFIED IDEOGRAPH-2B81D", "\U0002B81D")
|
||||
self.checkletter("CJK UNIFIED IDEOGRAPH-3134A", "\U0003134A")
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_bmp_characters(self):
|
||||
for code in range(0x10000):
|
||||
char = chr(code)
|
||||
|
||||
Vendored
-2
@@ -721,8 +721,6 @@ class UnicodeTest(string_tests.CommonTest,
|
||||
'\U0001F40D', '\U0001F46F']:
|
||||
self.assertFalse(ch.isspace(), '{!a} is not space.'.format(ch))
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
@support.requires_resource('cpu')
|
||||
def test_isspace_invariant(self):
|
||||
for codepoint in range(sys.maxunicode + 1):
|
||||
|
||||
Vendored
-6
@@ -99,8 +99,6 @@ class UnicodeFunctionsTest(UnicodeDatabaseTest):
|
||||
result = h.hexdigest()
|
||||
self.assertEqual(result, self.expectedchecksum)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
@requires_resource('cpu')
|
||||
def test_name_inverse_lookup(self):
|
||||
for i in range(sys.maxunicode + 1):
|
||||
@@ -326,8 +324,6 @@ class UnicodeMiscTest(UnicodeDatabaseTest):
|
||||
self.assertTrue("\u1d79".upper()=='\ua77d')
|
||||
self.assertTrue(".".upper()=='.')
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_bug_5828(self):
|
||||
self.assertEqual("\u1d79".lower(), "\u1d79")
|
||||
# Only U+0000 should have U+0000 as its upper/lower/titlecase variant
|
||||
@@ -347,8 +343,6 @@ class UnicodeMiscTest(UnicodeDatabaseTest):
|
||||
self.assertEqual("\u01c5".title(), "\u01c5")
|
||||
self.assertEqual("\u01c6".title(), "\u01c5")
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_linebreak_7643(self):
|
||||
for i in range(0x10000):
|
||||
lines = (chr(i) + 'A').splitlines()
|
||||
|
||||
@@ -552,6 +552,12 @@ impl Extend<CodePoint> for Wtf8Buf {
|
||||
}
|
||||
}
|
||||
|
||||
impl Extend<char> for Wtf8Buf {
|
||||
fn extend<T: IntoIterator<Item = char>>(&mut self, iter: T) {
|
||||
self.extend(iter.into_iter().map(CodePoint::from))
|
||||
}
|
||||
}
|
||||
|
||||
impl<W: AsRef<Wtf8>> Extend<W> for Wtf8Buf {
|
||||
fn extend<T: IntoIterator<Item = W>>(&mut self, iter: T) {
|
||||
iter.into_iter()
|
||||
@@ -1004,6 +1010,14 @@ impl Wtf8 {
|
||||
memchr::memmem::rfind(self.as_bytes(), pat.as_bytes())
|
||||
}
|
||||
|
||||
pub fn find_iter(&self, pat: &Wtf8) -> impl Iterator<Item = usize> {
|
||||
memchr::memmem::find_iter(self.as_bytes(), pat.as_bytes())
|
||||
}
|
||||
|
||||
pub fn rfind_iter(&self, pat: &Wtf8) -> impl Iterator<Item = usize> {
|
||||
memchr::memmem::rfind_iter(self.as_bytes(), pat.as_bytes())
|
||||
}
|
||||
|
||||
pub fn contains(&self, pat: &Wtf8) -> bool {
|
||||
self.bytes.contains_str(pat)
|
||||
}
|
||||
@@ -1057,6 +1071,11 @@ impl Wtf8 {
|
||||
let w = self.bytes.replace(from, to);
|
||||
unsafe { Wtf8Buf::from_bytes_unchecked(w) }
|
||||
}
|
||||
|
||||
pub fn replacen(&self, from: &Wtf8, to: &Wtf8, n: usize) -> Wtf8Buf {
|
||||
let w = self.bytes.replacen(from, to, n);
|
||||
unsafe { Wtf8Buf::from_bytes_unchecked(w) }
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<Wtf8> for str {
|
||||
|
||||
@@ -2929,9 +2929,12 @@ mod _sqlite {
|
||||
}
|
||||
|
||||
fn str_to_ptr_len(s: &PyStr, vm: &VirtualMachine) -> PyResult<(*const libc::c_char, i32)> {
|
||||
let len = c_int::try_from(s.byte_len())
|
||||
let s = s
|
||||
.to_str()
|
||||
.ok_or_else(|| vm.new_unicode_encode_error("surrogates not allowed".to_owned()))?;
|
||||
let len = c_int::try_from(s.len())
|
||||
.map_err(|_| vm.new_overflow_error("TEXT longer than INT_MAX bytes".to_owned()))?;
|
||||
let ptr = s.as_str().as_ptr().cast();
|
||||
let ptr = s.as_ptr().cast();
|
||||
Ok((ptr, len))
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -403,8 +403,8 @@ mod _ssl {
|
||||
.to_str()
|
||||
.unwrap();
|
||||
let (cert_file, cert_dir) = get_cert_file_dir();
|
||||
let cert_file = OsPath::new_str(cert_file).filename(vm)?;
|
||||
let cert_dir = OsPath::new_str(cert_dir).filename(vm)?;
|
||||
let cert_file = OsPath::new_str(cert_file).filename(vm);
|
||||
let cert_dir = OsPath::new_str(cert_dir).filename(vm);
|
||||
Ok((cert_file_env, cert_file, cert_dir_env, cert_dir))
|
||||
}
|
||||
|
||||
|
||||
+88
-53
@@ -38,7 +38,7 @@ use rustpython_common::{
|
||||
str::DeduceStrKind,
|
||||
wtf8::{CodePoint, Wtf8, Wtf8Buf, Wtf8Chunk},
|
||||
};
|
||||
use std::{char, fmt, ops::Range};
|
||||
use std::{borrow::Cow, char, fmt, ops::Range};
|
||||
use unic_ucd_bidi::BidiClass;
|
||||
use unic_ucd_category::GeneralCategory;
|
||||
use unic_ucd_ident::{is_xid_continue, is_xid_start};
|
||||
@@ -420,6 +420,16 @@ impl PyStr {
|
||||
self.data.as_str().expect("str has surrogates")
|
||||
}
|
||||
|
||||
pub fn to_str(&self) -> Option<&str> {
|
||||
self.data.as_str()
|
||||
}
|
||||
|
||||
pub fn to_string_lossy(&self) -> Cow<'_, str> {
|
||||
self.to_str()
|
||||
.map(Cow::Borrowed)
|
||||
.unwrap_or_else(|| self.as_wtf8().to_string_lossy())
|
||||
}
|
||||
|
||||
pub fn kind(&self) -> StrKind {
|
||||
self.data.kind()
|
||||
}
|
||||
@@ -629,16 +639,42 @@ impl PyStr {
|
||||
}
|
||||
|
||||
#[pymethod]
|
||||
fn capitalize(&self) -> String {
|
||||
let mut chars = self.as_str().chars();
|
||||
if let Some(first_char) = chars.next() {
|
||||
format!(
|
||||
"{}{}",
|
||||
first_char.to_uppercase(),
|
||||
&chars.as_str().to_lowercase(),
|
||||
)
|
||||
} else {
|
||||
"".to_owned()
|
||||
fn capitalize(&self) -> Wtf8Buf {
|
||||
match self.as_str_kind() {
|
||||
PyKindStr::Ascii(s) => {
|
||||
let mut s = s.to_owned();
|
||||
if let [first, rest @ ..] = s.as_mut_slice() {
|
||||
first.make_ascii_uppercase();
|
||||
ascii::AsciiStr::make_ascii_lowercase(rest.into());
|
||||
}
|
||||
s.into()
|
||||
}
|
||||
PyKindStr::Utf8(s) => {
|
||||
let mut chars = s.chars();
|
||||
let mut out = String::with_capacity(s.len());
|
||||
if let Some(c) = chars.next() {
|
||||
out.extend(c.to_titlecase());
|
||||
out.push_str(&chars.as_str().to_lowercase());
|
||||
}
|
||||
out.into()
|
||||
}
|
||||
PyKindStr::Wtf8(s) => {
|
||||
let mut out = Wtf8Buf::with_capacity(s.len());
|
||||
let mut chars = s.code_points();
|
||||
if let Some(ch) = chars.next() {
|
||||
match ch.to_char() {
|
||||
Some(ch) => out.extend(ch.to_titlecase()),
|
||||
None => out.push(ch),
|
||||
}
|
||||
for chunk in chars.as_wtf8().chunks() {
|
||||
match chunk {
|
||||
Wtf8Chunk::Utf8(s) => out.push_str(&s.to_lowercase()),
|
||||
Wtf8Chunk::Surrogate(ch) => out.push(ch),
|
||||
}
|
||||
}
|
||||
}
|
||||
out
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -844,14 +880,11 @@ impl PyStr {
|
||||
#[pymethod]
|
||||
fn isdigit(&self) -> bool {
|
||||
// python's isdigit also checks if exponents are digits, these are the unicode codepoints for exponents
|
||||
let valid_codepoints: [u16; 10] = [
|
||||
0x2070, 0x00B9, 0x00B2, 0x00B3, 0x2074, 0x2075, 0x2076, 0x2077, 0x2078, 0x2079,
|
||||
];
|
||||
let s = self.as_str();
|
||||
!s.is_empty()
|
||||
&& s.chars()
|
||||
.filter(|c| !c.is_ascii_digit())
|
||||
.all(|c| valid_codepoints.contains(&(c as u16)))
|
||||
!self.data.is_empty()
|
||||
&& self.char_all(|c| {
|
||||
c.is_ascii_digit()
|
||||
|| matches!(c, '⁰' | '¹' | '²' | '³' | '⁴' | '⁵' | '⁶' | '⁷' | '⁸' | '⁹')
|
||||
})
|
||||
}
|
||||
|
||||
#[pymethod]
|
||||
@@ -910,43 +943,45 @@ impl PyStr {
|
||||
/// Return a titlecased version of the string where words start with an
|
||||
/// uppercase character and the remaining characters are lowercase.
|
||||
#[pymethod]
|
||||
fn title(&self) -> String {
|
||||
let mut title = String::with_capacity(self.data.len());
|
||||
fn title(&self) -> Wtf8Buf {
|
||||
let mut title = Wtf8Buf::with_capacity(self.data.len());
|
||||
let mut previous_is_cased = false;
|
||||
for c in self.as_str().chars() {
|
||||
for c_orig in self.as_wtf8().code_points() {
|
||||
let c = c_orig.to_char_lossy();
|
||||
if c.is_lowercase() {
|
||||
if !previous_is_cased {
|
||||
title.extend(c.to_titlecase());
|
||||
} else {
|
||||
title.push(c);
|
||||
title.push_char(c);
|
||||
}
|
||||
previous_is_cased = true;
|
||||
} else if c.is_uppercase() || c.is_titlecase() {
|
||||
if previous_is_cased {
|
||||
title.extend(c.to_lowercase());
|
||||
} else {
|
||||
title.push(c);
|
||||
title.push_char(c);
|
||||
}
|
||||
previous_is_cased = true;
|
||||
} else {
|
||||
previous_is_cased = false;
|
||||
title.push(c);
|
||||
title.push(c_orig);
|
||||
}
|
||||
}
|
||||
title
|
||||
}
|
||||
|
||||
#[pymethod]
|
||||
fn swapcase(&self) -> String {
|
||||
let mut swapped_str = String::with_capacity(self.data.len());
|
||||
for c in self.as_str().chars() {
|
||||
fn swapcase(&self) -> Wtf8Buf {
|
||||
let mut swapped_str = Wtf8Buf::with_capacity(self.data.len());
|
||||
for c_orig in self.as_wtf8().code_points() {
|
||||
let c = c_orig.to_char_lossy();
|
||||
// to_uppercase returns an iterator, to_ascii_uppercase returns the char
|
||||
if c.is_lowercase() {
|
||||
swapped_str.push(c.to_ascii_uppercase());
|
||||
swapped_str.push_char(c.to_ascii_uppercase());
|
||||
} else if c.is_uppercase() {
|
||||
swapped_str.push(c.to_ascii_lowercase());
|
||||
swapped_str.push_char(c.to_ascii_lowercase());
|
||||
} else {
|
||||
swapped_str.push(c);
|
||||
swapped_str.push(c_orig);
|
||||
}
|
||||
}
|
||||
swapped_str
|
||||
@@ -958,20 +993,20 @@ impl PyStr {
|
||||
}
|
||||
|
||||
#[pymethod]
|
||||
fn replace(&self, old: PyStrRef, new: PyStrRef, count: OptionalArg<isize>) -> String {
|
||||
let s = self.as_str();
|
||||
fn replace(&self, old: PyStrRef, new: PyStrRef, count: OptionalArg<isize>) -> Wtf8Buf {
|
||||
let s = self.as_wtf8();
|
||||
match count {
|
||||
OptionalArg::Present(max_count) if max_count >= 0 => {
|
||||
if max_count == 0 || (s.is_empty() && !old.is_empty()) {
|
||||
// nothing to do; return the original bytes
|
||||
s.to_owned()
|
||||
} else if s.is_empty() && old.is_empty() {
|
||||
new.as_str().to_owned()
|
||||
new.as_wtf8().to_owned()
|
||||
} else {
|
||||
s.replacen(old.as_str(), new.as_str(), max_count as usize)
|
||||
s.replacen(old.as_wtf8(), new.as_wtf8(), max_count as usize)
|
||||
}
|
||||
}
|
||||
_ => s.replace(old.as_str(), new.as_str()),
|
||||
_ => s.replace(old.as_wtf8(), new.as_wtf8()),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1148,7 +1183,7 @@ impl PyStr {
|
||||
if has_mid {
|
||||
sep
|
||||
} else {
|
||||
vm.ctx.new_str(ascii!(""))
|
||||
vm.ctx.empty_str.to_owned()
|
||||
},
|
||||
self.new_substr(back),
|
||||
)
|
||||
@@ -1165,7 +1200,7 @@ impl PyStr {
|
||||
|
||||
let mut cased = false;
|
||||
let mut previous_is_cased = false;
|
||||
for c in self.as_str().chars() {
|
||||
for c in self.as_wtf8().code_points().map(CodePoint::to_char_lossy) {
|
||||
if c.is_uppercase() || c.is_titlecase() {
|
||||
if previous_is_cased {
|
||||
return false;
|
||||
@@ -1188,15 +1223,15 @@ impl PyStr {
|
||||
#[pymethod]
|
||||
fn count(&self, args: FindArgs) -> usize {
|
||||
let (needle, range) = args.get_value(self.len());
|
||||
self.as_str()
|
||||
.py_count(needle.as_str(), range, |h, n| h.matches(n).count())
|
||||
self.as_wtf8()
|
||||
.py_count(needle.as_wtf8(), range, |h, n| h.find_iter(n).count())
|
||||
}
|
||||
|
||||
#[pymethod]
|
||||
fn zfill(&self, width: isize) -> String {
|
||||
fn zfill(&self, width: isize) -> Wtf8Buf {
|
||||
unsafe {
|
||||
// SAFETY: this is safe-guaranteed because the original self.as_str() is valid utf8
|
||||
String::from_utf8_unchecked(self.as_str().py_zfill(width))
|
||||
// SAFETY: this is safe-guaranteed because the original self.as_wtf8() is valid wtf8
|
||||
Wtf8Buf::from_bytes_unchecked(self.as_wtf8().py_zfill(width))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1205,20 +1240,20 @@ impl PyStr {
|
||||
&self,
|
||||
width: isize,
|
||||
fillchar: OptionalArg<PyStrRef>,
|
||||
pad: fn(&str, usize, char, usize) -> String,
|
||||
pad: fn(&Wtf8, usize, CodePoint, usize) -> Wtf8Buf,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<String> {
|
||||
let fillchar = fillchar.map_or(Ok(' '), |ref s| {
|
||||
s.as_str().chars().exactly_one().map_err(|_| {
|
||||
) -> PyResult<Wtf8Buf> {
|
||||
let fillchar = fillchar.map_or(Ok(' '.into()), |ref s| {
|
||||
s.as_wtf8().code_points().exactly_one().map_err(|_| {
|
||||
vm.new_type_error(
|
||||
"The fill character must be exactly one character long".to_owned(),
|
||||
)
|
||||
})
|
||||
})?;
|
||||
Ok(if self.len() as isize >= width {
|
||||
String::from(self.as_str())
|
||||
self.as_wtf8().to_owned()
|
||||
} else {
|
||||
pad(self.as_str(), width as usize, fillchar, self.len())
|
||||
pad(self.as_wtf8(), width as usize, fillchar, self.len())
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1228,7 +1263,7 @@ impl PyStr {
|
||||
width: isize,
|
||||
fillchar: OptionalArg<PyStrRef>,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<String> {
|
||||
) -> PyResult<Wtf8Buf> {
|
||||
self._pad(width, fillchar, AnyStr::py_center, vm)
|
||||
}
|
||||
|
||||
@@ -1238,7 +1273,7 @@ impl PyStr {
|
||||
width: isize,
|
||||
fillchar: OptionalArg<PyStrRef>,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<String> {
|
||||
) -> PyResult<Wtf8Buf> {
|
||||
self._pad(width, fillchar, AnyStr::py_ljust, vm)
|
||||
}
|
||||
|
||||
@@ -1248,7 +1283,7 @@ impl PyStr {
|
||||
width: isize,
|
||||
fillchar: OptionalArg<PyStrRef>,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<String> {
|
||||
) -> PyResult<Wtf8Buf> {
|
||||
self._pad(width, fillchar, AnyStr::py_rjust, vm)
|
||||
}
|
||||
|
||||
@@ -2186,7 +2221,7 @@ mod tests {
|
||||
("Greek ῼitlecases ...", "greek ῳitlecases ..."),
|
||||
];
|
||||
for (title, input) in tests {
|
||||
assert_eq!(PyStr::from(input).title().as_str(), title);
|
||||
assert_eq!(PyStr::from(input).title().as_str(), Ok(title));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+12
-10
@@ -426,12 +426,13 @@ fn xmlcharrefreplace_errors(err: PyObjectRef, vm: &VirtualMachine) -> PyResult<(
|
||||
}
|
||||
let range = extract_unicode_error_range(&err, vm)?;
|
||||
let s = PyStrRef::try_from_object(vm, err.get_attr("object", vm)?)?;
|
||||
let s_after_start = crate::common::str::try_get_chars(s.as_str(), range.start..).unwrap_or("");
|
||||
let s_after_start =
|
||||
crate::common::str::try_get_codepoints(s.as_wtf8(), range.start..).unwrap_or_default();
|
||||
let num_chars = range.len();
|
||||
// capacity rough guess; assuming that the codepoints are 3 digits in decimal + the &#;
|
||||
let mut out = String::with_capacity(num_chars * 6);
|
||||
for c in s_after_start.chars().take(num_chars) {
|
||||
write!(out, "&#{};", c as u32).unwrap()
|
||||
for c in s_after_start.code_points().take(num_chars) {
|
||||
write!(out, "&#{};", c.to_u32()).unwrap()
|
||||
}
|
||||
Ok((out, range.end))
|
||||
}
|
||||
@@ -450,12 +451,13 @@ fn backslashreplace_errors(err: PyObjectRef, vm: &VirtualMachine) -> PyResult<(S
|
||||
}
|
||||
let range = extract_unicode_error_range(&err, vm)?;
|
||||
let s = PyStrRef::try_from_object(vm, err.get_attr("object", vm)?)?;
|
||||
let s_after_start = crate::common::str::try_get_chars(s.as_str(), range.start..).unwrap_or("");
|
||||
let s_after_start =
|
||||
crate::common::str::try_get_codepoints(s.as_wtf8(), range.start..).unwrap_or_default();
|
||||
let num_chars = range.len();
|
||||
// minimum 4 output bytes per char: \xNN
|
||||
let mut out = String::with_capacity(num_chars * 4);
|
||||
for c in s_after_start.chars().take(num_chars) {
|
||||
let c = c as u32;
|
||||
for c in s_after_start.code_points().take(num_chars) {
|
||||
let c = c.to_u32();
|
||||
if c >= 0x10000 {
|
||||
write!(out, "\\U{c:08x}").unwrap();
|
||||
} else if c >= 0x100 {
|
||||
@@ -472,12 +474,12 @@ fn namereplace_errors(err: PyObjectRef, vm: &VirtualMachine) -> PyResult<(String
|
||||
let range = extract_unicode_error_range(&err, vm)?;
|
||||
let s = PyStrRef::try_from_object(vm, err.get_attr("object", vm)?)?;
|
||||
let s_after_start =
|
||||
crate::common::str::try_get_chars(s.as_str(), range.start..).unwrap_or("");
|
||||
crate::common::str::try_get_codepoints(s.as_wtf8(), range.start..).unwrap_or_default();
|
||||
let num_chars = range.len();
|
||||
let mut out = String::with_capacity(num_chars * 4);
|
||||
for c in s_after_start.chars().take(num_chars) {
|
||||
let c_u32 = c as u32;
|
||||
if let Some(c_name) = unicode_names2::name(c) {
|
||||
for c in s_after_start.code_points().take(num_chars) {
|
||||
let c_u32 = c.to_u32();
|
||||
if let Some(c_name) = unicode_names2::name(c.to_char_lossy()) {
|
||||
write!(out, "\\N{{{c_name}}}").unwrap();
|
||||
} else if c_u32 >= 0x10000 {
|
||||
write!(out, "\\U{c_u32:08x}").unwrap();
|
||||
|
||||
@@ -74,10 +74,10 @@ impl FsPath {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_str(&self) -> &str {
|
||||
pub fn to_string_lossy(&self) -> Cow<'_, str> {
|
||||
match self {
|
||||
FsPath::Bytes(b) => std::str::from_utf8(b).unwrap(),
|
||||
FsPath::Str(s) => s.as_str(),
|
||||
FsPath::Str(s) => s.to_string_lossy(),
|
||||
FsPath::Bytes(s) => String::from_utf8_lossy(s),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -155,7 +155,7 @@ mod builtins {
|
||||
return ast::compile(
|
||||
vm,
|
||||
args.source,
|
||||
args.filename.as_str(),
|
||||
&args.filename.to_string_lossy(),
|
||||
mode,
|
||||
Some(optimize),
|
||||
);
|
||||
@@ -204,7 +204,7 @@ mod builtins {
|
||||
.compile_with_opts(
|
||||
source,
|
||||
mode,
|
||||
args.filename.as_str().to_owned(),
|
||||
args.filename.to_string_lossy().into_owned(),
|
||||
opts,
|
||||
)
|
||||
.map_err(|err| (err, Some(source)).to_pyexception(vm))?;
|
||||
|
||||
+14
-6
@@ -916,12 +916,20 @@ impl VirtualMachine {
|
||||
}
|
||||
|
||||
pub fn fsdecode(&self, s: impl Into<OsString>) -> PyStrRef {
|
||||
let bytes = self.ctx.new_bytes(s.into().into_encoded_bytes());
|
||||
let errors = self.fs_encode_errors().to_owned();
|
||||
self.state
|
||||
.codec_registry
|
||||
.decode_text(bytes.into(), "utf-8", Some(errors), self)
|
||||
.unwrap() // this should never fail, since fsdecode should be lossless from the fs encoding
|
||||
match s.into().into_string() {
|
||||
Ok(s) => self.ctx.new_str(s),
|
||||
Err(s) => {
|
||||
let bytes = self.ctx.new_bytes(s.into_encoded_bytes());
|
||||
let errors = self.fs_encode_errors().to_owned();
|
||||
let res = self.state.codec_registry.decode_text(
|
||||
bytes.into(),
|
||||
"utf-8",
|
||||
Some(errors),
|
||||
self,
|
||||
);
|
||||
self.expect_pyresult(res, "fsdecode should be lossless and never fail")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fsencode<'a>(&self, s: &'a Py<PyStr>) -> PyResult<Cow<'a, OsStr>> {
|
||||
|
||||
Reference in new issue
Block a user