Skip to content

Commit c2a7393

Browse files
committed
uniform __str__
1 parent a4e60f5 commit c2a7393

File tree

7 files changed

+104
-88
lines changed

7 files changed

+104
-88
lines changed

crates/stdlib/src/ssl/error.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ pub(crate) use ssl_error::*;
55
#[pymodule(sub)]
66
pub(crate) mod ssl_error {
77
use crate::vm::{
8-
PyPayload, PyRef, PyResult, VirtualMachine,
9-
builtins::{PyBaseExceptionRef, PyOSError, PyStrRef},
8+
Py, PyPayload, PyRef, PyResult, VirtualMachine,
9+
builtins::{PyBaseException, PyOSError, PyStrRef},
1010
types::{Constructor, Initializer},
1111
};
1212

@@ -42,7 +42,7 @@ pub(crate) mod ssl_error {
4242
impl PySSLError {
4343
// Returns strerror attribute if available, otherwise str(args)
4444
#[pymethod]
45-
fn __str__(exc: PyBaseExceptionRef, vm: &VirtualMachine) -> PyResult<PyStrRef> {
45+
fn __str__(exc: &Py<PyBaseException>, vm: &VirtualMachine) -> PyResult<PyStrRef> {
4646
use crate::vm::AsObject;
4747
// Try to get strerror attribute first (OSError compatibility)
4848
if let Ok(strerror) = exc.as_object().get_attr("strerror", vm)

crates/vm/src/builtins/str.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -529,7 +529,6 @@ impl Py<PyStr> {
529529
#[pyclass(
530530
flags(BASETYPE, _MATCH_SELF),
531531
with(
532-
PyRef,
533532
AsMapping,
534533
AsNumber,
535534
AsSequence,
@@ -1448,15 +1447,16 @@ impl PyStr {
14481447
fn __getnewargs__(zelf: PyRef<Self>, vm: &VirtualMachine) -> PyObjectRef {
14491448
(zelf.as_str(),).to_pyobject(vm)
14501449
}
1451-
}
14521450

1453-
#[pyclass]
1454-
impl PyRef<PyStr> {
14551451
#[pymethod]
1456-
fn __str__(self, vm: &VirtualMachine) -> PyRefExact<PyStr> {
1457-
self.into_exact_or(&vm.ctx, |zelf| {
1458-
PyStr::from(zelf.data.clone()).into_exact_ref(&vm.ctx)
1459-
})
1452+
fn __str__(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<PyStrRef> {
1453+
if zelf.class().is(vm.ctx.types.str_type) {
1454+
// Already exact str, just return a reference
1455+
Ok(zelf.to_owned())
1456+
} else {
1457+
// Subclass, create a new exact str
1458+
Ok(PyStr::from(zelf.data.clone()).into_ref(&vm.ctx))
1459+
}
14601460
}
14611461
}
14621462

crates/vm/src/builtins/weakproxy.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,8 +79,8 @@ impl PyWeakProxy {
7979
}
8080

8181
#[pymethod]
82-
fn __str__(&self, vm: &VirtualMachine) -> PyResult<PyStrRef> {
83-
self.try_upgrade(vm)?.str(vm)
82+
fn __str__(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<PyStrRef> {
83+
zelf.try_upgrade(vm)?.str(vm)
8484
}
8585

8686
fn len(&self, vm: &VirtualMachine) -> PyResult<usize> {

crates/vm/src/exception_group.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ pub(super) mod types {
182182
}
183183

184184
#[pymethod]
185-
fn __str__(zelf: PyRef<PyBaseException>, vm: &VirtualMachine) -> PyResult<String> {
185+
fn __str__(zelf: &Py<PyBaseException>, vm: &VirtualMachine) -> PyResult<PyStrRef> {
186186
let message = zelf
187187
.get_arg(0)
188188
.map(|m| m.str(vm))
@@ -196,10 +196,10 @@ pub(super) mod types {
196196
.unwrap_or(0);
197197

198198
let suffix = if num_excs == 1 { "" } else { "s" };
199-
Ok(format!(
199+
Ok(vm.ctx.new_str(format!(
200200
"{} ({} sub-exception{})",
201201
message, num_excs, suffix
202-
))
202+
)))
203203
}
204204

205205
#[pymethod]

crates/vm/src/exceptions.rs

Lines changed: 76 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -560,7 +560,7 @@ impl PyBaseException {
560560
}
561561

562562
#[pyclass(
563-
with(PyRef, Constructor, Initializer, Representable),
563+
with(Py, PyRef, Constructor, Initializer, Representable),
564564
flags(BASETYPE, HAS_DICT)
565565
)]
566566
impl PyBaseException {
@@ -633,15 +633,18 @@ impl PyBaseException {
633633
fn set_suppress_context(&self, suppress_context: bool) {
634634
self.suppress_context.store(suppress_context);
635635
}
636+
}
636637

638+
#[pyclass]
639+
impl Py<PyBaseException> {
637640
#[pymethod]
638-
pub(super) fn __str__(&self, vm: &VirtualMachine) -> PyStrRef {
641+
pub(super) fn __str__(&self, vm: &VirtualMachine) -> PyResult<PyStrRef> {
639642
let str_args = vm.exception_args_as_string(self.args(), true);
640-
match str_args.into_iter().exactly_one() {
643+
Ok(match str_args.into_iter().exactly_one() {
641644
Err(i) if i.len() == 0 => vm.ctx.empty_str.to_owned(),
642645
Ok(s) => s,
643646
Err(i) => PyStr::from(format!("({})", i.format(", "))).into_ref(&vm.ctx),
644-
}
647+
})
645648
}
646649
}
647650

@@ -1527,16 +1530,16 @@ pub(super) mod types {
15271530
#[pyexception]
15281531
impl PyKeyError {
15291532
#[pymethod]
1530-
fn __str__(exc: PyBaseExceptionRef, vm: &VirtualMachine) -> PyStrRef {
1531-
let args = exc.args();
1532-
if args.len() == 1 {
1533+
fn __str__(zelf: &Py<PyBaseException>, vm: &VirtualMachine) -> PyResult<PyStrRef> {
1534+
let args = zelf.args();
1535+
Ok(if args.len() == 1 {
15331536
vm.exception_args_as_string(args, false)
15341537
.into_iter()
15351538
.exactly_one()
15361539
.unwrap()
15371540
} else {
1538-
exc.__str__(vm)
1539-
}
1541+
zelf.__str__(vm)?
1542+
})
15401543
}
15411544
}
15421545

@@ -1731,8 +1734,8 @@ pub(super) mod types {
17311734
#[pyexception(with(Constructor, Initializer))]
17321735
impl PyOSError {
17331736
#[pymethod]
1734-
fn __str__(exc: PyBaseExceptionRef, vm: &VirtualMachine) -> PyResult<PyStrRef> {
1735-
let obj = exc.as_object().to_owned();
1737+
fn __str__(zelf: &Py<PyBaseException>, vm: &VirtualMachine) -> PyResult<PyStrRef> {
1738+
let obj = zelf.as_object();
17361739

17371740
// Get OSError fields directly
17381741
let errno_field = obj.get_attr("errno", vm).ok().filter(|v| !vm.is_none(v));
@@ -1819,7 +1822,7 @@ pub(super) mod types {
18191822
}
18201823

18211824
// fallback to BaseException.__str__
1822-
Ok(exc.__str__(vm))
1825+
zelf.__str__(vm)
18231826
}
18241827

18251828
#[pymethod]
@@ -2026,7 +2029,7 @@ pub(super) mod types {
20262029
#[pyexception(with(Initializer))]
20272030
impl PySyntaxError {
20282031
#[pymethod]
2029-
fn __str__(exc: PyBaseExceptionRef, vm: &VirtualMachine) -> PyStrRef {
2032+
fn __str__(zelf: &Py<PyBaseException>, vm: &VirtualMachine) -> PyResult<PyStrRef> {
20302033
fn basename(filename: &str) -> &str {
20312034
let splitted = if cfg!(windows) {
20322035
filename.rsplit(&['/', '\\']).next()
@@ -2036,24 +2039,24 @@ pub(super) mod types {
20362039
splitted.unwrap_or(filename)
20372040
}
20382041

2039-
let maybe_lineno = exc.as_object().get_attr("lineno", vm).ok().map(|obj| {
2042+
let maybe_lineno = zelf.as_object().get_attr("lineno", vm).ok().map(|obj| {
20402043
obj.str(vm)
20412044
.unwrap_or_else(|_| vm.ctx.new_str("<lineno str() failed>"))
20422045
});
2043-
let maybe_filename = exc.as_object().get_attr("filename", vm).ok().map(|obj| {
2046+
let maybe_filename = zelf.as_object().get_attr("filename", vm).ok().map(|obj| {
20442047
obj.str(vm)
20452048
.unwrap_or_else(|_| vm.ctx.new_str("<filename str() failed>"))
20462049
});
20472050

2048-
let args = exc.args();
2051+
let args = zelf.args();
20492052

20502053
let msg = if args.len() == 1 {
20512054
vm.exception_args_as_string(args, false)
20522055
.into_iter()
20532056
.exactly_one()
20542057
.unwrap()
20552058
} else {
2056-
return exc.__str__(vm);
2059+
return zelf.__str__(vm);
20572060
};
20582061

20592062
let msg_with_location_info: String = match (maybe_lineno, maybe_filename) {
@@ -2069,7 +2072,7 @@ pub(super) mod types {
20692072
(None, None) => msg.to_string(),
20702073
};
20712074

2072-
vm.ctx.new_str(msg_with_location_info)
2075+
Ok(vm.ctx.new_str(msg_with_location_info))
20732076
}
20742077
}
20752078

@@ -2178,29 +2181,32 @@ pub(super) mod types {
21782181
#[pyexception(with(Initializer))]
21792182
impl PyUnicodeDecodeError {
21802183
#[pymethod]
2181-
fn __str__(exc: PyBaseExceptionRef, vm: &VirtualMachine) -> PyResult<String> {
2182-
let Ok(object) = exc.as_object().get_attr("object", vm) else {
2183-
return Ok("".to_owned());
2184+
fn __str__(zelf: &Py<PyBaseException>, vm: &VirtualMachine) -> PyResult<PyStrRef> {
2185+
let Ok(object) = zelf.as_object().get_attr("object", vm) else {
2186+
return Ok(vm.ctx.empty_str.to_owned());
21842187
};
21852188
let object: ArgBytesLike = object.try_into_value(vm)?;
2186-
let encoding: PyStrRef = exc
2189+
let encoding: PyStrRef = zelf
21872190
.as_object()
21882191
.get_attr("encoding", vm)?
21892192
.try_into_value(vm)?;
2190-
let start: usize = exc.as_object().get_attr("start", vm)?.try_into_value(vm)?;
2191-
let end: usize = exc.as_object().get_attr("end", vm)?.try_into_value(vm)?;
2192-
let reason: PyStrRef = exc.as_object().get_attr("reason", vm)?.try_into_value(vm)?;
2193-
if start < object.len() && end <= object.len() && end == start + 1 {
2193+
let start: usize = zelf.as_object().get_attr("start", vm)?.try_into_value(vm)?;
2194+
let end: usize = zelf.as_object().get_attr("end", vm)?.try_into_value(vm)?;
2195+
let reason: PyStrRef = zelf
2196+
.as_object()
2197+
.get_attr("reason", vm)?
2198+
.try_into_value(vm)?;
2199+
Ok(vm.ctx.new_str(if start < object.len() && end <= object.len() && end == start + 1 {
21942200
let b = object.borrow_buf()[start];
2195-
Ok(format!(
2201+
format!(
21962202
"'{encoding}' codec can't decode byte {b:#02x} in position {start}: {reason}"
2197-
))
2203+
)
21982204
} else {
2199-
Ok(format!(
2205+
format!(
22002206
"'{encoding}' codec can't decode bytes in position {start}-{}: {reason}",
22012207
end - 1,
2202-
))
2203-
}
2208+
)
2209+
}))
22042210
}
22052211
}
22062212

@@ -2232,30 +2238,33 @@ pub(super) mod types {
22322238
#[pyexception(with(Initializer))]
22332239
impl PyUnicodeEncodeError {
22342240
#[pymethod]
2235-
fn __str__(exc: PyBaseExceptionRef, vm: &VirtualMachine) -> PyResult<String> {
2236-
let Ok(object) = exc.as_object().get_attr("object", vm) else {
2237-
return Ok("".to_owned());
2241+
fn __str__(zelf: &Py<PyBaseException>, vm: &VirtualMachine) -> PyResult<PyStrRef> {
2242+
let Ok(object) = zelf.as_object().get_attr("object", vm) else {
2243+
return Ok(vm.ctx.empty_str.to_owned());
22382244
};
22392245
let object: PyStrRef = object.try_into_value(vm)?;
2240-
let encoding: PyStrRef = exc
2246+
let encoding: PyStrRef = zelf
22412247
.as_object()
22422248
.get_attr("encoding", vm)?
22432249
.try_into_value(vm)?;
2244-
let start: usize = exc.as_object().get_attr("start", vm)?.try_into_value(vm)?;
2245-
let end: usize = exc.as_object().get_attr("end", vm)?.try_into_value(vm)?;
2246-
let reason: PyStrRef = exc.as_object().get_attr("reason", vm)?.try_into_value(vm)?;
2247-
if start < object.char_len() && end <= object.char_len() && end == start + 1 {
2250+
let start: usize = zelf.as_object().get_attr("start", vm)?.try_into_value(vm)?;
2251+
let end: usize = zelf.as_object().get_attr("end", vm)?.try_into_value(vm)?;
2252+
let reason: PyStrRef = zelf
2253+
.as_object()
2254+
.get_attr("reason", vm)?
2255+
.try_into_value(vm)?;
2256+
Ok(vm.ctx.new_str(if start < object.char_len() && end <= object.char_len() && end == start + 1 {
22482257
let ch = object.as_wtf8().code_points().nth(start).unwrap();
2249-
Ok(format!(
2258+
format!(
22502259
"'{encoding}' codec can't encode character '{}' in position {start}: {reason}",
22512260
UnicodeEscapeCodepoint(ch)
2252-
))
2261+
)
22532262
} else {
2254-
Ok(format!(
2263+
format!(
22552264
"'{encoding}' codec can't encode characters in position {start}-{}: {reason}",
22562265
end - 1,
2257-
))
2258-
}
2266+
)
2267+
}))
22592268
}
22602269
}
22612270

@@ -2286,26 +2295,31 @@ pub(super) mod types {
22862295
#[pyexception(with(Initializer))]
22872296
impl PyUnicodeTranslateError {
22882297
#[pymethod]
2289-
fn __str__(exc: PyBaseExceptionRef, vm: &VirtualMachine) -> PyResult<String> {
2290-
let Ok(object) = exc.as_object().get_attr("object", vm) else {
2291-
return Ok("".to_owned());
2298+
fn __str__(zelf: &Py<PyBaseException>, vm: &VirtualMachine) -> PyResult<PyStrRef> {
2299+
let Ok(object) = zelf.as_object().get_attr("object", vm) else {
2300+
return Ok(vm.ctx.empty_str.to_owned());
22922301
};
22932302
let object: PyStrRef = object.try_into_value(vm)?;
2294-
let start: usize = exc.as_object().get_attr("start", vm)?.try_into_value(vm)?;
2295-
let end: usize = exc.as_object().get_attr("end", vm)?.try_into_value(vm)?;
2296-
let reason: PyStrRef = exc.as_object().get_attr("reason", vm)?.try_into_value(vm)?;
2297-
if start < object.char_len() && end <= object.char_len() && end == start + 1 {
2298-
let ch = object.as_wtf8().code_points().nth(start).unwrap();
2299-
Ok(format!(
2300-
"can't translate character '{}' in position {start}: {reason}",
2301-
UnicodeEscapeCodepoint(ch)
2302-
))
2303-
} else {
2304-
Ok(format!(
2305-
"can't translate characters in position {start}-{}: {reason}",
2306-
end - 1,
2307-
))
2308-
}
2303+
let start: usize = zelf.as_object().get_attr("start", vm)?.try_into_value(vm)?;
2304+
let end: usize = zelf.as_object().get_attr("end", vm)?.try_into_value(vm)?;
2305+
let reason: PyStrRef = zelf
2306+
.as_object()
2307+
.get_attr("reason", vm)?
2308+
.try_into_value(vm)?;
2309+
Ok(vm.ctx.new_str(
2310+
if start < object.char_len() && end <= object.char_len() && end == start + 1 {
2311+
let ch = object.as_wtf8().code_points().nth(start).unwrap();
2312+
format!(
2313+
"can't translate character '{}' in position {start}: {reason}",
2314+
UnicodeEscapeCodepoint(ch)
2315+
)
2316+
} else {
2317+
format!(
2318+
"can't translate characters in position {start}-{}: {reason}",
2319+
end - 1,
2320+
)
2321+
},
2322+
))
23092323
}
23102324
}
23112325

crates/vm/src/stdlib/io.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1466,7 +1466,11 @@ mod _io {
14661466
let zelf: PyRef<Self> = zelf.try_into_value(vm)?;
14671467
let (raw, BufferSize { buffer_size }): (PyObjectRef, _) =
14681468
args.bind(vm).map_err(|e| {
1469-
let msg = format!("{}() {}", Self::CLASS_NAME, *e.__str__(vm));
1469+
let str_repr = e
1470+
.__str__(vm)
1471+
.map(|s| s.as_str().to_owned())
1472+
.unwrap_or_else(|_| "<error getting exception str>".to_owned());
1473+
let msg = format!("{}() {}", Self::CLASS_NAME, str_repr);
14701474
vm.new_exception_msg(e.class().to_owned(), msg)
14711475
})?;
14721476
zelf.init(raw, BufferSize { buffer_size }, vm)

0 commit comments

Comments
 (0)