Skip to content

Commit 4fb6586

Browse files
committed
Fix winerror handling
1 parent 9d2dd17 commit 4fb6586

File tree

1 file changed

+86
-34
lines changed

1 file changed

+86
-34
lines changed

crates/vm/src/exceptions.rs

Lines changed: 86 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1708,53 +1708,105 @@ pub(super) mod types {
17081708
}
17091709
}
17101710

1711-
// args are truncated to 2 for compatibility (only when 2-5 args)
1712-
if (3..=5).contains(&len) {
1711+
// args are truncated to 2 for compatibility (only when 2-5 args and filename is not None)
1712+
// truncation happens inside "if (filename && filename != Py_None)" block
1713+
let has_filename = exc.filename.to_owned().filter(|f| !vm.is_none(f)).is_some();
1714+
if (3..=5).contains(&len) && has_filename {
17131715
new_args.args.truncate(2);
17141716
}
17151717
PyBaseException::slot_init(zelf, new_args, vm)
17161718
}
17171719

17181720
#[pymethod]
17191721
fn __str__(exc: PyBaseExceptionRef, vm: &VirtualMachine) -> PyResult<PyStrRef> {
1720-
let args = exc.args();
17211722
let obj = exc.as_object().to_owned();
17221723

1723-
let str = if args.len() == 2 {
1724-
// SAFETY: len() == 2 is checked so get_arg 1 or 2 won't panic
1725-
let errno = exc.get_arg(0).unwrap().str(vm)?;
1726-
let msg = exc.get_arg(1).unwrap().str(vm)?;
1727-
1728-
// On Windows, use [WinError X] format when winerror is set
1729-
#[cfg(windows)]
1730-
let (label, code) = match obj.get_attr("winerror", vm) {
1731-
Ok(winerror) if !vm.is_none(&winerror) => ("WinError", winerror.str(vm)?),
1732-
_ => ("Errno", errno.clone()),
1733-
};
1734-
#[cfg(not(windows))]
1735-
let (label, code) = ("Errno", errno.clone());
1724+
// Get OSError fields directly
1725+
let errno_field = obj.get_attr("errno", vm).ok().filter(|v| !vm.is_none(v));
1726+
let strerror = obj.get_attr("strerror", vm).ok().filter(|v| !vm.is_none(v));
1727+
let filename = obj.get_attr("filename", vm).ok().filter(|v| !vm.is_none(v));
1728+
let filename2 = obj
1729+
.get_attr("filename2", vm)
1730+
.ok()
1731+
.filter(|v| !vm.is_none(v));
1732+
#[cfg(windows)]
1733+
let winerror = obj.get_attr("winerror", vm).ok().filter(|v| !vm.is_none(v));
17361734

1737-
let s = match obj.get_attr("filename", vm) {
1738-
Ok(filename) if !vm.is_none(&filename) => match obj.get_attr("filename2", vm) {
1739-
Ok(filename2) if !vm.is_none(&filename2) => format!(
1740-
"[{} {}] {}: '{}' -> '{}'",
1741-
label,
1735+
// Windows: winerror takes priority over errno
1736+
#[cfg(windows)]
1737+
if let Some(ref win_err) = winerror {
1738+
let code = win_err.str(vm)?;
1739+
if let Some(ref f) = filename {
1740+
let msg = strerror
1741+
.as_ref()
1742+
.map(|s| s.str(vm))
1743+
.transpose()?
1744+
.map(|s| s.to_string())
1745+
.unwrap_or_else(|| "None".to_owned());
1746+
if let Some(ref f2) = filename2 {
1747+
return Ok(vm.ctx.new_str(format!(
1748+
"[WinError {}] {}: '{}' -> '{}'",
17421749
code,
17431750
msg,
1744-
filename.str(vm)?,
1745-
filename2.str(vm)?
1746-
),
1747-
_ => format!("[{} {}] {}: '{}'", label, code, msg, filename.str(vm)?),
1748-
},
1749-
_ => {
1750-
format!("[{label} {code}] {msg}")
1751+
f.str(vm)?,
1752+
f2.str(vm)?
1753+
)));
17511754
}
1752-
};
1753-
vm.ctx.new_str(s)
1754-
} else {
1755-
exc.__str__(vm)
1756-
};
1757-
Ok(str)
1755+
return Ok(vm.ctx.new_str(format!(
1756+
"[WinError {}] {}: '{}'",
1757+
code,
1758+
msg,
1759+
f.str(vm)?
1760+
)));
1761+
}
1762+
// winerror && strerror (no filename)
1763+
if let Some(ref s) = strerror {
1764+
return Ok(vm
1765+
.ctx
1766+
.new_str(format!("[WinError {}] {}", code, s.str(vm)?)));
1767+
}
1768+
}
1769+
1770+
// Non-Windows or fallback: use errno
1771+
if let Some(ref f) = filename {
1772+
let errno_str = errno_field
1773+
.as_ref()
1774+
.map(|e| e.str(vm))
1775+
.transpose()?
1776+
.map(|s| s.to_string())
1777+
.unwrap_or_else(|| "None".to_owned());
1778+
let msg = strerror
1779+
.as_ref()
1780+
.map(|s| s.str(vm))
1781+
.transpose()?
1782+
.map(|s| s.to_string())
1783+
.unwrap_or_else(|| "None".to_owned());
1784+
if let Some(ref f2) = filename2 {
1785+
return Ok(vm.ctx.new_str(format!(
1786+
"[Errno {}] {}: '{}' -> '{}'",
1787+
errno_str,
1788+
msg,
1789+
f.str(vm)?,
1790+
f2.str(vm)?
1791+
)));
1792+
}
1793+
return Ok(vm.ctx.new_str(format!(
1794+
"[Errno {}] {}: '{}'",
1795+
errno_str,
1796+
msg,
1797+
f.str(vm)?
1798+
)));
1799+
}
1800+
1801+
// errno && strerror (no filename)
1802+
if let (Some(e), Some(s)) = (&errno_field, &strerror) {
1803+
return Ok(vm
1804+
.ctx
1805+
.new_str(format!("[Errno {}] {}", e.str(vm)?, s.str(vm)?)));
1806+
}
1807+
1808+
// fallback to BaseException.__str__
1809+
Ok(exc.__str__(vm))
17581810
}
17591811

17601812
#[pymethod]

0 commit comments

Comments
 (0)