mirror of
https://github.com/RustPython/RustPython.git
synced 2026-10-28 10:44:36 +08:00
Remove PyStr::as_str, use as_wtf8/PyUtf8Str instead (#7218)
- Remove as_str() from PyStr/Py<PyStr> (was panicking on surrogates) - Add Wtf8Concat trait and concat! macro for WTF-8 formatting - Add impl From<&str> for &Wtf8 conversion - Add AsPyStr/DictKey impls for PyUtf8Str types - Migrate all call sites to as_wtf8(), to_str(), or PyUtf8Str - Fix exception message APIs to accept Wtf8Buf - Deduplicate inner-scope imports across modules
This commit is contained in:
1 parent
35e897e58a
commit
7311b87c7b
121 files changed
+1954
-1457
No files matched your search
Vendored
-1
@@ -4,7 +4,6 @@ from test.test_string._support import TStringBaseCase, fstring
|
||||
|
||||
|
||||
class TestTString(unittest.TestCase, TStringBaseCase):
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; + Template(strings=('Hello',), interpolations=())
|
||||
def test_string_representation(self):
|
||||
# Test __repr__
|
||||
t = t"Hello"
|
||||
|
||||
@@ -258,6 +258,7 @@ impl StrData {
|
||||
&self.data
|
||||
}
|
||||
|
||||
// TODO: rename to to_str
|
||||
#[inline]
|
||||
pub fn as_str(&self) -> Option<&str> {
|
||||
self.kind
|
||||
@@ -429,13 +430,13 @@ pub fn zfill(bytes: &[u8], width: usize) -> Vec<u8> {
|
||||
|
||||
/// Convert a string to ascii compatible, escaping unicode-s into escape
|
||||
/// sequences.
|
||||
pub fn to_ascii(value: &str) -> AsciiString {
|
||||
pub fn to_ascii(value: &Wtf8) -> AsciiString {
|
||||
let mut ascii = Vec::new();
|
||||
for c in value.chars() {
|
||||
if c.is_ascii() {
|
||||
ascii.push(c as u8);
|
||||
for cp in value.code_points() {
|
||||
if cp.is_ascii() {
|
||||
ascii.push(cp.to_u32() as u8);
|
||||
} else {
|
||||
let c = c as i64;
|
||||
let c = cp.to_u32();
|
||||
let hex = if c < 0x100 {
|
||||
format!("\\x{c:02x}")
|
||||
} else if c < 0x10000 {
|
||||
|
||||
@@ -6,6 +6,7 @@ pub(crate) use _asyncio::module_def;
|
||||
|
||||
#[pymodule]
|
||||
pub(crate) mod _asyncio {
|
||||
use crate::common::wtf8::{Wtf8Buf, wtf8_concat};
|
||||
use crate::{
|
||||
common::lock::PyRwLock,
|
||||
vm::{
|
||||
@@ -859,7 +860,7 @@ pub(crate) mod _asyncio {
|
||||
}
|
||||
}
|
||||
|
||||
fn get_future_repr_info(future: &PyObject, vm: &VirtualMachine) -> PyResult<String> {
|
||||
fn get_future_repr_info(future: &PyObject, vm: &VirtualMachine) -> PyResult<Wtf8Buf> {
|
||||
// Try to use asyncio.base_futures._future_repr_info
|
||||
// Import from sys.modules if available, otherwise try regular import
|
||||
let sys_modules = vm.sys_module.get_attr("modules", vm)?;
|
||||
@@ -892,29 +893,34 @@ pub(crate) mod _asyncio {
|
||||
Err(_) => return get_future_repr_info_fallback(future, vm),
|
||||
};
|
||||
|
||||
let parts: Vec<String> = list
|
||||
.borrow_vec()
|
||||
.iter()
|
||||
.filter_map(|x: &PyObjectRef| x.str(vm).ok().map(|s| s.as_str().to_string()))
|
||||
.collect();
|
||||
Ok(parts.join(" "))
|
||||
let mut result = Wtf8Buf::new();
|
||||
let parts = list.borrow_vec();
|
||||
for (i, x) in parts.iter().enumerate() {
|
||||
if i > 0 {
|
||||
result.push_str(" ");
|
||||
}
|
||||
if let Ok(s) = x.str(vm) {
|
||||
result.push_wtf8(s.as_wtf8());
|
||||
}
|
||||
}
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
fn get_future_repr_info_fallback(future: &PyObject, vm: &VirtualMachine) -> PyResult<String> {
|
||||
fn get_future_repr_info_fallback(future: &PyObject, vm: &VirtualMachine) -> PyResult<Wtf8Buf> {
|
||||
// Fallback: build repr from properties directly
|
||||
if let Ok(Some(state)) =
|
||||
vm.get_attribute_opt(future.to_owned(), vm.ctx.intern_str("_state"))
|
||||
{
|
||||
let state_str = state
|
||||
let s = state
|
||||
.str(vm)
|
||||
.map(|s| s.as_str().to_lowercase())
|
||||
.unwrap_or_else(|_| "unknown".to_string());
|
||||
return Ok(state_str);
|
||||
.map(|s| s.as_wtf8().to_lowercase())
|
||||
.unwrap_or_else(|_| Wtf8Buf::from("unknown"));
|
||||
return Ok(s);
|
||||
}
|
||||
Ok("state=unknown".to_string())
|
||||
Ok(Wtf8Buf::from("state=unknown"))
|
||||
}
|
||||
|
||||
fn get_task_repr_info(task: &PyObject, vm: &VirtualMachine) -> PyResult<String> {
|
||||
fn get_task_repr_info(task: &PyObject, vm: &VirtualMachine) -> PyResult<Wtf8Buf> {
|
||||
// vm.import returns the top-level module, get base_tasks submodule
|
||||
match vm
|
||||
.import("asyncio.base_tasks", 0)
|
||||
@@ -927,12 +933,15 @@ pub(crate) mod _asyncio {
|
||||
let list: PyListRef = info.downcast().map_err(|_| {
|
||||
vm.new_type_error("_task_repr_info should return a list")
|
||||
})?;
|
||||
let parts: Vec<String> = list
|
||||
.borrow_vec()
|
||||
.iter()
|
||||
.map(|x: &PyObjectRef| x.str(vm).map(|s| s.as_str().to_string()))
|
||||
.collect::<PyResult<Vec<_>>>()?;
|
||||
Ok(parts.join(" "))
|
||||
let mut result = Wtf8Buf::new();
|
||||
let parts = list.borrow_vec();
|
||||
for (i, x) in parts.iter().enumerate() {
|
||||
if i > 0 {
|
||||
result.push_str(" ");
|
||||
}
|
||||
result.push_wtf8(x.str(vm)?.as_wtf8());
|
||||
}
|
||||
Ok(result)
|
||||
}
|
||||
_ => get_future_repr_info(task, vm),
|
||||
}
|
||||
@@ -1928,40 +1937,28 @@ pub(crate) mod _asyncio {
|
||||
}
|
||||
|
||||
impl Representable for PyTask {
|
||||
fn repr_str(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<String> {
|
||||
fn repr_wtf8(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<Wtf8Buf> {
|
||||
let class_name = zelf.class().name().to_string();
|
||||
|
||||
if let Some(_guard) = ReprGuard::enter(vm, zelf.as_object()) {
|
||||
// Try to use _task_repr_info if available
|
||||
if let Ok(info) = get_task_repr_info(zelf.as_object(), vm)
|
||||
&& info != "state=unknown"
|
||||
&& info.as_bytes() != b"state=unknown"
|
||||
{
|
||||
return Ok(format!("<{} {}>", class_name, info));
|
||||
return Ok(wtf8_concat!("<", class_name, " ", info, ">"));
|
||||
}
|
||||
|
||||
// Fallback: build repr from task properties directly
|
||||
let state = zelf.base.fut_state.load().as_str().to_lowercase();
|
||||
let name = zelf
|
||||
.task_name
|
||||
.read()
|
||||
.as_ref()
|
||||
.and_then(|n| n.str(vm).ok())
|
||||
.map(|s| s.as_str().to_string())
|
||||
.unwrap_or_else(|| "?".to_string());
|
||||
let coro_repr = zelf
|
||||
.task_coro
|
||||
.read()
|
||||
.as_ref()
|
||||
.and_then(|c| c.repr(vm).ok())
|
||||
.map(|s| s.as_str().to_string())
|
||||
.unwrap_or_else(|| "?".to_string());
|
||||
|
||||
Ok(format!(
|
||||
"<{} {} name='{}' coro={}>",
|
||||
class_name, state, name, coro_repr
|
||||
let name = zelf.task_name.read().as_ref().and_then(|n| n.str(vm).ok());
|
||||
let coro_repr = zelf.task_coro.read().as_ref().and_then(|c| c.repr(vm).ok());
|
||||
let name = name.as_ref().map_or("?".as_ref(), |s| s.as_wtf8());
|
||||
let coro_repr = coro_repr.as_ref().map_or("?".as_ref(), |s| s.as_wtf8());
|
||||
Ok(wtf8_concat!(
|
||||
"<", class_name, " ", state, " name='", name, "' coro=", coro_repr, ">"
|
||||
))
|
||||
} else {
|
||||
Ok(format!("<{} ...>", class_name))
|
||||
Ok(Wtf8Buf::from(format!("<{class_name} ...>")))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2151,10 +2148,8 @@ pub(crate) mod _asyncio {
|
||||
// Check if task awaits on itself
|
||||
let task_obj: PyObjectRef = task.clone().into();
|
||||
if result.is(&task_obj) {
|
||||
let msg = format!(
|
||||
"Task cannot await on itself: {}",
|
||||
task_obj.repr(vm)?.as_str()
|
||||
);
|
||||
let task_repr = task_obj.repr(vm)?;
|
||||
let msg = format!("Task cannot await on itself: {}", task_repr.as_wtf8());
|
||||
task.base.fut_state.store(FutureState::Finished);
|
||||
*task.base.fut_exception.write() = Some(vm.new_runtime_error(msg).into());
|
||||
PyTask::schedule_callbacks(task, vm)?;
|
||||
@@ -2254,7 +2249,8 @@ pub(crate) mod _asyncio {
|
||||
vm.call_method(&loop_obj, "call_soon", (step_wrapper,))?;
|
||||
}
|
||||
} else {
|
||||
let msg = format!("Task got bad yield: {}", result.repr(vm)?.as_str());
|
||||
let result_repr = result.repr(vm)?;
|
||||
let msg = format!("Task got bad yield: {}", result_repr.as_wtf8());
|
||||
task.base.fut_state.store(FutureState::Finished);
|
||||
*task.base.fut_exception.write() = Some(vm.new_runtime_error(msg).into());
|
||||
PyTask::schedule_callbacks(task, vm)?;
|
||||
|
||||
@@ -89,7 +89,7 @@ mod _sqlite3 {
|
||||
$(
|
||||
#[allow(dead_code)]
|
||||
fn [<new_ $x:snake>](vm: &VirtualMachine, msg: String) -> PyBaseExceptionRef {
|
||||
vm.new_exception_msg([<$x:snake _type>]().to_owned(), msg)
|
||||
vm.new_exception_msg([<$x:snake _type>]().to_owned(), msg.into())
|
||||
}
|
||||
fn [<$x:snake _type>]() -> &'static Py<PyType> {
|
||||
[<$x:snake:upper>].get().expect("exception type not initialize")
|
||||
@@ -723,7 +723,7 @@ mod _sqlite3 {
|
||||
converter: ArgCallable,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<()> {
|
||||
let name = typename.as_str().to_uppercase();
|
||||
let name = typename.expect_str().to_uppercase();
|
||||
converters().set_item(&name, converter.into(), vm)
|
||||
}
|
||||
|
||||
@@ -2194,8 +2194,8 @@ mod _sqlite3 {
|
||||
let Some(obj) = obj.downcast_ref::<PyStr>() else {
|
||||
break;
|
||||
};
|
||||
let a_iter = name.as_str().chars().flat_map(|x| x.to_uppercase());
|
||||
let b_iter = obj.as_str().chars().flat_map(|x| x.to_uppercase());
|
||||
let a_iter = name.expect_str().chars().flat_map(|x| x.to_uppercase());
|
||||
let b_iter = obj.expect_str().chars().flat_map(|x| x.to_uppercase());
|
||||
|
||||
if a_iter.eq(b_iter) {
|
||||
return self.data.getitem_by_index(vm, i);
|
||||
@@ -2918,7 +2918,7 @@ mod _sqlite3 {
|
||||
};
|
||||
let mut s = Vec::with_capacity(16);
|
||||
s.extend(b"BEGIN ");
|
||||
s.extend(isolation_level.as_str().bytes());
|
||||
s.extend(isolation_level.expect_str().bytes());
|
||||
s.push(b'\0');
|
||||
self._exec(&s, vm)
|
||||
}
|
||||
@@ -3469,7 +3469,7 @@ mod _sqlite3 {
|
||||
return e;
|
||||
}
|
||||
|
||||
vm.new_exception_msg_dict(typ, msg, dict)
|
||||
vm.new_exception_msg_dict(typ, msg.into(), dict)
|
||||
}
|
||||
|
||||
static BEGIN_STATEMENTS: &[&[u8]] = &[
|
||||
|
||||
@@ -19,7 +19,7 @@ mod array {
|
||||
builtins::{
|
||||
PositionIterInternal, PyByteArray, PyBytes, PyBytesRef, PyDictRef, PyFloat,
|
||||
PyGenericAlias, PyInt, PyList, PyListRef, PyStr, PyStrRef, PyTupleRef, PyType,
|
||||
PyTypeRef, builtins_iter,
|
||||
PyTypeRef, PyUtf8StrRef, builtins_iter,
|
||||
},
|
||||
class_or_notimplemented,
|
||||
convert::{ToPyObject, ToPyResult, TryFromBorrowedObject, TryFromObject},
|
||||
@@ -559,7 +559,7 @@ mod array {
|
||||
|
||||
impl ArrayElement for WideChar {
|
||||
fn try_into_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
|
||||
PyStrRef::try_from_object(vm, obj)?
|
||||
PyUtf8StrRef::try_from_object(vm, obj)?
|
||||
.as_str()
|
||||
.chars()
|
||||
.exactly_one()
|
||||
@@ -625,7 +625,7 @@ mod array {
|
||||
#[derive(FromArgs)]
|
||||
pub struct ArrayNewArgs {
|
||||
#[pyarg(positional)]
|
||||
spec: PyStrRef,
|
||||
spec: PyUtf8StrRef,
|
||||
#[pyarg(positional, optional)]
|
||||
init: OptionalArg<PyObjectRef>,
|
||||
}
|
||||
@@ -884,7 +884,7 @@ mod array {
|
||||
if not_enough_bytes {
|
||||
Err(vm.new_exception_msg(
|
||||
vm.ctx.exceptions.eof_error.to_owned(),
|
||||
"read() didn't return enough bytes".to_owned(),
|
||||
"read() didn't return enough bytes".into(),
|
||||
))
|
||||
} else {
|
||||
Ok(())
|
||||
@@ -1425,7 +1425,7 @@ mod array {
|
||||
#[pyarg(positional)]
|
||||
arraytype: PyTypeRef,
|
||||
#[pyarg(positional)]
|
||||
typecode: PyStrRef,
|
||||
typecode: PyUtf8StrRef,
|
||||
#[pyarg(positional)]
|
||||
mformat_code: MachineFormatCode,
|
||||
#[pyarg(positional)]
|
||||
@@ -1568,7 +1568,7 @@ mod array {
|
||||
Ok(typ)
|
||||
}
|
||||
|
||||
fn check_type_code(spec: PyStrRef, vm: &VirtualMachine) -> PyResult<ArrayContentType> {
|
||||
fn check_type_code(spec: PyUtf8StrRef, vm: &VirtualMachine) -> PyResult<ArrayContentType> {
|
||||
let spec = spec.as_str().chars().exactly_one().map_err(|_| {
|
||||
vm.new_type_error(
|
||||
"_array_reconstructor() argument 2 must be a unicode character, not str",
|
||||
|
||||
@@ -849,7 +849,7 @@ mod decl {
|
||||
struct Base64DecodeError(base64::DecodeError);
|
||||
|
||||
fn new_binascii_error(msg: String, vm: &VirtualMachine) -> PyBaseExceptionRef {
|
||||
vm.new_exception_msg(decl::error_type(vm), msg)
|
||||
vm.new_exception_msg(decl::error_type(vm), msg.into())
|
||||
}
|
||||
|
||||
impl ToPyException for Base64DecodeError {
|
||||
|
||||
@@ -15,7 +15,7 @@ mod _contextvars {
|
||||
AsObject, Py, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine, atomic_func,
|
||||
builtins::{PyGenericAlias, PyList, PyStrRef, PyType, PyTypeRef},
|
||||
class::StaticType,
|
||||
common::hash::PyHash,
|
||||
common::{hash::PyHash, wtf8::Wtf8Buf},
|
||||
function::{ArgCallable, FuncArgs, OptionalArg},
|
||||
protocol::{PyMappingMethods, PySequenceMethods},
|
||||
types::{AsMapping, AsSequence, Constructor, Hashable, Iterable, Representable},
|
||||
@@ -333,7 +333,7 @@ mod _contextvars {
|
||||
if vars.swap_remove(zelf).is_none() {
|
||||
// TODO:
|
||||
// PyErr_SetObject(PyExc_LookupError, (PyObject *)var);
|
||||
let msg = zelf.as_object().repr(vm)?.as_str().to_owned();
|
||||
let msg = zelf.as_object().repr(vm)?.as_wtf8().to_owned();
|
||||
return Err(vm.new_lookup_error(msg));
|
||||
}
|
||||
|
||||
@@ -409,7 +409,7 @@ mod _contextvars {
|
||||
default.clone()
|
||||
} else {
|
||||
let msg = zelf.as_object().repr(vm)?;
|
||||
return Err(vm.new_lookup_error(msg.as_str().to_owned()));
|
||||
return Err(vm.new_lookup_error(msg.as_wtf8().to_owned()));
|
||||
};
|
||||
Ok(Some(value))
|
||||
}
|
||||
@@ -611,11 +611,14 @@ mod _contextvars {
|
||||
|
||||
impl Representable for ContextToken {
|
||||
#[inline]
|
||||
fn repr_str(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<String> {
|
||||
fn repr_wtf8(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<Wtf8Buf> {
|
||||
let used = if zelf.used.get() { " used" } else { "" };
|
||||
let var = Representable::repr_str(&zelf.var, vm)?;
|
||||
let var = Representable::repr_wtf8(&zelf.var, vm)?;
|
||||
let ptr = zelf.as_object().get_id() as *const u8;
|
||||
Ok(format!("<Token{used} var={var} at {ptr:p}>"))
|
||||
let mut result = Wtf8Buf::from(format!("<Token{used} var="));
|
||||
result.push_wtf8(&var);
|
||||
result.push_str(&format!(" at {ptr:p}>"));
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+46
-45
@@ -6,7 +6,7 @@ mod _csv {
|
||||
use crate::vm::{
|
||||
AsObject, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, TryFromObject,
|
||||
VirtualMachine,
|
||||
builtins::{PyBaseExceptionRef, PyInt, PyNone, PyStr, PyType, PyTypeRef},
|
||||
builtins::{PyBaseExceptionRef, PyInt, PyNone, PyStr, PyType, PyTypeRef, PyUtf8StrRef},
|
||||
function::{ArgIterable, ArgumentError, FromArgs, FuncArgs, OptionalArg},
|
||||
protocol::{PyIter, PyIterReturn},
|
||||
raise_if_stop,
|
||||
@@ -16,7 +16,7 @@ mod _csv {
|
||||
use csv_core::Terminator;
|
||||
use itertools::{self, Itertools};
|
||||
use parking_lot::Mutex;
|
||||
use rustpython_common::lock::LazyLock;
|
||||
use rustpython_common::{lock::LazyLock, wtf8::Wtf8Buf};
|
||||
use rustpython_vm::{match_class, sliceable::SliceableSequenceOp};
|
||||
use std::collections::HashMap;
|
||||
|
||||
@@ -50,8 +50,8 @@ mod _csv {
|
||||
});
|
||||
static GLOBAL_FIELD_LIMIT: LazyLock<Mutex<isize>> = LazyLock::new(|| Mutex::new(131072));
|
||||
|
||||
fn new_csv_error(vm: &VirtualMachine, msg: String) -> PyBaseExceptionRef {
|
||||
vm.new_exception_msg(super::_csv::error(vm), msg)
|
||||
fn new_csv_error(vm: &VirtualMachine, msg: impl Into<Wtf8Buf>) -> PyBaseExceptionRef {
|
||||
vm.new_exception_msg(super::_csv::error(vm), msg.into())
|
||||
}
|
||||
|
||||
#[pyattr]
|
||||
@@ -138,7 +138,7 @@ mod _csv {
|
||||
} else {
|
||||
match_class!(match obj.to_owned() {
|
||||
s @ PyStr => {
|
||||
Ok(s.as_str().bytes().exactly_one().map_err(|_| {
|
||||
Ok(s.as_bytes().iter().copied().exactly_one().map_err(|_| {
|
||||
vm.new_type_error(format!(
|
||||
r#""delimiter" must be a unicode character, not a string of length {}"#,
|
||||
s.len()
|
||||
@@ -159,19 +159,16 @@ mod _csv {
|
||||
fn parse_quotechar_from_obj(vm: &VirtualMachine, obj: &PyObject) -> PyResult<Option<u8>> {
|
||||
match_class!(match obj.get_attr("quotechar", vm)? {
|
||||
s @ PyStr => {
|
||||
Ok(Some(s.as_str().bytes().exactly_one().map_err(|_| {
|
||||
vm.new_exception_msg(
|
||||
super::_csv::error(vm),
|
||||
format!(r#""quotechar" must be a unicode character or None, not a string of length {}"#, s.len()),
|
||||
)
|
||||
Ok(Some(s.as_bytes().iter().copied().exactly_one().map_err(|_| {
|
||||
new_csv_error(vm, format!(r#""quotechar" must be a unicode character or None, not a string of length {}"#, s.len()))
|
||||
})?))
|
||||
}
|
||||
_n @ PyNone => {
|
||||
Ok(None)
|
||||
}
|
||||
attr => {
|
||||
Err(vm.new_exception_msg(
|
||||
super::_csv::error(vm),
|
||||
Err(new_csv_error(
|
||||
vm,
|
||||
format!(
|
||||
r#""quotechar" must be a unicode character or None, not {}"#,
|
||||
attr.class()
|
||||
@@ -183,9 +180,9 @@ mod _csv {
|
||||
fn parse_escapechar_from_obj(vm: &VirtualMachine, obj: &PyObject) -> PyResult<Option<u8>> {
|
||||
match_class!(match obj.get_attr("escapechar", vm)? {
|
||||
s @ PyStr => {
|
||||
Ok(Some(s.as_str().bytes().exactly_one().map_err(|_| {
|
||||
vm.new_exception_msg(
|
||||
super::_csv::error(vm),
|
||||
Ok(Some(s.as_bytes().iter().copied().exactly_one().map_err(|_| {
|
||||
new_csv_error(
|
||||
vm,
|
||||
format!(r#""escapechar" must be a unicode character or None, not a string of length {}"#, s.len()),
|
||||
)
|
||||
})?))
|
||||
@@ -213,10 +210,7 @@ mod _csv {
|
||||
// only capture the first character
|
||||
csv_core::Terminator::Any(*t)
|
||||
} else {
|
||||
return Err(vm.new_exception_msg(
|
||||
super::_csv::error(vm),
|
||||
r#""lineterminator" must be a string"#.to_owned(),
|
||||
));
|
||||
return Err(new_csv_error(vm, r#""lineterminator" must be a string"#));
|
||||
})
|
||||
}
|
||||
attr => {
|
||||
@@ -278,9 +272,10 @@ mod _csv {
|
||||
mut _rest: FuncArgs,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<()> {
|
||||
let Some(name) = name.downcast_ref::<PyStr>() else {
|
||||
return Err(vm.new_type_error("argument 0 must be a string"));
|
||||
};
|
||||
let name = name
|
||||
.downcast::<PyStr>()
|
||||
.map_err(|_| vm.new_type_error("argument 0 must be a string"))?;
|
||||
let name: PyUtf8StrRef = name.try_into_utf8(vm)?;
|
||||
let dialect = match dialect {
|
||||
OptionalArg::Present(d) => PyDialect::try_from_object(vm, d)
|
||||
.map_err(|_| vm.new_type_error("argument 1 must be a dialect object"))?,
|
||||
@@ -299,17 +294,18 @@ mod _csv {
|
||||
mut _rest: FuncArgs,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<PyDialect> {
|
||||
let Some(name) = name.downcast_ref::<PyStr>() else {
|
||||
return Err(vm.new_exception_msg(
|
||||
super::_csv::error(vm),
|
||||
format!("argument 0 must be a string, not '{}'", name.class()),
|
||||
));
|
||||
};
|
||||
let name = name.downcast::<PyStr>().map_err(|obj| {
|
||||
new_csv_error(
|
||||
vm,
|
||||
format!("argument 0 must be a string, not '{}'", obj.class()),
|
||||
)
|
||||
})?;
|
||||
let name: PyUtf8StrRef = name.try_into_utf8(vm)?;
|
||||
let g = GLOBAL_HASHMAP.lock();
|
||||
if let Some(dialect) = g.get(name.as_str()) {
|
||||
return Ok(*dialect);
|
||||
}
|
||||
Err(vm.new_exception_msg(super::_csv::error(vm), "unknown dialect".to_string()))
|
||||
Err(new_csv_error(vm, "unknown dialect"))
|
||||
}
|
||||
|
||||
#[pyfunction]
|
||||
@@ -318,17 +314,18 @@ mod _csv {
|
||||
mut _rest: FuncArgs,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<()> {
|
||||
let Some(name) = name.downcast_ref::<PyStr>() else {
|
||||
return Err(vm.new_exception_msg(
|
||||
super::_csv::error(vm),
|
||||
format!("argument 0 must be a string, not '{}'", name.class()),
|
||||
));
|
||||
};
|
||||
let name = name.downcast::<PyStr>().map_err(|obj| {
|
||||
new_csv_error(
|
||||
vm,
|
||||
format!("argument 0 must be a string, not '{}'", obj.class()),
|
||||
)
|
||||
})?;
|
||||
let name: PyUtf8StrRef = name.try_into_utf8(vm)?;
|
||||
let mut g = GLOBAL_HASHMAP.lock();
|
||||
if let Some(_removed) = g.remove(name.as_str()) {
|
||||
return Ok(());
|
||||
}
|
||||
Err(vm.new_exception_msg(super::_csv::error(vm), "unknown dialect".to_string()))
|
||||
Err(new_csv_error(vm, "unknown dialect"))
|
||||
}
|
||||
|
||||
#[pyfunction]
|
||||
@@ -537,7 +534,8 @@ mod _csv {
|
||||
) -> Result<DialectItem, ArgumentError> {
|
||||
match_class!(match obj {
|
||||
s @ PyStr => {
|
||||
Ok(DialectItem::Str(s.as_str().to_string()))
|
||||
let s = s.try_into_utf8(vm).map_err(ArgumentError::Exception)?;
|
||||
Ok(DialectItem::Str(s.as_str().to_owned()))
|
||||
}
|
||||
PyNone => {
|
||||
Err(ArgumentError::InvalidKeywordArgument("dialect".to_string()))
|
||||
@@ -581,10 +579,11 @@ mod _csv {
|
||||
|
||||
if let Some(escapechar) = args.kwargs.swap_remove("escapechar") {
|
||||
res.escapechar = match_class!(match escapechar {
|
||||
s @ PyStr => Some(s.as_str().bytes().exactly_one().map_err(|_| {
|
||||
let msg = r#""escapechar" must be a 1-character string"#;
|
||||
vm.new_type_error(msg.to_owned())
|
||||
})?),
|
||||
s @ PyStr =>
|
||||
Some(s.as_bytes().iter().copied().exactly_one().map_err(|_| {
|
||||
let msg = r#""escapechar" must be a 1-character string"#;
|
||||
vm.new_type_error(msg.to_owned())
|
||||
})?),
|
||||
_ => None,
|
||||
})
|
||||
};
|
||||
@@ -626,10 +625,12 @@ mod _csv {
|
||||
};
|
||||
if let Some(quotechar) = args.kwargs.swap_remove("quotechar") {
|
||||
res.quotechar = match_class!(match quotechar {
|
||||
s @ PyStr => Some(Some(s.as_str().bytes().exactly_one().map_err(|_| {
|
||||
let msg = r#""quotechar" must be a 1-character string"#;
|
||||
vm.new_type_error(msg.to_owned())
|
||||
})?)),
|
||||
s @ PyStr => Some(Some(s.as_bytes().iter().copied().exactly_one().map_err(
|
||||
|_| {
|
||||
let msg = r#""quotechar" must be a 1-character string"#;
|
||||
vm.new_type_error(msg.to_owned())
|
||||
}
|
||||
)?)),
|
||||
PyNone => {
|
||||
if let Some(QuoteStyle::All) = res.quoting {
|
||||
let msg = "quotechar must be set if quoting enabled";
|
||||
|
||||
@@ -5,7 +5,7 @@ pub(crate) use grp::module_def;
|
||||
mod grp {
|
||||
use crate::vm::{
|
||||
PyObjectRef, PyResult, VirtualMachine,
|
||||
builtins::{PyIntRef, PyListRef, PyStrRef},
|
||||
builtins::{PyIntRef, PyListRef, PyUtf8StrRef},
|
||||
convert::{IntoPyException, ToPyObject},
|
||||
exceptions,
|
||||
types::PyStructSequence,
|
||||
@@ -67,7 +67,7 @@ mod grp {
|
||||
}
|
||||
|
||||
#[pyfunction]
|
||||
fn getgrnam(name: PyStrRef, vm: &VirtualMachine) -> PyResult<GroupData> {
|
||||
fn getgrnam(name: PyUtf8StrRef, vm: &VirtualMachine) -> PyResult<GroupData> {
|
||||
let gr_name = name.as_str();
|
||||
if gr_name.contains('\0') {
|
||||
return Err(exceptions::cstring_error(vm));
|
||||
|
||||
@@ -11,7 +11,7 @@ pub mod _hashlib {
|
||||
use crate::vm::{
|
||||
Py, PyObjectRef, PyPayload, PyResult, VirtualMachine,
|
||||
builtins::{
|
||||
PyBaseExceptionRef, PyBytes, PyFrozenSet, PyStr, PyStrRef, PyTypeRef, PyValueError,
|
||||
PyBaseExceptionRef, PyBytes, PyFrozenSet, PyStr, PyTypeRef, PyUtf8StrRef, PyValueError,
|
||||
},
|
||||
class::StaticType,
|
||||
convert::ToPyObject,
|
||||
@@ -79,7 +79,7 @@ pub mod _hashlib {
|
||||
#[allow(unused)]
|
||||
struct NewHashArgs {
|
||||
#[pyarg(positional)]
|
||||
name: PyStrRef,
|
||||
name: PyUtf8StrRef,
|
||||
#[pyarg(any, optional)]
|
||||
data: OptionalArg<ArgBytesLike>,
|
||||
#[pyarg(named, default = true)]
|
||||
@@ -188,7 +188,7 @@ pub mod _hashlib {
|
||||
#[allow(unused)]
|
||||
struct Pbkdf2HmacArgs {
|
||||
#[pyarg(any)]
|
||||
hash_name: PyStrRef,
|
||||
hash_name: PyUtf8StrRef,
|
||||
#[pyarg(any)]
|
||||
password: ArgBytesLike,
|
||||
#[pyarg(any)]
|
||||
@@ -218,18 +218,21 @@ pub mod _hashlib {
|
||||
}
|
||||
|
||||
fn resolve_digestmod(digestmod: &PyObjectRef, vm: &VirtualMachine) -> PyResult<String> {
|
||||
if let Some(name) = digestmod.downcast_ref::<PyStr>() {
|
||||
return Ok(name.as_str().to_lowercase());
|
||||
if let Some(name) = digestmod.downcast_ref::<PyStr>()
|
||||
&& let Some(name_str) = name.to_str()
|
||||
{
|
||||
return Ok(name_str.to_lowercase());
|
||||
}
|
||||
if let Ok(name_obj) = digestmod.get_attr("__name__", vm)
|
||||
&& let Some(name) = name_obj.downcast_ref::<PyStr>()
|
||||
&& let Some(algo) = name.as_str().strip_prefix("openssl_")
|
||||
&& let Some(name_str) = name.to_str()
|
||||
&& let Some(algo) = name_str.strip_prefix("openssl_")
|
||||
{
|
||||
return Ok(algo.to_owned());
|
||||
}
|
||||
Err(vm.new_exception_msg(
|
||||
UnsupportedDigestmodError::static_type().to_owned(),
|
||||
"unsupported digestmod".to_owned(),
|
||||
"unsupported digestmod".into(),
|
||||
))
|
||||
}
|
||||
|
||||
@@ -254,7 +257,7 @@ pub mod _hashlib {
|
||||
fn unsupported_hash(name: &str, vm: &VirtualMachine) -> PyBaseExceptionRef {
|
||||
vm.new_exception_msg(
|
||||
UnsupportedDigestmodError::static_type().to_owned(),
|
||||
format!("unsupported hash type {name}"),
|
||||
format!("unsupported hash type {name}").into(),
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
+20
-25
@@ -6,7 +6,7 @@ mod _json {
|
||||
use super::machinery;
|
||||
use crate::vm::{
|
||||
AsObject, Py, PyObjectRef, PyPayload, PyResult, VirtualMachine,
|
||||
builtins::{PyBaseExceptionRef, PyStrRef, PyType},
|
||||
builtins::{PyBaseExceptionRef, PyStrRef, PyType, PyUtf8StrRef},
|
||||
convert::ToPyResult,
|
||||
function::{IntoFuncArgs, OptionalArg},
|
||||
protocol::PyIterReturn,
|
||||
@@ -91,14 +91,14 @@ mod _json {
|
||||
impl JsonScanner {
|
||||
fn parse(
|
||||
&self,
|
||||
pystr: PyStrRef,
|
||||
pystr: PyUtf8StrRef,
|
||||
char_idx: usize,
|
||||
byte_idx: usize,
|
||||
scan_once: PyObjectRef,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<PyIterReturn> {
|
||||
flame_guard!("JsonScanner::parse");
|
||||
let bytes = pystr.as_str().as_bytes();
|
||||
let bytes = pystr.as_bytes();
|
||||
let wtf8 = pystr.as_wtf8();
|
||||
|
||||
let first_byte = match bytes.get(byte_idx) {
|
||||
@@ -115,7 +115,7 @@ mod _json {
|
||||
// Parse string - pass slice starting after the quote
|
||||
let (wtf8_result, chars_consumed, _bytes_consumed) =
|
||||
machinery::scanstring(&wtf8[byte_idx + 1..], char_idx + 1, self.strict)
|
||||
.map_err(|e| py_decode_error(e, pystr.clone(), vm))?;
|
||||
.map_err(|e| py_decode_error(e, pystr.clone().into_wtf8(), vm))?;
|
||||
let end_char_idx = char_idx + 1 + chars_consumed;
|
||||
return Ok(PyIterReturn::Return(
|
||||
vm.new_tuple((wtf8_result, end_char_idx)).into(),
|
||||
@@ -228,7 +228,7 @@ mod _json {
|
||||
/// Returns (parsed_object, end_char_index, end_byte_index).
|
||||
fn parse_object(
|
||||
&self,
|
||||
pystr: PyStrRef,
|
||||
pystr: PyUtf8StrRef,
|
||||
start_char_idx: usize,
|
||||
start_byte_idx: usize,
|
||||
scan_once: &PyObjectRef,
|
||||
@@ -237,7 +237,7 @@ mod _json {
|
||||
) -> PyResult<(PyObjectRef, usize, usize)> {
|
||||
flame_guard!("JsonScanner::parse_object");
|
||||
|
||||
let bytes = pystr.as_str().as_bytes();
|
||||
let bytes = pystr.as_bytes();
|
||||
let wtf8 = pystr.as_wtf8();
|
||||
let mut char_idx = start_char_idx;
|
||||
let mut byte_idx = start_byte_idx;
|
||||
@@ -275,7 +275,7 @@ mod _json {
|
||||
// Parse key string using scanstring with byte slice
|
||||
let (key_wtf8, chars_consumed, bytes_consumed) =
|
||||
machinery::scanstring(&wtf8[byte_idx..], char_idx, self.strict)
|
||||
.map_err(|e| py_decode_error(e, pystr.clone(), vm))?;
|
||||
.map_err(|e| py_decode_error(e, pystr.clone().into_wtf8(), vm))?;
|
||||
|
||||
char_idx += chars_consumed;
|
||||
byte_idx += bytes_consumed;
|
||||
@@ -389,7 +389,7 @@ mod _json {
|
||||
/// Returns (parsed_array, end_char_index, end_byte_index).
|
||||
fn parse_array(
|
||||
&self,
|
||||
pystr: PyStrRef,
|
||||
pystr: PyUtf8StrRef,
|
||||
start_char_idx: usize,
|
||||
start_byte_idx: usize,
|
||||
scan_once: &PyObjectRef,
|
||||
@@ -398,7 +398,7 @@ mod _json {
|
||||
) -> PyResult<(PyObjectRef, usize, usize)> {
|
||||
flame_guard!("JsonScanner::parse_array");
|
||||
|
||||
let bytes = pystr.as_str().as_bytes();
|
||||
let bytes = pystr.as_bytes();
|
||||
let mut char_idx = start_char_idx;
|
||||
let mut byte_idx = start_byte_idx;
|
||||
|
||||
@@ -507,15 +507,15 @@ mod _json {
|
||||
fn call_scan_once(
|
||||
&self,
|
||||
scan_once: &PyObjectRef,
|
||||
pystr: PyStrRef,
|
||||
pystr: PyUtf8StrRef,
|
||||
char_idx: usize,
|
||||
byte_idx: usize,
|
||||
memo: &mut HashMap<String, PyStrRef>,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<(PyObjectRef, usize, usize)> {
|
||||
let s = pystr.as_str();
|
||||
let bytes = s.as_bytes();
|
||||
let bytes = pystr.as_bytes();
|
||||
let wtf8 = pystr.as_wtf8();
|
||||
let s = pystr.as_str();
|
||||
|
||||
let first_byte = match bytes.get(byte_idx) {
|
||||
Some(&b) => b,
|
||||
@@ -527,7 +527,7 @@ mod _json {
|
||||
// String - pass slice starting after the quote
|
||||
let (wtf8_result, chars_consumed, bytes_consumed) =
|
||||
machinery::scanstring(&wtf8[byte_idx + 1..], char_idx + 1, self.strict)
|
||||
.map_err(|e| py_decode_error(e, pystr.clone(), vm))?;
|
||||
.map_err(|e| py_decode_error(e, pystr.clone().into_wtf8(), vm))?;
|
||||
let py_str = vm.ctx.new_str(wtf8_result.to_string());
|
||||
Ok((
|
||||
py_str.into(),
|
||||
@@ -620,12 +620,12 @@ mod _json {
|
||||
fn make_decode_error(
|
||||
&self,
|
||||
msg: &str,
|
||||
s: PyStrRef,
|
||||
s: PyUtf8StrRef,
|
||||
pos: usize,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyBaseExceptionRef {
|
||||
let err = machinery::DecodeError::new(msg, pos);
|
||||
py_decode_error(err, s, vm)
|
||||
py_decode_error(err, s.into_wtf8(), vm)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -636,6 +636,7 @@ mod _json {
|
||||
return Err(vm.new_value_error("idx cannot be negative"));
|
||||
}
|
||||
let char_idx = char_idx as usize;
|
||||
let pystr = pystr.try_into_utf8(vm)?;
|
||||
let s = pystr.as_str();
|
||||
|
||||
// Calculate byte index from char index (O(char_idx) but only at entry point)
|
||||
@@ -652,14 +653,8 @@ mod _json {
|
||||
}
|
||||
};
|
||||
|
||||
zelf.parse(
|
||||
pystr.clone(),
|
||||
char_idx,
|
||||
byte_idx,
|
||||
zelf.to_owned().into(),
|
||||
vm,
|
||||
)
|
||||
.and_then(|x| x.to_pyresult(vm))
|
||||
zelf.parse(pystr, char_idx, byte_idx, zelf.to_owned().into(), vm)
|
||||
.and_then(|x| x.to_pyresult(vm))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -674,12 +669,12 @@ mod _json {
|
||||
}
|
||||
|
||||
#[pyfunction]
|
||||
fn encode_basestring(s: PyStrRef) -> String {
|
||||
fn encode_basestring(s: PyUtf8StrRef) -> String {
|
||||
encode_string(s.as_str(), false)
|
||||
}
|
||||
|
||||
#[pyfunction]
|
||||
fn encode_basestring_ascii(s: PyStrRef) -> String {
|
||||
fn encode_basestring_ascii(s: PyUtf8StrRef) -> String {
|
||||
encode_string(s.as_str(), true)
|
||||
}
|
||||
|
||||
|
||||
+13
-13
@@ -45,7 +45,7 @@ mod _locale {
|
||||
use core::{ffi::CStr, ptr};
|
||||
use rustpython_vm::{
|
||||
PyObjectRef, PyResult, VirtualMachine,
|
||||
builtins::{PyDictRef, PyIntRef, PyListRef, PyStrRef, PyTypeRef},
|
||||
builtins::{PyDictRef, PyIntRef, PyListRef, PyTypeRef, PyUtf8StrRef},
|
||||
convert::ToPyException,
|
||||
function::OptionalArg,
|
||||
};
|
||||
@@ -149,14 +149,14 @@ mod _locale {
|
||||
}
|
||||
|
||||
#[pyfunction]
|
||||
fn strcoll(string1: PyStrRef, string2: PyStrRef, vm: &VirtualMachine) -> PyResult {
|
||||
fn strcoll(string1: PyUtf8StrRef, string2: PyUtf8StrRef, vm: &VirtualMachine) -> PyResult {
|
||||
let cstr1 = CString::new(string1.as_str()).map_err(|e| e.to_pyexception(vm))?;
|
||||
let cstr2 = CString::new(string2.as_str()).map_err(|e| e.to_pyexception(vm))?;
|
||||
Ok(vm.new_pyobj(unsafe { libc::strcoll(cstr1.as_ptr(), cstr2.as_ptr()) }))
|
||||
}
|
||||
|
||||
#[pyfunction]
|
||||
fn strxfrm(string: PyStrRef, vm: &VirtualMachine) -> PyResult {
|
||||
fn strxfrm(string: PyUtf8StrRef, vm: &VirtualMachine) -> PyResult {
|
||||
// https://github.com/python/cpython/blob/eaae563b6878aa050b4ad406b67728b6b066220e/Modules/_localemodule.c#L390-L442
|
||||
let n1 = string.byte_len() + 1;
|
||||
let mut buff = vec![0u8; n1];
|
||||
@@ -227,7 +227,7 @@ mod _locale {
|
||||
#[pyarg(any)]
|
||||
category: i32,
|
||||
#[pyarg(any, optional)]
|
||||
locale: OptionalArg<Option<PyStrRef>>,
|
||||
locale: OptionalArg<Option<PyUtf8StrRef>>,
|
||||
}
|
||||
|
||||
/// Maximum code page encoding name length on Windows
|
||||
@@ -262,34 +262,34 @@ mod _locale {
|
||||
fn setlocale(args: LocaleArgs, vm: &VirtualMachine) -> PyResult {
|
||||
let error = error(vm);
|
||||
if cfg!(windows) && (args.category < LC_ALL || args.category > LC_TIME) {
|
||||
return Err(vm.new_exception_msg(error, String::from("unsupported locale setting")));
|
||||
return Err(vm.new_exception_msg(error, "unsupported locale setting".into()));
|
||||
}
|
||||
unsafe {
|
||||
let result = match args.locale.flatten() {
|
||||
None => libc::setlocale(args.category, ptr::null()),
|
||||
Some(locale) => {
|
||||
let locale_str = locale.as_str();
|
||||
// On Windows, validate encoding name length
|
||||
#[cfg(windows)]
|
||||
{
|
||||
let valid = if args.category == LC_ALL {
|
||||
check_locale_name_all(locale.as_str())
|
||||
check_locale_name_all(locale_str)
|
||||
} else {
|
||||
check_locale_name(locale.as_str())
|
||||
check_locale_name(locale_str)
|
||||
};
|
||||
if !valid {
|
||||
return Err(vm.new_exception_msg(
|
||||
error,
|
||||
String::from("unsupported locale setting"),
|
||||
));
|
||||
return Err(
|
||||
vm.new_exception_msg(error, "unsupported locale setting".into())
|
||||
);
|
||||
}
|
||||
}
|
||||
let c_locale: CString =
|
||||
CString::new(locale.as_str()).map_err(|e| e.to_pyexception(vm))?;
|
||||
CString::new(locale_str).map_err(|e| e.to_pyexception(vm))?;
|
||||
libc::setlocale(args.category, c_locale.as_ptr())
|
||||
}
|
||||
};
|
||||
if result.is_null() {
|
||||
return Err(vm.new_exception_msg(error, String::from("unsupported locale setting")));
|
||||
return Err(vm.new_exception_msg(error, "unsupported locale setting".into()));
|
||||
}
|
||||
pystr_from_raw_cstr(vm, result)
|
||||
}
|
||||
|
||||
@@ -82,7 +82,8 @@ mod _lzma {
|
||||
}
|
||||
|
||||
fn new_lzma_error(message: impl Into<String>, vm: &VirtualMachine) -> PyBaseExceptionRef {
|
||||
vm.new_exception_msg(vm.class("lzma", "LZMAError"), message.into())
|
||||
let msg: String = message.into();
|
||||
vm.new_exception_msg(vm.class("lzma", "LZMAError"), msg.into())
|
||||
}
|
||||
|
||||
#[pyfunction]
|
||||
|
||||
@@ -224,7 +224,7 @@ mod _multiprocessing {
|
||||
if !ismine!(self) {
|
||||
return Err(vm.new_exception_msg(
|
||||
vm.ctx.exceptions.assertion_error.to_owned(),
|
||||
"attempt to release recursive lock not owned by thread".to_owned(),
|
||||
"attempt to release recursive lock not owned by thread".into(),
|
||||
));
|
||||
}
|
||||
if self.count.load(Ordering::Acquire) > 1 {
|
||||
@@ -796,7 +796,7 @@ mod _multiprocessing {
|
||||
if !ismine!(self) {
|
||||
return Err(vm.new_exception_msg(
|
||||
vm.ctx.exceptions.assertion_error.to_owned(),
|
||||
"attempt to release recursive lock not owned by thread".to_owned(),
|
||||
"attempt to release recursive lock not owned by thread".into(),
|
||||
));
|
||||
}
|
||||
// if (self->count > 1) { --self->count; Py_RETURN_NONE; }
|
||||
|
||||
@@ -309,7 +309,7 @@ mod _overlapped {
|
||||
let mut addr: SOCKADDR_IN = unsafe { core::mem::zeroed() };
|
||||
addr.sin_family = AF_INET;
|
||||
|
||||
let host_wide: Vec<u16> = host.as_str().encode_utf16().chain([0]).collect();
|
||||
let host_wide: Vec<u16> = host.as_wtf8().encode_wide().chain([0]).collect();
|
||||
let mut addr_len = core::mem::size_of::<SOCKADDR_IN>() as i32;
|
||||
|
||||
let ret = unsafe {
|
||||
@@ -348,7 +348,7 @@ mod _overlapped {
|
||||
let mut addr: SOCKADDR_IN6 = unsafe { core::mem::zeroed() };
|
||||
addr.sin6_family = AF_INET6;
|
||||
|
||||
let host_wide: Vec<u16> = host.as_str().encode_utf16().chain([0]).collect();
|
||||
let host_wide: Vec<u16> = host.as_wtf8().encode_wide().chain([0]).collect();
|
||||
let mut addr_len = core::mem::size_of::<SOCKADDR_IN6>() as i32;
|
||||
|
||||
let ret = unsafe {
|
||||
|
||||
@@ -8,13 +8,15 @@ mod _posixshmem {
|
||||
|
||||
use crate::{
|
||||
common::os::errno_io_error,
|
||||
vm::{FromArgs, PyResult, VirtualMachine, builtins::PyStrRef, convert::IntoPyException},
|
||||
vm::{
|
||||
FromArgs, PyResult, VirtualMachine, builtins::PyUtf8StrRef, convert::IntoPyException,
|
||||
},
|
||||
};
|
||||
|
||||
#[derive(FromArgs)]
|
||||
struct ShmOpenArgs {
|
||||
#[pyarg(any)]
|
||||
name: PyStrRef,
|
||||
name: PyUtf8StrRef,
|
||||
#[pyarg(any)]
|
||||
flags: libc::c_int,
|
||||
#[pyarg(any, default = 0o600)]
|
||||
@@ -37,7 +39,7 @@ mod _posixshmem {
|
||||
}
|
||||
|
||||
#[pyfunction]
|
||||
fn shm_unlink(name: PyStrRef, vm: &VirtualMachine) -> PyResult<()> {
|
||||
fn shm_unlink(name: PyUtf8StrRef, vm: &VirtualMachine) -> PyResult<()> {
|
||||
let name = CString::new(name.as_str()).map_err(|e| e.into_pyexception(vm))?;
|
||||
// SAFETY: `name` is a valid NUL-terminated string and `shm_unlink` only reads it.
|
||||
let ret = unsafe { libc::shm_unlink(name.as_ptr()) };
|
||||
|
||||
@@ -41,7 +41,7 @@ macro_rules! create_bool_property {
|
||||
mod _pyexpat {
|
||||
use crate::vm::{
|
||||
Context, Py, PyObjectRef, PyPayload, PyRef, PyResult, TryFromObject, VirtualMachine,
|
||||
builtins::{PyBytesRef, PyException, PyModule, PyStr, PyStrRef, PyType},
|
||||
builtins::{PyBytesRef, PyException, PyModule, PyStr, PyStrRef, PyType, PyUtf8StrRef},
|
||||
extend_module,
|
||||
function::{ArgBytesLike, Either, IntoFuncArgs, OptionalArg},
|
||||
types::Constructor,
|
||||
@@ -390,7 +390,7 @@ mod _pyexpat {
|
||||
#[pyarg(any, optional)]
|
||||
encoding: Option<PyStrRef>,
|
||||
#[pyarg(any, optional)]
|
||||
namespace_separator: Option<PyStrRef>,
|
||||
namespace_separator: Option<PyUtf8StrRef>,
|
||||
#[pyarg(any, optional)]
|
||||
intern: Option<PyObjectRef>,
|
||||
}
|
||||
@@ -403,11 +403,9 @@ mod _pyexpat {
|
||||
// Validate namespace_separator: must be at most one character
|
||||
let ns_sep = match args.namespace_separator {
|
||||
Some(ref s) => {
|
||||
let chars: Vec<char> = s.as_str().chars().collect();
|
||||
if chars.len() > 1 {
|
||||
if s.as_str().chars().count() > 1 {
|
||||
return Err(vm.new_value_error(
|
||||
"namespace_separator must be at most one character, omitted, or None"
|
||||
.to_owned(),
|
||||
"namespace_separator must be at most one character, omitted, or None",
|
||||
));
|
||||
}
|
||||
Some(s.as_str().to_owned())
|
||||
|
||||
@@ -19,6 +19,7 @@ pub(crate) mod _struct {
|
||||
types::{Constructor, IterNext, Iterable, Representable, SelfIter},
|
||||
};
|
||||
use crossbeam_utils::atomic::AtomicCell;
|
||||
use rustpython_common::wtf8::{Wtf8Buf, wtf8_concat};
|
||||
|
||||
#[derive(Traverse)]
|
||||
struct IntoStructFormatBytes(PyStrRef);
|
||||
@@ -305,8 +306,8 @@ pub(crate) mod _struct {
|
||||
|
||||
impl Representable for PyStruct {
|
||||
#[inline]
|
||||
fn repr_str(zelf: &Py<Self>, _vm: &VirtualMachine) -> PyResult<String> {
|
||||
Ok(format!("Struct('{}')", zelf.format.as_str()))
|
||||
fn repr_wtf8(zelf: &Py<Self>, _vm: &VirtualMachine) -> PyResult<Wtf8Buf> {
|
||||
Ok(wtf8_concat!("Struct('", zelf.format.as_wtf8(), "')"))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+19
-14
@@ -12,6 +12,7 @@ mod _socket {
|
||||
AsObject, Py, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine,
|
||||
builtins::{
|
||||
PyBaseExceptionRef, PyListRef, PyModule, PyOSError, PyStrRef, PyTupleRef, PyTypeRef,
|
||||
PyUtf8StrRef,
|
||||
},
|
||||
common::os::ErrorExt,
|
||||
convert::{IntoPyException, ToPyObject, TryFromBorrowedObject, TryFromObject},
|
||||
@@ -1224,6 +1225,7 @@ mod _socket {
|
||||
obj.class().name()
|
||||
))
|
||||
})?;
|
||||
let interface = interface.try_into_utf8(vm).map_err(IoOrPyException::from)?;
|
||||
let ifname = interface.as_str();
|
||||
|
||||
// Get interface index
|
||||
@@ -1291,6 +1293,8 @@ mod _socket {
|
||||
))
|
||||
})?;
|
||||
|
||||
let alg_type = alg_type.try_into_utf8(vm).map_err(IoOrPyException::from)?;
|
||||
let alg_name = alg_name.try_into_utf8(vm).map_err(IoOrPyException::from)?;
|
||||
let type_str = alg_type.as_str();
|
||||
let name_str = alg_name.as_str();
|
||||
|
||||
@@ -2134,7 +2138,7 @@ mod _socket {
|
||||
&& let Ok(addr) = sock.local_addr()
|
||||
&& let Ok(repr) = get_addr_tuple(&addr, vm).repr(vm)
|
||||
{
|
||||
format!(", laddr={}", repr.as_str())
|
||||
format!(", laddr={}", repr.as_wtf8())
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
@@ -2484,7 +2488,7 @@ mod _socket {
|
||||
}
|
||||
|
||||
struct Address {
|
||||
host: PyStrRef,
|
||||
host: PyUtf8StrRef,
|
||||
port: u16,
|
||||
}
|
||||
|
||||
@@ -2509,6 +2513,7 @@ mod _socket {
|
||||
impl Address {
|
||||
fn from_tuple(tuple: &[PyObjectRef], vm: &VirtualMachine) -> PyResult<Self> {
|
||||
let host = PyStrRef::try_from_object(vm, tuple[0].clone())?;
|
||||
let host = host.try_into_utf8(vm)?;
|
||||
let port = i32::try_from_borrowed_object(vm, &tuple[1])?;
|
||||
let port = port
|
||||
.to_u16()
|
||||
@@ -2625,12 +2630,12 @@ mod _socket {
|
||||
|
||||
#[cfg(all(unix, not(target_os = "redox")))]
|
||||
#[pyfunction]
|
||||
fn sethostname(hostname: PyStrRef) -> nix::Result<()> {
|
||||
fn sethostname(hostname: PyUtf8StrRef) -> nix::Result<()> {
|
||||
nix::unistd::sethostname(hostname.as_str())
|
||||
}
|
||||
|
||||
#[pyfunction]
|
||||
fn inet_aton(ip_string: PyStrRef, vm: &VirtualMachine) -> PyResult<Vec<u8>> {
|
||||
fn inet_aton(ip_string: PyUtf8StrRef, vm: &VirtualMachine) -> PyResult<Vec<u8>> {
|
||||
ip_string
|
||||
.as_str()
|
||||
.parse::<Ipv4Addr>()
|
||||
@@ -2904,7 +2909,7 @@ mod _socket {
|
||||
|
||||
#[pyfunction]
|
||||
fn gethostbyaddr(
|
||||
addr: PyStrRef,
|
||||
addr: PyUtf8StrRef,
|
||||
vm: &VirtualMachine,
|
||||
) -> Result<(String, PyListRef, PyListRef), IoOrPyException> {
|
||||
let addr = get_addr(vm, addr, c::AF_UNSPEC)?;
|
||||
@@ -2919,7 +2924,7 @@ mod _socket {
|
||||
}
|
||||
|
||||
#[pyfunction]
|
||||
fn gethostbyname(name: PyStrRef, vm: &VirtualMachine) -> Result<String, IoOrPyException> {
|
||||
fn gethostbyname(name: PyUtf8StrRef, vm: &VirtualMachine) -> Result<String, IoOrPyException> {
|
||||
let addr = get_addr(vm, name, c::AF_INET)?;
|
||||
match addr {
|
||||
SocketAddr::V4(ip) => Ok(ip.ip().to_string()),
|
||||
@@ -2929,7 +2934,7 @@ mod _socket {
|
||||
|
||||
#[pyfunction]
|
||||
fn gethostbyname_ex(
|
||||
name: PyStrRef,
|
||||
name: PyUtf8StrRef,
|
||||
vm: &VirtualMachine,
|
||||
) -> Result<(String, PyListRef, PyListRef), IoOrPyException> {
|
||||
let addr = get_addr(vm, name, c::AF_INET)?;
|
||||
@@ -2944,7 +2949,7 @@ mod _socket {
|
||||
}
|
||||
|
||||
#[pyfunction]
|
||||
fn inet_pton(af_inet: i32, ip_string: PyStrRef, vm: &VirtualMachine) -> PyResult<Vec<u8>> {
|
||||
fn inet_pton(af_inet: i32, ip_string: PyUtf8StrRef, vm: &VirtualMachine) -> PyResult<Vec<u8>> {
|
||||
static ERROR_MSG: &str = "illegal IP address string passed to inet_pton";
|
||||
let ip_addr = match af_inet {
|
||||
c::AF_INET => ip_string
|
||||
@@ -3015,10 +3020,10 @@ mod _socket {
|
||||
protocol: 0,
|
||||
};
|
||||
let service = addr.port.to_string();
|
||||
let mut res =
|
||||
dns_lookup::getaddrinfo(Some(addr.host.as_str()), Some(&service), Some(hints))
|
||||
.map_err(|e| convert_socket_error(vm, e, SocketError::GaiError))?
|
||||
.filter_map(Result::ok);
|
||||
let host_str = addr.host.as_str();
|
||||
let mut res = dns_lookup::getaddrinfo(Some(host_str), Some(&service), Some(hints))
|
||||
.map_err(|e| convert_socket_error(vm, e, SocketError::GaiError))?
|
||||
.filter_map(Result::ok);
|
||||
let mut ainfo = res.next().unwrap();
|
||||
if res.next().is_some() {
|
||||
return Err(vm
|
||||
@@ -3183,7 +3188,7 @@ mod _socket {
|
||||
|
||||
fn get_addr(
|
||||
vm: &VirtualMachine,
|
||||
pyname: PyStrRef,
|
||||
pyname: PyUtf8StrRef,
|
||||
af: i32,
|
||||
) -> Result<SocketAddr, IoOrPyException> {
|
||||
let name = pyname.as_str();
|
||||
@@ -3236,7 +3241,7 @@ mod _socket {
|
||||
let name = vm
|
||||
.state
|
||||
.codec_registry
|
||||
.encode_text(pyname, "idna", None, vm)?;
|
||||
.encode_text(pyname.into_wtf8(), "idna", None, vm)?;
|
||||
let name = core::str::from_utf8(name.as_bytes())
|
||||
.map_err(|_| vm.new_runtime_error("idna output is not utf8"))?;
|
||||
let mut res = dns_lookup::getaddrinfo(Some(name), None, Some(hints))
|
||||
|
||||
+54
-48
@@ -40,7 +40,10 @@ mod _ssl {
|
||||
vm::{
|
||||
AsObject, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, TryFromObject,
|
||||
VirtualMachine,
|
||||
builtins::{PyBaseExceptionRef, PyBytesRef, PyListRef, PyStrRef, PyType, PyTypeRef},
|
||||
builtins::{
|
||||
PyBaseExceptionRef, PyBytesRef, PyListRef, PyStrRef, PyType, PyTypeRef,
|
||||
PyUtf8StrRef,
|
||||
},
|
||||
convert::IntoPyException,
|
||||
function::{
|
||||
ArgBytesLike, ArgMemoryBuffer, Either, FuncArgs, OptionalArg, PyComparisonValue,
|
||||
@@ -57,10 +60,16 @@ mod _ssl {
|
||||
};
|
||||
use alloc::sync::Arc;
|
||||
use core::{
|
||||
hash::{Hash, Hasher},
|
||||
sync::atomic::{AtomicUsize, Ordering},
|
||||
time::Duration,
|
||||
};
|
||||
use std::{collections::HashMap, time::SystemTime};
|
||||
use rustls::crypto::aws_lc_rs::ALL_CIPHER_SUITES;
|
||||
use std::{
|
||||
collections::{HashMap, hash_map::DefaultHasher},
|
||||
io::BufRead,
|
||||
time::SystemTime,
|
||||
};
|
||||
|
||||
// Rustls imports
|
||||
use parking_lot::{Mutex as ParkingMutex, RwLock as ParkingRwLock};
|
||||
@@ -597,8 +606,6 @@ mod _ssl {
|
||||
/// - "ALL" or "DEFAULT" → all available
|
||||
/// - "!MD5" → exclusion (ignored, rustls doesn't support weak ciphers anyway)
|
||||
fn parse_cipher_string(cipher_str: &str) -> Result<Vec<rustls::SupportedCipherSuite>, String> {
|
||||
use rustls::crypto::aws_lc_rs::ALL_CIPHER_SUITES;
|
||||
|
||||
if cipher_str.is_empty() {
|
||||
return Err("No cipher can be selected".to_string());
|
||||
}
|
||||
@@ -801,7 +808,7 @@ mod _ssl {
|
||||
sock: PyObjectRef,
|
||||
server_side: bool,
|
||||
#[pyarg(positional, optional)]
|
||||
server_hostname: OptionalArg<Option<PyStrRef>>,
|
||||
server_hostname: OptionalArg<Option<PyUtf8StrRef>>,
|
||||
#[pyarg(named, optional)]
|
||||
owner: OptionalArg<PyObjectRef>,
|
||||
#[pyarg(named, optional)]
|
||||
@@ -815,7 +822,7 @@ mod _ssl {
|
||||
#[pyarg(named, optional)]
|
||||
server_side: OptionalArg<bool>,
|
||||
#[pyarg(named, optional)]
|
||||
server_hostname: OptionalArg<Option<PyStrRef>>,
|
||||
server_hostname: OptionalArg<Option<PyUtf8StrRef>>,
|
||||
#[pyarg(named, optional)]
|
||||
owner: OptionalArg<PyObjectRef>,
|
||||
#[pyarg(named, optional)]
|
||||
@@ -1080,7 +1087,7 @@ mod _ssl {
|
||||
|
||||
// Convert callable result to string
|
||||
let password_from_callable = if let Ok(pwd_str) =
|
||||
PyStrRef::try_from_object(vm, pwd_result.clone())
|
||||
PyUtf8StrRef::try_from_object(vm, pwd_result.clone())
|
||||
{
|
||||
pwd_str.as_str().to_owned()
|
||||
} else if let Ok(pwd_bytes_like) = ArgBytesLike::try_from_object(vm, pwd_result) {
|
||||
@@ -1501,7 +1508,7 @@ mod _ssl {
|
||||
}
|
||||
|
||||
#[pymethod]
|
||||
fn set_ciphers(&self, ciphers: PyStrRef, vm: &VirtualMachine) -> PyResult<()> {
|
||||
fn set_ciphers(&self, ciphers: PyUtf8StrRef, vm: &VirtualMachine) -> PyResult<()> {
|
||||
let cipher_str = ciphers.as_str();
|
||||
|
||||
// Parse cipher string and store selected ciphers
|
||||
@@ -1520,7 +1527,6 @@ mod _ssl {
|
||||
fn get_ciphers(&self, vm: &VirtualMachine) -> PyResult<PyListRef> {
|
||||
// Dynamically generate cipher list from rustls ALL_CIPHER_SUITES
|
||||
// This automatically includes all cipher suites supported by the current rustls version
|
||||
use rustls::crypto::aws_lc_rs::ALL_CIPHER_SUITES;
|
||||
|
||||
let cipher_list = ALL_CIPHER_SUITES
|
||||
.iter()
|
||||
@@ -1741,7 +1747,7 @@ mod _ssl {
|
||||
}
|
||||
|
||||
// Validate filepath is str or bytes
|
||||
let path_str = if let Ok(s) = PyStrRef::try_from_object(vm, filepath.clone()) {
|
||||
let path_str = if let Ok(s) = PyUtf8StrRef::try_from_object(vm, filepath.clone()) {
|
||||
s.as_str().to_owned()
|
||||
} else if let Ok(b) = ArgBytesLike::try_from_object(vm, filepath) {
|
||||
String::from_utf8(b.borrow_buf().to_vec())
|
||||
@@ -1796,7 +1802,7 @@ mod _ssl {
|
||||
}
|
||||
|
||||
// Validate name is str or bytes
|
||||
let curve_name = if let Ok(s) = PyStrRef::try_from_object(vm, name.clone()) {
|
||||
let curve_name = if let Ok(s) = PyUtf8StrRef::try_from_object(vm, name.clone()) {
|
||||
s.as_str().to_owned()
|
||||
} else if let Ok(b) = ArgBytesLike::try_from_object(vm, name) {
|
||||
String::from_utf8(b.borrow_buf().to_vec())
|
||||
@@ -1995,7 +2001,7 @@ mod _ssl {
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<String> {
|
||||
match arg {
|
||||
Either::A(s) => Ok(s.as_str().to_owned()),
|
||||
Either::A(s) => Ok(s.clone().try_into_utf8(vm)?.as_str().to_owned()),
|
||||
Either::B(b) => String::from_utf8(b.borrow_buf().to_vec())
|
||||
.map_err(|_| vm.new_value_error("path contains invalid UTF-8".to_owned())),
|
||||
}
|
||||
@@ -2013,7 +2019,7 @@ mod _ssl {
|
||||
match password {
|
||||
OptionalArg::Present(p) => {
|
||||
// Try string first
|
||||
if let Ok(pwd_str) = PyStrRef::try_from_object(vm, p.clone()) {
|
||||
if let Ok(pwd_str) = PyUtf8StrRef::try_from_object(vm, p.clone()) {
|
||||
Ok((Some(pwd_str.as_str().to_owned()), None))
|
||||
}
|
||||
// Try bytes-like
|
||||
@@ -2171,10 +2177,10 @@ mod _ssl {
|
||||
fn parse_cadata_arg(
|
||||
&self,
|
||||
arg: &Either<PyStrRef, ArgBytesLike>,
|
||||
_vm: &VirtualMachine,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<Vec<u8>> {
|
||||
match arg {
|
||||
Either::A(s) => Ok(s.as_str().as_bytes().to_vec()),
|
||||
Either::A(s) => Ok(s.clone().try_into_utf8(vm)?.as_str().as_bytes().to_vec()),
|
||||
Either::B(b) => Ok(b.borrow_buf().to_vec()),
|
||||
}
|
||||
}
|
||||
@@ -3577,7 +3583,7 @@ mod _ssl {
|
||||
Some(conn) => conn,
|
||||
None => return Err(create_ssl_eof_error(vm).upcast()),
|
||||
};
|
||||
use std::io::BufRead;
|
||||
|
||||
let mut reader = conn.reader();
|
||||
reader.fill_buf().map(|buf| buf.len()).unwrap_or(0)
|
||||
};
|
||||
@@ -3606,7 +3612,7 @@ mod _ssl {
|
||||
Some(conn) => conn,
|
||||
None => return Err(create_ssl_zero_return_error(vm).upcast()),
|
||||
};
|
||||
use std::io::BufRead;
|
||||
|
||||
let mut reader = conn.reader();
|
||||
reader.fill_buf().map(|buf| buf.len()).unwrap_or(0)
|
||||
};
|
||||
@@ -3674,7 +3680,6 @@ mod _ssl {
|
||||
// Use rustls Reader's fill_buf() to check buffered plaintext
|
||||
// fill_buf() returns a reference to buffered data without consuming it
|
||||
// This matches OpenSSL's SSL_pending() behavior
|
||||
use std::io::BufRead;
|
||||
let mut reader = conn.reader();
|
||||
match reader.fill_buf() {
|
||||
Ok(buf) => Ok(buf.len()),
|
||||
@@ -3894,7 +3899,7 @@ mod _ssl {
|
||||
#[pygetset(setter)]
|
||||
fn set_server_hostname(
|
||||
&self,
|
||||
value: Option<PyStrRef>,
|
||||
value: Option<PyUtf8StrRef>,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<()> {
|
||||
// Check if handshake is already done
|
||||
@@ -3905,11 +3910,14 @@ mod _ssl {
|
||||
}
|
||||
|
||||
// Validate hostname
|
||||
if let Some(hostname_str) = &value {
|
||||
validate_hostname(hostname_str.as_str(), vm)?;
|
||||
}
|
||||
let hostname_string = value
|
||||
.map(|s| {
|
||||
validate_hostname(s.as_str(), vm)?;
|
||||
Ok::<String, _>(s.as_str().to_owned())
|
||||
})
|
||||
.transpose()?;
|
||||
|
||||
*self.server_hostname.write() = value.map(|s| s.as_str().to_string());
|
||||
*self.server_hostname.write() = hostname_string;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -4183,7 +4191,7 @@ mod _ssl {
|
||||
// Timeout reached - raise TimeoutError
|
||||
return Err(vm.new_exception_msg(
|
||||
vm.ctx.exceptions.timeout_error.to_owned(),
|
||||
"The read operation timed out".to_owned(),
|
||||
"The read operation timed out".into(),
|
||||
));
|
||||
}
|
||||
Some(dl - now)
|
||||
@@ -4203,7 +4211,7 @@ mod _ssl {
|
||||
// Raise TimeoutError
|
||||
return Err(vm.new_exception_msg(
|
||||
vm.ctx.exceptions.timeout_error.to_owned(),
|
||||
"The read operation timed out".to_owned(),
|
||||
"The read operation timed out".into(),
|
||||
));
|
||||
}
|
||||
|
||||
@@ -4529,7 +4537,7 @@ mod _ssl {
|
||||
#[pymethod]
|
||||
fn get_channel_binding(
|
||||
&self,
|
||||
cb_type: OptionalArg<PyStrRef>,
|
||||
cb_type: OptionalArg<PyUtf8StrRef>,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<Option<PyBytesRef>> {
|
||||
let cb_type_str = cb_type.as_ref().map_or("tls-unique", |s| s.as_str());
|
||||
@@ -4628,7 +4636,7 @@ mod _ssl {
|
||||
if !is_contiguous {
|
||||
return Err(vm.new_exception_msg(
|
||||
vm.ctx.exceptions.buffer_error.to_owned(),
|
||||
"non-contiguous buffer is not supported".to_owned(),
|
||||
"non-contiguous buffer is not supported".into(),
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -4724,8 +4732,6 @@ mod _ssl {
|
||||
#[pygetset]
|
||||
fn id(&self, vm: &VirtualMachine) -> PyBytesRef {
|
||||
// Return session ID (hash of session data for uniqueness)
|
||||
use core::hash::{Hash, Hasher};
|
||||
use std::collections::hash_map::DefaultHasher;
|
||||
|
||||
let mut hasher = DefaultHasher::new();
|
||||
self.session_data.hash(&mut hasher);
|
||||
@@ -4755,7 +4761,7 @@ mod _ssl {
|
||||
|
||||
#[derive(FromArgs)]
|
||||
struct Txt2ObjArgs {
|
||||
txt: PyStrRef,
|
||||
txt: PyUtf8StrRef,
|
||||
#[pyarg(named, optional)]
|
||||
name: OptionalArg<bool>,
|
||||
}
|
||||
@@ -4901,13 +4907,13 @@ mod _ssl {
|
||||
/// This is a simplified wrapper around cert_der_to_dict_helper that handles
|
||||
/// file reading and PEM/DER auto-detection. Used by test suite.
|
||||
#[pyfunction]
|
||||
fn _test_decode_cert(path: PyStrRef, vm: &VirtualMachine) -> PyResult<PyObjectRef> {
|
||||
fn _test_decode_cert(path: PyUtf8StrRef, vm: &VirtualMachine) -> PyResult<PyObjectRef> {
|
||||
// Read certificate file
|
||||
let cert_data = std::fs::read(path.as_str()).map_err(|e| {
|
||||
let path_str = path.as_str();
|
||||
let cert_data = std::fs::read(path_str).map_err(|e| {
|
||||
vm.new_os_error(format!(
|
||||
"Failed to read certificate file {}: {}",
|
||||
path.as_str(),
|
||||
e
|
||||
path_str, e
|
||||
))
|
||||
})?;
|
||||
|
||||
@@ -4944,11 +4950,9 @@ mod _ssl {
|
||||
}
|
||||
|
||||
#[pyfunction]
|
||||
fn PEM_cert_to_DER_cert(pem_cert: PyStrRef, vm: &VirtualMachine) -> PyResult<PyBytesRef> {
|
||||
let pem_str = pem_cert.as_str();
|
||||
|
||||
fn PEM_cert_to_DER_cert(pem_cert: PyUtf8StrRef, vm: &VirtualMachine) -> PyResult<PyBytesRef> {
|
||||
// Parse PEM format
|
||||
let mut cursor = std::io::Cursor::new(pem_str.as_bytes());
|
||||
let mut cursor = std::io::Cursor::new(pem_cert.as_bytes());
|
||||
let mut certs = rustls_pemfile::certs(&mut cursor);
|
||||
|
||||
if let Some(Ok(cert)) = certs.next() {
|
||||
@@ -4961,22 +4965,27 @@ mod _ssl {
|
||||
// Windows-specific certificate store enumeration functions
|
||||
#[cfg(windows)]
|
||||
#[pyfunction]
|
||||
fn enum_certificates(store_name: PyStrRef, vm: &VirtualMachine) -> PyResult<Vec<PyObjectRef>> {
|
||||
fn enum_certificates(
|
||||
store_name: PyUtf8StrRef,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<Vec<PyObjectRef>> {
|
||||
use schannel::{RawPointer, cert_context::ValidUses, cert_store::CertStore};
|
||||
use windows_sys::Win32::Security::Cryptography;
|
||||
|
||||
let store_name_str = store_name.as_str();
|
||||
|
||||
// Try both Current User and Local Machine stores
|
||||
let open_fns = [CertStore::open_current_user, CertStore::open_local_machine];
|
||||
let stores = open_fns
|
||||
.iter()
|
||||
.filter_map(|open| open(store_name.as_str()).ok())
|
||||
.filter_map(|open| open(store_name_str).ok())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
// If no stores could be opened, raise OSError
|
||||
if stores.is_empty() {
|
||||
return Err(vm.new_os_error(format!(
|
||||
"failed to open certificate store {:?}",
|
||||
store_name.as_str()
|
||||
store_name_str
|
||||
)));
|
||||
}
|
||||
|
||||
@@ -5011,14 +5020,14 @@ mod _ssl {
|
||||
|
||||
#[cfg(windows)]
|
||||
#[pyfunction]
|
||||
fn enum_crls(store_name: PyStrRef, vm: &VirtualMachine) -> PyResult<Vec<PyObjectRef>> {
|
||||
fn enum_crls(store_name: PyUtf8StrRef, vm: &VirtualMachine) -> PyResult<Vec<PyObjectRef>> {
|
||||
use windows_sys::Win32::Security::Cryptography::{
|
||||
CRL_CONTEXT, CertCloseStore, CertEnumCRLsInStore, CertOpenSystemStoreW,
|
||||
X509_ASN_ENCODING,
|
||||
};
|
||||
|
||||
let store_name_wide: Vec<u16> = store_name
|
||||
.as_str()
|
||||
let store_name_str = store_name.as_str();
|
||||
let store_name_wide: Vec<u16> = store_name_str
|
||||
.encode_utf16()
|
||||
.chain(core::iter::once(0))
|
||||
.collect();
|
||||
@@ -5029,7 +5038,7 @@ mod _ssl {
|
||||
if store.is_null() {
|
||||
return Err(vm.new_os_error(format!(
|
||||
"failed to open certificate store {:?}",
|
||||
store_name.as_str()
|
||||
store_name_str
|
||||
)));
|
||||
}
|
||||
|
||||
@@ -5135,9 +5144,6 @@ mod _ssl {
|
||||
// Implement Hashable trait for PySSLCertificate
|
||||
impl Hashable for PySSLCertificate {
|
||||
fn hash(zelf: &Py<Self>, _vm: &VirtualMachine) -> PyResult<PyHash> {
|
||||
use core::hash::{Hash, Hasher};
|
||||
use std::collections::hash_map::DefaultHasher;
|
||||
|
||||
let mut hasher = DefaultHasher::new();
|
||||
zelf.der_bytes.hash(&mut hasher);
|
||||
Ok(hasher.finish() as PyHash)
|
||||
|
||||
@@ -20,14 +20,12 @@ enum NormalizeForm {
|
||||
impl<'a> TryFromBorrowedObject<'a> for NormalizeForm {
|
||||
fn try_from_borrowed_object(vm: &VirtualMachine, obj: &'a PyObject) -> PyResult<Self> {
|
||||
obj.try_value_with(
|
||||
|form: &PyStr| {
|
||||
Ok(match form.as_str() {
|
||||
"NFC" => Self::Nfc,
|
||||
"NFKC" => Self::Nfkc,
|
||||
"NFD" => Self::Nfd,
|
||||
"NFKD" => Self::Nfkd,
|
||||
_ => return Err(vm.new_value_error("invalid normalization form")),
|
||||
})
|
||||
|form: &PyStr| match form.as_bytes() {
|
||||
b"NFC" => Ok(Self::Nfc),
|
||||
b"NFKC" => Ok(Self::Nfkc),
|
||||
b"NFD" => Ok(Self::Nfd),
|
||||
b"NFKD" => Ok(Self::Nfkd),
|
||||
_ => Err(vm.new_value_error("invalid normalization form")),
|
||||
},
|
||||
vm,
|
||||
)
|
||||
@@ -36,6 +34,7 @@ impl<'a> TryFromBorrowedObject<'a> for NormalizeForm {
|
||||
|
||||
#[pymodule]
|
||||
mod unicodedata {
|
||||
use super::NormalizeForm::*;
|
||||
use crate::vm::{
|
||||
Py, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine,
|
||||
builtins::{PyModule, PyStrRef},
|
||||
@@ -126,7 +125,8 @@ mod unicodedata {
|
||||
|
||||
#[pymethod]
|
||||
fn lookup(&self, name: PyStrRef, vm: &VirtualMachine) -> PyResult<String> {
|
||||
if let Some(character) = unicode_names2::character(name.as_str())
|
||||
if let Some(name_str) = name.to_str()
|
||||
&& let Some(character) = unicode_names2::character(name_str)
|
||||
&& self.check_age(character.into())
|
||||
{
|
||||
return Ok(character.to_string());
|
||||
@@ -188,7 +188,6 @@ mod unicodedata {
|
||||
|
||||
#[pymethod]
|
||||
fn normalize(&self, form: super::NormalizeForm, unistr: PyStrRef) -> PyResult<Wtf8Buf> {
|
||||
use super::NormalizeForm::*;
|
||||
let text = unistr.as_wtf8();
|
||||
let normalized_text = match form {
|
||||
Nfc => text.map_utf8(|s| s.nfc()).collect(),
|
||||
@@ -201,7 +200,6 @@ mod unicodedata {
|
||||
|
||||
#[pymethod]
|
||||
fn is_normalized(&self, form: super::NormalizeForm, unistr: PyStrRef) -> PyResult<bool> {
|
||||
use super::NormalizeForm::*;
|
||||
let text = unistr.as_wtf8();
|
||||
let normalized: Wtf8Buf = match form {
|
||||
Nfc => text.map_utf8(|s| s.nfc()).collect(),
|
||||
|
||||
@@ -466,7 +466,8 @@ mod zlib {
|
||||
}
|
||||
|
||||
fn new_zlib_error(message: impl Into<String>, vm: &VirtualMachine) -> PyBaseExceptionRef {
|
||||
vm.new_exception_msg(vm.class("zlib", "error"), message.into())
|
||||
let msg: String = message.into();
|
||||
vm.new_exception_msg(vm.class("zlib", "error"), msg.into())
|
||||
}
|
||||
|
||||
struct Level(Option<flate2::Compression>);
|
||||
|
||||
@@ -725,5 +725,6 @@ pub fn struct_error_type(vm: &VirtualMachine) -> &'static PyTypeRef {
|
||||
pub fn new_struct_error(vm: &VirtualMachine, msg: impl Into<String>) -> PyBaseExceptionRef {
|
||||
// can't just STRUCT_ERROR.get().unwrap() cause this could be called before from buffer
|
||||
// machinery, independent of whether _struct was ever imported
|
||||
let msg: String = msg.into();
|
||||
vm.new_exception_msg(struct_error_type(vm).clone(), msg.into())
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
use super::{PyInt, PyStrRef, PyType, PyTypeRef};
|
||||
use super::{PyInt, PyStrRef, PyType, PyTypeRef, PyUtf8StrRef};
|
||||
use crate::common::format::FormatSpec;
|
||||
use crate::{
|
||||
AsObject, Context, Py, PyObject, PyObjectRef, PyResult, TryFromBorrowedObject, VirtualMachine,
|
||||
@@ -101,7 +101,7 @@ impl Constructor for PyBool {
|
||||
#[pyclass(with(Constructor, AsNumber, Representable), flags(_MATCH_SELF))]
|
||||
impl PyBool {
|
||||
#[pymethod]
|
||||
fn __format__(obj: PyObjectRef, spec: PyStrRef, vm: &VirtualMachine) -> PyResult<String> {
|
||||
fn __format__(obj: PyObjectRef, spec: PyUtf8StrRef, vm: &VirtualMachine) -> PyResult<String> {
|
||||
let new_bool = obj.try_to_bool(vm)?;
|
||||
FormatSpec::parse(spec.as_str())
|
||||
.and_then(|format_spec| format_spec.format_bool(new_bool))
|
||||
|
||||
@@ -211,11 +211,11 @@ impl Representable for PyClassMethod {
|
||||
class
|
||||
.__qualname__(vm)
|
||||
.downcast_ref::<PyStr>()
|
||||
.map(|n| n.as_str()),
|
||||
.map(|n| n.as_wtf8()),
|
||||
class
|
||||
.__module__(vm)
|
||||
.downcast_ref::<PyStr>()
|
||||
.map(|m| m.as_str()),
|
||||
.map(|m| m.as_wtf8()),
|
||||
) {
|
||||
(None, _) => return Err(vm.new_type_error("Unknown qualified name")),
|
||||
(Some(qualname), Some(module)) if module != "builtins" => {
|
||||
|
||||
@@ -459,7 +459,7 @@ impl Constructor for PyCode {
|
||||
let s = obj.downcast_ref::<super::pystr::PyStr>().ok_or_else(|| {
|
||||
vm.new_type_error("names must be tuple of strings".to_owned())
|
||||
})?;
|
||||
Ok(vm.ctx.intern_str(s.as_str()))
|
||||
Ok(vm.ctx.intern_str(s.as_wtf8()))
|
||||
})
|
||||
.collect::<PyResult<Vec<_>>>()?
|
||||
.into_boxed_slice();
|
||||
@@ -471,7 +471,7 @@ impl Constructor for PyCode {
|
||||
let s = obj.downcast_ref::<super::pystr::PyStr>().ok_or_else(|| {
|
||||
vm.new_type_error("varnames must be tuple of strings".to_owned())
|
||||
})?;
|
||||
Ok(vm.ctx.intern_str(s.as_str()))
|
||||
Ok(vm.ctx.intern_str(s.as_wtf8()))
|
||||
})
|
||||
.collect::<PyResult<Vec<_>>>()?
|
||||
.into_boxed_slice();
|
||||
@@ -483,7 +483,7 @@ impl Constructor for PyCode {
|
||||
let s = obj.downcast_ref::<super::pystr::PyStr>().ok_or_else(|| {
|
||||
vm.new_type_error("cellvars must be tuple of strings".to_owned())
|
||||
})?;
|
||||
Ok(vm.ctx.intern_str(s.as_str()))
|
||||
Ok(vm.ctx.intern_str(s.as_wtf8()))
|
||||
})
|
||||
.collect::<PyResult<Vec<_>>>()?
|
||||
.into_boxed_slice();
|
||||
@@ -495,7 +495,7 @@ impl Constructor for PyCode {
|
||||
let s = obj.downcast_ref::<super::pystr::PyStr>().ok_or_else(|| {
|
||||
vm.new_type_error("freevars must be tuple of strings".to_owned())
|
||||
})?;
|
||||
Ok(vm.ctx.intern_str(s.as_str()))
|
||||
Ok(vm.ctx.intern_str(s.as_wtf8()))
|
||||
})
|
||||
.collect::<PyResult<Vec<_>>>()?
|
||||
.into_boxed_slice();
|
||||
@@ -551,15 +551,15 @@ impl Constructor for PyCode {
|
||||
posonlyarg_count: args.posonlyargcount,
|
||||
arg_count: args.argcount,
|
||||
kwonlyarg_count: args.kwonlyargcount,
|
||||
source_path: vm.ctx.intern_str(args.filename.as_str()),
|
||||
source_path: vm.ctx.intern_str(args.filename.as_wtf8()),
|
||||
first_line_number: if args.firstlineno > 0 {
|
||||
OneIndexed::new(args.firstlineno as usize)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
max_stackdepth: args.stacksize,
|
||||
obj_name: vm.ctx.intern_str(args.name.as_str()),
|
||||
qualname: vm.ctx.intern_str(args.qualname.as_str()),
|
||||
obj_name: vm.ctx.intern_str(args.name.as_wtf8()),
|
||||
qualname: vm.ctx.intern_str(args.qualname.as_wtf8()),
|
||||
cell2arg: None, // TODO: reuse `fn cell2arg`
|
||||
constants,
|
||||
names,
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
use super::{PyStr, PyType, PyTypeRef, float};
|
||||
use crate::{
|
||||
AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine,
|
||||
builtins::PyStrRef,
|
||||
builtins::PyUtf8StrRef,
|
||||
class::PyClassImpl,
|
||||
common::format::FormatSpec,
|
||||
common::{format::FormatSpec, wtf8::Wtf8Buf},
|
||||
convert::{IntoPyException, ToPyObject, ToPyResult},
|
||||
function::{FuncArgs, OptionalArg, PyComparisonValue},
|
||||
protocol::PyNumberMethods,
|
||||
@@ -276,13 +276,14 @@ impl PyComplex {
|
||||
}
|
||||
|
||||
#[pymethod]
|
||||
fn __format__(zelf: &Py<Self>, spec: PyStrRef, vm: &VirtualMachine) -> PyResult<String> {
|
||||
fn __format__(zelf: &Py<Self>, spec: PyUtf8StrRef, vm: &VirtualMachine) -> PyResult<Wtf8Buf> {
|
||||
// Empty format spec: equivalent to str(self)
|
||||
if spec.is_empty() {
|
||||
return Ok(zelf.as_object().str(vm)?.as_str().to_owned());
|
||||
return Ok(zelf.as_object().str(vm)?.as_wtf8().to_owned());
|
||||
}
|
||||
FormatSpec::parse(spec.as_str())
|
||||
.and_then(|format_spec| format_spec.format_complex(&zelf.value))
|
||||
.map(Wtf8Buf::from_string)
|
||||
.map_err(|err| err.into_pyexception(vm))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ use crate::{
|
||||
};
|
||||
use alloc::fmt;
|
||||
use rustpython_common::lock::PyMutex;
|
||||
use rustpython_common::wtf8::Wtf8Buf;
|
||||
|
||||
pub type DictContentType = dict_inner::Dict;
|
||||
|
||||
@@ -543,14 +544,19 @@ impl Representable for PyDict {
|
||||
#[inline]
|
||||
fn repr(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<PyStrRef> {
|
||||
let s = if let Some(_guard) = ReprGuard::enter(vm, zelf.as_object()) {
|
||||
let mut str_parts = Vec::with_capacity(zelf.__len__());
|
||||
let mut result = Wtf8Buf::from("{");
|
||||
let mut first = true;
|
||||
for (key, value) in zelf {
|
||||
let key_repr = &key.repr(vm)?;
|
||||
let value_repr = value.repr(vm)?;
|
||||
str_parts.push(format!("{key_repr}: {value_repr}"));
|
||||
if !first {
|
||||
result.push_str(", ");
|
||||
}
|
||||
first = false;
|
||||
result.push_wtf8(key.repr(vm)?.as_wtf8());
|
||||
result.push_str(": ");
|
||||
result.push_wtf8(value.repr(vm)?.as_wtf8());
|
||||
}
|
||||
|
||||
vm.ctx.new_str(format!("{{{}}}", str_parts.join(", ")))
|
||||
result.push_char('}');
|
||||
vm.ctx.new_str(result)
|
||||
} else {
|
||||
vm.ctx.intern_str("{...}").to_owned()
|
||||
};
|
||||
@@ -848,13 +854,17 @@ macro_rules! dict_view {
|
||||
#[inline]
|
||||
fn repr(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<PyStrRef> {
|
||||
let s = if let Some(_guard) = ReprGuard::enter(vm, zelf.as_object()) {
|
||||
let mut str_parts = Vec::with_capacity(zelf.__len__());
|
||||
let mut result = Wtf8Buf::from(format!("{}([", Self::NAME));
|
||||
let mut first = true;
|
||||
for (key, value) in zelf.dict().clone() {
|
||||
let s = &Self::item(vm, key, value).repr(vm)?;
|
||||
str_parts.push(s.as_str().to_owned());
|
||||
if !first {
|
||||
result.push_str(", ");
|
||||
}
|
||||
first = false;
|
||||
result.push_wtf8(Self::item(vm, key, value).repr(vm)?.as_wtf8());
|
||||
}
|
||||
vm.ctx
|
||||
.new_str(format!("{}([{}])", Self::NAME, str_parts.join(", ")))
|
||||
result.push_str("])");
|
||||
vm.ctx.new_str(result)
|
||||
} else {
|
||||
vm.ctx.intern_str("{...}").to_owned()
|
||||
};
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
use super::{
|
||||
PyByteArray, PyBytes, PyInt, PyIntRef, PyStr, PyStrRef, PyType, PyTypeRef, try_bigint_to_f64,
|
||||
PyByteArray, PyBytes, PyInt, PyIntRef, PyStr, PyType, PyTypeRef, PyUtf8StrRef,
|
||||
try_bigint_to_f64,
|
||||
};
|
||||
use crate::{
|
||||
AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult,
|
||||
TryFromBorrowedObject, TryFromObject, VirtualMachine,
|
||||
class::PyClassImpl,
|
||||
common::{float_ops, format::FormatSpec, hash},
|
||||
common::{float_ops, format::FormatSpec, hash, wtf8::Wtf8Buf},
|
||||
convert::{IntoPyException, ToPyObject, ToPyResult},
|
||||
function::{
|
||||
ArgBytesLike, FuncArgs, OptionalArg, OptionalOption, PyArithmeticValue::*,
|
||||
@@ -214,18 +215,19 @@ fn float_from_string(val: PyObjectRef, vm: &VirtualMachine) -> PyResult<f64> {
|
||||
)]
|
||||
impl PyFloat {
|
||||
#[pymethod]
|
||||
fn __format__(zelf: &Py<Self>, spec: PyStrRef, vm: &VirtualMachine) -> PyResult<String> {
|
||||
fn __format__(zelf: &Py<Self>, spec: PyUtf8StrRef, vm: &VirtualMachine) -> PyResult<Wtf8Buf> {
|
||||
// Empty format spec: equivalent to str(self)
|
||||
if spec.is_empty() {
|
||||
return Ok(zelf.as_object().str(vm)?.as_str().to_owned());
|
||||
return Ok(zelf.as_object().str(vm)?.as_wtf8().to_owned());
|
||||
}
|
||||
FormatSpec::parse(spec.as_str())
|
||||
.and_then(|format_spec| format_spec.format_float(zelf.value))
|
||||
.map(Wtf8Buf::from_string)
|
||||
.map_err(|err| err.into_pyexception(vm))
|
||||
}
|
||||
|
||||
#[pystaticmethod]
|
||||
fn __getformat__(spec: PyStrRef, vm: &VirtualMachine) -> PyResult<String> {
|
||||
fn __getformat__(spec: PyUtf8StrRef, vm: &VirtualMachine) -> PyResult<String> {
|
||||
if !matches!(spec.as_str(), "double" | "float") {
|
||||
return Err(
|
||||
vm.new_value_error("__getformat__() argument 1 must be 'double' or 'float'")
|
||||
@@ -340,7 +342,7 @@ impl PyFloat {
|
||||
}
|
||||
|
||||
#[pyclassmethod]
|
||||
fn fromhex(cls: PyTypeRef, string: PyStrRef, vm: &VirtualMachine) -> PyResult {
|
||||
fn fromhex(cls: PyTypeRef, string: PyUtf8StrRef, vm: &VirtualMachine) -> PyResult {
|
||||
let result = crate::literal::float::from_hex(string.as_str().trim())
|
||||
.ok_or_else(|| vm.new_value_error("invalid hexadecimal floating-point string"))?;
|
||||
PyType::call(&cls, vec![vm.ctx.new_float(result).into()].into(), vm)
|
||||
|
||||
@@ -14,6 +14,7 @@ use crate::{
|
||||
AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine,
|
||||
bytecode,
|
||||
class::PyClassImpl,
|
||||
common::wtf8::{Wtf8Buf, wtf8_concat},
|
||||
frame::Frame,
|
||||
function::{FuncArgs, OptionalArg, PyComparisonValue, PySetterValue},
|
||||
scope::Scope,
|
||||
@@ -1074,28 +1075,6 @@ impl PyBoundMethod {
|
||||
fn __module__(&self, vm: &VirtualMachine) -> Option<PyObjectRef> {
|
||||
self.function.get_attr("__module__", vm).ok()
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
fn __qualname__(&self, vm: &VirtualMachine) -> PyResult {
|
||||
if self
|
||||
.function
|
||||
.fast_isinstance(vm.ctx.types.builtin_function_or_method_type)
|
||||
{
|
||||
// Special case: we work with `__new__`, which is not really a method.
|
||||
// It is a function, so its `__qualname__` is just `__new__`.
|
||||
// We need to add object's part manually.
|
||||
let obj_name = vm.get_attribute_opt(self.object.clone(), "__qualname__")?;
|
||||
let obj_name: Option<PyStrRef> = obj_name.and_then(|o| o.downcast().ok());
|
||||
return Ok(vm
|
||||
.ctx
|
||||
.new_str(format!(
|
||||
"{}.__new__",
|
||||
obj_name.as_ref().map_or("?", |s| s.as_str())
|
||||
))
|
||||
.into());
|
||||
}
|
||||
self.function.get_attr("__qualname__", vm)
|
||||
}
|
||||
}
|
||||
|
||||
impl PyPayload for PyBoundMethod {
|
||||
@@ -1107,21 +1086,23 @@ impl PyPayload for PyBoundMethod {
|
||||
|
||||
impl Representable for PyBoundMethod {
|
||||
#[inline]
|
||||
fn repr_str(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<String> {
|
||||
let func_name =
|
||||
if let Some(qname) = vm.get_attribute_opt(zelf.function.clone(), "__qualname__")? {
|
||||
Some(qname)
|
||||
} else {
|
||||
vm.get_attribute_opt(zelf.function.clone(), "__name__")?
|
||||
};
|
||||
let func_name: Option<PyStrRef> = func_name.and_then(|o| o.downcast().ok());
|
||||
let formatted_func_name = match func_name {
|
||||
Some(name) => name.to_string(),
|
||||
None => "?".to_string(),
|
||||
fn repr_wtf8(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<Wtf8Buf> {
|
||||
let func_name = if let Some(qname) =
|
||||
vm.get_attribute_opt(zelf.function.clone(), identifier!(vm, __qualname__))?
|
||||
{
|
||||
Some(qname)
|
||||
} else {
|
||||
vm.get_attribute_opt(zelf.function.clone(), identifier!(vm, __name__))?
|
||||
};
|
||||
let func_name: Option<PyStrRef> = func_name.and_then(|o| o.downcast().ok());
|
||||
let object_repr = zelf.object.repr(vm)?;
|
||||
Ok(format!(
|
||||
"<bound method {formatted_func_name} of {object_repr}>",
|
||||
let name = func_name.as_ref().map_or("?".as_ref(), |s| s.as_wtf8());
|
||||
Ok(wtf8_concat!(
|
||||
"<bound method ",
|
||||
name,
|
||||
" of ",
|
||||
object_repr.as_wtf8(),
|
||||
">"
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ impl ToPyObject for AbiValue {
|
||||
|
||||
pub fn new_jit_error(msg: String, vm: &VirtualMachine) -> PyBaseExceptionRef {
|
||||
let jit_error = vm.ctx.exceptions.jit_error.to_owned();
|
||||
vm.new_exception_msg(jit_error, msg)
|
||||
vm.new_exception_msg(jit_error, msg.into())
|
||||
}
|
||||
|
||||
fn get_jit_arg_type(dict: &Py<PyDict>, name: &str, vm: &VirtualMachine) -> PyResult<JitType> {
|
||||
|
||||
@@ -635,14 +635,14 @@ impl Hashable for PyGenericAlias {
|
||||
|
||||
impl GetAttr for PyGenericAlias {
|
||||
fn getattro(zelf: &Py<Self>, attr: &Py<PyStr>, vm: &VirtualMachine) -> PyResult {
|
||||
let attr_str = attr.as_str();
|
||||
let attr_str = attr.as_wtf8();
|
||||
for exc in &ATTR_EXCEPTIONS {
|
||||
if *exc == attr_str {
|
||||
if attr_str == *exc {
|
||||
return zelf.as_object().generic_getattr(attr, vm);
|
||||
}
|
||||
}
|
||||
for blocked in &ATTR_BLOCKED {
|
||||
if *blocked == attr_str {
|
||||
if attr_str == *blocked {
|
||||
return zelf.as_object().generic_getattr(attr, vm);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,13 +2,14 @@ use super::{PyByteArray, PyBytes, PyStr, PyType, PyTypeRef, float};
|
||||
use crate::{
|
||||
AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyRefExact, PyResult,
|
||||
TryFromBorrowedObject, VirtualMachine,
|
||||
builtins::PyStrRef,
|
||||
builtins::PyUtf8StrRef,
|
||||
bytes_inner::PyBytesInner,
|
||||
class::PyClassImpl,
|
||||
common::{
|
||||
format::FormatSpec,
|
||||
hash,
|
||||
int::{bigint_to_finite_float, bytes_to_int, true_div},
|
||||
wtf8::Wtf8Buf,
|
||||
},
|
||||
convert::{IntoPyException, ToPyObject, ToPyResult},
|
||||
function::{
|
||||
@@ -197,7 +198,7 @@ fn inner_truediv(i1: &BigInt, i2: &BigInt, vm: &VirtualMachine) -> PyResult {
|
||||
if float.is_infinite() {
|
||||
Err(vm.new_exception_msg(
|
||||
vm.ctx.exceptions.overflow_error.to_owned(),
|
||||
"integer division result too large for a float".to_owned(),
|
||||
"integer division result too large for a float".into(),
|
||||
))
|
||||
} else {
|
||||
Ok(vm.ctx.new_float(float).into())
|
||||
@@ -444,13 +445,14 @@ impl PyInt {
|
||||
}
|
||||
|
||||
#[pymethod]
|
||||
fn __format__(zelf: &Py<Self>, spec: PyStrRef, vm: &VirtualMachine) -> PyResult<String> {
|
||||
fn __format__(zelf: &Py<Self>, spec: PyUtf8StrRef, vm: &VirtualMachine) -> PyResult<Wtf8Buf> {
|
||||
// Empty format spec on a subclass: equivalent to str(self)
|
||||
if spec.is_empty() && !zelf.class().is(vm.ctx.types.int_type) {
|
||||
return Ok(zelf.as_object().str(vm)?.as_str().to_owned());
|
||||
return Ok(zelf.as_object().str(vm)?.as_wtf8().to_owned());
|
||||
}
|
||||
FormatSpec::parse(spec.as_str())
|
||||
.and_then(|format_spec| format_spec.format_int(&zelf.value))
|
||||
.map(Wtf8Buf::from_string)
|
||||
.map_err(|err| err.into_pyexception(vm))
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,8 @@ use crate::{
|
||||
function::{OptionalArg, PyComparisonValue},
|
||||
types::{Comparable, Constructor, Hashable, PyComparisonOp, Representable},
|
||||
};
|
||||
use itertools::Itertools;
|
||||
use rustpython_common::wtf8::Wtf8Buf;
|
||||
|
||||
/// Interpolation object for t-strings (PEP 750).
|
||||
///
|
||||
@@ -42,11 +44,11 @@ impl PyInterpolation {
|
||||
let is_valid = vm.is_none(&conversion)
|
||||
|| conversion
|
||||
.downcast_ref::<PyStr>()
|
||||
.is_some_and(|s| matches!(s.as_str(), "s" | "r" | "a"));
|
||||
.is_some_and(|s| matches!(s.to_str(), Some("s") | Some("r") | Some("a")));
|
||||
if !is_valid {
|
||||
return Err(vm.new_exception_msg(
|
||||
vm.ctx.exceptions.system_error.to_owned(),
|
||||
"Interpolation() argument 'conversion' must be one of 's', 'a' or 'r'".to_owned(),
|
||||
"Interpolation() argument 'conversion' must be one of 's', 'a' or 'r'".into(),
|
||||
));
|
||||
}
|
||||
Ok(Self {
|
||||
@@ -63,8 +65,13 @@ impl Constructor for PyInterpolation {
|
||||
|
||||
fn py_new(_cls: &Py<PyType>, args: Self::Args, vm: &VirtualMachine) -> PyResult<Self> {
|
||||
let conversion: PyObjectRef = if let Some(s) = args.conversion {
|
||||
let s_str = s.as_str();
|
||||
if s_str.len() != 1 || !matches!(s_str.chars().next(), Some('s' | 'r' | 'a')) {
|
||||
let has_flag = s
|
||||
.as_bytes()
|
||||
.iter()
|
||||
.exactly_one()
|
||||
.ok()
|
||||
.is_some_and(|s| matches!(*s, b's' | b'r' | b'a'));
|
||||
if !has_flag {
|
||||
return Err(vm.new_value_error(
|
||||
"Interpolation() argument 'conversion' must be one of 's', 'a' or 'r'",
|
||||
));
|
||||
@@ -194,25 +201,26 @@ impl Hashable for PyInterpolation {
|
||||
|
||||
impl Representable for PyInterpolation {
|
||||
#[inline]
|
||||
fn repr_str(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<String> {
|
||||
fn repr_wtf8(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<Wtf8Buf> {
|
||||
let value_repr = zelf.value.repr(vm)?;
|
||||
let expr_repr = zelf.expression.repr(vm)?;
|
||||
|
||||
let conv_str = if vm.is_none(&zelf.conversion) {
|
||||
"None".to_owned()
|
||||
} else {
|
||||
zelf.conversion.repr(vm)?.as_str().to_owned()
|
||||
};
|
||||
|
||||
let spec_repr = zelf.format_spec.repr(vm)?;
|
||||
|
||||
Ok(format!(
|
||||
"Interpolation({}, {}, {}, {})",
|
||||
value_repr.as_str(),
|
||||
expr_repr.as_str(),
|
||||
conv_str,
|
||||
spec_repr.as_str()
|
||||
))
|
||||
let mut result = Wtf8Buf::from("Interpolation(");
|
||||
result.push_wtf8(value_repr.as_wtf8());
|
||||
result.push_str(", ");
|
||||
result.push_str(&expr_repr);
|
||||
result.push_str(", ");
|
||||
if vm.is_none(&zelf.conversion) {
|
||||
result.push_str("None");
|
||||
} else {
|
||||
result.push_wtf8(zelf.conversion.repr(vm)?.as_wtf8());
|
||||
}
|
||||
result.push_str(", ");
|
||||
result.push_str(&spec_repr);
|
||||
result.push_char(')');
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ use crate::common::lock::{
|
||||
use crate::object::{Traverse, TraverseFn};
|
||||
use crate::{
|
||||
AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult,
|
||||
builtins::PyStr,
|
||||
class::PyClassImpl,
|
||||
convert::ToPyObject,
|
||||
function::{ArgSize, FuncArgs, OptionalArg, PyComparisonValue},
|
||||
@@ -516,19 +517,25 @@ impl Comparable for PyList {
|
||||
|
||||
impl Representable for PyList {
|
||||
#[inline]
|
||||
fn repr_str(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<String> {
|
||||
let s = if zelf.__len__() == 0 {
|
||||
"[]".to_owned()
|
||||
} else if let Some(_guard) = ReprGuard::enter(vm, zelf.as_object()) {
|
||||
fn repr(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<PyRef<PyStr>> {
|
||||
if zelf.__len__() == 0 {
|
||||
return Ok(vm.ctx.intern_str("[]").to_owned());
|
||||
}
|
||||
if let Some(_guard) = ReprGuard::enter(vm, zelf.as_object()) {
|
||||
// Clone elements before calling repr to release the read lock.
|
||||
// Element repr may mutate the list (e.g., list.clear()), which
|
||||
// needs a write lock and would deadlock if read lock is held.
|
||||
let elements: Vec<PyObjectRef> = zelf.borrow_vec().to_vec();
|
||||
collection_repr(None, "[", "]", elements.iter(), vm)?
|
||||
Ok(vm
|
||||
.ctx
|
||||
.new_str(collection_repr(None, "[", "]", elements.iter(), vm)?))
|
||||
} else {
|
||||
"[...]".to_owned()
|
||||
};
|
||||
Ok(s)
|
||||
Ok(vm.ctx.intern_str("[...]").to_owned())
|
||||
}
|
||||
}
|
||||
|
||||
fn repr_str(_zelf: &Py<Self>, _vm: &VirtualMachine) -> PyResult<String> {
|
||||
unreachable!("repr() is overridden directly")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
use super::{PyDict, PyDictRef, PyGenericAlias, PyList, PyTuple, PyType, PyTypeRef};
|
||||
use crate::common::lock::LazyLock;
|
||||
use crate::{
|
||||
AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine,
|
||||
atomic_func,
|
||||
class::PyClassImpl,
|
||||
common::hash,
|
||||
common::{hash, lock::LazyLock},
|
||||
convert::ToPyObject,
|
||||
function::{ArgMapping, OptionalArg, PyComparisonValue},
|
||||
object::{Traverse, TraverseFn},
|
||||
@@ -14,6 +13,7 @@ use crate::{
|
||||
PyComparisonOp, Representable,
|
||||
},
|
||||
};
|
||||
use rustpython_common::wtf8::{Wtf8Buf, wtf8_concat};
|
||||
|
||||
#[pyclass(module = false, name = "mappingproxy", traverse)]
|
||||
#[derive(Debug)]
|
||||
@@ -287,9 +287,9 @@ impl Iterable for PyMappingProxy {
|
||||
|
||||
impl Representable for PyMappingProxy {
|
||||
#[inline]
|
||||
fn repr_str(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<String> {
|
||||
fn repr_wtf8(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<Wtf8Buf> {
|
||||
let obj = zelf.to_object(vm)?;
|
||||
Ok(format!("mappingproxy({})", obj.repr(vm)?))
|
||||
Ok(wtf8_concat!("mappingproxy(", obj.repr(vm)?.as_wtf8(), ')'))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use super::{
|
||||
PositionIterInternal, PyBytes, PyBytesRef, PyGenericAlias, PyInt, PyListRef, PySlice, PyStr,
|
||||
PyStrRef, PyTuple, PyTupleRef, PyType, PyTypeRef, iter::builtins_iter,
|
||||
PyStrRef, PyTuple, PyTupleRef, PyType, PyTypeRef, PyUtf8StrRef, iter::builtins_iter,
|
||||
};
|
||||
use crate::common::lock::LazyLock;
|
||||
use crate::{
|
||||
@@ -805,8 +805,9 @@ impl PyMemoryView {
|
||||
Err(vm.new_value_error("memoryview.index(x): x not in memoryview"))
|
||||
}
|
||||
|
||||
fn cast_to_1d(&self, format: PyStrRef, vm: &VirtualMachine) -> PyResult<Self> {
|
||||
let format_spec = Self::parse_format(format.as_str(), vm)?;
|
||||
fn cast_to_1d(&self, format: PyUtf8StrRef, vm: &VirtualMachine) -> PyResult<Self> {
|
||||
let format_str = format.as_str();
|
||||
let format_spec = Self::parse_format(format_str, vm)?;
|
||||
let itemsize = format_spec.size();
|
||||
if !self.desc.len.is_multiple_of(itemsize) {
|
||||
return Err(vm.new_type_error("memoryview: length is not a multiple of itemsize"));
|
||||
@@ -821,7 +822,7 @@ impl PyMemoryView {
|
||||
len: self.desc.len,
|
||||
readonly: self.desc.readonly,
|
||||
itemsize,
|
||||
format: format.to_string().into(),
|
||||
format: format_str.to_owned().into(),
|
||||
dim_desc: vec![(self.desc.len / itemsize, itemsize as isize, 0)],
|
||||
},
|
||||
hash: OnceCell::new(),
|
||||
@@ -956,7 +957,7 @@ impl Py<PyMemoryView> {
|
||||
#[derive(FromArgs)]
|
||||
struct CastArgs {
|
||||
#[pyarg(any)]
|
||||
format: PyStrRef,
|
||||
format: PyUtf8StrRef,
|
||||
#[pyarg(any, optional)]
|
||||
shape: OptionalArg<Either<PyTupleRef, PyListRef>>,
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ pub(crate) mod bool_;
|
||||
pub use bool_::PyBool;
|
||||
#[path = "str.rs"]
|
||||
pub(crate) mod pystr;
|
||||
pub use pystr::{PyStr, PyStrInterned, PyStrRef, PyUtf8Str, PyUtf8StrRef};
|
||||
pub use pystr::{PyStr, PyStrInterned, PyStrRef, PyUtf8Str, PyUtf8StrInterned, PyUtf8StrRef};
|
||||
#[path = "super.rs"]
|
||||
pub(crate) mod super_;
|
||||
pub use super_::PySuper;
|
||||
|
||||
@@ -160,9 +160,10 @@ impl Py<PyModule> {
|
||||
.get_item_opt(identifier!(vm, __name__), vm)
|
||||
.ok()
|
||||
.flatten();
|
||||
let mod_name_str = mod_name_obj
|
||||
.as_ref()
|
||||
.and_then(|n| n.downcast_ref::<PyStr>().map(|s| s.as_str().to_owned()));
|
||||
let mod_name_str = mod_name_obj.as_ref().and_then(|n| {
|
||||
n.downcast_ref::<PyStr>()
|
||||
.map(|s| s.to_string_lossy().into_owned())
|
||||
});
|
||||
|
||||
// If __name__ is not set or not a string, use a simpler error message
|
||||
let mod_display = match mod_name_str.as_deref() {
|
||||
@@ -424,7 +425,7 @@ impl Initializer for PyModule {
|
||||
.flags
|
||||
.has_feature(crate::types::PyTypeFlags::HAS_DICT)
|
||||
);
|
||||
zelf.init_dict(vm.ctx.intern_str(args.name.as_str()), args.doc, vm);
|
||||
zelf.init_dict(vm.ctx.intern_str(args.name.as_wtf8()), args.doc, vm);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use super::{PyTupleRef, PyType, tuple::IntoPyTuple};
|
||||
use super::{PyStr, PyTupleRef, PyType, tuple::IntoPyTuple};
|
||||
use crate::{
|
||||
AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine,
|
||||
builtins::PyDict,
|
||||
@@ -9,6 +9,7 @@ use crate::{
|
||||
Comparable, Constructor, DefaultConstructor, Initializer, PyComparisonOp, Representable,
|
||||
},
|
||||
};
|
||||
use rustpython_common::wtf8::Wtf8Buf;
|
||||
|
||||
/// A simple attribute-based namespace.
|
||||
///
|
||||
@@ -138,7 +139,7 @@ impl Comparable for PyNamespace {
|
||||
|
||||
impl Representable for PyNamespace {
|
||||
#[inline]
|
||||
fn repr_str(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<String> {
|
||||
fn repr_wtf8(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<Wtf8Buf> {
|
||||
let o = zelf.as_object();
|
||||
let name = if o.class().is(vm.ctx.types.namespace_type) {
|
||||
"namespace".to_owned()
|
||||
@@ -148,16 +149,27 @@ impl Representable for PyNamespace {
|
||||
|
||||
let repr = if let Some(_guard) = ReprGuard::enter(vm, zelf.as_object()) {
|
||||
let dict = zelf.as_object().dict().unwrap();
|
||||
let mut parts = Vec::with_capacity(dict.__len__());
|
||||
let mut result = Wtf8Buf::from(format!("{name}("));
|
||||
let mut first = true;
|
||||
for (key, value) in dict {
|
||||
let k = key.repr(vm)?;
|
||||
let key_str = k.as_wtf8();
|
||||
let value_repr = value.repr(vm)?;
|
||||
parts.push(format!("{}={}", &key_str[1..key_str.len() - 1], value_repr));
|
||||
let Some(key_str) = key.downcast_ref::<PyStr>() else {
|
||||
continue;
|
||||
};
|
||||
if key_str.as_wtf8().is_empty() {
|
||||
continue;
|
||||
}
|
||||
if !first {
|
||||
result.push_str(", ");
|
||||
}
|
||||
first = false;
|
||||
result.push_wtf8(key_str.as_wtf8());
|
||||
result.push_char('=');
|
||||
result.push_wtf8(value.repr(vm)?.as_wtf8());
|
||||
}
|
||||
format!("{}({})", name, parts.join(", "))
|
||||
result.push_char(')');
|
||||
result
|
||||
} else {
|
||||
format!("{name}(...)")
|
||||
Wtf8Buf::from(format!("{name}(...)"))
|
||||
};
|
||||
Ok(repr)
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use super::{PyDictRef, PyList, PyStr, PyStrRef, PyType, PyTypeRef};
|
||||
use super::{PyDictRef, PyList, PyStr, PyStrRef, PyType, PyTypeRef, PyUtf8StrRef};
|
||||
use crate::common::hash::PyHash;
|
||||
use crate::types::PyTypeFlags;
|
||||
use crate::{
|
||||
@@ -82,9 +82,12 @@ impl Constructor for PyBaseObject {
|
||||
if let Some(abs_methods) = cls.get_attr(identifier!(vm, __abstractmethods__))
|
||||
&& let Some(unimplemented_abstract_method_count) = abs_methods.length_opt(vm)
|
||||
{
|
||||
let methods: Vec<PyStrRef> = abs_methods.try_to_value(vm)?;
|
||||
let methods: String =
|
||||
Itertools::intersperse(methods.iter().map(|name| name.as_str()), "', '").collect();
|
||||
let methods: Vec<PyUtf8StrRef> = abs_methods.try_to_value(vm)?;
|
||||
let methods: String = Itertools::intersperse(
|
||||
methods.iter().map(|name| name.as_str().to_owned()),
|
||||
"', '".to_owned(),
|
||||
)
|
||||
.collect();
|
||||
|
||||
let unimplemented_abstract_method_count = unimplemented_abstract_method_count?;
|
||||
let name = cls.name().to_string();
|
||||
@@ -218,7 +221,7 @@ fn object_getstate_default(obj: &PyObject, required: bool, vm: &VirtualMachine)
|
||||
let has_weakref = if let Some(ref ext) = obj.class().heaptype_ext {
|
||||
match &ext.slots {
|
||||
None => true, // Heap type without __slots__ has automatic weakref
|
||||
Some(slots) => slots.iter().any(|s| s.as_str() == "__weakref__"),
|
||||
Some(slots) => slots.iter().any(|s| s.as_bytes() == b"__weakref__"),
|
||||
}
|
||||
} else {
|
||||
let weakref_name = vm.ctx.intern_str("__weakref__");
|
||||
@@ -255,7 +258,7 @@ fn object_getstate_default(obj: &PyObject, required: bool, vm: &VirtualMachine)
|
||||
let Ok(value) = obj.get_attr(name, vm) else {
|
||||
continue;
|
||||
};
|
||||
slots.set_item(name.as_str(), value, vm).unwrap();
|
||||
slots.set_item(name.as_wtf8(), value, vm).unwrap();
|
||||
}
|
||||
|
||||
if !slots.is_empty() {
|
||||
@@ -385,11 +388,11 @@ impl PyBaseObject {
|
||||
class
|
||||
.__qualname__(vm)
|
||||
.downcast_ref::<PyStr>()
|
||||
.map(|n| n.as_str()),
|
||||
.map(|n| n.as_wtf8()),
|
||||
class
|
||||
.__module__(vm)
|
||||
.downcast_ref::<PyStr>()
|
||||
.map(|m| m.as_str()),
|
||||
.map(|m| m.as_wtf8()),
|
||||
) {
|
||||
(None, _) => Err(vm.new_type_error("Unknown qualified name")),
|
||||
(Some(qualname), Some(module)) if module != "builtins" => Ok(PyStr::from(format!(
|
||||
@@ -473,7 +476,7 @@ impl PyBaseObject {
|
||||
a.len() == b.len()
|
||||
&& a.iter()
|
||||
.zip(b.iter())
|
||||
.all(|(x, y)| x.as_str() == y.as_str())
|
||||
.all(|(x, y)| x.as_wtf8() == y.as_wtf8())
|
||||
}
|
||||
(None, None) => true,
|
||||
_ => false,
|
||||
|
||||
@@ -52,8 +52,9 @@ fn iter_search(
|
||||
SearchType::Index => Err(vm.new_value_error(format!(
|
||||
"{} not in range",
|
||||
item.repr(vm)
|
||||
.map(|v| v.as_str().to_owned())
|
||||
.unwrap_or_else(|_| "value".to_owned())
|
||||
.as_ref()
|
||||
.map_or("value".as_ref(), |s| s.as_wtf8())
|
||||
.to_owned()
|
||||
))),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ use crate::{
|
||||
AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, TryFromObject,
|
||||
atomic_func,
|
||||
class::PyClassImpl,
|
||||
common::{ascii, hash::PyHash, lock::PyMutex, rc::PyRc},
|
||||
common::{ascii, hash::PyHash, lock::PyMutex, rc::PyRc, wtf8::Wtf8Buf},
|
||||
convert::ToPyResult,
|
||||
dict_inner::{self, DictSize},
|
||||
function::{ArgIterable, FuncArgs, OptionalArg, PosArgs, PyArithmeticValue, PyComparisonValue},
|
||||
@@ -318,7 +318,7 @@ impl PySetInner {
|
||||
}
|
||||
}
|
||||
|
||||
fn repr(&self, class_name: Option<&str>, vm: &VirtualMachine) -> PyResult<String> {
|
||||
fn repr(&self, class_name: Option<&str>, vm: &VirtualMachine) -> PyResult<Wtf8Buf> {
|
||||
collection_repr(class_name, "{", "}", self.elements().iter(), vm)
|
||||
}
|
||||
|
||||
@@ -942,23 +942,23 @@ impl AsNumber for PySet {
|
||||
|
||||
impl Representable for PySet {
|
||||
#[inline]
|
||||
fn repr_str(zelf: &crate::Py<Self>, vm: &VirtualMachine) -> PyResult<String> {
|
||||
fn repr_wtf8(zelf: &crate::Py<Self>, vm: &VirtualMachine) -> PyResult<Wtf8Buf> {
|
||||
let class = zelf.class();
|
||||
let borrowed_name = class.name();
|
||||
let class_name = borrowed_name.deref();
|
||||
let s = if zelf.inner.len() == 0 {
|
||||
format!("{class_name}()")
|
||||
} else if let Some(_guard) = ReprGuard::enter(vm, zelf.as_object()) {
|
||||
if zelf.inner.len() == 0 {
|
||||
return Ok(Wtf8Buf::from(format!("{class_name}()")));
|
||||
}
|
||||
if let Some(_guard) = ReprGuard::enter(vm, zelf.as_object()) {
|
||||
let name = if class_name != "set" {
|
||||
Some(class_name)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
zelf.inner.repr(name, vm)?
|
||||
zelf.inner.repr(name, vm)
|
||||
} else {
|
||||
format!("{class_name}(...)")
|
||||
};
|
||||
Ok(s)
|
||||
Ok(Wtf8Buf::from(format!("{class_name}(...)")))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1289,18 +1289,18 @@ impl AsNumber for PyFrozenSet {
|
||||
|
||||
impl Representable for PyFrozenSet {
|
||||
#[inline]
|
||||
fn repr_str(zelf: &crate::Py<Self>, vm: &VirtualMachine) -> PyResult<String> {
|
||||
fn repr_wtf8(zelf: &crate::Py<Self>, vm: &VirtualMachine) -> PyResult<Wtf8Buf> {
|
||||
let inner = &zelf.inner;
|
||||
let class = zelf.class();
|
||||
let class_name = class.name();
|
||||
let s = if inner.len() == 0 {
|
||||
format!("{class_name}()")
|
||||
} else if let Some(_guard) = ReprGuard::enter(vm, zelf.as_object()) {
|
||||
inner.repr(Some(&class_name), vm)?
|
||||
if inner.len() == 0 {
|
||||
return Ok(Wtf8Buf::from(format!("{class_name}()")));
|
||||
}
|
||||
if let Some(_guard) = ReprGuard::enter(vm, zelf.as_object()) {
|
||||
inner.repr(Some(&class_name), vm)
|
||||
} else {
|
||||
format!("{class_name}(...)")
|
||||
};
|
||||
Ok(s)
|
||||
Ok(Wtf8Buf::from(format!("{class_name}(...)")))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
// sliceobject.{h,c} in CPython
|
||||
// spell-checker:ignore sliceobject
|
||||
use rustpython_common::wtf8::{Wtf8Buf, wtf8_concat};
|
||||
|
||||
use super::{PyGenericAlias, PyStrRef, PyTupleRef, PyType, PyTypeRef};
|
||||
use crate::{
|
||||
AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine,
|
||||
@@ -291,12 +293,20 @@ impl Comparable for PySlice {
|
||||
|
||||
impl Representable for PySlice {
|
||||
#[inline]
|
||||
fn repr_str(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<String> {
|
||||
fn repr_wtf8(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<Wtf8Buf> {
|
||||
let start_repr = zelf.start_ref(vm).repr(vm)?;
|
||||
let stop_repr = zelf.stop.repr(vm)?;
|
||||
let step_repr = zelf.step_ref(vm).repr(vm)?;
|
||||
|
||||
Ok(format!("slice({start_repr}, {stop_repr}, {step_repr})"))
|
||||
Ok(wtf8_concat!(
|
||||
"slice(",
|
||||
start_repr.as_wtf8(),
|
||||
", ",
|
||||
stop_repr.as_wtf8(),
|
||||
", ",
|
||||
step_repr.as_wtf8(),
|
||||
")"
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -182,11 +182,11 @@ impl Representable for PyStaticMethod {
|
||||
class
|
||||
.__qualname__(vm)
|
||||
.downcast_ref::<PyStr>()
|
||||
.map(|n| n.as_str()),
|
||||
.map(|n| n.as_wtf8()),
|
||||
class
|
||||
.__module__(vm)
|
||||
.downcast_ref::<PyStr>()
|
||||
.map(|m| m.as_str()),
|
||||
.map(|m| m.as_wtf8()),
|
||||
) {
|
||||
(None, _) => Err(vm.new_type_error("Unknown qualified name")),
|
||||
(Some(qualname), Some(module)) if module != "builtins" => {
|
||||
|
||||
+172
-67
@@ -41,7 +41,7 @@ use rustpython_common::{
|
||||
hash,
|
||||
lock::PyMutex,
|
||||
str::DeduceStrKind,
|
||||
wtf8::{CodePoint, Wtf8, Wtf8Buf, Wtf8Chunk},
|
||||
wtf8::{CodePoint, Wtf8, Wtf8Buf, Wtf8Chunk, Wtf8Concat},
|
||||
};
|
||||
use unic_ucd_bidi::BidiClass;
|
||||
use unic_ucd_category::GeneralCategory;
|
||||
@@ -50,13 +50,13 @@ use unicode_casing::CharExt;
|
||||
|
||||
impl<'a> TryFromBorrowedObject<'a> for String {
|
||||
fn try_from_borrowed_object(vm: &VirtualMachine, obj: &'a PyObject) -> PyResult<Self> {
|
||||
obj.try_value_with(|pystr: &PyStr| Ok(pystr.as_str().to_owned()), vm)
|
||||
obj.try_value_with(|pystr: &PyUtf8Str| Ok(pystr.as_str().to_owned()), vm)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> TryFromBorrowedObject<'a> for &'a str {
|
||||
fn try_from_borrowed_object(vm: &VirtualMachine, obj: &'a PyObject) -> PyResult<Self> {
|
||||
let pystr: &Py<PyStr> = TryFromBorrowedObject::try_from_borrowed_object(vm, obj)?;
|
||||
let pystr: &Py<PyUtf8Str> = TryFromBorrowedObject::try_from_borrowed_object(vm, obj)?;
|
||||
Ok(pystr.as_str())
|
||||
}
|
||||
}
|
||||
@@ -90,21 +90,21 @@ impl fmt::Debug for PyStr {
|
||||
impl AsRef<str> for PyStr {
|
||||
#[track_caller] // <- can remove this once it doesn't panic
|
||||
fn as_ref(&self) -> &str {
|
||||
self.as_str()
|
||||
self.to_str().expect("str has surrogates")
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<str> for Py<PyStr> {
|
||||
#[track_caller] // <- can remove this once it doesn't panic
|
||||
fn as_ref(&self) -> &str {
|
||||
self.as_str()
|
||||
self.to_str().expect("str has surrogates")
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<str> for PyStrRef {
|
||||
#[track_caller] // <- can remove this once it doesn't panic
|
||||
fn as_ref(&self) -> &str {
|
||||
self.as_str()
|
||||
self.to_str().expect("str has surrogates")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,6 +126,20 @@ impl AsRef<Wtf8> for PyStrRef {
|
||||
}
|
||||
}
|
||||
|
||||
impl Wtf8Concat for PyStr {
|
||||
#[inline]
|
||||
fn fmt_wtf8(&self, buf: &mut Wtf8Buf) {
|
||||
buf.push_wtf8(self.as_wtf8());
|
||||
}
|
||||
}
|
||||
|
||||
impl Wtf8Concat for Py<PyStr> {
|
||||
#[inline]
|
||||
fn fmt_wtf8(&self, buf: &mut Wtf8Buf) {
|
||||
buf.push_wtf8(self.as_wtf8());
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a AsciiStr> for PyStr {
|
||||
fn from(s: &'a AsciiStr) -> Self {
|
||||
s.to_owned().into()
|
||||
@@ -249,6 +263,13 @@ impl<'a> AsPyStr<'a> for &'a Py<PyStr> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> AsPyStr<'a> for &'a Py<PyUtf8Str> {
|
||||
#[inline]
|
||||
fn as_pystr(self, _ctx: &Context) -> &'a Py<PyStr> {
|
||||
Py::<PyUtf8Str>::as_pystr(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> AsPyStr<'a> for &'a PyStrRef {
|
||||
#[inline]
|
||||
fn as_pystr(self, _ctx: &Context) -> &'a Py<PyStr> {
|
||||
@@ -256,6 +277,13 @@ impl<'a> AsPyStr<'a> for &'a PyStrRef {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> AsPyStr<'a> for &'a PyUtf8StrRef {
|
||||
#[inline]
|
||||
fn as_pystr(self, _ctx: &Context) -> &'a Py<PyStr> {
|
||||
Py::<PyUtf8Str>::as_pystr(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl AsPyStr<'static> for &'static str {
|
||||
#[inline]
|
||||
fn as_pystr(self, ctx: &Context) -> &'static Py<PyStr> {
|
||||
@@ -270,6 +298,13 @@ impl<'a> AsPyStr<'a> for &'a PyStrInterned {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> AsPyStr<'a> for &'a PyUtf8StrInterned {
|
||||
#[inline]
|
||||
fn as_pystr(self, _ctx: &Context) -> &'a Py<PyStr> {
|
||||
Py::<PyUtf8Str>::as_pystr(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[pyclass(module = false, name = "str_iterator", traverse = "manual")]
|
||||
#[derive(Debug)]
|
||||
pub struct PyStrIterator {
|
||||
@@ -350,9 +385,9 @@ pub struct StrArgs {
|
||||
#[pyarg(any, optional)]
|
||||
object: OptionalArg<PyObjectRef>,
|
||||
#[pyarg(any, optional)]
|
||||
encoding: OptionalArg<PyStrRef>,
|
||||
encoding: OptionalArg<PyUtf8StrRef>,
|
||||
#[pyarg(any, optional)]
|
||||
errors: OptionalArg<PyStrRef>,
|
||||
errors: OptionalArg<PyUtf8StrRef>,
|
||||
}
|
||||
|
||||
impl Constructor for PyStr {
|
||||
@@ -440,17 +475,20 @@ impl PyStr {
|
||||
self.data.as_wtf8().as_bytes()
|
||||
}
|
||||
|
||||
// FIXME: make this return an Option
|
||||
#[inline]
|
||||
#[track_caller] // <- can remove this once it doesn't panic
|
||||
pub fn as_str(&self) -> &str {
|
||||
self.data.as_str().expect("str has surrogates")
|
||||
}
|
||||
|
||||
pub fn to_str(&self) -> Option<&str> {
|
||||
self.data.as_str()
|
||||
}
|
||||
|
||||
/// Returns `&str`
|
||||
///
|
||||
/// # Panic
|
||||
/// If the string contains surrogates.
|
||||
#[inline]
|
||||
#[track_caller]
|
||||
pub fn expect_str(&self) -> &str {
|
||||
self.to_str().expect("PyStr contains surrogates")
|
||||
}
|
||||
|
||||
pub(crate) fn ensure_valid_utf8(&self, vm: &VirtualMachine) -> PyResult<()> {
|
||||
if self.is_utf8() {
|
||||
Ok(())
|
||||
@@ -660,14 +698,7 @@ impl PyStr {
|
||||
match self.as_str_kind() {
|
||||
PyKindStr::Ascii(s) => s.to_ascii_lowercase().into(),
|
||||
PyKindStr::Utf8(s) => s.to_lowercase().into(),
|
||||
PyKindStr::Wtf8(w) => w
|
||||
.chunks()
|
||||
.map(|c| match c {
|
||||
Wtf8Chunk::Utf8(s) => s.to_lowercase().into(),
|
||||
Wtf8Chunk::Surrogate(c) => Wtf8Buf::from(c),
|
||||
})
|
||||
.collect::<Wtf8Buf>()
|
||||
.into(),
|
||||
PyKindStr::Wtf8(w) => w.to_lowercase().into(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -693,14 +724,7 @@ impl PyStr {
|
||||
match self.as_str_kind() {
|
||||
PyKindStr::Ascii(s) => s.to_ascii_uppercase().into(),
|
||||
PyKindStr::Utf8(s) => s.to_uppercase().into(),
|
||||
PyKindStr::Wtf8(w) => w
|
||||
.chunks()
|
||||
.map(|c| match c {
|
||||
Wtf8Chunk::Utf8(s) => s.to_uppercase().into(),
|
||||
Wtf8Chunk::Surrogate(c) => Wtf8Buf::from(c),
|
||||
})
|
||||
.collect::<Wtf8Buf>()
|
||||
.into(),
|
||||
PyKindStr::Wtf8(w) => w.to_uppercase().into(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -732,12 +756,7 @@ impl PyStr {
|
||||
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.push_wtf8(&chars.as_wtf8().to_lowercase());
|
||||
}
|
||||
out
|
||||
}
|
||||
@@ -972,10 +991,9 @@ impl PyStr {
|
||||
#[pymethod]
|
||||
fn __format__(
|
||||
zelf: PyRef<PyStr>,
|
||||
spec: PyStrRef,
|
||||
spec: PyUtf8StrRef,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<PyRef<PyStr>> {
|
||||
let spec = spec.as_str();
|
||||
if spec.is_empty() {
|
||||
return if zelf.class().is(vm.ctx.types.str_type) {
|
||||
Ok(zelf)
|
||||
@@ -984,7 +1002,7 @@ impl PyStr {
|
||||
};
|
||||
}
|
||||
let zelf = zelf.try_into_utf8(vm)?;
|
||||
let s = FormatSpec::parse(spec)
|
||||
let s = FormatSpec::parse(spec.as_str())
|
||||
.and_then(|format_spec| {
|
||||
format_spec.format_string(&CharLenStr(zelf.as_str(), zelf.char_len()))
|
||||
})
|
||||
@@ -1345,33 +1363,34 @@ impl PyStr {
|
||||
|
||||
// https://docs.python.org/3/library/stdtypes.html#str.translate
|
||||
#[pymethod]
|
||||
fn translate(&self, table: PyObjectRef, vm: &VirtualMachine) -> PyResult<String> {
|
||||
fn translate(&self, table: PyObjectRef, vm: &VirtualMachine) -> PyResult<Wtf8Buf> {
|
||||
vm.get_method_or_type_error(table.clone(), identifier!(vm, __getitem__), || {
|
||||
format!("'{}' object is not subscriptable", table.class().name())
|
||||
})?;
|
||||
|
||||
let mut translated = String::new();
|
||||
for c in self.as_str().chars() {
|
||||
match table.get_item(&*(c as u32).to_pyobject(vm), vm) {
|
||||
let mut translated = Wtf8Buf::new();
|
||||
for cp in self.as_wtf8().code_points() {
|
||||
match table.get_item(&*cp.to_u32().to_pyobject(vm), vm) {
|
||||
Ok(value) => {
|
||||
if let Some(text) = value.downcast_ref::<Self>() {
|
||||
translated.push_str(text.as_str());
|
||||
translated.push_wtf8(text.as_wtf8());
|
||||
} else if let Some(bigint) = value.downcast_ref::<PyInt>() {
|
||||
let ch = bigint
|
||||
let mapped = bigint
|
||||
.as_bigint()
|
||||
.to_u32()
|
||||
.and_then(core::char::from_u32)
|
||||
.and_then(CodePoint::from_u32)
|
||||
.ok_or_else(|| {
|
||||
vm.new_value_error("character mapping must be in range(0x110000)")
|
||||
})?;
|
||||
translated.push(ch);
|
||||
translated.push(mapped);
|
||||
} else if !vm.is_none(&value) {
|
||||
return Err(
|
||||
vm.new_type_error("character mapping must return integer, None or str")
|
||||
);
|
||||
}
|
||||
}
|
||||
_ => translated.push(c),
|
||||
Err(e) if e.fast_isinstance(vm.ctx.exceptions.key_error) => translated.push(cp),
|
||||
Err(e) => return Err(e),
|
||||
}
|
||||
}
|
||||
Ok(translated)
|
||||
@@ -1389,16 +1408,20 @@ impl PyStr {
|
||||
match dict_or_str.downcast::<Self>() {
|
||||
Ok(from_str) => {
|
||||
if to_str.len() == from_str.len() {
|
||||
for (c1, c2) in from_str.as_str().chars().zip(to_str.as_str().chars()) {
|
||||
for (c1, c2) in from_str
|
||||
.as_wtf8()
|
||||
.code_points()
|
||||
.zip(to_str.as_wtf8().code_points())
|
||||
{
|
||||
new_dict.set_item(
|
||||
&*vm.new_pyobj(c1 as u32),
|
||||
vm.new_pyobj(c2 as u32),
|
||||
&*vm.new_pyobj(c1.to_u32()),
|
||||
vm.new_pyobj(c2.to_u32()),
|
||||
vm,
|
||||
)?;
|
||||
}
|
||||
if let OptionalArg::Present(none_str) = none_str {
|
||||
for c in none_str.as_str().chars() {
|
||||
new_dict.set_item(&*vm.new_pyobj(c as u32), vm.ctx.none(), vm)?;
|
||||
for c in none_str.as_wtf8().code_points() {
|
||||
new_dict.set_item(&*vm.new_pyobj(c.to_u32()), vm.ctx.none(), vm)?;
|
||||
}
|
||||
}
|
||||
Ok(new_dict.to_pyobject(vm))
|
||||
@@ -1426,7 +1449,8 @@ impl PyStr {
|
||||
)?;
|
||||
} else if let Some(string) = key.downcast_ref::<Self>() {
|
||||
if string.len() == 1 {
|
||||
let num_value = string.as_str().chars().next().unwrap() as u32;
|
||||
let num_value =
|
||||
string.as_wtf8().code_points().next().unwrap().to_u32();
|
||||
new_dict.set_item(&*num_value.to_pyobject(vm), val, vm)?;
|
||||
} else {
|
||||
return Err(vm.new_value_error(
|
||||
@@ -1455,7 +1479,7 @@ impl PyStr {
|
||||
|
||||
#[pymethod]
|
||||
fn __getnewargs__(zelf: PyRef<Self>, vm: &VirtualMachine) -> PyObjectRef {
|
||||
(zelf.as_str(),).to_pyobject(vm)
|
||||
(zelf.as_wtf8(),).to_pyobject(vm)
|
||||
}
|
||||
|
||||
#[pymethod]
|
||||
@@ -1601,20 +1625,21 @@ impl AsSequence for PyStr {
|
||||
#[derive(FromArgs)]
|
||||
struct EncodeArgs {
|
||||
#[pyarg(any, default)]
|
||||
encoding: Option<PyStrRef>,
|
||||
encoding: Option<PyUtf8StrRef>,
|
||||
#[pyarg(any, default)]
|
||||
errors: Option<PyStrRef>,
|
||||
errors: Option<PyUtf8StrRef>,
|
||||
}
|
||||
|
||||
pub(crate) fn encode_string(
|
||||
s: PyStrRef,
|
||||
encoding: Option<PyStrRef>,
|
||||
errors: Option<PyStrRef>,
|
||||
encoding: Option<PyUtf8StrRef>,
|
||||
errors: Option<PyUtf8StrRef>,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<PyBytesRef> {
|
||||
let encoding = encoding
|
||||
.as_ref()
|
||||
.map_or(crate::codecs::DEFAULT_ENCODING, |s| s.as_str());
|
||||
let encoding = match encoding.as_ref() {
|
||||
None => crate::codecs::DEFAULT_ENCODING,
|
||||
Some(s) => s.as_str(),
|
||||
};
|
||||
vm.state.codec_registry.encode_text(s, encoding, errors, vm)
|
||||
}
|
||||
|
||||
@@ -1868,14 +1893,16 @@ impl SliceableSequenceOp for PyStr {
|
||||
}
|
||||
|
||||
impl AsRef<str> for PyRefExact<PyStr> {
|
||||
#[track_caller]
|
||||
fn as_ref(&self) -> &str {
|
||||
self.as_str()
|
||||
self.to_str().expect("str has surrogates")
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<str> for PyExact<PyStr> {
|
||||
#[track_caller]
|
||||
fn as_ref(&self) -> &str {
|
||||
self.as_str()
|
||||
self.to_str().expect("str has surrogates")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2033,7 +2060,7 @@ impl AsRef<Wtf8> for PyUtf8Str {
|
||||
impl AsRef<str> for PyUtf8Str {
|
||||
#[inline]
|
||||
fn as_ref(&self) -> &str {
|
||||
self.0.as_str()
|
||||
self.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2045,6 +2072,12 @@ impl PyUtf8Str {
|
||||
Self(PyStr::from(data))
|
||||
}
|
||||
|
||||
/// Returns the underlying WTF-8 slice (always valid UTF-8 for this type).
|
||||
#[inline]
|
||||
pub fn as_wtf8(&self) -> &Wtf8 {
|
||||
self.0.as_wtf8()
|
||||
}
|
||||
|
||||
/// Returns the underlying string slice.
|
||||
pub fn as_str(&self) -> &str {
|
||||
debug_assert!(
|
||||
@@ -2055,6 +2088,11 @@ impl PyUtf8Str {
|
||||
unsafe { self.0.to_str().unwrap_unchecked() }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn as_bytes(&self) -> &[u8] {
|
||||
self.as_str().as_bytes()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn byte_len(&self) -> usize {
|
||||
self.0.byte_len()
|
||||
@@ -2079,6 +2117,29 @@ impl Py<PyUtf8Str> {
|
||||
&*(self as *const Self as *const Py<PyStr>)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the underlying `&str`.
|
||||
#[inline]
|
||||
pub fn as_str(&self) -> &str {
|
||||
self.as_pystr().to_str().unwrap_or_else(|| {
|
||||
debug_assert!(false, "PyUtf8Str invariant violated");
|
||||
// Safety: PyUtf8Str guarantees valid UTF-8
|
||||
unsafe { core::hint::unreachable_unchecked() }
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl PyRef<PyUtf8Str> {
|
||||
/// Convert to PyStrRef. Safe because PyUtf8Str is a subtype of PyStr.
|
||||
pub fn into_wtf8(self) -> PyStrRef {
|
||||
unsafe { mem::transmute::<Self, PyStrRef>(self) }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PyRef<PyUtf8Str>> for PyRef<PyStr> {
|
||||
fn from(s: PyRef<PyUtf8Str>) -> Self {
|
||||
s.into_wtf8()
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for PyUtf8Str {
|
||||
@@ -2454,10 +2515,54 @@ impl AsRef<str> for PyStrInterned {
|
||||
}
|
||||
}
|
||||
|
||||
/// Interned PyUtf8Str — guaranteed UTF-8 at type level.
|
||||
/// Same layout as `PyStrInterned` due to `#[repr(transparent)]` on both
|
||||
/// `PyInterned<T>` and `PyUtf8Str`.
|
||||
pub type PyUtf8StrInterned = PyInterned<PyUtf8Str>;
|
||||
|
||||
impl PyUtf8StrInterned {
|
||||
/// Returns the underlying `&str`.
|
||||
#[inline]
|
||||
pub fn as_str(&self) -> &str {
|
||||
Py::<PyUtf8Str>::as_str(self)
|
||||
}
|
||||
|
||||
/// View as `PyStrInterned` (widening: UTF-8 → WTF-8).
|
||||
#[inline]
|
||||
pub fn as_interned_str(&self) -> &PyStrInterned {
|
||||
// Safety: PyUtf8Str is #[repr(transparent)] over PyStr,
|
||||
// so PyInterned<PyUtf8Str> has the same layout as PyInterned<PyStr>.
|
||||
unsafe { &*(self as *const Self as *const PyStrInterned) }
|
||||
}
|
||||
|
||||
/// Narrow a `PyStrInterned` to `PyUtf8StrInterned`.
|
||||
///
|
||||
/// # Safety
|
||||
/// The caller must ensure that the interned string is valid UTF-8.
|
||||
#[inline]
|
||||
pub unsafe fn from_str_interned_unchecked(s: &PyStrInterned) -> &Self {
|
||||
unsafe { &*(s as *const PyStrInterned as *const Self) }
|
||||
}
|
||||
}
|
||||
|
||||
impl core::fmt::Display for PyUtf8StrInterned {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
f.write_str(self.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<str> for PyUtf8StrInterned {
|
||||
#[inline(always)]
|
||||
fn as_ref(&self) -> &str {
|
||||
self.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::Interpreter;
|
||||
use rustpython_common::wtf8::Wtf8Buf;
|
||||
|
||||
#[test]
|
||||
fn str_title() {
|
||||
@@ -2525,7 +2630,7 @@ mod tests {
|
||||
.unwrap();
|
||||
let text = PyStr::from("abc");
|
||||
let translated = text.translate(translated, vm).unwrap();
|
||||
assert_eq!(translated, "🎅xda".to_owned());
|
||||
assert_eq!(translated, Wtf8Buf::from("🎅xda"));
|
||||
let translated = text.translate(vm.ctx.new_int(3).into(), vm);
|
||||
assert_eq!("TypeError", &*translated.unwrap_err().class().name(),);
|
||||
})
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
use super::{PyStr, PyTupleRef, PyType, PyTypeRef, genericalias::PyGenericAlias};
|
||||
use super::{
|
||||
PyStr, PyTupleRef, PyType, PyTypeRef, genericalias::PyGenericAlias,
|
||||
interpolation::PyInterpolation,
|
||||
};
|
||||
use crate::{
|
||||
AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine,
|
||||
atomic_func,
|
||||
@@ -11,8 +14,7 @@ use crate::{
|
||||
SelfIter,
|
||||
},
|
||||
};
|
||||
|
||||
use super::interpolation::PyInterpolation;
|
||||
use rustpython_common::wtf8::{Wtf8Buf, wtf8_concat};
|
||||
|
||||
/// Template object for t-strings (PEP 750).
|
||||
///
|
||||
@@ -58,8 +60,9 @@ impl Constructor for PyTemplate {
|
||||
// Concatenate adjacent strings
|
||||
if let Some(last) = strings.last_mut() {
|
||||
let last_str = last.downcast_ref::<PyStr>().unwrap();
|
||||
let concatenated = format!("{}{}", last_str.as_str(), s.as_str());
|
||||
*last = vm.ctx.new_str(concatenated).into();
|
||||
let mut buf = last_str.as_wtf8().to_owned();
|
||||
buf.push_wtf8(s.as_wtf8());
|
||||
*last = vm.ctx.new_str(buf).into();
|
||||
}
|
||||
} else {
|
||||
strings.push(s.into());
|
||||
@@ -143,18 +146,22 @@ impl PyTemplate {
|
||||
}
|
||||
|
||||
// Concatenate last string of self with first string of other
|
||||
let last_self = self
|
||||
let mut buf = Wtf8Buf::new();
|
||||
if let Some(s) = self
|
||||
.strings
|
||||
.get(self_strings_len.saturating_sub(1))
|
||||
.and_then(|s| s.downcast_ref::<PyStr>().map(|s| s.as_str().to_owned()))
|
||||
.unwrap_or_default();
|
||||
let first_other = other
|
||||
.and_then(|s| s.downcast_ref::<PyStr>())
|
||||
{
|
||||
buf.push_wtf8(s.as_wtf8());
|
||||
}
|
||||
if let Some(s) = other
|
||||
.strings
|
||||
.first()
|
||||
.and_then(|s| s.downcast_ref::<PyStr>().map(|s| s.as_str().to_owned()))
|
||||
.unwrap_or_default();
|
||||
let concatenated = format!("{}{}", last_self, first_other);
|
||||
new_strings.push(vm.ctx.new_str(concatenated).into());
|
||||
.and_then(|s| s.downcast_ref::<PyStr>())
|
||||
{
|
||||
buf.push_wtf8(s.as_wtf8());
|
||||
}
|
||||
new_strings.push(vm.ctx.new_str(buf).into());
|
||||
|
||||
// Add remaining strings from other (skip first)
|
||||
for i in 1..other.strings.len() {
|
||||
@@ -243,31 +250,16 @@ impl Iterable for PyTemplate {
|
||||
|
||||
impl Representable for PyTemplate {
|
||||
#[inline]
|
||||
fn repr_str(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<String> {
|
||||
let mut parts = Vec::new();
|
||||
|
||||
let strings_len = zelf.strings.len();
|
||||
let interps_len = zelf.interpolations.len();
|
||||
|
||||
for i in 0..strings_len.max(interps_len * 2 + 1) {
|
||||
if i % 2 == 0 {
|
||||
// String position
|
||||
let idx = i / 2;
|
||||
if idx < strings_len {
|
||||
let s = zelf.strings.get(idx).unwrap();
|
||||
parts.push(s.repr(vm)?.as_str().to_owned());
|
||||
}
|
||||
} else {
|
||||
// Interpolation position
|
||||
let idx = i / 2;
|
||||
if idx < interps_len {
|
||||
let interp = zelf.interpolations.get(idx).unwrap();
|
||||
parts.push(interp.repr(vm)?.as_str().to_owned());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(format!("Template({})", parts.join(", ")))
|
||||
fn repr_wtf8(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<Wtf8Buf> {
|
||||
let strings_repr = zelf.strings.as_object().repr(vm)?;
|
||||
let interp_repr = zelf.interpolations.as_object().repr(vm)?;
|
||||
Ok(wtf8_concat!(
|
||||
"Template(strings=",
|
||||
strings_repr.as_wtf8(),
|
||||
", interpolations=",
|
||||
interp_repr.as_wtf8(),
|
||||
')',
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -317,7 +309,7 @@ impl IterNext for PyTemplateIter {
|
||||
|
||||
// Skip empty strings
|
||||
if let Some(s) = item.downcast_ref::<PyStr>()
|
||||
&& s.as_str().is_empty()
|
||||
&& s.as_wtf8().is_empty()
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ use crate::common::lock::LazyLock;
|
||||
use crate::common::{
|
||||
hash::{PyHash, PyUHash},
|
||||
lock::PyMutex,
|
||||
wtf8::wtf8_concat,
|
||||
};
|
||||
use crate::object::{Traverse, TraverseFn};
|
||||
use crate::{
|
||||
@@ -481,7 +482,7 @@ impl Representable for PyTuple {
|
||||
vm.ctx.intern_str("()").to_owned()
|
||||
} else if let Some(_guard) = ReprGuard::enter(vm, zelf.as_object()) {
|
||||
let s = if zelf.len() == 1 {
|
||||
format!("({},)", zelf.elements[0].repr(vm)?)
|
||||
wtf8_concat!("(", zelf.elements[0].repr(vm)?.as_wtf8(), ",)")
|
||||
} else {
|
||||
collection_repr(None, "(", ")", zelf.elements.iter(), vm)?
|
||||
};
|
||||
|
||||
+158
-151
@@ -1,6 +1,6 @@
|
||||
use super::{
|
||||
PyClassMethod, PyDictRef, PyList, PyStaticMethod, PyStr, PyStrInterned, PyStrRef, PyTupleRef,
|
||||
PyWeak, mappingproxy::PyMappingProxy, object, union_,
|
||||
PyUtf8StrRef, PyWeak, mappingproxy::PyMappingProxy, object, union_,
|
||||
};
|
||||
use crate::{
|
||||
AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, TryFromObject,
|
||||
@@ -32,6 +32,7 @@ use core::{any::Any, borrow::Borrow, ops::Deref, pin::Pin, ptr::NonNull};
|
||||
use indexmap::{IndexMap, map::Entry};
|
||||
use itertools::Itertools;
|
||||
use num_traits::ToPrimitive;
|
||||
use rustpython_common::wtf8::Wtf8;
|
||||
use std::collections::HashSet;
|
||||
|
||||
#[pyclass(module = false, name = "type", traverse = "manual")]
|
||||
@@ -88,7 +89,7 @@ unsafe impl crate::object::Traverse for PyType {
|
||||
|
||||
// PyHeapTypeObject in CPython
|
||||
pub struct HeapTypeExt {
|
||||
pub name: PyRwLock<PyStrRef>,
|
||||
pub name: PyRwLock<PyUtf8StrRef>,
|
||||
pub qualname: PyRwLock<PyStrRef>,
|
||||
pub slots: Option<PyRef<PyTuple<PyStrRef>>>,
|
||||
pub type_data: PyRwLock<Option<TypeDataSlot>>,
|
||||
@@ -215,9 +216,10 @@ impl PyType {
|
||||
// Set HEAPTYPE flag for heap-allocated types
|
||||
slots.flags |= PyTypeFlags::HEAPTYPE;
|
||||
|
||||
let name = ctx.new_str(name);
|
||||
let name_utf8 = ctx.new_utf8_str(name);
|
||||
let name = name_utf8.clone().into_wtf8();
|
||||
let heaptype_ext = HeapTypeExt {
|
||||
name: PyRwLock::new(name.clone()),
|
||||
name: PyRwLock::new(name_utf8),
|
||||
qualname: PyRwLock::new(name),
|
||||
slots: None,
|
||||
type_data: PyRwLock::new(None),
|
||||
@@ -619,14 +621,24 @@ impl PyType {
|
||||
pub fn slot_name(&self) -> BorrowedValue<'_, str> {
|
||||
self.name_inner(
|
||||
|name| name.into(),
|
||||
|ext| PyRwLockReadGuard::map(ext.name.read(), |name| name.as_str()).into(),
|
||||
|ext| {
|
||||
PyRwLockReadGuard::map(ext.name.read(), |name: &PyUtf8StrRef| -> &str {
|
||||
name.as_str()
|
||||
})
|
||||
.into()
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
pub fn name(&self) -> BorrowedValue<'_, str> {
|
||||
self.name_inner(
|
||||
|name| name.rsplit_once('.').map_or(name, |(_, name)| name).into(),
|
||||
|ext| PyRwLockReadGuard::map(ext.name.read(), |name| name.as_str()).into(),
|
||||
|ext| {
|
||||
PyRwLockReadGuard::map(ext.name.read(), |name: &PyUtf8StrRef| -> &str {
|
||||
name.as_str()
|
||||
})
|
||||
.into()
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@@ -837,7 +849,7 @@ impl PyType {
|
||||
})
|
||||
.to_owned()
|
||||
},
|
||||
|ext| ext.name.read().clone(),
|
||||
|ext| ext.name.read().clone().into_wtf8(),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1158,7 +1170,7 @@ impl PyType {
|
||||
if name.as_bytes().contains(&0) {
|
||||
return Err(vm.new_value_error("type name must not contain null characters"));
|
||||
}
|
||||
name.ensure_valid_utf8(vm)?;
|
||||
let name = name.try_into_utf8(vm)?;
|
||||
|
||||
let heap_type = self.heaptype_ext.as_ref().ok_or_else(|| {
|
||||
vm.new_type_error(format!(
|
||||
@@ -1257,7 +1269,7 @@ impl Constructor for PyType {
|
||||
if name.as_bytes().contains(&0) {
|
||||
return Err(vm.new_value_error("type name must not contain null characters"));
|
||||
}
|
||||
name.ensure_valid_utf8(vm)?;
|
||||
let name = name.try_into_utf8(vm)?;
|
||||
|
||||
let (metatype, base, bases, base_is_type) = if bases.is_empty() {
|
||||
let base = vm.ctx.types.object_type.to_owned();
|
||||
@@ -1306,7 +1318,7 @@ impl Constructor for PyType {
|
||||
.transpose()?
|
||||
.unwrap_or_else(|| {
|
||||
// If __qualname__ is not provided, we can use the name as default
|
||||
name.clone()
|
||||
name.clone().into_wtf8()
|
||||
});
|
||||
let mut attributes = dict.to_attributes(vm);
|
||||
|
||||
@@ -1355,144 +1367,141 @@ impl Constructor for PyType {
|
||||
attributes.insert(identifier!(vm, __hash__), vm.ctx.none.clone().into());
|
||||
}
|
||||
|
||||
let (heaptype_slots, add_dict): (Option<PyRef<PyTuple<PyStrRef>>>, bool) = if let Some(x) =
|
||||
attributes.get(identifier!(vm, __slots__))
|
||||
{
|
||||
// Check if __slots__ is bytes - not allowed
|
||||
if x.class().is(vm.ctx.types.bytes_type) {
|
||||
return Err(
|
||||
vm.new_type_error("__slots__ items must be strings, not 'bytes'".to_owned())
|
||||
);
|
||||
}
|
||||
let (heaptype_slots, add_dict): (Option<PyRef<PyTuple<PyStrRef>>>, bool) =
|
||||
if let Some(x) = attributes.get(identifier!(vm, __slots__)) {
|
||||
// Check if __slots__ is bytes - not allowed
|
||||
if x.class().is(vm.ctx.types.bytes_type) {
|
||||
return Err(vm.new_type_error("__slots__ items must be strings, not 'bytes'"));
|
||||
}
|
||||
|
||||
let slots = if x.class().is(vm.ctx.types.str_type) {
|
||||
let x = unsafe { x.downcast_unchecked_ref::<PyStr>() };
|
||||
PyTuple::new_ref_typed(vec![x.to_owned()], &vm.ctx)
|
||||
} else {
|
||||
let iter = x.get_iter(vm)?;
|
||||
let elements = {
|
||||
let mut elements = Vec::new();
|
||||
while let PyIterReturn::Return(element) = iter.next(vm)? {
|
||||
// Check if any slot item is bytes
|
||||
if element.class().is(vm.ctx.types.bytes_type) {
|
||||
return Err(vm.new_type_error(
|
||||
"__slots__ items must be strings, not 'bytes'".to_owned(),
|
||||
));
|
||||
let slots = if x.class().is(vm.ctx.types.str_type) {
|
||||
let x = unsafe { x.downcast_unchecked_ref::<PyStr>() };
|
||||
PyTuple::new_ref_typed(vec![x.to_owned()], &vm.ctx)
|
||||
} else {
|
||||
let iter = x.get_iter(vm)?;
|
||||
let elements = {
|
||||
let mut elements = Vec::new();
|
||||
while let PyIterReturn::Return(element) = iter.next(vm)? {
|
||||
// Check if any slot item is bytes
|
||||
if element.class().is(vm.ctx.types.bytes_type) {
|
||||
return Err(vm.new_type_error(
|
||||
"__slots__ items must be strings, not 'bytes'",
|
||||
));
|
||||
}
|
||||
elements.push(element);
|
||||
}
|
||||
elements.push(element);
|
||||
}
|
||||
elements
|
||||
elements
|
||||
};
|
||||
let tuple = elements.into_pytuple(vm);
|
||||
tuple.try_into_typed(vm)?
|
||||
};
|
||||
let tuple = elements.into_pytuple(vm);
|
||||
tuple.try_into_typed(vm)?
|
||||
};
|
||||
|
||||
// Check if base has itemsize > 0 - can't add arbitrary slots to variable-size types
|
||||
// Types like int, bytes, tuple have itemsize > 0 and don't allow custom slots
|
||||
// But types like weakref.ref have itemsize = 0 and DO allow slots
|
||||
let has_custom_slots = slots
|
||||
.iter()
|
||||
.any(|s| s.as_str() != "__dict__" && s.as_str() != "__weakref__");
|
||||
if has_custom_slots && base.slots.itemsize > 0 {
|
||||
return Err(vm.new_type_error(format!(
|
||||
"nonempty __slots__ not supported for subtype of '{}'",
|
||||
base.name()
|
||||
)));
|
||||
}
|
||||
|
||||
// Validate slot names and track duplicates
|
||||
let mut seen_dict = false;
|
||||
let mut seen_weakref = false;
|
||||
for slot in slots.iter() {
|
||||
// Use isidentifier for validation (handles Unicode properly)
|
||||
if !slot.isidentifier() {
|
||||
return Err(vm.new_type_error("__slots__ must be identifiers".to_owned()));
|
||||
}
|
||||
|
||||
let slot_name = slot.as_str();
|
||||
|
||||
// Check for duplicate __dict__
|
||||
if slot_name == "__dict__" {
|
||||
if seen_dict {
|
||||
return Err(vm.new_type_error(
|
||||
"__dict__ slot disallowed: we already got one".to_owned(),
|
||||
));
|
||||
}
|
||||
seen_dict = true;
|
||||
}
|
||||
|
||||
// Check for duplicate __weakref__
|
||||
if slot_name == "__weakref__" {
|
||||
if seen_weakref {
|
||||
return Err(vm.new_type_error(
|
||||
"__weakref__ slot disallowed: we already got one".to_owned(),
|
||||
));
|
||||
}
|
||||
seen_weakref = true;
|
||||
}
|
||||
|
||||
// Check if slot name conflicts with class attributes
|
||||
if attributes.contains_key(vm.ctx.intern_str(slot_name)) {
|
||||
return Err(vm.new_value_error(format!(
|
||||
"'{}' in __slots__ conflicts with a class variable",
|
||||
slot_name
|
||||
// Check if base has itemsize > 0 - can't add arbitrary slots to variable-size types
|
||||
// Types like int, bytes, tuple have itemsize > 0 and don't allow custom slots
|
||||
// But types like weakref.ref have itemsize = 0 and DO allow slots
|
||||
let has_custom_slots = slots
|
||||
.iter()
|
||||
.any(|s| !matches!(s.as_bytes(), b"__dict__" | b"__weakref__"));
|
||||
if has_custom_slots && base.slots.itemsize > 0 {
|
||||
return Err(vm.new_type_error(format!(
|
||||
"nonempty __slots__ not supported for subtype of '{}'",
|
||||
base.name()
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
// Check if base class already has __dict__ - can't redefine it
|
||||
if seen_dict && base.slots.flags.has_feature(PyTypeFlags::HAS_DICT) {
|
||||
return Err(
|
||||
vm.new_type_error("__dict__ slot disallowed: we already got one".to_owned())
|
||||
);
|
||||
}
|
||||
|
||||
// Check if base class already has __weakref__ - can't redefine it
|
||||
// A base has weakref support if:
|
||||
// 1. It's a heap type without explicit __slots__ (automatic weakref), OR
|
||||
// 2. It's a heap type with __weakref__ in its __slots__
|
||||
if seen_weakref {
|
||||
let base_has_weakref = if let Some(ref ext) = base.heaptype_ext {
|
||||
match &ext.slots {
|
||||
// Heap type without __slots__ - has automatic weakref
|
||||
None => true,
|
||||
// Heap type with __slots__ - check if __weakref__ is in slots
|
||||
Some(base_slots) => base_slots.iter().any(|s| s.as_str() == "__weakref__"),
|
||||
// Validate slot names and track duplicates
|
||||
let mut seen_dict = false;
|
||||
let mut seen_weakref = false;
|
||||
for slot in slots.iter() {
|
||||
// Use isidentifier for validation (handles Unicode properly)
|
||||
if !slot.isidentifier() {
|
||||
return Err(vm.new_type_error("__slots__ must be identifiers"));
|
||||
}
|
||||
|
||||
let slot_name = slot.as_bytes();
|
||||
|
||||
// Check for duplicate __dict__
|
||||
if slot_name == b"__dict__" {
|
||||
if seen_dict {
|
||||
return Err(
|
||||
vm.new_type_error("__dict__ slot disallowed: we already got one")
|
||||
);
|
||||
}
|
||||
seen_dict = true;
|
||||
}
|
||||
|
||||
// Check for duplicate __weakref__
|
||||
if slot_name == b"__weakref__" {
|
||||
if seen_weakref {
|
||||
return Err(vm.new_type_error(
|
||||
"__weakref__ slot disallowed: we already got one",
|
||||
));
|
||||
}
|
||||
seen_weakref = true;
|
||||
}
|
||||
|
||||
// Check if slot name conflicts with class attributes
|
||||
if attributes.contains_key(vm.ctx.intern_str(slot.as_wtf8())) {
|
||||
return Err(vm.new_value_error(format!(
|
||||
"'{}' in __slots__ conflicts with a class variable",
|
||||
slot.as_wtf8()
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
// Check if base class already has __dict__ - can't redefine it
|
||||
if seen_dict && base.slots.flags.has_feature(PyTypeFlags::HAS_DICT) {
|
||||
return Err(vm.new_type_error("__dict__ slot disallowed: we already got one"));
|
||||
}
|
||||
|
||||
// Check if base class already has __weakref__ - can't redefine it
|
||||
// A base has weakref support if:
|
||||
// 1. It's a heap type without explicit __slots__ (automatic weakref), OR
|
||||
// 2. It's a heap type with __weakref__ in its __slots__
|
||||
if seen_weakref {
|
||||
let base_has_weakref = if let Some(ref ext) = base.heaptype_ext {
|
||||
match &ext.slots {
|
||||
// Heap type without __slots__ - has automatic weakref
|
||||
None => true,
|
||||
// Heap type with __slots__ - check if __weakref__ is in slots
|
||||
Some(base_slots) => {
|
||||
base_slots.iter().any(|s| s.as_bytes() == b"__weakref__")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Builtin type - check if it has __weakref__ descriptor
|
||||
let weakref_name = vm.ctx.intern_str("__weakref__");
|
||||
base.attributes.read().contains_key(weakref_name)
|
||||
};
|
||||
|
||||
if base_has_weakref {
|
||||
return Err(
|
||||
vm.new_type_error("__weakref__ slot disallowed: we already got one")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Check if __dict__ is in slots
|
||||
let dict_name = "__dict__";
|
||||
let has_dict = slots.iter().any(|s| s.as_wtf8() == dict_name);
|
||||
|
||||
// Filter out __dict__ from slots
|
||||
let filtered_slots = if has_dict {
|
||||
let filtered: Vec<PyStrRef> = slots
|
||||
.iter()
|
||||
.filter(|s| s.as_wtf8() != dict_name)
|
||||
.cloned()
|
||||
.collect();
|
||||
PyTuple::new_ref_typed(filtered, &vm.ctx)
|
||||
} else {
|
||||
// Builtin type - check if it has __weakref__ descriptor
|
||||
let weakref_name = vm.ctx.intern_str("__weakref__");
|
||||
base.attributes.read().contains_key(weakref_name)
|
||||
slots
|
||||
};
|
||||
|
||||
if base_has_weakref {
|
||||
return Err(vm.new_type_error(
|
||||
"__weakref__ slot disallowed: we already got one".to_owned(),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// Check if __dict__ is in slots
|
||||
let dict_name = "__dict__";
|
||||
let has_dict = slots.iter().any(|s| s.as_str() == dict_name);
|
||||
|
||||
// Filter out __dict__ from slots
|
||||
let filtered_slots = if has_dict {
|
||||
let filtered: Vec<PyStrRef> = slots
|
||||
.iter()
|
||||
.filter(|s| s.as_str() != dict_name)
|
||||
.cloned()
|
||||
.collect();
|
||||
PyTuple::new_ref_typed(filtered, &vm.ctx)
|
||||
(Some(filtered_slots), has_dict)
|
||||
} else {
|
||||
slots
|
||||
(None, false)
|
||||
};
|
||||
|
||||
(Some(filtered_slots), has_dict)
|
||||
} else {
|
||||
(None, false)
|
||||
};
|
||||
|
||||
// FIXME: this is a temporary fix. multi bases with multiple slots will break object
|
||||
let base_member_count = bases
|
||||
.iter()
|
||||
@@ -1548,7 +1557,10 @@ impl Constructor for PyType {
|
||||
let class_name = typ.name().to_string();
|
||||
for member in slots.as_slice() {
|
||||
// Apply name mangling for private attributes (__x -> _ClassName__x)
|
||||
let mangled_name = mangle_name(&class_name, member.as_str());
|
||||
let member_str = member.to_str().ok_or_else(|| {
|
||||
vm.new_type_error("__slots__ must be valid UTF-8 strings".to_owned())
|
||||
})?;
|
||||
let mangled_name = mangle_name(&class_name, member_str);
|
||||
let member_def = PyMemberDef {
|
||||
name: mangled_name.clone(),
|
||||
kind: MemberKind::ObjectEx,
|
||||
@@ -1720,7 +1732,7 @@ impl GetAttr for PyType {
|
||||
#[cold]
|
||||
fn attribute_error(
|
||||
zelf: &Py<PyType>,
|
||||
name: &str,
|
||||
name: &Wtf8,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyBaseExceptionRef {
|
||||
vm.new_attribute_error(format!(
|
||||
@@ -1731,7 +1743,7 @@ impl GetAttr for PyType {
|
||||
}
|
||||
|
||||
let Some(name) = vm.ctx.interned_str(name_str) else {
|
||||
return Err(attribute_error(zelf, name_str.as_str(), vm));
|
||||
return Err(attribute_error(zelf, name_str.as_wtf8(), vm));
|
||||
};
|
||||
vm_trace!("type.__getattribute__({:?}, {:?})", zelf, name);
|
||||
let mcl = zelf.class();
|
||||
@@ -1761,7 +1773,7 @@ impl GetAttr for PyType {
|
||||
} else if let Some(attr) = mcl_attr {
|
||||
vm.call_if_get_descriptor(&attr, zelf.to_owned().into())
|
||||
} else {
|
||||
Err(attribute_error(zelf, name_str.as_str(), vm))
|
||||
Err(attribute_error(zelf, name_str.as_wtf8(), vm))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1863,8 +1875,7 @@ impl SetAttr for PyType {
|
||||
value: PySetterValue,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<()> {
|
||||
// TODO: pass PyRefExact instead of &str
|
||||
let attr_name = vm.ctx.intern_str(attr_name.as_str());
|
||||
let attr_name = vm.ctx.intern_str(attr_name.as_wtf8());
|
||||
if zelf.slots.flags.has_feature(PyTypeFlags::IMMUTABLETYPE) {
|
||||
return Err(vm.new_type_error(format!(
|
||||
"cannot set '{}' attribute of immutable type '{}'",
|
||||
@@ -1888,11 +1899,11 @@ impl SetAttr for PyType {
|
||||
return Err(vm.new_attribute_error(format!(
|
||||
"type object '{}' has no attribute '{}'",
|
||||
zelf.name(),
|
||||
attr_name.as_str(),
|
||||
attr_name,
|
||||
)));
|
||||
}
|
||||
}
|
||||
if attr_name.as_str().starts_with("__") && attr_name.as_str().ends_with("__") {
|
||||
if attr_name.as_wtf8().starts_with("__") && attr_name.as_wtf8().ends_with("__") {
|
||||
if assign {
|
||||
zelf.update_slot::<true>(attr_name, &vm.ctx);
|
||||
} else {
|
||||
@@ -1949,19 +1960,15 @@ impl Representable for PyType {
|
||||
#[inline]
|
||||
fn repr_str(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<String> {
|
||||
let module = zelf.__module__(vm);
|
||||
let module = module.downcast_ref::<PyStr>().map(|m| m.as_str());
|
||||
let module = module.downcast_ref::<PyStr>().map(|m| m.as_wtf8());
|
||||
|
||||
let repr = match module {
|
||||
Some(module) if module != "builtins" => {
|
||||
let qualname = zelf.__qualname__(vm);
|
||||
let qualname = qualname.downcast_ref::<PyStr>().map(|n| n.as_wtf8());
|
||||
let name = zelf.name();
|
||||
format!(
|
||||
"<class '{}.{}'>",
|
||||
module,
|
||||
zelf.__qualname__(vm)
|
||||
.downcast_ref::<PyStr>()
|
||||
.map(|n| n.as_str())
|
||||
.unwrap_or_else(|| &name)
|
||||
)
|
||||
let qualname = qualname.unwrap_or_else(|| name.as_ref());
|
||||
format!("<class '{module}.{qualname}'>")
|
||||
}
|
||||
_ => format!("<class '{}'>", zelf.slot_name()),
|
||||
};
|
||||
|
||||
@@ -142,7 +142,7 @@ impl IterNext for PyWeakProxy {
|
||||
fn new_reference_error(vm: &VirtualMachine) -> PyRef<super::PyBaseException> {
|
||||
vm.new_exception_msg(
|
||||
vm.ctx.exceptions.reference_error.to_owned(),
|
||||
"weakly-referenced object no longer exists".to_owned(),
|
||||
"weakly-referenced object no longer exists".into(),
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ use crate::{
|
||||
anystr::{self, AnyStr, AnyStrContainer, AnyStrWrapper},
|
||||
builtins::{
|
||||
PyBaseExceptionRef, PyByteArray, PyBytes, PyBytesRef, PyInt, PyIntRef, PyStr, PyStrRef,
|
||||
pystr,
|
||||
pystr, pystr::PyUtf8StrRef,
|
||||
},
|
||||
byte::bytes_from_object,
|
||||
cformat::cformat_bytes,
|
||||
@@ -45,16 +45,16 @@ pub struct ByteInnerNewOptions {
|
||||
#[pyarg(any, optional)]
|
||||
pub source: OptionalArg<PyObjectRef>,
|
||||
#[pyarg(any, optional)]
|
||||
pub encoding: OptionalArg<PyStrRef>,
|
||||
pub encoding: OptionalArg<PyUtf8StrRef>,
|
||||
#[pyarg(any, optional)]
|
||||
pub errors: OptionalArg<PyStrRef>,
|
||||
pub errors: OptionalArg<PyUtf8StrRef>,
|
||||
}
|
||||
|
||||
impl ByteInnerNewOptions {
|
||||
fn get_value_from_string(
|
||||
s: PyStrRef,
|
||||
encoding: PyStrRef,
|
||||
errors: OptionalArg<PyStrRef>,
|
||||
encoding: PyUtf8StrRef,
|
||||
errors: OptionalArg<PyUtf8StrRef>,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<PyBytesInner> {
|
||||
let bytes = pystr::encode_string(s, Some(encoding), errors.into_option(), vm)?;
|
||||
@@ -1114,9 +1114,9 @@ impl AnyStr for [u8] {
|
||||
#[derive(FromArgs)]
|
||||
pub struct DecodeArgs {
|
||||
#[pyarg(any, default)]
|
||||
encoding: Option<PyStrRef>,
|
||||
encoding: Option<PyUtf8StrRef>,
|
||||
#[pyarg(any, default)]
|
||||
errors: Option<PyStrRef>,
|
||||
errors: Option<PyUtf8StrRef>,
|
||||
}
|
||||
|
||||
pub fn bytes_decode(
|
||||
@@ -1125,9 +1125,10 @@ pub fn bytes_decode(
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<PyStrRef> {
|
||||
let DecodeArgs { encoding, errors } = args;
|
||||
let encoding = encoding
|
||||
.as_ref()
|
||||
.map_or(crate::codecs::DEFAULT_ENCODING, |s| s.as_str());
|
||||
let encoding = match encoding.as_ref() {
|
||||
None => crate::codecs::DEFAULT_ENCODING,
|
||||
Some(e) => e.as_str(),
|
||||
};
|
||||
vm.state
|
||||
.codec_registry
|
||||
.decode_text(zelf, encoding, errors, vm)
|
||||
@@ -1205,7 +1206,7 @@ pub fn bytes_to_hex(
|
||||
let b_guard;
|
||||
let sep = match &sep {
|
||||
Either::A(s) => {
|
||||
s_guard = s.as_str();
|
||||
s_guard = s.as_wtf8();
|
||||
s_guard.as_bytes()
|
||||
}
|
||||
Either::B(bytes) => {
|
||||
|
||||
+23
-19
@@ -10,9 +10,12 @@ use rustpython_common::{
|
||||
|
||||
use crate::common::lock::OnceCell;
|
||||
use crate::{
|
||||
AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyResult, TryFromBorrowedObject,
|
||||
TryFromObject, VirtualMachine,
|
||||
builtins::{PyBaseExceptionRef, PyBytes, PyBytesRef, PyStr, PyStrRef, PyTuple, PyTupleRef},
|
||||
AsObject, Context, Py, PyObject, PyObjectRef, PyResult, TryFromBorrowedObject, TryFromObject,
|
||||
VirtualMachine,
|
||||
builtins::{
|
||||
PyBaseExceptionRef, PyBytes, PyBytesRef, PyStr, PyStrRef, PyTuple, PyTupleRef, PyUtf8Str,
|
||||
PyUtf8StrRef,
|
||||
},
|
||||
common::{ascii, lock::PyRwLock},
|
||||
convert::ToPyObject,
|
||||
function::{ArgBytesLike, PyMethodDef},
|
||||
@@ -71,11 +74,11 @@ impl PyCodec {
|
||||
pub fn encode(
|
||||
&self,
|
||||
obj: PyObjectRef,
|
||||
errors: Option<PyStrRef>,
|
||||
errors: Option<PyUtf8StrRef>,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult {
|
||||
let args = match errors {
|
||||
Some(errors) => vec![obj, errors.into()],
|
||||
Some(errors) => vec![obj, errors.into_wtf8().into()],
|
||||
None => vec![obj],
|
||||
};
|
||||
let res = self.get_encode_func().call(args, vm)?;
|
||||
@@ -91,11 +94,11 @@ impl PyCodec {
|
||||
pub fn decode(
|
||||
&self,
|
||||
obj: PyObjectRef,
|
||||
errors: Option<PyStrRef>,
|
||||
errors: Option<PyUtf8StrRef>,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult {
|
||||
let args = match errors {
|
||||
Some(errors) => vec![obj, errors.into()],
|
||||
Some(errors) => vec![obj, errors.into_wtf8().into()],
|
||||
None => vec![obj],
|
||||
};
|
||||
let res = self.get_decode_func().call(args, vm)?;
|
||||
@@ -263,7 +266,7 @@ impl CodecsRegistry {
|
||||
}
|
||||
inner.search_path.clone()
|
||||
};
|
||||
let encoding = PyStr::from(encoding.into_owned()).into_ref(&vm.ctx);
|
||||
let encoding: PyUtf8StrRef = vm.ctx.new_utf8_str(encoding.as_ref());
|
||||
for func in search_path {
|
||||
let res = func.call((encoding.clone(),), vm)?;
|
||||
let res: Option<PyCodec> = res.try_into_value(vm)?;
|
||||
@@ -305,7 +308,7 @@ impl CodecsRegistry {
|
||||
&self,
|
||||
obj: PyObjectRef,
|
||||
encoding: &str,
|
||||
errors: Option<PyStrRef>,
|
||||
errors: Option<PyUtf8StrRef>,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult {
|
||||
let codec = self.lookup(encoding, vm)?;
|
||||
@@ -318,7 +321,7 @@ impl CodecsRegistry {
|
||||
&self,
|
||||
obj: PyObjectRef,
|
||||
encoding: &str,
|
||||
errors: Option<PyStrRef>,
|
||||
errors: Option<PyUtf8StrRef>,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult {
|
||||
let codec = self.lookup(encoding, vm)?;
|
||||
@@ -331,7 +334,7 @@ impl CodecsRegistry {
|
||||
&self,
|
||||
obj: PyStrRef,
|
||||
encoding: &str,
|
||||
errors: Option<PyStrRef>,
|
||||
errors: Option<PyUtf8StrRef>,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<PyBytesRef> {
|
||||
let codec = self._lookup_text_encoding(encoding, "codecs.encode()", vm)?;
|
||||
@@ -355,7 +358,7 @@ impl CodecsRegistry {
|
||||
&self,
|
||||
obj: PyObjectRef,
|
||||
encoding: &str,
|
||||
errors: Option<PyStrRef>,
|
||||
errors: Option<PyUtf8StrRef>,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<PyStrRef> {
|
||||
let codec = self._lookup_text_encoding(encoding, "codecs.decode()", vm)?;
|
||||
@@ -868,7 +871,7 @@ impl<'a> DecodeErrorHandler<PyDecodeContext<'a>> for StandardError {
|
||||
}
|
||||
|
||||
pub struct ErrorsHandler<'a> {
|
||||
errors: &'a Py<PyStr>,
|
||||
errors: &'a Py<PyUtf8Str>,
|
||||
resolved: OnceCell<ResolvedError>,
|
||||
}
|
||||
enum ResolvedError {
|
||||
@@ -878,14 +881,14 @@ enum ResolvedError {
|
||||
|
||||
impl<'a> ErrorsHandler<'a> {
|
||||
#[inline]
|
||||
pub fn new(errors: Option<&'a Py<PyStr>>, vm: &VirtualMachine) -> Self {
|
||||
pub fn new(errors: Option<&'a Py<PyUtf8Str>>, vm: &VirtualMachine) -> Self {
|
||||
match errors {
|
||||
Some(errors) => Self {
|
||||
errors,
|
||||
resolved: OnceCell::new(),
|
||||
},
|
||||
None => Self {
|
||||
errors: identifier!(vm, strict).as_ref(),
|
||||
errors: identifier_utf8!(vm, strict),
|
||||
resolved: OnceCell::from(ResolvedError::Standard(StandardError::Strict)),
|
||||
},
|
||||
}
|
||||
@@ -895,12 +898,13 @@ impl<'a> ErrorsHandler<'a> {
|
||||
if let Some(val) = self.resolved.get() {
|
||||
return Ok(val);
|
||||
}
|
||||
let val = if let Ok(standard) = self.errors.as_str().parse() {
|
||||
let errors_str = self.errors.as_str();
|
||||
let val = if let Ok(standard) = errors_str.parse() {
|
||||
ResolvedError::Standard(standard)
|
||||
} else {
|
||||
vm.state
|
||||
.codec_registry
|
||||
.lookup_error(self.errors.as_str(), vm)
|
||||
.lookup_error(errors_str, vm)
|
||||
.map(ResolvedError::Handler)?
|
||||
};
|
||||
let _ = self.resolved.set(val);
|
||||
@@ -1020,7 +1024,7 @@ where
|
||||
// let err = err.
|
||||
let range = extract_unicode_error_range(&err, vm)?;
|
||||
let s = PyStrRef::try_from_object(vm, err.get_attr("object", vm)?)?;
|
||||
let s_encoding = PyStrRef::try_from_object(vm, err.get_attr("encoding", vm)?)?;
|
||||
let s_encoding = PyUtf8StrRef::try_from_object(vm, err.get_attr("encoding", vm)?)?;
|
||||
let mut ctx = PyEncodeContext {
|
||||
vm,
|
||||
encoding: s_encoding.as_str(),
|
||||
@@ -1059,7 +1063,7 @@ where
|
||||
{
|
||||
let range = extract_unicode_error_range(&err, vm)?;
|
||||
let s = ArgBytesLike::try_from_object(vm, err.get_attr("object", vm)?)?;
|
||||
let s_encoding = PyStrRef::try_from_object(vm, err.get_attr("encoding", vm)?)?;
|
||||
let s_encoding = PyUtf8StrRef::try_from_object(vm, err.get_attr("encoding", vm)?)?;
|
||||
let mut ctx = PyDecodeContext {
|
||||
vm,
|
||||
encoding: s_encoding.as_str(),
|
||||
|
||||
@@ -257,7 +257,7 @@ impl Coro {
|
||||
format!(
|
||||
"<{} object {} at {:#x}>",
|
||||
gen_name(jen, vm),
|
||||
qualname.as_str(),
|
||||
qualname.as_wtf8(),
|
||||
id
|
||||
)
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
use crate::{
|
||||
AsObject, Py, PyExact, PyObject, PyObjectRef, PyRefExact, PyResult, VirtualMachine,
|
||||
builtins::{PyBytes, PyInt, PyStr, PyStrInterned, PyStrRef},
|
||||
builtins::{PyBytes, PyInt, PyStr, PyStrInterned, PyStrRef, PyUtf8Str, PyUtf8StrRef},
|
||||
convert::ToPyObject,
|
||||
};
|
||||
use crate::{
|
||||
@@ -818,6 +818,33 @@ impl DictKey for Py<PyStr> {
|
||||
}
|
||||
}
|
||||
|
||||
impl DictKey for Py<PyUtf8Str> {
|
||||
type Owned = PyUtf8StrRef;
|
||||
#[inline(always)]
|
||||
fn _to_owned(&self, _vm: &VirtualMachine) -> Self::Owned {
|
||||
self.to_owned()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn key_hash(&self, vm: &VirtualMachine) -> PyResult<HashValue> {
|
||||
self.as_pystr().key_hash(vm)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn key_is(&self, other: &PyObject) -> bool {
|
||||
self.as_pystr().key_is(other)
|
||||
}
|
||||
|
||||
fn key_eq(&self, vm: &VirtualMachine, other_key: &PyObject) -> PyResult<bool> {
|
||||
self.as_pystr().key_eq(vm, other_key)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn key_as_isize(&self, vm: &VirtualMachine) -> PyResult<isize> {
|
||||
self.as_pystr().key_as_isize(vm)
|
||||
}
|
||||
}
|
||||
|
||||
impl DictKey for PyStrInterned {
|
||||
type Owned = PyRefExact<PyStr>;
|
||||
|
||||
|
||||
@@ -8,6 +8,8 @@ use crate::types::{PyTypeFlags, PyTypeSlots};
|
||||
use crate::{
|
||||
AsObject, Context, Py, PyObject, PyObjectRef, PyRef, PyResult, TryFromObject, VirtualMachine,
|
||||
};
|
||||
use core::fmt::Write;
|
||||
use rustpython_common::wtf8::{Wtf8, Wtf8Buf};
|
||||
|
||||
use crate::exceptions::types::PyBaseException;
|
||||
|
||||
@@ -183,12 +185,7 @@ pub(super) mod types {
|
||||
|
||||
#[pymethod]
|
||||
fn __str__(zelf: &Py<PyBaseException>, vm: &VirtualMachine) -> PyResult<PyStrRef> {
|
||||
let message = zelf
|
||||
.get_arg(0)
|
||||
.map(|m| m.str(vm))
|
||||
.transpose()?
|
||||
.map(|s| s.as_str().to_owned())
|
||||
.unwrap_or_default();
|
||||
let message = zelf.get_arg(0).map(|m| m.str(vm)).transpose()?;
|
||||
|
||||
let num_excs = zelf
|
||||
.get_arg(1)
|
||||
@@ -196,10 +193,12 @@ pub(super) mod types {
|
||||
.unwrap_or(0);
|
||||
|
||||
let suffix = if num_excs == 1 { "" } else { "s" };
|
||||
Ok(vm.ctx.new_str(format!(
|
||||
"{} ({} sub-exception{})",
|
||||
message, num_excs, suffix
|
||||
)))
|
||||
let mut result = match message {
|
||||
Some(s) => s.as_wtf8().to_owned(),
|
||||
None => Wtf8Buf::new(),
|
||||
};
|
||||
write!(result, " ({num_excs} sub-exception{suffix})").unwrap();
|
||||
Ok(vm.ctx.new_str(result))
|
||||
}
|
||||
|
||||
#[pyslot]
|
||||
@@ -208,31 +207,28 @@ pub(super) mod types {
|
||||
.downcast_ref::<PyBaseException>()
|
||||
.expect("exception group must be BaseException");
|
||||
let class_name = zelf.class().name().to_owned();
|
||||
let message = zelf
|
||||
.get_arg(0)
|
||||
.map(|m| m.repr(vm))
|
||||
.transpose()?
|
||||
.map(|s| s.as_str().to_owned())
|
||||
.unwrap_or_else(|| "''".to_owned());
|
||||
let message = zelf.get_arg(0).map(|m| m.repr(vm)).transpose()?;
|
||||
|
||||
// Format exceptions as list [exc1, exc2, ...] instead of tuple (exc1, exc2, ...)
|
||||
// CPython displays exceptions in list format even though they're stored as tuple
|
||||
let exceptions_str = if let Some(exceptions_obj) = zelf.get_arg(1) {
|
||||
// Get exceptions using ArgIterable for robustness
|
||||
let mut result = Wtf8Buf::new();
|
||||
write!(result, "{class_name}(").unwrap();
|
||||
let message_wtf8: &Wtf8 = message.as_ref().map_or("''".as_ref(), |s| s.as_wtf8());
|
||||
result.push_wtf8(message_wtf8);
|
||||
result.push_str(", [");
|
||||
if let Some(exceptions_obj) = zelf.get_arg(1) {
|
||||
let iter: ArgIterable<PyObjectRef> =
|
||||
ArgIterable::try_from_object(vm, exceptions_obj.clone())?;
|
||||
let mut exc_repr_list = Vec::new();
|
||||
let mut first = true;
|
||||
for exc in iter.iter(vm)? {
|
||||
exc_repr_list.push(exc?.repr(vm)?.as_str().to_owned());
|
||||
if !first {
|
||||
result.push_str(", ");
|
||||
}
|
||||
first = false;
|
||||
result.push_wtf8(exc?.repr(vm)?.as_wtf8());
|
||||
}
|
||||
format!("[{}]", exc_repr_list.join(", "))
|
||||
} else {
|
||||
"[]".to_owned()
|
||||
};
|
||||
}
|
||||
result.push_str("])");
|
||||
|
||||
Ok(vm
|
||||
.ctx
|
||||
.new_str(format!("{}({}, {})", class_name, message, exceptions_str)))
|
||||
Ok(vm.ctx.new_str(result))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+44
-22
@@ -211,8 +211,16 @@ impl VirtualMachine {
|
||||
|
||||
if let Some(text) = maybe_text {
|
||||
// if text ends with \n or \r\n, remove it
|
||||
let r_text = text.as_str().trim_end_matches(['\n', '\r']);
|
||||
let l_text = r_text.trim_start_matches([' ', '\n', '\x0c']); // \x0c is \f
|
||||
use rustpython_common::wtf8::CodePoint;
|
||||
let text_wtf8 = text.as_wtf8();
|
||||
let r_text = text_wtf8.trim_end_matches(|cp: CodePoint| {
|
||||
cp == CodePoint::from_char('\n') || cp == CodePoint::from_char('\r')
|
||||
});
|
||||
let l_text = r_text.trim_start_matches(|cp: CodePoint| {
|
||||
cp == CodePoint::from_char(' ')
|
||||
|| cp == CodePoint::from_char('\n')
|
||||
|| cp == CodePoint::from_char('\x0c') // \f
|
||||
});
|
||||
let spaces = (r_text.len() - l_text.len()) as isize;
|
||||
|
||||
writeln!(output, " {l_text}")?;
|
||||
@@ -249,9 +257,9 @@ impl VirtualMachine {
|
||||
let end_colno = end_offset - 1 - spaces;
|
||||
if colno >= 0 {
|
||||
let caret_space = l_text
|
||||
.chars()
|
||||
.code_points()
|
||||
.take(colno as usize)
|
||||
.map(|c| if c.is_whitespace() { c } else { ' ' })
|
||||
.map(|cp| cp.to_char().filter(|c| c.is_whitespace()).unwrap_or(' '))
|
||||
.collect::<String>();
|
||||
|
||||
let mut error_width = end_colno - colno;
|
||||
@@ -716,7 +724,7 @@ impl PyRef<PyBaseException> {
|
||||
|
||||
for (key, value) in &dict {
|
||||
let key_str = key.str(vm)?;
|
||||
if key_str.as_str().starts_with("__") {
|
||||
if key_str.as_bytes().starts_with(b"__") {
|
||||
continue;
|
||||
}
|
||||
self.as_object().set_attr(&key_str, value.clone(), vm)?;
|
||||
@@ -1459,7 +1467,10 @@ pub(super) mod types {
|
||||
};
|
||||
use crossbeam_utils::atomic::AtomicCell;
|
||||
use itertools::Itertools;
|
||||
use rustpython_common::str::UnicodeEscapeCodepoint;
|
||||
use rustpython_common::{
|
||||
str::UnicodeEscapeCodepoint,
|
||||
wtf8::{Wtf8, Wtf8Buf, wtf8_concat},
|
||||
};
|
||||
|
||||
// Re-export exception group types from dedicated module
|
||||
pub use crate::exception_group::types::PyBaseExceptionGroup;
|
||||
@@ -2317,19 +2328,25 @@ pub(super) mod types {
|
||||
impl PySyntaxError {
|
||||
#[pymethod]
|
||||
fn __str__(zelf: &Py<PyBaseException>, vm: &VirtualMachine) -> PyResult<PyStrRef> {
|
||||
fn basename(filename: &str) -> &str {
|
||||
let splitted = if cfg!(windows) {
|
||||
filename.rsplit(&['/', '\\']).next()
|
||||
fn basename(filename: &Wtf8) -> &Wtf8 {
|
||||
let bytes = filename.as_bytes();
|
||||
let pos = if cfg!(windows) {
|
||||
bytes.iter().rposition(|&b| b == b'/' || b == b'\\')
|
||||
} else {
|
||||
filename.rsplit('/').next()
|
||||
bytes.iter().rposition(|&b| b == b'/')
|
||||
};
|
||||
splitted.unwrap_or(filename)
|
||||
match pos {
|
||||
// SAFETY: splitting at ASCII byte boundary preserves WTF-8 validity
|
||||
Some(pos) => unsafe { Wtf8::from_bytes_unchecked(&bytes[pos + 1..]) },
|
||||
None => filename,
|
||||
}
|
||||
}
|
||||
|
||||
let maybe_lineno = zelf.as_object().get_attr("lineno", vm).ok().map(|obj| {
|
||||
obj.str(vm)
|
||||
.unwrap_or_else(|_| vm.ctx.new_str("<lineno str() failed>"))
|
||||
});
|
||||
let maybe_lineno = zelf
|
||||
.as_object()
|
||||
.get_attr("lineno", vm)
|
||||
.and_then(|obj| obj.str_utf8(vm))
|
||||
.ok();
|
||||
let maybe_filename = zelf.as_object().get_attr("filename", vm).ok().map(|obj| {
|
||||
obj.str(vm)
|
||||
.unwrap_or_else(|_| vm.ctx.new_str("<filename str() failed>"))
|
||||
@@ -2345,17 +2362,22 @@ pub(super) mod types {
|
||||
}
|
||||
};
|
||||
|
||||
let msg_with_location_info: String = match (maybe_lineno, maybe_filename) {
|
||||
(Some(lineno), Some(filename)) => {
|
||||
format!("{} ({}, line {})", msg, basename(filename.as_str()), lineno)
|
||||
}
|
||||
let msg_with_location_info: Wtf8Buf = match (maybe_lineno, maybe_filename) {
|
||||
(Some(lineno), Some(filename)) => wtf8_concat!(
|
||||
msg.as_wtf8(),
|
||||
" (",
|
||||
basename(filename.as_wtf8()),
|
||||
", line ",
|
||||
lineno.as_str(),
|
||||
")"
|
||||
),
|
||||
(Some(lineno), None) => {
|
||||
format!("{msg} (line {lineno})")
|
||||
wtf8_concat!(msg.as_wtf8(), " (line ", lineno.as_str(), ")")
|
||||
}
|
||||
(None, Some(filename)) => {
|
||||
format!("{} ({})", msg, basename(filename.as_str()))
|
||||
wtf8_concat!(msg.as_wtf8(), " (", basename(filename.as_wtf8()), ")")
|
||||
}
|
||||
(None, None) => msg.to_string(),
|
||||
(None, None) => msg.as_wtf8().to_owned(),
|
||||
};
|
||||
|
||||
Ok(vm.ctx.new_str(msg_with_location_info))
|
||||
|
||||
+55
-39
@@ -5,7 +5,7 @@ use crate::{
|
||||
builtins::{
|
||||
PyBaseException, PyBaseExceptionRef, PyCode, PyCoroutine, PyDict, PyDictRef, PyGenerator,
|
||||
PyInterpolation, PyList, PySet, PySlice, PyStr, PyStrInterned, PyTemplate, PyTraceback,
|
||||
PyType,
|
||||
PyType, PyUtf8Str,
|
||||
asyncgenerator::PyAsyncGenWrappedValue,
|
||||
function::{PyCell, PyCellRef, PyFunction},
|
||||
tuple::{PyTuple, PyTupleRef},
|
||||
@@ -24,13 +24,18 @@ use crate::{
|
||||
vm::{Context, PyMethod},
|
||||
};
|
||||
use alloc::fmt;
|
||||
use bstr::ByteSlice;
|
||||
use core::iter::zip;
|
||||
use core::sync::atomic;
|
||||
use core::sync::atomic::AtomicPtr;
|
||||
use indexmap::IndexMap;
|
||||
use itertools::Itertools;
|
||||
|
||||
use rustpython_common::{boxvec::BoxVec, lock::PyMutex, wtf8::Wtf8Buf};
|
||||
use rustpython_common::{
|
||||
boxvec::BoxVec,
|
||||
lock::PyMutex,
|
||||
wtf8::{Wtf8, Wtf8Buf, wtf8_concat},
|
||||
};
|
||||
use rustpython_compiler_core::SourceLocation;
|
||||
|
||||
pub type FrameRef = PyRef<Frame>;
|
||||
@@ -416,8 +421,8 @@ impl Py<Frame> {
|
||||
pub fn is_internal_frame(&self) -> bool {
|
||||
let code = self.f_code();
|
||||
let filename = code.co_filename();
|
||||
let filename_s = filename.as_str();
|
||||
filename_s.contains("importlib") && filename_s.contains("_bootstrap")
|
||||
let filename = filename.as_bytes();
|
||||
filename.find(b"importlib").is_some() && filename.find(b"_bootstrap").is_some()
|
||||
}
|
||||
|
||||
pub fn next_external_frame(&self, vm: &VirtualMachine) -> Option<FrameRef> {
|
||||
@@ -795,7 +800,7 @@ impl ExecutingFrame<'_> {
|
||||
if let Some(&name) = self.code.cellvars.get(i) {
|
||||
vm.new_exception_msg(
|
||||
vm.ctx.exceptions.unbound_local_error.to_owned(),
|
||||
format!("local variable '{name}' referenced before assignment"),
|
||||
format!("local variable '{name}' referenced before assignment").into(),
|
||||
)
|
||||
} else {
|
||||
let name = self.code.freevars[i - self.code.cellvars.len()];
|
||||
@@ -1052,7 +1057,8 @@ impl ExecutingFrame<'_> {
|
||||
format!(
|
||||
"local variable '{}' referenced before assignment",
|
||||
self.code.varnames[idx]
|
||||
),
|
||||
)
|
||||
.into(),
|
||||
));
|
||||
}
|
||||
fastlocals[idx] = None;
|
||||
@@ -1157,7 +1163,7 @@ impl ExecutingFrame<'_> {
|
||||
return Err(vm.new_type_error(format!(
|
||||
"{} got multiple values for keyword argument '{}'",
|
||||
func_str,
|
||||
key_str.as_str()
|
||||
key_str.as_wtf8()
|
||||
)));
|
||||
}
|
||||
let value = vm.call_method(&source, "__getitem__", (key.clone(),))?;
|
||||
@@ -1536,7 +1542,7 @@ impl ExecutingFrame<'_> {
|
||||
) -> PyBaseExceptionRef {
|
||||
vm.new_exception_msg(
|
||||
vm.ctx.exceptions.unbound_local_error.to_owned(),
|
||||
format!("local variable '{varname}' referenced before assignment",),
|
||||
format!("local variable '{varname}' referenced before assignment").into(),
|
||||
)
|
||||
}
|
||||
let idx = idx.get(arg) as usize;
|
||||
@@ -1566,7 +1572,8 @@ impl ExecutingFrame<'_> {
|
||||
format!(
|
||||
"local variable '{}' referenced before assignment",
|
||||
self.code.varnames[idx]
|
||||
),
|
||||
)
|
||||
.into(),
|
||||
)
|
||||
})?;
|
||||
self.push_value(x);
|
||||
@@ -1585,7 +1592,8 @@ impl ExecutingFrame<'_> {
|
||||
format!(
|
||||
"local variable '{}' referenced before assignment",
|
||||
self.code.varnames[idx1]
|
||||
),
|
||||
)
|
||||
.into(),
|
||||
)
|
||||
})?;
|
||||
let x2 = fastlocals[idx2].clone().ok_or_else(|| {
|
||||
@@ -1594,7 +1602,8 @@ impl ExecutingFrame<'_> {
|
||||
format!(
|
||||
"local variable '{}' referenced before assignment",
|
||||
self.code.varnames[idx2]
|
||||
),
|
||||
)
|
||||
.into(),
|
||||
)
|
||||
})?;
|
||||
drop(fastlocals);
|
||||
@@ -1618,7 +1627,8 @@ impl ExecutingFrame<'_> {
|
||||
format!(
|
||||
"local variable '{}' referenced before assignment",
|
||||
self.code.varnames[idx]
|
||||
),
|
||||
)
|
||||
.into(),
|
||||
)
|
||||
})?;
|
||||
self.push_value(x);
|
||||
@@ -1636,7 +1646,8 @@ impl ExecutingFrame<'_> {
|
||||
format!(
|
||||
"local variable '{}' referenced before assignment",
|
||||
self.code.varnames[idx1]
|
||||
),
|
||||
)
|
||||
.into(),
|
||||
)
|
||||
})?;
|
||||
let x2 = fastlocals[idx2].clone().ok_or_else(|| {
|
||||
@@ -1645,7 +1656,8 @@ impl ExecutingFrame<'_> {
|
||||
format!(
|
||||
"local variable '{}' referenced before assignment",
|
||||
self.code.varnames[idx2]
|
||||
),
|
||||
)
|
||||
.into(),
|
||||
)
|
||||
})?;
|
||||
drop(fastlocals);
|
||||
@@ -1744,8 +1756,9 @@ impl ExecutingFrame<'_> {
|
||||
// Get type names for error message
|
||||
let type_name = cls
|
||||
.downcast::<crate::builtins::PyType>()
|
||||
.map(|t| t.__name__(vm).as_str().to_owned())
|
||||
.unwrap_or_else(|_| String::from("?"));
|
||||
.ok()
|
||||
.and_then(|t| t.__name__(vm).to_str().map(str::to_owned))
|
||||
.unwrap_or_else(|| String::from("?"));
|
||||
let match_args_type_name = match_args.class().__name__(vm);
|
||||
return Err(vm.new_type_error(format!(
|
||||
"{}.__match_args__ must be a tuple (got {})",
|
||||
@@ -2413,7 +2426,7 @@ impl ExecutingFrame<'_> {
|
||||
let mod_name_obj = module.get_attr(identifier!(vm, __name__), vm).ok();
|
||||
let mod_name_str = mod_name_obj
|
||||
.as_ref()
|
||||
.and_then(|n| n.downcast_ref::<PyStr>().map(|s| s.as_str().to_owned()));
|
||||
.and_then(|n| n.downcast_ref::<PyUtf8Str>().map(|s| s.as_str().to_owned()));
|
||||
let module_name = mod_name_str.as_deref().unwrap_or("<unknown module name>");
|
||||
|
||||
let spec = module
|
||||
@@ -2472,7 +2485,7 @@ impl ExecutingFrame<'_> {
|
||||
format!("cannot import name '{name}' from '{module_name}' (unknown location)")
|
||||
}
|
||||
};
|
||||
let err = vm.new_import_error(msg, vm.ctx.new_str(module_name));
|
||||
let err = vm.new_import_error(msg, vm.ctx.new_utf8_str(module_name));
|
||||
|
||||
if let Some(ref path) = origin {
|
||||
let _ignore = err
|
||||
@@ -2502,14 +2515,14 @@ impl ExecutingFrame<'_> {
|
||||
let require_str = |obj: PyObjectRef, attr: &str| -> PyResult<PyRef<PyStr>> {
|
||||
obj.downcast().map_err(|obj: PyObjectRef| {
|
||||
let source = if let Some(ref mod_name) = mod_name {
|
||||
format!("{}.{attr}", mod_name.as_str())
|
||||
format!("{}.{attr}", mod_name.as_wtf8())
|
||||
} else {
|
||||
attr.to_owned()
|
||||
};
|
||||
let repr = obj.repr(vm).unwrap_or_else(|_| vm.ctx.new_str("?"));
|
||||
vm.new_type_error(format!(
|
||||
"{} in {} must be str, not {}",
|
||||
repr.as_str(),
|
||||
repr.as_wtf8(),
|
||||
source,
|
||||
obj.class().name()
|
||||
))
|
||||
@@ -2528,7 +2541,7 @@ impl ExecutingFrame<'_> {
|
||||
} else {
|
||||
for (k, v) in dict {
|
||||
let k = require_str(k, "__dict__")?;
|
||||
if !k.as_str().starts_with('_') {
|
||||
if !k.as_bytes().starts_with(b"_") {
|
||||
self.locals.mapping().ass_subscript(&k, Some(v), vm)?;
|
||||
}
|
||||
}
|
||||
@@ -2650,10 +2663,13 @@ impl ExecutingFrame<'_> {
|
||||
.expect("kwarg names should be tuple of strings");
|
||||
let args = self.pop_multiple(nargs as usize);
|
||||
|
||||
let kwarg_names = kwarg_names
|
||||
.as_slice()
|
||||
.iter()
|
||||
.map(|pyobj| pyobj.downcast_ref::<PyStr>().unwrap().as_str().to_owned());
|
||||
let kwarg_names = kwarg_names.as_slice().iter().map(|pyobj| {
|
||||
pyobj
|
||||
.downcast_ref::<PyUtf8Str>()
|
||||
.unwrap()
|
||||
.as_str()
|
||||
.to_owned()
|
||||
});
|
||||
FuncArgs::with_kwargs_names(args, kwarg_names)
|
||||
}
|
||||
|
||||
@@ -2668,7 +2684,7 @@ impl ExecutingFrame<'_> {
|
||||
|
||||
Self::iterate_mapping_keys(vm, &kw_obj, &func_str, |key| {
|
||||
let key_str = key
|
||||
.downcast_ref::<PyStr>()
|
||||
.downcast_ref::<PyUtf8Str>()
|
||||
.ok_or_else(|| vm.new_type_error("keywords must be strings"))?;
|
||||
let value = kw_obj.get_item(&*key, vm)?;
|
||||
kwargs.insert(key_str.as_str().to_owned(), value);
|
||||
@@ -2708,26 +2724,26 @@ impl ExecutingFrame<'_> {
|
||||
/// Returns a display string for a callable object for use in error messages.
|
||||
/// For objects with `__qualname__`, returns "module.qualname()" or "qualname()".
|
||||
/// For other objects, returns repr(obj).
|
||||
fn object_function_str(obj: &PyObject, vm: &VirtualMachine) -> String {
|
||||
fn object_function_str(obj: &PyObject, vm: &VirtualMachine) -> Wtf8Buf {
|
||||
let repr_fallback = || {
|
||||
obj.repr(vm)
|
||||
.as_ref()
|
||||
.map_or("?".as_ref(), |s| s.as_wtf8())
|
||||
.to_owned()
|
||||
};
|
||||
let Ok(qualname) = obj.get_attr(vm.ctx.intern_str("__qualname__"), vm) else {
|
||||
return obj
|
||||
.repr(vm)
|
||||
.map(|s| s.as_str().to_owned())
|
||||
.unwrap_or_else(|_| "?".to_owned());
|
||||
return repr_fallback();
|
||||
};
|
||||
let Some(qualname_str) = qualname.downcast_ref::<PyStr>() else {
|
||||
return obj
|
||||
.repr(vm)
|
||||
.map(|s| s.as_str().to_owned())
|
||||
.unwrap_or_else(|_| "?".to_owned());
|
||||
return repr_fallback();
|
||||
};
|
||||
if let Ok(module) = obj.get_attr(vm.ctx.intern_str("__module__"), vm)
|
||||
&& let Some(module_str) = module.downcast_ref::<PyStr>()
|
||||
&& module_str.as_str() != "builtins"
|
||||
&& module_str.as_bytes() != b"builtins"
|
||||
{
|
||||
return format!("{}.{}()", module_str.as_str(), qualname_str.as_str());
|
||||
return wtf8_concat!(module_str.as_wtf8(), ".", qualname_str.as_wtf8(), "()");
|
||||
}
|
||||
format!("{}()", qualname_str.as_str())
|
||||
wtf8_concat!(qualname_str.as_wtf8(), "()")
|
||||
}
|
||||
|
||||
/// Helper function to iterate over mapping keys using the keys() method.
|
||||
@@ -2735,7 +2751,7 @@ impl ExecutingFrame<'_> {
|
||||
fn iterate_mapping_keys<F>(
|
||||
vm: &VirtualMachine,
|
||||
mapping: &PyObject,
|
||||
func_str: &str,
|
||||
func_str: &Wtf8,
|
||||
mut key_handler: F,
|
||||
) -> PyResult<()>
|
||||
where
|
||||
|
||||
@@ -172,7 +172,7 @@ impl TryFromObject for ArgAsciiBuffer {
|
||||
fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
|
||||
match obj.downcast::<PyStr>() {
|
||||
Ok(string) => {
|
||||
if string.as_str().is_ascii() {
|
||||
if string.as_wtf8().is_ascii() {
|
||||
Ok(Self::String(string))
|
||||
} else {
|
||||
Err(vm.new_value_error("string argument should contain only ASCII characters"))
|
||||
@@ -186,7 +186,7 @@ impl TryFromObject for ArgAsciiBuffer {
|
||||
impl ArgAsciiBuffer {
|
||||
pub fn len(&self) -> usize {
|
||||
match self {
|
||||
Self::String(s) => s.as_str().len(),
|
||||
Self::String(s) => s.as_wtf8().len(),
|
||||
Self::Buffer(buffer) => buffer.len(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,7 +106,7 @@ impl FsPath {
|
||||
|
||||
pub fn to_path_buf(&self, vm: &VirtualMachine) -> PyResult<PathBuf> {
|
||||
let path = match self {
|
||||
Self::Str(s) => PathBuf::from(s.as_str()),
|
||||
Self::Str(s) => PathBuf::from(vm.fsencode(s)?.as_ref() as &OsStr),
|
||||
Self::Bytes(b) => PathBuf::from(Self::bytes_as_os_str(b, vm)?),
|
||||
};
|
||||
Ok(path)
|
||||
|
||||
@@ -36,9 +36,9 @@ pub enum ArgByteOrder {
|
||||
impl<'a> TryFromBorrowedObject<'a> for ArgByteOrder {
|
||||
fn try_from_borrowed_object(vm: &VirtualMachine, obj: &'a PyObject) -> PyResult<Self> {
|
||||
obj.try_value_with(
|
||||
|s: &PyStr| match s.as_str() {
|
||||
"big" => Ok(Self::Big),
|
||||
"little" => Ok(Self::Little),
|
||||
|s: &PyStr| match s.as_bytes() {
|
||||
b"big" => Ok(Self::Big),
|
||||
b"little" => Ok(Self::Little),
|
||||
_ => Err(vm.new_value_error("byteorder must be either 'little' or 'big'")),
|
||||
},
|
||||
vm,
|
||||
|
||||
+19
-19
@@ -2,7 +2,7 @@
|
||||
|
||||
use crate::{
|
||||
AsObject, Py, PyObjectRef, PyPayload, PyRef, PyResult,
|
||||
builtins::{PyCode, PyStr, PyStrRef, traceback::PyTraceback},
|
||||
builtins::{PyCode, PyStr, PyUtf8Str, PyUtf8StrRef, traceback::PyTraceback},
|
||||
exceptions::types::PyBaseException,
|
||||
scope::Scope,
|
||||
vm::{VirtualMachine, resolve_frozen_alias, thread},
|
||||
@@ -69,7 +69,7 @@ pub fn make_frozen(vm: &VirtualMachine, name: &str) -> PyResult<PyRef<PyCode>> {
|
||||
let frozen = vm.state.frozen.get(name).ok_or_else(|| {
|
||||
vm.new_import_error(
|
||||
format!("No such frozen object named {name}"),
|
||||
vm.ctx.new_str(name),
|
||||
vm.ctx.new_utf8_str(name),
|
||||
)
|
||||
})?;
|
||||
Ok(vm.ctx.new_code(frozen.code))
|
||||
@@ -79,13 +79,13 @@ pub fn import_frozen(vm: &VirtualMachine, module_name: &str) -> PyResult {
|
||||
let frozen = vm.state.frozen.get(module_name).ok_or_else(|| {
|
||||
vm.new_import_error(
|
||||
format!("No such frozen object named {module_name}"),
|
||||
vm.ctx.new_str(module_name),
|
||||
vm.ctx.new_utf8_str(module_name),
|
||||
)
|
||||
})?;
|
||||
let module = import_code_obj(vm, module_name, vm.ctx.new_code(frozen.code), false)?;
|
||||
debug_assert!(module.get_attr(identifier!(vm, __name__), vm).is_ok());
|
||||
let origname = resolve_frozen_alias(module_name);
|
||||
module.set_attr("__origname__", vm.ctx.new_str(origname), vm)?;
|
||||
module.set_attr("__origname__", vm.ctx.new_utf8_str(origname), vm)?;
|
||||
Ok(module)
|
||||
}
|
||||
|
||||
@@ -118,7 +118,7 @@ pub fn import_builtin(vm: &VirtualMachine, module_name: &str) -> PyResult {
|
||||
// Module not found in module_defs
|
||||
Err(vm.new_import_error(
|
||||
format!("Cannot import builtin module {module_name}"),
|
||||
vm.ctx.new_str(module_name),
|
||||
vm.ctx.new_utf8_str(module_name),
|
||||
))
|
||||
}
|
||||
|
||||
@@ -169,7 +169,7 @@ fn import_ensure_initialized(
|
||||
};
|
||||
if initializing {
|
||||
let lock_unlock = vm.importlib.get_attr("_lock_unlock_module", vm)?;
|
||||
lock_unlock.call((vm.ctx.new_str(name),), vm)?;
|
||||
lock_unlock.call((vm.ctx.new_utf8_str(name),), vm)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -183,7 +183,7 @@ pub fn import_code_obj(
|
||||
let attrs = vm.ctx.new_dict();
|
||||
attrs.set_item(
|
||||
identifier!(vm, __name__),
|
||||
vm.ctx.new_str(module_name).into(),
|
||||
vm.ctx.new_utf8_str(module_name).into(),
|
||||
vm,
|
||||
)?;
|
||||
if set_file_attr {
|
||||
@@ -312,7 +312,7 @@ pub(crate) fn is_possibly_shadowing_path(origin: &str, vm: &VirtualMachine) -> b
|
||||
let sys_path_0 = (|| -> Option<String> {
|
||||
let argv = vm.sys_module.get_attr("argv", vm).ok()?;
|
||||
let argv0 = argv.get_item(&0usize, vm).ok()?;
|
||||
let argv0_str = argv0.downcast_ref::<PyStr>()?;
|
||||
let argv0_str = argv0.downcast_ref::<PyUtf8Str>()?;
|
||||
let s = argv0_str.as_str();
|
||||
|
||||
// For -c and REPL, original sys.path[0] is ""
|
||||
@@ -412,7 +412,7 @@ pub(crate) fn import_module_level(
|
||||
if package.is_empty() {
|
||||
return Err(vm.new_import_error(
|
||||
"attempted relative import with no known parent package".to_owned(),
|
||||
vm.ctx.new_str(""),
|
||||
vm.ctx.new_utf8_str(""),
|
||||
));
|
||||
}
|
||||
resolve_name(name_str, &package, level as usize, vm)?
|
||||
@@ -432,7 +432,7 @@ pub(crate) fn import_module_level(
|
||||
}
|
||||
_ => {
|
||||
let find_and_load = vm.importlib.get_attr("_find_and_load", vm)?;
|
||||
let abs_name_obj = vm.ctx.new_str(&*abs_name);
|
||||
let abs_name_obj = vm.ctx.new_utf8_str(&*abs_name);
|
||||
find_and_load.call((abs_name_obj, vm.import_func.clone()), vm)?
|
||||
}
|
||||
};
|
||||
@@ -474,14 +474,14 @@ pub(crate) fn import_module_level(
|
||||
// For absolute imports (level 0), try importing the
|
||||
// parent. Matches _bootstrap.__import__ behavior.
|
||||
let find_and_load = vm.importlib.get_attr("_find_and_load", vm)?;
|
||||
let to_return_obj = vm.ctx.new_str(&*to_return);
|
||||
let to_return_obj = vm.ctx.new_utf8_str(&*to_return);
|
||||
find_and_load.call((to_return_obj, vm.import_func.clone()), vm)
|
||||
}
|
||||
Err(_) => {
|
||||
// For relative imports (level > 0), raise KeyError
|
||||
let to_return_obj: PyObjectRef = vm
|
||||
.ctx
|
||||
.new_str(format!("'{to_return}' not in sys.modules as expected"))
|
||||
.new_utf8_str(format!("'{to_return}' not in sys.modules as expected"))
|
||||
.into();
|
||||
Err(vm.new_key_error(to_return_obj))
|
||||
}
|
||||
@@ -501,7 +501,7 @@ fn resolve_name(name: &str, package: &str, level: usize, vm: &VirtualMachine) ->
|
||||
if parts.len() < level {
|
||||
return Err(vm.new_import_error(
|
||||
"attempted relative import beyond top-level package".to_owned(),
|
||||
vm.ctx.new_str(name),
|
||||
vm.ctx.new_utf8_str(name),
|
||||
));
|
||||
}
|
||||
// rsplitn returns parts right-to-left, so last() is the leftmost (base)
|
||||
@@ -518,7 +518,7 @@ fn calc_package(globals: Option<&PyObjectRef>, vm: &VirtualMachine) -> PyResult<
|
||||
let globals = globals.ok_or_else(|| {
|
||||
vm.new_import_error(
|
||||
"attempted relative import with no known parent package".to_owned(),
|
||||
vm.ctx.new_str(""),
|
||||
vm.ctx.new_utf8_str(""),
|
||||
)
|
||||
})?;
|
||||
|
||||
@@ -528,7 +528,7 @@ fn calc_package(globals: Option<&PyObjectRef>, vm: &VirtualMachine) -> PyResult<
|
||||
if let Some(ref pkg) = package
|
||||
&& !vm.is_none(pkg)
|
||||
{
|
||||
let pkg_str: PyStrRef = pkg
|
||||
let pkg_str: PyUtf8StrRef = pkg
|
||||
.clone()
|
||||
.downcast()
|
||||
.map_err(|_| vm.new_type_error("package must be a string".to_owned()))?;
|
||||
@@ -543,7 +543,7 @@ fn calc_package(globals: Option<&PyObjectRef>, vm: &VirtualMachine) -> PyResult<
|
||||
.unwrap_or(false)
|
||||
{
|
||||
let parent_repr = parent
|
||||
.repr(vm)
|
||||
.repr_utf8(vm)
|
||||
.map(|s| s.as_str().to_owned())
|
||||
.unwrap_or_default();
|
||||
let msg = format!(
|
||||
@@ -570,7 +570,7 @@ fn calc_package(globals: Option<&PyObjectRef>, vm: &VirtualMachine) -> PyResult<
|
||||
&& let Ok(parent) = spec.get_attr("parent", vm)
|
||||
&& !vm.is_none(&parent)
|
||||
{
|
||||
let parent_str: PyStrRef = parent
|
||||
let parent_str: PyUtf8StrRef = parent
|
||||
.downcast()
|
||||
.map_err(|_| vm.new_type_error("package set to non-string".to_owned()))?;
|
||||
return Ok(parent_str.as_str().to_owned());
|
||||
@@ -593,10 +593,10 @@ fn calc_package(globals: Option<&PyObjectRef>, vm: &VirtualMachine) -> PyResult<
|
||||
let mod_name = globals.get_item("__name__", vm).map_err(|_| {
|
||||
vm.new_import_error(
|
||||
"attempted relative import with no known parent package".to_owned(),
|
||||
vm.ctx.new_str(""),
|
||||
vm.ctx.new_utf8_str(""),
|
||||
)
|
||||
})?;
|
||||
let mod_name_str: PyStrRef = mod_name
|
||||
let mod_name_str: PyUtf8StrRef = mod_name
|
||||
.downcast()
|
||||
.map_err(|_| vm.new_type_error("__name__ must be a string".to_owned()))?;
|
||||
let mut package = mod_name_str.as_str().to_owned();
|
||||
|
||||
@@ -152,10 +152,25 @@ impl CachedPyStrRef {
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct PyInterned<T> {
|
||||
inner: Py<T>,
|
||||
}
|
||||
|
||||
impl PyInterned<PyStr> {
|
||||
/// Returns `&str` for interned strings.
|
||||
///
|
||||
/// # Panics
|
||||
/// Panics if the interned string contains unpaired surrogates (WTF-8 content).
|
||||
/// Most interned strings are valid UTF-8, so this is an ergonomic default.
|
||||
#[inline]
|
||||
pub fn as_str(&self) -> &str {
|
||||
self.inner
|
||||
.to_str()
|
||||
.unwrap_or_else(|| panic!("interned str is always valid UTF-8"))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: PyPayload> PyInterned<T> {
|
||||
#[inline]
|
||||
pub fn leak(cache: PyRef<T>) -> &'static Self {
|
||||
|
||||
@@ -188,11 +188,11 @@ macro_rules! identifier(
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! identifier_utf8(
|
||||
($as_ctx:expr, $name:ident) => {
|
||||
($as_ctx:expr, $name:ident) => {{
|
||||
// Safety: All known identifiers are ascii strings.
|
||||
#[allow(clippy::macro_metavars_in_unsafe, reason = "known identifiers are ASCII and downcast target is fixed")]
|
||||
unsafe { $as_ctx.as_ref().names.$name.as_object().downcast_unchecked_ref::<$crate::builtins::PyUtf8Str>() }
|
||||
};
|
||||
let interned = $as_ctx.as_ref().names.$name;
|
||||
unsafe { $crate::builtins::PyUtf8StrInterned::from_str_interned_unchecked(interned) }
|
||||
}};
|
||||
);
|
||||
|
||||
/// Super detailed logging. Might soon overflow your log buffers
|
||||
|
||||
@@ -362,8 +362,8 @@ impl PyObject {
|
||||
}
|
||||
|
||||
pub fn ascii(&self, vm: &VirtualMachine) -> PyResult<ascii::AsciiString> {
|
||||
let repr = self.repr_utf8(vm)?;
|
||||
let ascii = to_ascii(repr.as_str());
|
||||
let repr = self.repr(vm)?;
|
||||
let ascii = to_ascii(repr.as_wtf8());
|
||||
Ok(ascii)
|
||||
}
|
||||
|
||||
@@ -658,7 +658,7 @@ impl PyObject {
|
||||
|
||||
Err(vm.new_exception_msg(
|
||||
vm.ctx.exceptions.type_error.to_owned(),
|
||||
format!("unhashable type: '{}'", self.class().name()),
|
||||
format!("unhashable type: '{}'", self.class().name()).into(),
|
||||
))
|
||||
}
|
||||
|
||||
|
||||
@@ -365,7 +365,7 @@ mod _abc {
|
||||
if !ok.is(&vm.ctx.not_implemented) {
|
||||
return Err(vm.new_exception_msg(
|
||||
vm.ctx.exceptions.assertion_error.to_owned(),
|
||||
"__subclasshook__ must return either False, True, or NotImplemented".to_owned(),
|
||||
"__subclasshook__ must return either False, True, or NotImplemented".into(),
|
||||
));
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ mod _winapi {
|
||||
windows::{WinHandle, WindowsSysResult},
|
||||
};
|
||||
use core::ptr::{null, null_mut};
|
||||
use rustpython_common::wtf8::Wtf8Buf;
|
||||
use windows_sys::Win32::Foundation::{HANDLE, MAX_PATH};
|
||||
|
||||
#[pyattr]
|
||||
@@ -296,19 +297,19 @@ mod _winapi {
|
||||
.map_or_else(null_mut, |l| l.attrlist.as_mut_ptr() as _);
|
||||
|
||||
let wstr = |s: PyStrRef| {
|
||||
let ws = widestring::WideCString::from_str(s.as_str())
|
||||
let ws = widestring::WideCString::from_str(s.expect_str())
|
||||
.map_err(|err| err.to_pyexception(vm))?;
|
||||
Ok(ws.into_vec_with_nul())
|
||||
};
|
||||
|
||||
// Validate no embedded null bytes in command name and command line
|
||||
if let Some(ref name) = args.name
|
||||
&& name.as_str().contains('\0')
|
||||
&& name.as_bytes().contains(&0)
|
||||
{
|
||||
return Err(crate::exceptions::cstring_error(vm));
|
||||
}
|
||||
if let Some(ref cmd) = args.command_line
|
||||
&& cmd.as_str().contains('\0')
|
||||
&& cmd.as_bytes().contains(&0)
|
||||
{
|
||||
return Err(crate::exceptions::cstring_error(vm));
|
||||
}
|
||||
@@ -396,8 +397,8 @@ mod _winapi {
|
||||
dest_path: PyStrRef,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<()> {
|
||||
let src_path = std::path::Path::new(src_path.as_str());
|
||||
let dest_path = std::path::Path::new(dest_path.as_str());
|
||||
let src_path = std::path::Path::new(src_path.expect_str());
|
||||
let dest_path = std::path::Path::new(dest_path.expect_str());
|
||||
|
||||
junction::create(src_path, dest_path).map_err(|e| e.to_pyexception(vm))
|
||||
}
|
||||
@@ -418,9 +419,9 @@ mod _winapi {
|
||||
let mut last_entry: HashMap<String, widestring::WideString> = HashMap::new();
|
||||
for (k, v) in keys.into_iter().zip(values.into_iter()) {
|
||||
let k = PyStrRef::try_from_object(vm, k)?;
|
||||
let k = k.as_str();
|
||||
let k = k.expect_str();
|
||||
let v = PyStrRef::try_from_object(vm, v)?;
|
||||
let v = v.as_str();
|
||||
let v = v.expect_str();
|
||||
if k.contains('\0') || v.contains('\0') {
|
||||
return Err(crate::exceptions::cstring_error(vm));
|
||||
}
|
||||
@@ -677,7 +678,6 @@ mod _winapi {
|
||||
src: PyStrRef,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<PyStrRef> {
|
||||
use rustpython_common::wtf8::Wtf8Buf;
|
||||
use windows_sys::Win32::Globalization::{
|
||||
LCMAP_BYTEREV, LCMAP_HASH, LCMAP_SORTHANDLE, LCMAP_SORTKEY,
|
||||
LCMapStringEx as WinLCMapStringEx,
|
||||
@@ -1032,8 +1032,6 @@ mod _winapi {
|
||||
api_fn: unsafe extern "system" fn(*const u16, *mut u16, u32) -> u32,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<PyStrRef> {
|
||||
use rustpython_common::wtf8::Wtf8Buf;
|
||||
|
||||
let path_wide = path.as_wtf8().to_wide_with_nul();
|
||||
|
||||
// First call to get required buffer size
|
||||
@@ -1808,7 +1806,7 @@ mod _winapi {
|
||||
use windows_sys::Win32::System::Memory::CreateFileMappingW;
|
||||
|
||||
if let Some(ref n) = name
|
||||
&& n.as_str().contains('\0')
|
||||
&& n.as_bytes().contains(&0)
|
||||
{
|
||||
return Err(vm.new_value_error(
|
||||
"CreateFileMapping: name must not contain null characters".to_owned(),
|
||||
@@ -1844,7 +1842,7 @@ mod _winapi {
|
||||
) -> PyResult<WinHandle> {
|
||||
use windows_sys::Win32::System::Memory::OpenFileMappingW;
|
||||
|
||||
if name.as_str().contains('\0') {
|
||||
if name.as_bytes().contains(&0) {
|
||||
return Err(vm.new_value_error(
|
||||
"OpenFileMapping: name must not contain null characters".to_owned(),
|
||||
));
|
||||
|
||||
@@ -561,7 +561,7 @@ mod _wmi {
|
||||
/// by null characters.
|
||||
#[pyfunction]
|
||||
fn exec_query(query: PyStrRef, vm: &VirtualMachine) -> PyResult<String> {
|
||||
let query_str = query.as_str();
|
||||
let query_str = query.expect_str();
|
||||
|
||||
if !query_str
|
||||
.get(..7)
|
||||
|
||||
@@ -15,7 +15,7 @@ use crate::{
|
||||
AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyRefExact, PyResult,
|
||||
TryFromObject, VirtualMachine,
|
||||
builtins::PyIntRef,
|
||||
builtins::{PyDict, PyModule, PyStrRef, PyType},
|
||||
builtins::{PyDict, PyModule, PyType, PyUtf8StrRef},
|
||||
class::{PyClassImpl, StaticType},
|
||||
compiler::{CompileError, ParseError},
|
||||
convert::ToPyObject,
|
||||
|
||||
@@ -13,7 +13,7 @@ impl Node for ast::Identifier {
|
||||
_source_file: &SourceFile,
|
||||
object: PyObjectRef,
|
||||
) -> PyResult<Self> {
|
||||
let py_str = PyStrRef::try_from_object(vm, object)?;
|
||||
let py_str = PyUtf8StrRef::try_from_object(vm, object)?;
|
||||
Ok(Self::new(py_str.as_str(), TextRange::default()))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,8 +8,9 @@ use super::{
|
||||
pub(crate) mod _ast {
|
||||
use crate::{
|
||||
AsObject, Context, Py, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine,
|
||||
builtins::{PyStr, PyStrRef, PyTupleRef, PyType, PyTypeRef},
|
||||
builtins::{PyStr, PyStrRef, PyTupleRef, PyType, PyTypeRef, PyUtf8Str, PyUtf8StrRef},
|
||||
class::{PyClassImpl, StaticType},
|
||||
common::wtf8::Wtf8,
|
||||
function::{FuncArgs, KwArgs, PyMethodDef, PyMethodFlags},
|
||||
stdlib::ast::repr,
|
||||
types::{Constructor, Initializer},
|
||||
@@ -99,7 +100,7 @@ pub(crate) mod _ast {
|
||||
let fields: Vec<PyStrRef> = fields.try_to_value(vm)?;
|
||||
let mut positional: Vec<PyObjectRef> = Vec::new();
|
||||
for field in fields {
|
||||
if dict.get_item_opt::<str>(field.as_str(), vm)?.is_some() {
|
||||
if dict.get_item_opt::<Wtf8>(field.as_wtf8(), vm)?.is_some() {
|
||||
positional.push(vm.ctx.none());
|
||||
} else {
|
||||
break;
|
||||
@@ -127,13 +128,13 @@ pub(crate) mod _ast {
|
||||
|
||||
let mut expecting: std::collections::HashSet<String> = std::collections::HashSet::new();
|
||||
if let Some(fields) = fields.clone() {
|
||||
let fields: Vec<PyStrRef> = fields.try_to_value(vm)?;
|
||||
let fields: Vec<PyUtf8StrRef> = fields.try_to_value(vm)?;
|
||||
for field in fields {
|
||||
expecting.insert(field.as_str().to_owned());
|
||||
}
|
||||
}
|
||||
if let Some(attributes) = attributes.clone() {
|
||||
let attributes: Vec<PyStrRef> = attributes.try_to_value(vm)?;
|
||||
let attributes: Vec<PyUtf8StrRef> = attributes.try_to_value(vm)?;
|
||||
for attr in attributes {
|
||||
expecting.insert(attr.as_str().to_owned());
|
||||
}
|
||||
@@ -151,12 +152,12 @@ pub(crate) mod _ast {
|
||||
|
||||
if let Some(dict) = dict.as_ref() {
|
||||
for (key, _value) in dict.items_vec() {
|
||||
if let Ok(key) = key.downcast::<PyStr>() {
|
||||
if let Ok(key) = key.downcast::<PyUtf8Str>() {
|
||||
expecting.remove(key.as_str());
|
||||
}
|
||||
}
|
||||
if let Some(attributes) = attributes.clone() {
|
||||
let attributes: Vec<PyStrRef> = attributes.try_to_value(vm)?;
|
||||
let attributes: Vec<PyUtf8StrRef> = attributes.try_to_value(vm)?;
|
||||
for attr in attributes {
|
||||
expecting.remove(attr.as_str());
|
||||
}
|
||||
@@ -168,7 +169,7 @@ pub(crate) mod _ast {
|
||||
&& let Ok(field_types) = field_types.downcast::<crate::builtins::PyDict>()
|
||||
{
|
||||
for (key, value) in field_types.items_vec() {
|
||||
let Ok(key) = key.downcast::<PyStr>() else {
|
||||
let Ok(key) = key.downcast::<PyUtf8Str>() else {
|
||||
continue;
|
||||
};
|
||||
if value.fast_isinstance(vm.ctx.types.union_type) {
|
||||
@@ -199,7 +200,7 @@ pub(crate) mod _ast {
|
||||
if let Some(fields) = fields.clone() {
|
||||
let fields: Vec<PyStrRef> = fields.try_to_value(vm)?;
|
||||
for field in fields {
|
||||
if let Some(value) = dict.get_item_opt::<str>(field.as_str(), vm)? {
|
||||
if let Some(value) = dict.get_item_opt::<Wtf8>(field.as_wtf8(), vm)? {
|
||||
payload.set_item(field.as_object(), value, vm)?;
|
||||
}
|
||||
}
|
||||
@@ -207,7 +208,7 @@ pub(crate) mod _ast {
|
||||
if let Some(attributes) = attributes.clone() {
|
||||
let attributes: Vec<PyStrRef> = attributes.try_to_value(vm)?;
|
||||
for attr in attributes {
|
||||
if let Some(value) = dict.get_item_opt::<str>(attr.as_str(), vm)? {
|
||||
if let Some(value) = dict.get_item_opt::<Wtf8>(attr.as_wtf8(), vm)? {
|
||||
payload.set_item(attr.as_object(), value, vm)?;
|
||||
}
|
||||
}
|
||||
@@ -223,7 +224,7 @@ pub(crate) mod _ast {
|
||||
.into_iter()
|
||||
.map(|(key, value)| {
|
||||
let key = key
|
||||
.downcast::<PyStr>()
|
||||
.downcast::<PyUtf8Str>()
|
||||
.map_err(|_| vm.new_type_error("keywords must be strings".to_owned()))?;
|
||||
Ok((key.as_str().to_owned(), value))
|
||||
})
|
||||
@@ -292,7 +293,7 @@ pub(crate) mod _ast {
|
||||
zelf.class().name()
|
||||
))
|
||||
})?;
|
||||
let fields: Vec<PyStrRef> = fields.try_to_value(vm)?;
|
||||
let fields: Vec<PyUtf8StrRef> = fields.try_to_value(vm)?;
|
||||
let n_args = args.args.len();
|
||||
if n_args > fields.len() {
|
||||
return Err(vm.new_type_error(format!(
|
||||
@@ -309,10 +310,10 @@ pub(crate) mod _ast {
|
||||
|
||||
for (name, arg) in fields.iter().zip(args.args) {
|
||||
zelf.set_attr(name, arg, vm)?;
|
||||
set_fields.insert(name.as_str().to_string());
|
||||
set_fields.insert(name.as_str().to_owned());
|
||||
}
|
||||
for (key, value) in args.kwargs {
|
||||
if let Some(pos) = fields.iter().position(|f| f.as_str() == key)
|
||||
if let Some(pos) = fields.iter().position(|f| f.as_bytes() == key.as_bytes())
|
||||
&& pos < n_args
|
||||
{
|
||||
return Err(vm.new_type_error(format!(
|
||||
@@ -322,7 +323,10 @@ pub(crate) mod _ast {
|
||||
)));
|
||||
}
|
||||
|
||||
if fields.iter().all(|field| field.as_str() != key) {
|
||||
if fields
|
||||
.iter()
|
||||
.all(|field| field.as_bytes() != key.as_bytes())
|
||||
{
|
||||
let attrs = if let Some(attrs) = &attributes {
|
||||
attrs
|
||||
} else {
|
||||
@@ -334,7 +338,7 @@ pub(crate) mod _ast {
|
||||
attributes = Some(attrs);
|
||||
attributes.as_ref().unwrap()
|
||||
};
|
||||
if attrs.iter().all(|attr| attr.as_str() != key) {
|
||||
if attrs.iter().all(|attr| attr.as_bytes() != key.as_bytes()) {
|
||||
let message = vm.ctx.new_str(format!(
|
||||
"{}.__init__ got an unexpected keyword argument '{}'. \
|
||||
Support for arbitrary keyword arguments is deprecated and will be removed in Python 3.15.",
|
||||
@@ -368,13 +372,13 @@ Support for arbitrary keyword arguments is deprecated and will be removed in Pyt
|
||||
if set_fields.contains(field.as_str()) {
|
||||
continue;
|
||||
}
|
||||
if let Some(ftype) = ft_dict.get_item_opt::<str>(field.as_str(), vm)? {
|
||||
if let Some(ftype) = ft_dict.get_item_opt::<Wtf8>(field.as_wtf8(), vm)? {
|
||||
if ftype.fast_isinstance(vm.ctx.types.union_type) {
|
||||
// Optional field (T | None) — no default
|
||||
} else if ftype.fast_isinstance(vm.ctx.types.generic_alias_type) {
|
||||
// List field (list[T]) — default to []
|
||||
let empty_list: PyObjectRef = vm.ctx.new_list(vec![]).into();
|
||||
zelf.set_attr(vm.ctx.intern_str(field.as_str()), empty_list, vm)?;
|
||||
zelf.set_attr(vm.ctx.intern_str(field.as_wtf8()), empty_list, vm)?;
|
||||
} else if ftype.is(&expr_ctx_type) {
|
||||
// expr_context — default to Load()
|
||||
let load_type =
|
||||
@@ -384,13 +388,13 @@ Support for arbitrary keyword arguments is deprecated and will be removed in Pyt
|
||||
.unwrap_or_else(|| {
|
||||
vm.ctx.new_base_object(load_type, Some(vm.ctx.new_dict()))
|
||||
});
|
||||
zelf.set_attr(vm.ctx.intern_str(field.as_str()), load_instance, vm)?;
|
||||
zelf.set_attr(vm.ctx.intern_str(field.as_wtf8()), load_instance, vm)?;
|
||||
} else {
|
||||
// Required field missing: emit DeprecationWarning (CPython behavior).
|
||||
// Required field missing: emit DeprecationWarning.
|
||||
let message = vm.ctx.new_str(format!(
|
||||
"{}.__init__ missing 1 required positional argument: '{}'",
|
||||
zelf.class().name(),
|
||||
field.as_str()
|
||||
field.as_wtf8()
|
||||
));
|
||||
warn::warn(
|
||||
message.into(),
|
||||
|
||||
@@ -4,14 +4,15 @@ use crate::{
|
||||
class::PyClassImpl,
|
||||
stdlib::ast::NodeAst,
|
||||
};
|
||||
use rustpython_common::wtf8::Wtf8Buf;
|
||||
|
||||
fn repr_ast_list(vm: &VirtualMachine, items: Vec<PyObjectRef>, depth: usize) -> PyResult<String> {
|
||||
fn repr_ast_list(vm: &VirtualMachine, items: Vec<PyObjectRef>, depth: usize) -> PyResult<Wtf8Buf> {
|
||||
if items.is_empty() {
|
||||
let empty_list: PyObjectRef = vm.ctx.new_list(vec![]).into();
|
||||
return Ok(empty_list.repr(vm)?.to_string());
|
||||
return Ok(empty_list.repr(vm)?.as_wtf8().to_owned());
|
||||
}
|
||||
|
||||
let mut parts: Vec<String> = Vec::new();
|
||||
let mut parts: Vec<Wtf8Buf> = Vec::new();
|
||||
let first = &items[0];
|
||||
let last = items.last().unwrap();
|
||||
|
||||
@@ -22,38 +23,38 @@ fn repr_ast_list(vm: &VirtualMachine, items: Vec<PyObjectRef>, depth: usize) ->
|
||||
let repr = if item.fast_isinstance(&NodeAst::make_class(&vm.ctx)) {
|
||||
repr_ast_node(vm, item, depth.saturating_sub(1))?
|
||||
} else {
|
||||
item.repr(vm)?.to_string()
|
||||
item.repr(vm)?.as_wtf8().to_owned()
|
||||
};
|
||||
parts.push(repr);
|
||||
}
|
||||
|
||||
let mut rendered = String::from("[");
|
||||
let mut rendered = Wtf8Buf::from("[");
|
||||
if !parts.is_empty() {
|
||||
rendered.push_str(&parts[0]);
|
||||
rendered.push_wtf8(&parts[0]);
|
||||
}
|
||||
if items.len() > 2 {
|
||||
if !parts[0].is_empty() {
|
||||
rendered.push_str(", ...");
|
||||
rendered.push_wtf8(", ...".as_ref());
|
||||
}
|
||||
if parts.len() > 1 {
|
||||
rendered.push_str(", ");
|
||||
rendered.push_str(&parts[1]);
|
||||
rendered.push_wtf8(", ".as_ref());
|
||||
rendered.push_wtf8(&parts[1]);
|
||||
}
|
||||
} else if parts.len() > 1 {
|
||||
rendered.push_str(", ");
|
||||
rendered.push_str(&parts[1]);
|
||||
rendered.push_wtf8(", ".as_ref());
|
||||
rendered.push_wtf8(&parts[1]);
|
||||
}
|
||||
rendered.push(']');
|
||||
rendered.push_wtf8("]".as_ref());
|
||||
Ok(rendered)
|
||||
}
|
||||
|
||||
fn repr_ast_tuple(vm: &VirtualMachine, items: Vec<PyObjectRef>, depth: usize) -> PyResult<String> {
|
||||
fn repr_ast_tuple(vm: &VirtualMachine, items: Vec<PyObjectRef>, depth: usize) -> PyResult<Wtf8Buf> {
|
||||
if items.is_empty() {
|
||||
let empty_tuple: PyObjectRef = vm.ctx.empty_tuple.clone().into();
|
||||
return Ok(empty_tuple.repr(vm)?.to_string());
|
||||
return Ok(empty_tuple.repr(vm)?.as_wtf8().to_owned());
|
||||
}
|
||||
|
||||
let mut parts: Vec<String> = Vec::new();
|
||||
let mut parts: Vec<Wtf8Buf> = Vec::new();
|
||||
let first = &items[0];
|
||||
let last = items.last().unwrap();
|
||||
|
||||
@@ -64,31 +65,31 @@ fn repr_ast_tuple(vm: &VirtualMachine, items: Vec<PyObjectRef>, depth: usize) ->
|
||||
let repr = if item.fast_isinstance(&NodeAst::make_class(&vm.ctx)) {
|
||||
repr_ast_node(vm, item, depth.saturating_sub(1))?
|
||||
} else {
|
||||
item.repr(vm)?.to_string()
|
||||
item.repr(vm)?.as_wtf8().to_owned()
|
||||
};
|
||||
parts.push(repr);
|
||||
}
|
||||
|
||||
let mut rendered = String::from("(");
|
||||
let mut rendered = Wtf8Buf::from("(");
|
||||
if !parts.is_empty() {
|
||||
rendered.push_str(&parts[0]);
|
||||
rendered.push_wtf8(&parts[0]);
|
||||
}
|
||||
if items.len() > 2 {
|
||||
if !parts[0].is_empty() {
|
||||
rendered.push_str(", ...");
|
||||
rendered.push_wtf8(", ...".as_ref());
|
||||
}
|
||||
if parts.len() > 1 {
|
||||
rendered.push_str(", ");
|
||||
rendered.push_str(&parts[1]);
|
||||
rendered.push_wtf8(", ".as_ref());
|
||||
rendered.push_wtf8(&parts[1]);
|
||||
}
|
||||
} else if parts.len() > 1 {
|
||||
rendered.push_str(", ");
|
||||
rendered.push_str(&parts[1]);
|
||||
rendered.push_wtf8(", ".as_ref());
|
||||
rendered.push_wtf8(&parts[1]);
|
||||
}
|
||||
if items.len() == 1 {
|
||||
rendered.push(',');
|
||||
rendered.push_wtf8(",".as_ref());
|
||||
}
|
||||
rendered.push(')');
|
||||
rendered.push_wtf8(")".as_ref());
|
||||
Ok(rendered)
|
||||
}
|
||||
|
||||
@@ -96,25 +97,32 @@ pub(crate) fn repr_ast_node(
|
||||
vm: &VirtualMachine,
|
||||
obj: &PyObjectRef,
|
||||
depth: usize,
|
||||
) -> PyResult<String> {
|
||||
) -> PyResult<Wtf8Buf> {
|
||||
let cls = obj.class();
|
||||
if depth == 0 {
|
||||
return Ok(format!("{}(...)", cls.name()));
|
||||
let mut s = Wtf8Buf::from(&*cls.name());
|
||||
s.push_wtf8("(...)".as_ref());
|
||||
return Ok(s);
|
||||
}
|
||||
|
||||
let fields = cls.get_attr(vm.ctx.intern_str("_fields"));
|
||||
let fields = match fields {
|
||||
Some(fields) => fields.try_to_value::<Vec<crate::builtins::PyStrRef>>(vm)?,
|
||||
None => return Ok(format!("{}(...)", cls.name())),
|
||||
None => {
|
||||
let mut s = Wtf8Buf::from(&*cls.name());
|
||||
s.push_wtf8("(...)".as_ref());
|
||||
return Ok(s);
|
||||
}
|
||||
};
|
||||
|
||||
if fields.is_empty() {
|
||||
return Ok(format!("{}()", cls.name()));
|
||||
let mut s = Wtf8Buf::from(&*cls.name());
|
||||
s.push_wtf8("()".as_ref());
|
||||
return Ok(s);
|
||||
}
|
||||
|
||||
let mut rendered = String::new();
|
||||
rendered.push_str(&cls.name());
|
||||
rendered.push('(');
|
||||
let mut rendered = Wtf8Buf::from(&*cls.name());
|
||||
rendered.push_wtf8("(".as_ref());
|
||||
|
||||
for (idx, field) in fields.iter().enumerate() {
|
||||
let value = obj.get_attr(field, vm)?;
|
||||
@@ -131,17 +139,17 @@ pub(crate) fn repr_ast_node(
|
||||
} else if value.fast_isinstance(&NodeAst::make_class(&vm.ctx)) {
|
||||
repr_ast_node(vm, &value, depth.saturating_sub(1))?
|
||||
} else {
|
||||
value.repr(vm)?.to_string()
|
||||
value.repr(vm)?.as_wtf8().to_owned()
|
||||
};
|
||||
|
||||
if idx > 0 {
|
||||
rendered.push_str(", ");
|
||||
rendered.push_wtf8(", ".as_ref());
|
||||
}
|
||||
rendered.push_str(field.as_str());
|
||||
rendered.push('=');
|
||||
rendered.push_str(&value_repr);
|
||||
rendered.push_wtf8(field.as_wtf8());
|
||||
rendered.push_wtf8("=".as_ref());
|
||||
rendered.push_wtf8(&value_repr);
|
||||
}
|
||||
|
||||
rendered.push(')');
|
||||
rendered.push_wtf8(")".as_ref());
|
||||
Ok(rendered)
|
||||
}
|
||||
@@ -11,6 +11,7 @@ mod builtins {
|
||||
AsObject, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, TryFromObject, VirtualMachine,
|
||||
builtins::{
|
||||
PyByteArray, PyBytes, PyDictRef, PyStr, PyStrRef, PyTuple, PyTupleRef, PyType,
|
||||
PyUtf8StrRef,
|
||||
enumerate::PyReverseSequenceIterator,
|
||||
function::{PyCellRef, PyFunction},
|
||||
int::PyIntRef,
|
||||
@@ -30,7 +31,7 @@ mod builtins {
|
||||
types::PyComparisonOp,
|
||||
};
|
||||
use itertools::Itertools;
|
||||
use num_traits::{Signed, ToPrimitive};
|
||||
use num_traits::{Signed, ToPrimitive, Zero};
|
||||
use rustpython_common::wtf8::CodePoint;
|
||||
|
||||
#[cfg(not(feature = "rustpython-compiler"))]
|
||||
@@ -65,7 +66,7 @@ mod builtins {
|
||||
#[pyfunction]
|
||||
pub fn ascii(obj: PyObjectRef, vm: &VirtualMachine) -> PyResult<ascii::AsciiString> {
|
||||
let repr = obj.repr(vm)?;
|
||||
let ascii = to_ascii(repr.as_str());
|
||||
let ascii = to_ascii(repr.as_wtf8());
|
||||
Ok(ascii)
|
||||
}
|
||||
|
||||
@@ -99,7 +100,7 @@ mod builtins {
|
||||
struct CompileArgs {
|
||||
source: PyObjectRef,
|
||||
filename: FsPath,
|
||||
mode: PyStrRef,
|
||||
mode: PyUtf8StrRef,
|
||||
#[pyarg(any, optional)]
|
||||
flags: OptionalArg<PyIntRef>,
|
||||
#[pyarg(any, optional)]
|
||||
@@ -139,7 +140,6 @@ mod builtins {
|
||||
.source
|
||||
.fast_isinstance(&ast::NodeAst::make_class(&vm.ctx))
|
||||
{
|
||||
use num_traits::Zero;
|
||||
let flags: i32 = args.flags.map_or(Ok(0), |v| v.try_to_primitive(vm))?;
|
||||
let is_ast_only = !(flags & ast::PY_CF_ONLY_AST).is_zero();
|
||||
|
||||
@@ -197,7 +197,7 @@ mod builtins {
|
||||
#[cfg(feature = "parser")]
|
||||
{
|
||||
use crate::convert::ToPyException;
|
||||
use num_traits::Zero;
|
||||
|
||||
use ruff_python_parser as parser;
|
||||
|
||||
let source = ArgStrOrBytesLike::try_from_object(vm, args.source)?;
|
||||
@@ -413,7 +413,7 @@ mod builtins {
|
||||
if source.contains(&0) {
|
||||
return Err(vm.new_exception_msg(
|
||||
vm.ctx.exceptions.syntax_error.to_owned(),
|
||||
"source code string cannot contain null bytes".to_owned(),
|
||||
"source code string cannot contain null bytes".into(),
|
||||
));
|
||||
}
|
||||
|
||||
@@ -424,9 +424,9 @@ mod builtins {
|
||||
err.valid_up_to()
|
||||
);
|
||||
|
||||
vm.new_exception_msg(vm.ctx.exceptions.syntax_error.to_owned(), msg)
|
||||
vm.new_exception_msg(vm.ctx.exceptions.syntax_error.to_owned(), msg.into())
|
||||
})?;
|
||||
Ok(Either::A(vm.ctx.new_str(source.trim_start())))
|
||||
Ok(Either::A(vm.ctx.new_utf8_str(source.trim_start())))
|
||||
}
|
||||
Either::B(code) => Ok(Either::B(code)),
|
||||
}?;
|
||||
@@ -435,7 +435,7 @@ mod builtins {
|
||||
|
||||
#[pyfunction]
|
||||
fn exec(
|
||||
source: Either<PyStrRef, PyRef<crate::builtins::PyCode>>,
|
||||
source: Either<PyUtf8StrRef, PyRef<crate::builtins::PyCode>>,
|
||||
scope: ScopeArgs,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult {
|
||||
@@ -445,7 +445,7 @@ mod builtins {
|
||||
|
||||
fn run_code(
|
||||
vm: &VirtualMachine,
|
||||
source: Either<PyStrRef, PyRef<crate::builtins::PyCode>>,
|
||||
source: Either<PyUtf8StrRef, PyRef<crate::builtins::PyCode>>,
|
||||
scope: crate::scope::Scope,
|
||||
#[allow(unused_variables)] mode: crate::compiler::Mode,
|
||||
func: &str,
|
||||
@@ -453,9 +453,11 @@ mod builtins {
|
||||
// Determine code object:
|
||||
let code_obj = match source {
|
||||
#[cfg(feature = "rustpython-compiler")]
|
||||
Either::A(string) => vm
|
||||
.compile(string.as_str(), mode, "<string>".to_owned())
|
||||
.map_err(|err| vm.new_syntax_error(&err, Some(string.as_str())))?,
|
||||
Either::A(string) => {
|
||||
let source = string.as_str();
|
||||
vm.compile(source, mode, "<string>".to_owned())
|
||||
.map_err(|err| vm.new_syntax_error(&err, Some(source)))?
|
||||
}
|
||||
#[cfg(not(feature = "rustpython-compiler"))]
|
||||
Either::A(_) => return Err(vm.new_type_error(CODEGEN_NOT_SUPPORTED.to_owned())),
|
||||
Either::B(code_obj) => code_obj,
|
||||
@@ -567,8 +569,15 @@ mod builtins {
|
||||
&& std::io::stdin().is_terminal()
|
||||
&& !is_pty_child();
|
||||
|
||||
// Disable rustyline if prompt contains surrogates (not valid UTF-8 for terminal)
|
||||
let prompt_str = match &prompt {
|
||||
OptionalArg::Present(s) => s.to_str(),
|
||||
OptionalArg::Missing => Some(""),
|
||||
};
|
||||
let use_rustyline = use_rustyline && prompt_str.is_some();
|
||||
|
||||
if use_rustyline {
|
||||
let prompt = prompt.as_ref().map_or("", |s| s.as_str());
|
||||
let prompt = prompt_str.unwrap();
|
||||
let mut readline = Readline::new(());
|
||||
match readline.readline(prompt) {
|
||||
ReadlineResult::Line(s) => Ok(vm.ctx.new_str(s).into()),
|
||||
|
||||
@@ -42,40 +42,59 @@ mod _codecs {
|
||||
struct CodeArgs {
|
||||
obj: PyObjectRef,
|
||||
#[pyarg(any, optional)]
|
||||
encoding: Option<PyStrRef>,
|
||||
encoding: Option<PyUtf8StrRef>,
|
||||
#[pyarg(any, optional)]
|
||||
errors: Option<PyStrRef>,
|
||||
errors: Option<PyUtf8StrRef>,
|
||||
}
|
||||
|
||||
impl CodeArgs {
|
||||
fn apply(
|
||||
self,
|
||||
vm: &VirtualMachine,
|
||||
f: fn(
|
||||
&codecs::CodecsRegistry,
|
||||
PyObjectRef,
|
||||
&str,
|
||||
Option<PyUtf8StrRef>,
|
||||
&VirtualMachine,
|
||||
) -> PyResult,
|
||||
) -> PyResult {
|
||||
let encoding = self
|
||||
.encoding
|
||||
.as_deref()
|
||||
.map(|s| s.as_str())
|
||||
.unwrap_or(codecs::DEFAULT_ENCODING);
|
||||
f(
|
||||
&vm.state.codec_registry,
|
||||
self.obj,
|
||||
encoding,
|
||||
self.errors,
|
||||
vm,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[pyfunction]
|
||||
fn encode(args: CodeArgs, vm: &VirtualMachine) -> PyResult {
|
||||
let encoding = args
|
||||
.encoding
|
||||
.as_ref()
|
||||
.map_or(codecs::DEFAULT_ENCODING, |s| s.as_str());
|
||||
vm.state
|
||||
.codec_registry
|
||||
.encode(args.obj, encoding, args.errors, vm)
|
||||
args.apply(vm, codecs::CodecsRegistry::encode)
|
||||
}
|
||||
|
||||
#[pyfunction]
|
||||
fn decode(args: CodeArgs, vm: &VirtualMachine) -> PyResult {
|
||||
let encoding = args
|
||||
.encoding
|
||||
.as_ref()
|
||||
.map_or(codecs::DEFAULT_ENCODING, |s| s.as_str());
|
||||
vm.state
|
||||
.codec_registry
|
||||
.decode(args.obj, encoding, args.errors, vm)
|
||||
args.apply(vm, codecs::CodecsRegistry::decode)
|
||||
}
|
||||
|
||||
#[pyfunction]
|
||||
fn _forget_codec(encoding: PyStrRef, vm: &VirtualMachine) {
|
||||
fn _forget_codec(encoding: PyUtf8StrRef, vm: &VirtualMachine) {
|
||||
vm.state.codec_registry.forget(encoding.as_str());
|
||||
}
|
||||
|
||||
#[pyfunction]
|
||||
fn register_error(name: PyStrRef, handler: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
|
||||
fn register_error(
|
||||
name: PyUtf8StrRef,
|
||||
handler: PyObjectRef,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<()> {
|
||||
if !handler.is_callable() {
|
||||
return Err(vm.new_type_error("handler must be callable".to_owned()));
|
||||
}
|
||||
@@ -86,28 +105,18 @@ mod _codecs {
|
||||
}
|
||||
|
||||
#[pyfunction]
|
||||
fn lookup_error(name: PyStrRef, vm: &VirtualMachine) -> PyResult {
|
||||
if name.as_wtf8().as_bytes().contains(&0) {
|
||||
fn lookup_error(name: PyUtf8StrRef, vm: &VirtualMachine) -> PyResult {
|
||||
if name.as_str().contains('\0') {
|
||||
return Err(cstring_error(vm));
|
||||
}
|
||||
if !name.as_wtf8().is_utf8() {
|
||||
return Err(vm.new_unicode_encode_error(
|
||||
"'utf-8' codec can't encode character: surrogates not allowed".to_owned(),
|
||||
));
|
||||
}
|
||||
vm.state.codec_registry.lookup_error(name.as_str(), vm)
|
||||
}
|
||||
|
||||
#[pyfunction]
|
||||
fn _unregister_error(errors: PyStrRef, vm: &VirtualMachine) -> PyResult<bool> {
|
||||
if errors.as_wtf8().as_bytes().contains(&0) {
|
||||
fn _unregister_error(errors: PyUtf8StrRef, vm: &VirtualMachine) -> PyResult<bool> {
|
||||
if errors.as_str().contains('\0') {
|
||||
return Err(cstring_error(vm));
|
||||
}
|
||||
if !errors.as_wtf8().is_utf8() {
|
||||
return Err(vm.new_unicode_encode_error(
|
||||
"'utf-8' codec can't encode character: surrogates not allowed".to_owned(),
|
||||
));
|
||||
}
|
||||
vm.state
|
||||
.codec_registry
|
||||
.unregister_error(errors.as_str(), vm)
|
||||
@@ -120,7 +129,7 @@ mod _codecs {
|
||||
#[pyarg(positional)]
|
||||
s: PyStrRef,
|
||||
#[pyarg(positional, optional)]
|
||||
errors: Option<PyStrRef>,
|
||||
errors: Option<PyUtf8StrRef>,
|
||||
}
|
||||
|
||||
impl EncodeArgs {
|
||||
@@ -143,7 +152,7 @@ mod _codecs {
|
||||
#[pyarg(positional)]
|
||||
data: ArgBytesLike,
|
||||
#[pyarg(positional, optional)]
|
||||
errors: Option<PyStrRef>,
|
||||
errors: Option<PyUtf8StrRef>,
|
||||
#[pyarg(positional, default = false)]
|
||||
final_decode: bool,
|
||||
}
|
||||
@@ -165,7 +174,7 @@ mod _codecs {
|
||||
#[pyarg(positional)]
|
||||
data: ArgBytesLike,
|
||||
#[pyarg(positional, optional)]
|
||||
errors: Option<PyStrRef>,
|
||||
errors: Option<PyUtf8StrRef>,
|
||||
}
|
||||
|
||||
impl DecodeArgsNoFinal {
|
||||
@@ -366,14 +375,14 @@ fn delegate_pycodecs(
|
||||
#[pymodule(sub)]
|
||||
mod _codecs_windows {
|
||||
use crate::{PyResult, VirtualMachine};
|
||||
use crate::{builtins::PyStrRef, function::ArgBytesLike};
|
||||
use crate::{builtins::PyStrRef, builtins::PyUtf8StrRef, function::ArgBytesLike};
|
||||
|
||||
#[derive(FromArgs)]
|
||||
struct MbcsEncodeArgs {
|
||||
#[pyarg(positional)]
|
||||
s: PyStrRef,
|
||||
#[pyarg(positional, optional)]
|
||||
errors: Option<PyStrRef>,
|
||||
errors: Option<PyUtf8StrRef>,
|
||||
}
|
||||
|
||||
#[pyfunction]
|
||||
@@ -461,7 +470,7 @@ mod _codecs_windows {
|
||||
#[pyarg(positional)]
|
||||
data: ArgBytesLike,
|
||||
#[pyarg(positional, optional)]
|
||||
errors: Option<PyStrRef>,
|
||||
errors: Option<PyUtf8StrRef>,
|
||||
#[pyarg(positional, default = false)]
|
||||
#[allow(dead_code)]
|
||||
r#final: bool,
|
||||
@@ -559,7 +568,7 @@ mod _codecs_windows {
|
||||
#[pyarg(positional)]
|
||||
s: PyStrRef,
|
||||
#[pyarg(positional, optional)]
|
||||
errors: Option<PyStrRef>,
|
||||
errors: Option<PyUtf8StrRef>,
|
||||
}
|
||||
|
||||
#[pyfunction]
|
||||
@@ -647,7 +656,7 @@ mod _codecs_windows {
|
||||
#[pyarg(positional)]
|
||||
data: ArgBytesLike,
|
||||
#[pyarg(positional, optional)]
|
||||
errors: Option<PyStrRef>,
|
||||
errors: Option<PyUtf8StrRef>,
|
||||
#[pyarg(positional, default = false)]
|
||||
#[allow(dead_code)]
|
||||
r#final: bool,
|
||||
@@ -747,7 +756,7 @@ mod _codecs_windows {
|
||||
#[pyarg(positional)]
|
||||
s: PyStrRef,
|
||||
#[pyarg(positional, optional)]
|
||||
errors: Option<PyStrRef>,
|
||||
errors: Option<PyUtf8StrRef>,
|
||||
}
|
||||
|
||||
fn code_page_encoding_name(code_page: u32) -> String {
|
||||
@@ -1074,7 +1083,7 @@ mod _codecs_windows {
|
||||
#[pyarg(positional)]
|
||||
data: ArgBytesLike,
|
||||
#[pyarg(positional, optional)]
|
||||
errors: Option<PyStrRef>,
|
||||
errors: Option<PyUtf8StrRef>,
|
||||
#[pyarg(positional, default = false)]
|
||||
r#final: bool,
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ mod _collections {
|
||||
atomic_func,
|
||||
builtins::{
|
||||
IterStatus::{Active, Exhausted},
|
||||
PositionIterInternal, PyGenericAlias, PyInt, PyType, PyTypeRef,
|
||||
PositionIterInternal, PyGenericAlias, PyInt, PyStr, PyType, PyTypeRef,
|
||||
},
|
||||
common::lock::{PyMutex, PyRwLock, PyRwLockReadGuard, PyRwLockWriteGuard},
|
||||
function::{KwArgs, OptionalArg, PyComparisonValue},
|
||||
@@ -559,7 +559,7 @@ mod _collections {
|
||||
|
||||
impl Representable for PyDeque {
|
||||
#[inline]
|
||||
fn repr_str(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<String> {
|
||||
fn repr(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<PyRef<PyStr>> {
|
||||
let deque = zelf.borrow_deque().clone();
|
||||
let class = zelf.class();
|
||||
let class_name = class.name();
|
||||
@@ -568,15 +568,24 @@ mod _collections {
|
||||
.map(|maxlen| format!("], maxlen={maxlen}"))
|
||||
.unwrap_or_else(|| "]".to_owned());
|
||||
|
||||
let s = if zelf.__len__() == 0 {
|
||||
format!("{class_name}([{closing_part})")
|
||||
} else if let Some(_guard) = ReprGuard::enter(vm, zelf.as_object()) {
|
||||
collection_repr(Some(&class_name), "[", &closing_part, deque.iter(), vm)?
|
||||
if zelf.__len__() == 0 {
|
||||
return Ok(vm.ctx.new_str(format!("{class_name}([{closing_part})")));
|
||||
}
|
||||
if let Some(_guard) = ReprGuard::enter(vm, zelf.as_object()) {
|
||||
Ok(vm.ctx.new_str(collection_repr(
|
||||
Some(&class_name),
|
||||
"[",
|
||||
&closing_part,
|
||||
deque.iter(),
|
||||
vm,
|
||||
)?))
|
||||
} else {
|
||||
"[...]".to_owned()
|
||||
};
|
||||
Ok(vm.ctx.intern_str("[...]").to_owned())
|
||||
}
|
||||
}
|
||||
|
||||
Ok(s)
|
||||
fn repr_str(_zelf: &Py<Self>, _vm: &VirtualMachine) -> PyResult<String> {
|
||||
unreachable!("repr() is overridden directly")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -78,7 +78,7 @@ impl PyType {
|
||||
{
|
||||
return Err(vm.new_exception_msg(
|
||||
vm.ctx.exceptions.system_error.to_owned(),
|
||||
format!("class \"{}\" already initialized", self.name()),
|
||||
format!("class \"{}\" already initialized", self.name()).into(),
|
||||
));
|
||||
}
|
||||
Ok(())
|
||||
@@ -627,7 +627,7 @@ pub(crate) mod _ctypes {
|
||||
#[pyfunction]
|
||||
fn dlsym(
|
||||
handle: usize,
|
||||
name: crate::builtins::PyStrRef,
|
||||
name: crate::builtins::PyUtf8StrRef,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<usize> {
|
||||
let symbol_name = alloc::ffi::CString::new(name.as_str())
|
||||
@@ -689,7 +689,7 @@ pub(crate) mod _ctypes {
|
||||
// PyUnicode_CheckExact(cls) - string creates incomplete pointer type
|
||||
if let Some(s) = cls.downcast_ref::<PyStr>() {
|
||||
// Incomplete pointer type: _type_ not set, cache key is id(result)
|
||||
let name = format!("LP_{}", s.as_str());
|
||||
let name = format!("LP_{}", s.as_wtf8());
|
||||
|
||||
let new_type = metaclass
|
||||
.as_object()
|
||||
@@ -1117,7 +1117,7 @@ pub(crate) mod _ctypes {
|
||||
arg_values.push(ptr);
|
||||
arg_types.push(Type::pointer());
|
||||
} else if let Some(s) = arg.downcast_ref::<crate::builtins::PyStr>() {
|
||||
let ptr = s.as_str().as_ptr() as isize;
|
||||
let ptr = s.as_bytes().as_ptr() as isize;
|
||||
arg_values.push(ptr);
|
||||
arg_types.push(Type::pointer());
|
||||
} else {
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
use super::StgInfo;
|
||||
use super::base::{CDATA_BUFFER_METHODS, PyCData};
|
||||
use super::type_info;
|
||||
use crate::common::lock::LazyLock;
|
||||
use crate::sliceable::SaturatedSliceIter;
|
||||
use crate::{
|
||||
AsObject, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, TryFromObject, VirtualMachine,
|
||||
atomic_func,
|
||||
@@ -455,7 +457,6 @@ impl Initializer for PyCArray {
|
||||
|
||||
impl AsSequence for PyCArray {
|
||||
fn as_sequence() -> &'static PySequenceMethods {
|
||||
use crate::common::lock::LazyLock;
|
||||
static AS_SEQUENCE: LazyLock<PySequenceMethods> = LazyLock::new(|| PySequenceMethods {
|
||||
length: atomic_func!(|seq, _vm| {
|
||||
let zelf = PyCArray::sequence_downcast(seq);
|
||||
@@ -480,7 +481,6 @@ impl AsSequence for PyCArray {
|
||||
|
||||
impl AsMapping for PyCArray {
|
||||
fn as_mapping() -> &'static PyMappingMethods {
|
||||
use crate::common::lock::LazyLock;
|
||||
static AS_MAPPING: LazyLock<PyMappingMethods> = LazyLock::new(|| PyMappingMethods {
|
||||
length: atomic_func!(|mapping, _vm| {
|
||||
let zelf = PyCArray::mapping_downcast(mapping);
|
||||
@@ -775,7 +775,12 @@ impl PyCArray {
|
||||
}
|
||||
Some("u") => {
|
||||
if let Some(s) = value.downcast_ref::<PyStr>() {
|
||||
let code = s.as_str().chars().next().map(|c| c as u32).unwrap_or(0);
|
||||
let code = s
|
||||
.as_wtf8()
|
||||
.code_points()
|
||||
.next()
|
||||
.map(|c| c.to_u32())
|
||||
.unwrap_or(0);
|
||||
if offset + WCHAR_SIZE <= buffer.len() {
|
||||
wchar_to_bytes(code, &mut buffer[offset..]);
|
||||
}
|
||||
@@ -808,7 +813,7 @@ impl PyCArray {
|
||||
let (ptr_val, converted) = if value.is(&vm.ctx.none) {
|
||||
(0usize, None)
|
||||
} else if let Some(s) = value.downcast_ref::<PyStr>() {
|
||||
let (holder, ptr) = super::base::str_to_wchar_bytes(s.as_str(), vm);
|
||||
let (holder, ptr) = super::base::str_to_wchar_bytes(s.as_wtf8(), vm);
|
||||
(ptr, Some(holder))
|
||||
} else if let Ok(int_val) = value.try_index(vm) {
|
||||
(int_val.as_bigint().to_usize().unwrap_or(0), None)
|
||||
@@ -965,8 +970,6 @@ impl PyCArray {
|
||||
|
||||
// Array_subscript slice handling
|
||||
fn getitem_by_slice(zelf: &Py<Self>, slice: &PySlice, vm: &VirtualMachine) -> PyResult {
|
||||
use crate::sliceable::SaturatedSliceIter;
|
||||
|
||||
let stg = zelf.class().stg_info_opt();
|
||||
let length = stg.as_ref().map_or(0, |i| i.length);
|
||||
|
||||
@@ -1089,8 +1092,6 @@ impl PyCArray {
|
||||
value: PyObjectRef,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<()> {
|
||||
use crate::sliceable::SaturatedSliceIter;
|
||||
|
||||
let length = zelf.class().stg_info_opt().map_or(0, |i| i.length);
|
||||
|
||||
// PySlice_Unpack + PySlice_AdjustIndices
|
||||
@@ -1249,15 +1250,15 @@ fn wchar_array_set_value(
|
||||
.ok_or_else(|| vm.new_type_error("unicode string expected"))?;
|
||||
let mut buffer = zelf.0.buffer.write();
|
||||
let wchar_count = buffer.len() / WCHAR_SIZE;
|
||||
let char_count = s.as_str().chars().count();
|
||||
let char_count = s.as_wtf8().code_points().count();
|
||||
|
||||
if char_count > wchar_count {
|
||||
return Err(vm.new_value_error("string too long"));
|
||||
}
|
||||
|
||||
for (i, ch) in s.as_str().chars().enumerate() {
|
||||
for (i, ch) in s.as_wtf8().code_points().enumerate() {
|
||||
let offset = i * WCHAR_SIZE;
|
||||
wchar_to_bytes(ch as u32, &mut buffer.to_mut()[offset..]);
|
||||
wchar_to_bytes(ch.to_u32(), &mut buffer.to_mut()[offset..]);
|
||||
}
|
||||
|
||||
let terminator_offset = char_count * WCHAR_SIZE;
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
use super::array::{WCHAR_SIZE, wchar_from_bytes, wchar_to_bytes};
|
||||
use crate::builtins::{PyBytes, PyDict, PyMemoryView, PyStr, PyTuple, PyType, PyTypeRef};
|
||||
use crate::builtins::{
|
||||
PyBytes, PyDict, PyList, PyMemoryView, PyStr, PyTuple, PyType, PyTypeRef, PyUtf8Str,
|
||||
};
|
||||
use crate::class::StaticType;
|
||||
use crate::convert::ToPyObject;
|
||||
use crate::function::{ArgBytesLike, OptionalArg, PySetterValue};
|
||||
use crate::protocol::{BufferMethods, PyBuffer};
|
||||
use crate::types::{Constructor, GetDescriptor, Representable};
|
||||
@@ -16,6 +19,7 @@ use core::mem;
|
||||
use crossbeam_utils::atomic::AtomicCell;
|
||||
use num_traits::{Signed, ToPrimitive};
|
||||
use rustpython_common::lock::PyRwLock;
|
||||
use rustpython_common::wtf8::Wtf8;
|
||||
use widestring::WideChar;
|
||||
|
||||
// StgInfo - Storage information for ctypes types
|
||||
@@ -363,11 +367,10 @@ pub(super) fn get_field_format(
|
||||
if let Ok(type_attr) = field_type.get_attr("_type_", vm)
|
||||
&& let Some(type_str) = type_attr.downcast_ref::<PyStr>()
|
||||
{
|
||||
let s = type_str.as_str();
|
||||
if !s.is_empty() {
|
||||
return format!("{}{}", endian_prefix, s);
|
||||
}
|
||||
return s.to_string();
|
||||
let s = type_str
|
||||
.to_str()
|
||||
.expect("_type_ is validated as ASCII at type creation");
|
||||
return format!("{}{}", endian_prefix, s);
|
||||
}
|
||||
|
||||
// Default: single byte
|
||||
@@ -431,10 +434,10 @@ pub(super) fn ensure_z_null_terminated(
|
||||
}
|
||||
|
||||
/// Convert str to null-terminated wchar_t buffer. Returns (PyBytes holder, pointer).
|
||||
pub(super) fn str_to_wchar_bytes(s: &str, vm: &VirtualMachine) -> (PyObjectRef, usize) {
|
||||
pub(super) fn str_to_wchar_bytes(s: &Wtf8, vm: &VirtualMachine) -> (PyObjectRef, usize) {
|
||||
let wchars: Vec<libc::wchar_t> = s
|
||||
.chars()
|
||||
.map(|c| c as libc::wchar_t)
|
||||
.code_points()
|
||||
.map(|cp| cp.to_u32() as libc::wchar_t)
|
||||
.chain(core::iter::once(0))
|
||||
.collect();
|
||||
let ptr = wchars.as_ptr() as usize;
|
||||
@@ -964,9 +967,9 @@ impl PyCData {
|
||||
if let Some(str_val) = value.downcast_ref::<PyStr>() {
|
||||
// Convert str to wchar_t bytes (platform-dependent size)
|
||||
let mut wchar_bytes = Vec::with_capacity(size);
|
||||
for ch in str_val.as_str().chars().take(size / WCHAR_SIZE) {
|
||||
for cp in str_val.as_wtf8().code_points().take(size / WCHAR_SIZE) {
|
||||
let mut bytes = [0u8; 4];
|
||||
wchar_to_bytes(ch as u32, &mut bytes);
|
||||
wchar_to_bytes(cp.to_u32(), &mut bytes);
|
||||
wchar_bytes.extend_from_slice(&bytes[..WCHAR_SIZE]);
|
||||
}
|
||||
// Pad with nulls to fill the array
|
||||
@@ -1299,13 +1302,13 @@ impl PyCData {
|
||||
.ok_or_else(|| vm.new_value_error("Library not found"))?;
|
||||
let inner_lib = library.lib.lock();
|
||||
|
||||
let symbol_name_with_nul = format!("{}\0", name.as_str());
|
||||
let symbol_name_with_nul = format!("{}\0", name.as_wtf8());
|
||||
let ptr: *const u8 = if let Some(lib) = &*inner_lib {
|
||||
unsafe {
|
||||
lib.get::<*const u8>(symbol_name_with_nul.as_bytes())
|
||||
.map(|sym| *sym)
|
||||
.map_err(|_| {
|
||||
vm.new_value_error(format!("symbol '{}' not found", name.as_str()))
|
||||
vm.new_value_error(format!("symbol '{}' not found", name.as_wtf8()))
|
||||
})?
|
||||
}
|
||||
} else {
|
||||
@@ -1315,7 +1318,7 @@ impl PyCData {
|
||||
// dlsym can return NULL for symbols that resolve to NULL (e.g., GNU IFUNC)
|
||||
// Treat NULL addresses as errors
|
||||
if ptr.is_null() {
|
||||
return Err(vm.new_value_error(format!("symbol '{}' not found", name.as_str())));
|
||||
return Err(vm.new_value_error(format!("symbol '{}' not found", name.as_wtf8())));
|
||||
}
|
||||
|
||||
// PyCData_AtAddress
|
||||
@@ -1706,7 +1709,7 @@ impl PyCField {
|
||||
"Z" => {
|
||||
// c_wchar_p: store pointer to null-terminated wchar_t buffer
|
||||
if let Some(s) = value.downcast_ref::<PyStr>() {
|
||||
let (holder, ptr) = str_to_wchar_bytes(s.as_str(), vm);
|
||||
let (holder, ptr) = str_to_wchar_bytes(s.as_wtf8(), vm);
|
||||
let mut result = vec![0u8; size];
|
||||
let addr_bytes = ptr.to_ne_bytes();
|
||||
let len = core::cmp::min(addr_bytes.len(), size);
|
||||
@@ -1759,7 +1762,7 @@ impl PyCField {
|
||||
if let Ok(type_code) = element_type.as_object().get_attr("_type_", vm)
|
||||
&& let Some(s) = type_code.downcast_ref::<PyStr>()
|
||||
{
|
||||
return s.as_str() == "c";
|
||||
return s.as_bytes() == b"c";
|
||||
}
|
||||
}
|
||||
false
|
||||
@@ -1776,7 +1779,7 @@ impl PyCField {
|
||||
if let Ok(type_code) = element_type.as_object().get_attr("_type_", vm)
|
||||
&& let Some(s) = type_code.downcast_ref::<PyStr>()
|
||||
{
|
||||
return s.as_str() == "u";
|
||||
return s.as_bytes() == b"u";
|
||||
}
|
||||
}
|
||||
false
|
||||
@@ -2508,9 +2511,6 @@ fn make_fields(
|
||||
offset: isize,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<()> {
|
||||
use crate::builtins::{PyList, PyTuple};
|
||||
use crate::convert::ToPyObject;
|
||||
|
||||
let fields = descr.proto.as_object().get_attr("_fields_", vm)?;
|
||||
let fieldlist: Vec<PyObjectRef> = if let Some(list) = fields.downcast_ref::<PyList>() {
|
||||
list.borrow_vec().to_vec()
|
||||
@@ -2532,7 +2532,7 @@ fn make_fields(
|
||||
let fname = field_tuple
|
||||
.first()
|
||||
.expect("len checked")
|
||||
.downcast_ref::<PyStr>()
|
||||
.downcast_ref::<PyUtf8Str>()
|
||||
.ok_or_else(|| vm.new_type_error("field name must be a string"))?;
|
||||
|
||||
let fdescr_obj = descr
|
||||
@@ -2555,7 +2555,10 @@ fn make_fields(
|
||||
}
|
||||
|
||||
let new_descr = super::PyCField::new_from_field(fdescr, index, offset);
|
||||
cls.set_attr(vm.ctx.intern_str(fname.as_str()), new_descr.to_pyobject(vm));
|
||||
cls.set_attr(
|
||||
vm.ctx.intern_str(fname.as_wtf8()),
|
||||
new_descr.to_pyobject(vm),
|
||||
);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -2563,9 +2566,6 @@ fn make_fields(
|
||||
|
||||
/// Process _anonymous_ attribute for struct/union
|
||||
pub(super) fn make_anon_fields(cls: &Py<PyType>, vm: &VirtualMachine) -> PyResult<()> {
|
||||
use crate::builtins::{PyList, PyTuple};
|
||||
use crate::convert::ToPyObject;
|
||||
|
||||
let anon = match cls.as_object().get_attr("_anonymous_", vm) {
|
||||
Ok(anon) => anon,
|
||||
Err(_) => return Ok(()),
|
||||
@@ -2586,18 +2586,21 @@ pub(super) fn make_anon_fields(cls: &Py<PyType>, vm: &VirtualMachine) -> PyResul
|
||||
|
||||
let descr_obj = cls
|
||||
.as_object()
|
||||
.get_attr(vm.ctx.intern_str(fname.as_str()), vm)?;
|
||||
.get_attr(vm.ctx.intern_str(fname.as_wtf8()), vm)?;
|
||||
|
||||
let descr = descr_obj.downcast_ref::<super::PyCField>().ok_or_else(|| {
|
||||
vm.new_attribute_error(format!(
|
||||
"'{}' is specified in _anonymous_ but not in _fields_",
|
||||
fname.as_str()
|
||||
fname.as_wtf8()
|
||||
))
|
||||
})?;
|
||||
|
||||
let mut new_descr = super::PyCField::new_from_field(descr, 0, 0);
|
||||
new_descr.set_anonymous(true);
|
||||
cls.set_attr(vm.ctx.intern_str(fname.as_str()), new_descr.to_pyobject(vm));
|
||||
cls.set_attr(
|
||||
vm.ctx.intern_str(fname.as_wtf8()),
|
||||
new_descr.to_pyobject(vm),
|
||||
);
|
||||
|
||||
make_fields(cls, descr, descr.index, descr.offset, vm)?;
|
||||
}
|
||||
|
||||
@@ -280,10 +280,10 @@ fn conv_param(value: &PyObject, vm: &VirtualMachine) -> PyResult<Argument> {
|
||||
|
||||
// 4. Python str -> wide string pointer (like PyUnicode_AsWideCharString)
|
||||
if let Some(s) = value.downcast_ref::<PyStr>() {
|
||||
// Convert to null-terminated UTF-16 (wide string)
|
||||
// Convert to null-terminated UTF-16, preserving lone surrogates
|
||||
let wide: Vec<u16> = s
|
||||
.as_str()
|
||||
.encode_utf16()
|
||||
.as_wtf8()
|
||||
.encode_wide()
|
||||
.chain(core::iter::once(0))
|
||||
.collect();
|
||||
let wide_bytes: Vec<u8> = wide.iter().flat_map(|&x| x.to_ne_bytes()).collect();
|
||||
@@ -448,7 +448,7 @@ impl ReturnType for PyTypeRef {
|
||||
// Try to get _type_ attribute first (for ctypes types like c_void_p)
|
||||
if let Ok(type_attr) = self.as_object().get_attr(vm.ctx.intern_str("_type_"), vm)
|
||||
&& let Some(s) = type_attr.downcast_ref::<PyStr>()
|
||||
&& let Some(ffi_type) = get_ffi_type(s.as_str())
|
||||
&& let Some(ffi_type) = s.to_str().and_then(get_ffi_type)
|
||||
{
|
||||
return Some(ffi_type);
|
||||
}
|
||||
@@ -598,7 +598,7 @@ fn extract_ptr_from_arg(arg: &PyObject, vm: &VirtualMachine) -> PyResult<usize>
|
||||
}
|
||||
// PyStr: return internal buffer address
|
||||
if let Some(s) = arg.downcast_ref::<PyStr>() {
|
||||
return Ok(s.as_str().as_ptr() as usize);
|
||||
return Ok(s.as_bytes().as_ptr() as usize);
|
||||
}
|
||||
// PyBytes: return internal buffer address
|
||||
if let Some(bytes) = arg.downcast_ref::<PyBytes>() {
|
||||
@@ -753,7 +753,9 @@ fn cast_check_pointertype(ctype: &PyObject, vm: &VirtualMachine) -> bool {
|
||||
if let Ok(type_attr) = ctype.get_attr("_type_", vm)
|
||||
&& let Some(s) = type_attr.downcast_ref::<PyStr>()
|
||||
{
|
||||
let c = s.as_str();
|
||||
let c = s
|
||||
.to_str()
|
||||
.expect("_type_ is validated as ASCII at type creation");
|
||||
if c.len() == 1 && "sPzUZXO".contains(c) {
|
||||
return true;
|
||||
}
|
||||
@@ -790,7 +792,7 @@ pub(super) fn cast_impl(
|
||||
bytes.as_bytes().as_ptr() as usize
|
||||
} else if let Some(s) = obj.downcast_ref::<PyStr>() {
|
||||
// unicode/str → buffer address (c_void_p_from_param: PyUnicode_Check)
|
||||
s.as_str().as_ptr() as usize
|
||||
s.as_bytes().as_ptr() as usize
|
||||
} else if let Some(ptr) = obj.downcast_ref::<PyCPointer>() {
|
||||
// Pointer instance → contained pointer value
|
||||
ptr.get_ptr_value()
|
||||
|
||||
@@ -639,7 +639,7 @@ impl PyCPointer {
|
||||
} else if type_code.as_deref() == Some("Z")
|
||||
&& let Some(s) = value.downcast_ref::<PyStr>()
|
||||
{
|
||||
let (holder, ptr_val) = super::base::str_to_wchar_bytes(s.as_str(), vm);
|
||||
let (holder, ptr_val) = super::base::str_to_wchar_bytes(s.as_wtf8(), vm);
|
||||
unsafe {
|
||||
*(addr as *mut usize) = ptr_val;
|
||||
}
|
||||
|
||||
@@ -117,8 +117,8 @@ fn set_primitive(_type_: &str, value: &PyObject, vm: &VirtualMachine) -> PyResul
|
||||
}
|
||||
}
|
||||
"u" => {
|
||||
if let Ok(b) = value.str(vm).map(|v| v.to_string().chars().count() == 1) {
|
||||
if b {
|
||||
if let Some(s) = value.downcast_ref::<PyStr>() {
|
||||
if s.as_wtf8().code_points().count() == 1 {
|
||||
Ok(value.to_owned())
|
||||
} else {
|
||||
Err(vm.new_type_error("one character unicode string expected"))
|
||||
@@ -331,7 +331,7 @@ impl PyCSimpleType {
|
||||
// c_wchar: 1 unicode character
|
||||
Some("u") => {
|
||||
if let Some(s) = value.downcast_ref::<PyStr>()
|
||||
&& s.as_str().chars().count() == 1
|
||||
&& s.as_wtf8().code_points().count() == 1
|
||||
{
|
||||
return create_simple_with_value("u", &value);
|
||||
}
|
||||
@@ -366,7 +366,7 @@ impl PyCSimpleType {
|
||||
Some("Z") => {
|
||||
// 1. str → create CArgObject with null-terminated wchar buffer
|
||||
if let Some(s) = value.downcast_ref::<PyStr>() {
|
||||
let (holder, ptr) = super::base::str_to_wchar_bytes(s.as_str(), vm);
|
||||
let (holder, ptr) = super::base::str_to_wchar_bytes(s.as_wtf8(), vm);
|
||||
return Ok(CArgObject {
|
||||
tag: b'Z',
|
||||
value: FfiArgValue::OwnedPointer(ptr, holder),
|
||||
@@ -407,7 +407,7 @@ impl PyCSimpleType {
|
||||
}
|
||||
// 3. str → create CArgObject with null-terminated wchar buffer
|
||||
if let Some(s) = value.downcast_ref::<PyStr>() {
|
||||
let (holder, ptr) = super::base::str_to_wchar_bytes(s.as_str(), vm);
|
||||
let (holder, ptr) = super::base::str_to_wchar_bytes(s.as_wtf8(), vm);
|
||||
return Ok(CArgObject {
|
||||
tag: b'Z',
|
||||
value: FfiArgValue::OwnedPointer(ptr, holder),
|
||||
@@ -775,15 +775,16 @@ fn value_to_bytes_endian(
|
||||
}
|
||||
"u" => {
|
||||
// c_wchar - platform-dependent size (2 on Windows, 4 on Unix)
|
||||
if let Ok(s) = value.str(vm)
|
||||
&& let Some(c) = s.as_str().chars().next()
|
||||
{
|
||||
let mut buffer = vec![0u8; WCHAR_SIZE];
|
||||
wchar_to_bytes(c as u32, &mut buffer);
|
||||
if swapped {
|
||||
buffer.reverse();
|
||||
if let Some(s) = value.downcast_ref::<PyStr>() {
|
||||
let mut cps = s.as_wtf8().code_points();
|
||||
if let (Some(c), None) = (cps.next(), cps.next()) {
|
||||
let mut buffer = vec![0u8; WCHAR_SIZE];
|
||||
wchar_to_bytes(c.to_u32(), &mut buffer);
|
||||
if swapped {
|
||||
buffer.reverse();
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
vec![0; WCHAR_SIZE]
|
||||
}
|
||||
@@ -1050,7 +1051,7 @@ impl Constructor for PyCSimple {
|
||||
} else if _type_ == "Z"
|
||||
&& let Some(s) = v.downcast_ref::<PyStr>()
|
||||
{
|
||||
let (holder, ptr) = super::base::str_to_wchar_bytes(s.as_str(), vm);
|
||||
let (holder, ptr) = super::base::str_to_wchar_bytes(s.as_wtf8(), vm);
|
||||
let buffer = ptr.to_ne_bytes().to_vec();
|
||||
let cdata = PyCData::from_bytes(buffer, Some(holder));
|
||||
return PyCSimple(cdata).into_ref_with_type(vm, cls).map(Into::into);
|
||||
@@ -1282,7 +1283,7 @@ impl PyCSimple {
|
||||
} else if type_code == "Z"
|
||||
&& let Some(s) = value.downcast_ref::<PyStr>()
|
||||
{
|
||||
let (holder, ptr) = super::base::str_to_wchar_bytes(s.as_str(), vm);
|
||||
let (holder, ptr) = super::base::str_to_wchar_bytes(s.as_wtf8(), vm);
|
||||
*zelf.0.buffer.write() = alloc::borrow::Cow::Owned(ptr.to_ne_bytes().to_vec());
|
||||
*zelf.0.objects.write() = Some(holder);
|
||||
return Ok(());
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use super::base::{CDATA_BUFFER_METHODS, PyCData, PyCField, StgInfo, StgInfoFlags};
|
||||
use crate::builtins::{PyList, PyStr, PyTuple, PyType, PyTypeRef};
|
||||
use crate::builtins::{PyList, PyStr, PyTuple, PyType, PyTypeRef, PyUtf8Str};
|
||||
use crate::convert::ToPyObject;
|
||||
use crate::function::{FuncArgs, OptionalArg, PySetterValue};
|
||||
use crate::protocol::{BufferDescriptor, PyBuffer, PyNumberMethods};
|
||||
@@ -318,9 +318,10 @@ impl PyCStructType {
|
||||
let name = field_tuple
|
||||
.first()
|
||||
.expect("len checked")
|
||||
.downcast_ref::<PyStr>()
|
||||
.downcast_ref::<PyUtf8Str>()
|
||||
.ok_or_else(|| vm.new_type_error("field name must be a string"))?
|
||||
.to_string();
|
||||
.as_str()
|
||||
.to_owned();
|
||||
|
||||
let field_type = field_tuple.get(1).expect("len checked").clone();
|
||||
|
||||
@@ -604,7 +605,7 @@ impl SetAttr for PyCStructType {
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<()> {
|
||||
// Check if _fields_ is being set
|
||||
if attr_name.as_str() == "_fields_" {
|
||||
if attr_name.as_bytes() == b"_fields_" {
|
||||
let pytype: &Py<PyType> = zelf.to_base();
|
||||
|
||||
// Check finalization in separate scope to release read lock before process_fields
|
||||
@@ -634,7 +635,7 @@ impl SetAttr for PyCStructType {
|
||||
return Ok(());
|
||||
}
|
||||
// Delegate to PyType's setattro logic for type attributes
|
||||
let attr_name_interned = vm.ctx.intern_str(attr_name.as_str());
|
||||
let attr_name_interned = vm.ctx.intern_str(attr_name.as_wtf8());
|
||||
let pytype: &Py<PyType> = zelf.to_base();
|
||||
|
||||
// Check for data descriptor first
|
||||
@@ -654,7 +655,7 @@ impl SetAttr for PyCStructType {
|
||||
return Err(vm.new_attribute_error(format!(
|
||||
"type object '{}' has no attribute '{}'",
|
||||
pytype.name(),
|
||||
attr_name.as_str(),
|
||||
attr_name.as_wtf8(),
|
||||
)));
|
||||
}
|
||||
}
|
||||
@@ -747,7 +748,7 @@ impl PyCStructure {
|
||||
}
|
||||
if let Some(tuple) = field.downcast_ref::<PyTuple>()
|
||||
&& let Some(name) = tuple.first()
|
||||
&& let Some(name_str) = name.downcast_ref::<PyStr>()
|
||||
&& let Some(name_str) = name.downcast_ref::<PyUtf8Str>()
|
||||
{
|
||||
let field_name = name_str.as_str().to_owned();
|
||||
// Check for duplicate in kwargs
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use super::base::{CDATA_BUFFER_METHODS, StgInfoFlags};
|
||||
use super::{PyCData, PyCField, StgInfo};
|
||||
use crate::builtins::{PyList, PyStr, PyTuple, PyType, PyTypeRef};
|
||||
use crate::builtins::{PyList, PyStr, PyTuple, PyType, PyTypeRef, PyUtf8Str};
|
||||
use crate::convert::ToPyObject;
|
||||
use crate::function::{ArgBytesLike, FuncArgs, OptionalArg, PySetterValue};
|
||||
use crate::protocol::{BufferDescriptor, PyBuffer};
|
||||
@@ -217,9 +217,10 @@ impl PyCUnionType {
|
||||
let name = field_tuple
|
||||
.first()
|
||||
.expect("len checked")
|
||||
.downcast_ref::<PyStr>()
|
||||
.downcast_ref::<PyUtf8Str>()
|
||||
.ok_or_else(|| vm.new_type_error("field name must be a string"))?
|
||||
.to_string();
|
||||
.as_str()
|
||||
.to_owned();
|
||||
|
||||
let field_type = field_tuple.get(1).expect("len checked").clone();
|
||||
|
||||
@@ -486,7 +487,7 @@ impl SetAttr for PyCUnionType {
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<()> {
|
||||
let pytype: &Py<PyType> = zelf.to_base();
|
||||
let attr_name_interned = vm.ctx.intern_str(attr_name.as_str());
|
||||
let attr_name_interned = vm.ctx.intern_str(attr_name.as_wtf8());
|
||||
|
||||
// 1. First, do PyType's setattro (PyType_Type.tp_setattro first)
|
||||
// Check for data descriptor first
|
||||
@@ -495,7 +496,7 @@ impl SetAttr for PyCUnionType {
|
||||
if let Some(descriptor) = descr_set {
|
||||
descriptor(&attr, pytype.to_owned().into(), value.clone(), vm)?;
|
||||
// After successful setattro, check if _fields_ and call process_fields
|
||||
if attr_name.as_str() == "_fields_"
|
||||
if attr_name.as_bytes() == b"_fields_"
|
||||
&& let PySetterValue::Assign(fields_value) = value
|
||||
{
|
||||
PyCUnionType::process_fields(pytype, fields_value, vm)?;
|
||||
@@ -506,7 +507,7 @@ impl SetAttr for PyCUnionType {
|
||||
|
||||
// 2. If _fields_, call process_fields (which checks FINAL internally)
|
||||
// Check BEFORE writing to dict to avoid storing _fields_ when FINAL
|
||||
if attr_name.as_str() == "_fields_"
|
||||
if attr_name.as_bytes() == b"_fields_"
|
||||
&& let PySetterValue::Assign(ref fields_value) = value
|
||||
{
|
||||
PyCUnionType::process_fields(pytype, fields_value.clone(), vm)?;
|
||||
@@ -526,7 +527,7 @@ impl SetAttr for PyCUnionType {
|
||||
return Err(vm.new_attribute_error(format!(
|
||||
"type object '{}' has no attribute '{}'",
|
||||
pytype.name(),
|
||||
attr_name.as_str(),
|
||||
attr_name.as_wtf8(),
|
||||
)));
|
||||
}
|
||||
}
|
||||
@@ -618,7 +619,7 @@ impl PyCUnion {
|
||||
}
|
||||
if let Some(tuple) = field.downcast_ref::<PyTuple>()
|
||||
&& let Some(name) = tuple.first()
|
||||
&& let Some(name_str) = name.downcast_ref::<PyStr>()
|
||||
&& let Some(name_str) = name.downcast_ref::<PyUtf8Str>()
|
||||
{
|
||||
let field_name = name_str.as_str().to_owned();
|
||||
// Check for duplicate in kwargs
|
||||
|
||||
@@ -14,6 +14,7 @@ mod _functools {
|
||||
types::{Callable, Constructor, GetDescriptor, Representable},
|
||||
};
|
||||
use indexmap::IndexMap;
|
||||
use rustpython_common::wtf8::Wtf8Buf;
|
||||
|
||||
#[derive(FromArgs)]
|
||||
struct ReduceArgs {
|
||||
@@ -44,7 +45,7 @@ mod _functools {
|
||||
let exc_type = vm.ctx.exceptions.type_error.to_owned();
|
||||
vm.new_exception_msg(
|
||||
exc_type,
|
||||
"reduce() of empty sequence with no initial value".to_owned(),
|
||||
"reduce() of empty sequence with no initial value".into(),
|
||||
)
|
||||
})?
|
||||
};
|
||||
@@ -424,9 +425,9 @@ mod _functools {
|
||||
// Add keywords from self.keywords
|
||||
for (key, value) in &*keywords {
|
||||
let key_str = key
|
||||
.downcast::<crate::builtins::PyStr>()
|
||||
.map_err(|_| vm.new_type_error("keywords must be strings"))?;
|
||||
final_kwargs.insert(key_str.as_str().to_owned(), value);
|
||||
.downcast_ref::<crate::builtins::PyStr>()
|
||||
.ok_or_else(|| vm.new_type_error("keywords must be strings"))?;
|
||||
final_kwargs.insert(key_str.expect_str().to_owned(), value);
|
||||
}
|
||||
|
||||
// Add keywords from args.kwargs (these override self.keywords)
|
||||
@@ -455,7 +456,7 @@ mod _functools {
|
||||
|
||||
impl Representable for PyPartial {
|
||||
#[inline]
|
||||
fn repr_str(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<String> {
|
||||
fn repr_wtf8(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<Wtf8Buf> {
|
||||
// Check for recursive repr
|
||||
let obj = zelf.as_object();
|
||||
if let Some(_guard) = ReprGuard::enter(vm, obj) {
|
||||
@@ -469,52 +470,46 @@ mod _functools {
|
||||
)
|
||||
};
|
||||
|
||||
let func_repr = func.repr(vm)?;
|
||||
let mut parts = vec![func_repr.as_str().to_owned()];
|
||||
let qualname = zelf.class().__qualname__(vm);
|
||||
let qualname_wtf8 = qualname
|
||||
.downcast_ref::<crate::builtins::PyStr>()
|
||||
.map(|s| s.as_wtf8().to_owned())
|
||||
.unwrap_or_else(|| Wtf8Buf::from(zelf.class().name().to_owned()));
|
||||
let module = zelf.class().__module__(vm);
|
||||
|
||||
let mut result = Wtf8Buf::new();
|
||||
if let Ok(module_str) = module.downcast::<crate::builtins::PyStr>() {
|
||||
let module_name = module_str.as_wtf8();
|
||||
if module_name != "builtins" && !module_name.is_empty() {
|
||||
result.push_wtf8(module_name);
|
||||
result.push_char('.');
|
||||
}
|
||||
}
|
||||
result.push_wtf8(&qualname_wtf8);
|
||||
result.push_char('(');
|
||||
result.push_wtf8(func.repr(vm)?.as_wtf8());
|
||||
|
||||
for arg in args.as_slice() {
|
||||
parts.push(arg.repr(vm)?.as_str().to_owned());
|
||||
result.push_str(", ");
|
||||
result.push_wtf8(arg.repr(vm)?.as_wtf8());
|
||||
}
|
||||
|
||||
for (key, value) in &*keywords {
|
||||
// For string keys, use them directly without quotes
|
||||
let key_part = if let Ok(s) = key.clone().downcast::<crate::builtins::PyStr>() {
|
||||
s.as_str().to_owned()
|
||||
result.push_str(", ");
|
||||
let key_str = if let Ok(s) = key.clone().downcast::<crate::builtins::PyStr>() {
|
||||
s
|
||||
} else {
|
||||
// For non-string keys, convert to string using __str__
|
||||
key.str(vm)?.as_str().to_owned()
|
||||
key.str(vm)?
|
||||
};
|
||||
let value_str = value.repr(vm)?;
|
||||
parts.push(format!(
|
||||
"{key_part}={value_str}",
|
||||
value_str = value_str.as_str()
|
||||
));
|
||||
result.push_wtf8(key_str.as_wtf8());
|
||||
result.push_char('=');
|
||||
result.push_wtf8(value.repr(vm)?.as_wtf8());
|
||||
}
|
||||
|
||||
let qualname = zelf.class().__qualname__(vm);
|
||||
let qualname_str = qualname
|
||||
.downcast::<crate::builtins::PyStr>()
|
||||
.map(|s| s.as_str().to_owned())
|
||||
.unwrap_or_else(|_| zelf.class().name().to_owned());
|
||||
let module = zelf.class().__module__(vm);
|
||||
|
||||
let qualified_name = match module.downcast::<crate::builtins::PyStr>() {
|
||||
Ok(module_str) => {
|
||||
let module_name = module_str.as_str();
|
||||
match module_name {
|
||||
"builtins" | "" => qualname_str,
|
||||
_ => format!("{module_name}.{qualname_str}"),
|
||||
}
|
||||
}
|
||||
Err(_) => qualname_str,
|
||||
};
|
||||
|
||||
Ok(format!(
|
||||
"{qualified_name}({parts})",
|
||||
parts = parts.join(", ")
|
||||
))
|
||||
result.push_char(')');
|
||||
Ok(result)
|
||||
} else {
|
||||
Ok("...".to_owned())
|
||||
Ok(Wtf8Buf::from("..."))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+23
-21
@@ -71,7 +71,7 @@ impl FrozenError {
|
||||
Excluded => format!("Excluded frozen object named {mod_name}"),
|
||||
Invalid => format!("Frozen object named {mod_name} is invalid"),
|
||||
};
|
||||
vm.new_import_error(msg, vm.ctx.new_str(mod_name))
|
||||
vm.new_import_error(msg, vm.ctx.new_utf8_str(mod_name))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,7 +106,7 @@ fn find_frozen(name: &str, vm: &VirtualMachine) -> Result<FrozenModule, FrozenEr
|
||||
mod _imp {
|
||||
use crate::{
|
||||
PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine,
|
||||
builtins::{PyBytesRef, PyCode, PyMemoryView, PyModule, PyStrRef},
|
||||
builtins::{PyBytesRef, PyCode, PyMemoryView, PyModule, PyStrRef, PyUtf8StrRef},
|
||||
convert::TryFromBorrowedObject,
|
||||
function::OptionalArg,
|
||||
import, version,
|
||||
@@ -127,27 +127,27 @@ mod _imp {
|
||||
}
|
||||
|
||||
#[pyfunction]
|
||||
fn is_builtin(name: PyStrRef, vm: &VirtualMachine) -> bool {
|
||||
fn is_builtin(name: PyUtf8StrRef, vm: &VirtualMachine) -> bool {
|
||||
vm.state.module_defs.contains_key(name.as_str())
|
||||
}
|
||||
|
||||
#[pyfunction]
|
||||
fn is_frozen(name: PyStrRef, vm: &VirtualMachine) -> bool {
|
||||
fn is_frozen(name: PyUtf8StrRef, vm: &VirtualMachine) -> bool {
|
||||
super::find_frozen(name.as_str(), vm).is_ok()
|
||||
}
|
||||
|
||||
#[pyfunction]
|
||||
fn create_builtin(spec: PyObjectRef, vm: &VirtualMachine) -> PyResult {
|
||||
let sys_modules = vm.sys_module.get_attr("modules", vm).unwrap();
|
||||
let name: PyStrRef = spec.get_attr("name", vm)?.try_into_value(vm)?;
|
||||
let name: PyUtf8StrRef = spec.get_attr("name", vm)?.try_into_value(vm)?;
|
||||
|
||||
// Check sys.modules first
|
||||
if let Ok(module) = sys_modules.get_item(&*name, vm) {
|
||||
return Ok(module);
|
||||
}
|
||||
|
||||
// Try multi-phase init modules first (they need special handling)
|
||||
if let Some(&def) = vm.state.module_defs.get(name.as_str()) {
|
||||
let name_str = name.as_str();
|
||||
if let Some(&def) = vm.state.module_defs.get(name_str) {
|
||||
// Phase 1: Create module (use create slot if provided, else default creation)
|
||||
let module = if let Some(create) = def.slots.create {
|
||||
// Custom module creation
|
||||
@@ -163,7 +163,7 @@ mod _imp {
|
||||
module.__init_methods(vm)?;
|
||||
|
||||
// Add to sys.modules BEFORE exec (critical for circular import handling)
|
||||
sys_modules.set_item(&*name, module.clone().into(), vm)?;
|
||||
sys_modules.set_item(name.as_pystr(), module.clone().into(), vm)?;
|
||||
|
||||
// Phase 2: Call exec slot (can safely import other modules now)
|
||||
if let Some(exec) = def.slots.exec {
|
||||
@@ -184,7 +184,7 @@ mod _imp {
|
||||
|
||||
#[pyfunction]
|
||||
fn get_frozen_object(
|
||||
name: PyStrRef,
|
||||
name: PyUtf8StrRef,
|
||||
data: OptionalArg<PyObjectRef>,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<PyRef<PyCode>> {
|
||||
@@ -198,7 +198,7 @@ mod _imp {
|
||||
let invalid_err = || {
|
||||
vm.new_import_error(
|
||||
format!("Frozen object named '{}' is invalid", name.as_str()),
|
||||
name.clone(),
|
||||
name.clone().into_wtf8(),
|
||||
)
|
||||
};
|
||||
let bag = crate::builtins::code::PyObjBag(&vm.ctx);
|
||||
@@ -211,15 +211,16 @@ mod _imp {
|
||||
}
|
||||
|
||||
#[pyfunction]
|
||||
fn init_frozen(name: PyStrRef, vm: &VirtualMachine) -> PyResult {
|
||||
fn init_frozen(name: PyUtf8StrRef, vm: &VirtualMachine) -> PyResult {
|
||||
import::import_frozen(vm, name.as_str())
|
||||
}
|
||||
|
||||
#[pyfunction]
|
||||
fn is_frozen_package(name: PyStrRef, vm: &VirtualMachine) -> PyResult<bool> {
|
||||
super::find_frozen(name.as_str(), vm)
|
||||
fn is_frozen_package(name: PyUtf8StrRef, vm: &VirtualMachine) -> PyResult<bool> {
|
||||
let name_str = name.as_str();
|
||||
super::find_frozen(name_str, vm)
|
||||
.map(|frozen| frozen.package)
|
||||
.map_err(|e| e.to_pyexception(name.as_str(), vm))
|
||||
.map_err(|e| e.to_pyexception(name_str, vm))
|
||||
}
|
||||
|
||||
#[pyfunction]
|
||||
@@ -230,7 +231,7 @@ mod _imp {
|
||||
#[pyfunction]
|
||||
fn _fix_co_filename(code: PyRef<PyCode>, path: PyStrRef, vm: &VirtualMachine) {
|
||||
let old_name = code.source_path();
|
||||
let new_name = vm.ctx.intern_str(path.as_str());
|
||||
let new_name = vm.ctx.intern_str(path.as_wtf8());
|
||||
super::update_code_filenames(&code, old_name, new_name);
|
||||
}
|
||||
|
||||
@@ -240,7 +241,7 @@ mod _imp {
|
||||
.state
|
||||
.frozen
|
||||
.keys()
|
||||
.map(|&name| vm.ctx.new_str(name).into())
|
||||
.map(|&name| vm.ctx.new_utf8_str(name).into())
|
||||
.collect();
|
||||
Ok(names)
|
||||
}
|
||||
@@ -248,7 +249,7 @@ mod _imp {
|
||||
#[allow(clippy::type_complexity)]
|
||||
#[pyfunction]
|
||||
fn find_frozen(
|
||||
name: PyStrRef,
|
||||
name: PyUtf8StrRef,
|
||||
withdata: OptionalArg<bool>,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<Option<(Option<PyRef<PyMemoryView>>, bool, Option<PyStrRef>)>> {
|
||||
@@ -259,19 +260,20 @@ mod _imp {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
let info = match super::find_frozen(name.as_str(), vm) {
|
||||
let name_str = name.as_str();
|
||||
let info = match super::find_frozen(name_str, vm) {
|
||||
Ok(info) => info,
|
||||
Err(NotFound | Disabled | BadName) => return Ok(None),
|
||||
Err(e) => return Err(e.to_pyexception(name.as_str(), vm)),
|
||||
Err(e) => return Err(e.to_pyexception(name_str, vm)),
|
||||
};
|
||||
|
||||
// When origname is empty (e.g. __hello_only__), return None.
|
||||
// Otherwise return the resolved alias name.
|
||||
let origname_str = super::resolve_frozen_alias(name.as_str());
|
||||
let origname_str = super::resolve_frozen_alias(name_str);
|
||||
let origname = if origname_str.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(vm.ctx.new_str(origname_str))
|
||||
Some(vm.ctx.new_utf8_str(origname_str).into())
|
||||
};
|
||||
Ok(Some((None, info.package, origname)))
|
||||
}
|
||||
|
||||
+46
-49
@@ -1575,10 +1575,11 @@ mod _io {
|
||||
args.bind(vm).map_err(|e| {
|
||||
let str_repr = e
|
||||
.__str__(vm)
|
||||
.map(|s| s.as_str().to_owned())
|
||||
.unwrap_or_else(|_| "<error getting exception str>".to_owned());
|
||||
.as_ref()
|
||||
.map_or("<error getting exception str>".as_ref(), |s| s.as_wtf8())
|
||||
.to_owned();
|
||||
let msg = format!("{}() {}", Self::CLASS_NAME, str_repr);
|
||||
vm.new_exception_msg(e.class().to_owned(), msg)
|
||||
vm.new_exception_msg(e.class().to_owned(), msg.into())
|
||||
})?;
|
||||
zelf.init(raw, BufferSize { buffer_size }, vm)
|
||||
}
|
||||
@@ -2273,7 +2274,7 @@ mod _io {
|
||||
#[pyarg(any, default)]
|
||||
encoding: Option<PyUtf8StrRef>,
|
||||
#[pyarg(any, default)]
|
||||
errors: Option<PyStrRef>,
|
||||
errors: Option<PyUtf8StrRef>,
|
||||
#[pyarg(any, default)]
|
||||
newline: OptionalOption<Newlines>,
|
||||
#[pyarg(any, default)]
|
||||
@@ -2457,7 +2458,7 @@ mod _io {
|
||||
encoder: Option<(PyObjectRef, Option<EncodeFunc>)>,
|
||||
decoder: Option<PyObjectRef>,
|
||||
encoding: PyUtf8StrRef,
|
||||
errors: PyStrRef,
|
||||
errors: PyUtf8StrRef,
|
||||
newline: Newlines,
|
||||
line_buffering: bool,
|
||||
write_through: bool,
|
||||
@@ -2727,17 +2728,14 @@ mod _io {
|
||||
|
||||
let encoding = Self::resolve_encoding(args.encoding, vm)?;
|
||||
|
||||
let errors = args
|
||||
.errors
|
||||
.unwrap_or_else(|| identifier!(vm, strict).to_owned());
|
||||
let errors = args.errors.unwrap_or_else(|| vm.ctx.new_utf8_str("strict"));
|
||||
Self::validate_errors(&errors, vm)?;
|
||||
|
||||
let has_read1 = vm.get_attribute_opt(buffer.clone(), "read1")?.is_some();
|
||||
let seekable = vm.call_method(&buffer, "seekable", ())?.try_to_bool(vm)?;
|
||||
|
||||
let newline = match args.newline {
|
||||
OptionalArg::Missing => Newlines::default(),
|
||||
OptionalArg::Present(None) => Newlines::default(),
|
||||
OptionalArg::Missing | OptionalArg::Present(None) => Newlines::default(),
|
||||
OptionalArg::Present(Some(newline)) => newline,
|
||||
};
|
||||
let (encoder, decoder) =
|
||||
@@ -2808,15 +2806,10 @@ mod _io {
|
||||
.map_err(|_| vm.new_value_error("I/O operation on uninitialized object"))
|
||||
}
|
||||
|
||||
fn validate_errors(errors: &PyStrRef, vm: &VirtualMachine) -> PyResult<()> {
|
||||
if errors.as_wtf8().as_bytes().contains(&0) {
|
||||
fn validate_errors(errors: &PyRef<PyUtf8Str>, vm: &VirtualMachine) -> PyResult<()> {
|
||||
if errors.as_str().contains('\0') {
|
||||
return Err(cstring_error(vm));
|
||||
}
|
||||
if !errors.as_wtf8().is_utf8() {
|
||||
return Err(vm.new_unicode_encode_error(
|
||||
"'utf-8' codec can't encode character: surrogates not allowed".to_owned(),
|
||||
));
|
||||
}
|
||||
vm.state
|
||||
.codec_registry
|
||||
.lookup_error(errors.as_str(), vm)
|
||||
@@ -2910,7 +2903,7 @@ mod _io {
|
||||
fn find_coder(
|
||||
buffer: &PyObject,
|
||||
encoding: &str,
|
||||
errors: &Py<PyStr>,
|
||||
errors: &Py<PyUtf8Str>,
|
||||
newline: Newlines,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<(
|
||||
@@ -2923,10 +2916,11 @@ mod _io {
|
||||
"'{encoding}' is not a text encoding; use codecs.open() to handle arbitrary codecs"
|
||||
)));
|
||||
}
|
||||
let errors = errors.to_owned().into_wtf8();
|
||||
|
||||
let encoder = if vm.call_method(buffer, "writable", ())?.try_to_bool(vm)? {
|
||||
let incremental_encoder =
|
||||
match codec.get_incremental_encoder(Some(errors.to_owned()), vm) {
|
||||
match codec.get_incremental_encoder(Some(errors.clone()), vm) {
|
||||
Ok(encoder) => encoder,
|
||||
Err(err)
|
||||
if err.fast_isinstance(vm.ctx.exceptions.type_error)
|
||||
@@ -2937,7 +2931,7 @@ mod _io {
|
||||
.and_then(|obj| obj.downcast::<PyStr>().ok());
|
||||
StatelessIncrementalEncoder {
|
||||
encode: codec.get_encode_func().to_owned(),
|
||||
errors: Some(errors.to_owned()),
|
||||
errors: Some(errors.clone()),
|
||||
name,
|
||||
}
|
||||
.into_ref(&vm.ctx)
|
||||
@@ -2948,7 +2942,7 @@ mod _io {
|
||||
let encoding_name = vm.get_attribute_opt(incremental_encoder.clone(), "name")?;
|
||||
let encode_func = encoding_name.and_then(|name| {
|
||||
let name = name.downcast_ref::<PyStr>()?;
|
||||
match name.as_str() {
|
||||
match name.to_str()? {
|
||||
"utf-8" => Some(textio_encode_utf8 as EncodeFunc),
|
||||
_ => None,
|
||||
}
|
||||
@@ -2959,7 +2953,7 @@ mod _io {
|
||||
};
|
||||
|
||||
let decoder = if vm.call_method(buffer, "readable", ())?.try_to_bool(vm)? {
|
||||
let decoder = match codec.get_incremental_decoder(Some(errors.to_owned()), vm) {
|
||||
let decoder = match codec.get_incremental_decoder(Some(errors.clone()), vm) {
|
||||
Ok(decoder) => decoder,
|
||||
Err(err)
|
||||
if err.fast_isinstance(vm.ctx.exceptions.type_error)
|
||||
@@ -2967,7 +2961,7 @@ mod _io {
|
||||
{
|
||||
StatelessIncrementalDecoder {
|
||||
decode: codec.get_decode_func().to_owned(),
|
||||
errors: Some(errors.to_owned()),
|
||||
errors: Some(errors),
|
||||
}
|
||||
.into_ref(&vm.ctx)
|
||||
.into()
|
||||
@@ -3040,7 +3034,7 @@ mod _io {
|
||||
errors_changed = errs.as_str() != errors.as_str();
|
||||
errors = errs;
|
||||
} else if encoding_changed {
|
||||
errors = identifier!(vm, strict).to_owned();
|
||||
errors = identifier_utf8!(vm, strict).to_owned();
|
||||
errors_changed = true;
|
||||
}
|
||||
|
||||
@@ -3433,7 +3427,7 @@ mod _io {
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
fn errors(&self, vm: &VirtualMachine) -> PyResult<PyStrRef> {
|
||||
fn errors(&self, vm: &VirtualMachine) -> PyResult<PyUtf8StrRef> {
|
||||
Ok(self.lock(vm)?.errors.clone())
|
||||
}
|
||||
|
||||
@@ -3995,7 +3989,7 @@ mod _io {
|
||||
|
||||
impl Representable for TextIOWrapper {
|
||||
#[inline]
|
||||
fn repr_str(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<String> {
|
||||
fn repr(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<PyRef<PyStr>> {
|
||||
let type_name = zelf.class().slot_name();
|
||||
let Some(_guard) = ReprGuard::enter(vm, zelf.as_object()) else {
|
||||
return Err(
|
||||
@@ -4004,19 +3998,19 @@ mod _io {
|
||||
};
|
||||
let Some(data) = zelf.data.lock() else {
|
||||
// Reentrant call
|
||||
return Ok(format!("<{type_name}>"));
|
||||
return Ok(vm.ctx.new_str(Wtf8Buf::from(format!("<{type_name}>"))));
|
||||
};
|
||||
let Some(data) = data.as_ref() else {
|
||||
return Err(vm.new_value_error("I/O operation on uninitialized object".to_owned()));
|
||||
};
|
||||
|
||||
let mut result = format!("<{type_name}");
|
||||
let mut result = Wtf8Buf::from(format!("<{type_name}"));
|
||||
|
||||
// Add name if present
|
||||
if let Ok(Some(name)) = vm.get_attribute_opt(data.buffer.clone(), "name") {
|
||||
let name_repr = name.repr(vm)?;
|
||||
result.push_str(" name=");
|
||||
result.push_str(name_repr.as_str());
|
||||
result.push_wtf8(" name=".as_ref());
|
||||
result.push_wtf8(name_repr.as_wtf8());
|
||||
}
|
||||
|
||||
// Add mode if present (prefer the wrapper's attribute)
|
||||
@@ -4029,17 +4023,20 @@ mod _io {
|
||||
};
|
||||
if let Some(mode) = mode_obj {
|
||||
let mode_repr = mode.repr(vm)?;
|
||||
result.push_str(" mode=");
|
||||
result.push_str(mode_repr.as_str());
|
||||
result.push_wtf8(" mode=".as_ref());
|
||||
result.push_wtf8(mode_repr.as_wtf8());
|
||||
}
|
||||
|
||||
// Add encoding
|
||||
result.push_str(" encoding='");
|
||||
result.push_str(data.encoding.as_str());
|
||||
result.push('\'');
|
||||
// Add encoding (always valid UTF-8)
|
||||
result.push_wtf8(" encoding='".as_ref());
|
||||
result.push_wtf8(data.encoding.as_str().as_ref());
|
||||
result.push_wtf8("'>".as_ref());
|
||||
|
||||
result.push('>');
|
||||
Ok(result)
|
||||
Ok(vm.ctx.new_str(result))
|
||||
}
|
||||
|
||||
fn repr_str(_zelf: &Py<Self>, _vm: &VirtualMachine) -> PyResult<String> {
|
||||
unreachable!("repr() is overridden directly")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4240,7 +4237,7 @@ mod _io {
|
||||
output.to_mut().insert(0, '\r'.into());
|
||||
self.pendingcr = false;
|
||||
}
|
||||
if !final_ && let Some(s) = output.strip_suffix("\r".as_ref()) {
|
||||
if !final_ && let Some(s) = output.strip_suffix("\r") {
|
||||
output = Cow::Owned(s.to_owned());
|
||||
self.pendingcr = true;
|
||||
}
|
||||
@@ -4935,9 +4932,9 @@ mod _io {
|
||||
#[pyarg(any, default)]
|
||||
pub encoding: Option<PyUtf8StrRef>,
|
||||
#[pyarg(any, default)]
|
||||
pub errors: Option<PyStrRef>,
|
||||
pub errors: Option<PyUtf8StrRef>,
|
||||
#[pyarg(any, default)]
|
||||
pub newline: Option<PyStrRef>,
|
||||
pub newline: Option<PyUtf8StrRef>,
|
||||
#[pyarg(any, default = true)]
|
||||
pub closefd: bool,
|
||||
#[pyarg(any, default)]
|
||||
@@ -5237,7 +5234,7 @@ mod fileio {
|
||||
AsObject, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, TryFromObject,
|
||||
VirtualMachine,
|
||||
builtins::{PyBaseExceptionRef, PyUtf8Str, PyUtf8StrRef},
|
||||
common::crt_fd,
|
||||
common::{crt_fd, wtf8::Wtf8Buf},
|
||||
convert::{IntoPyException, ToPyException},
|
||||
exceptions::OSErrorBuilder,
|
||||
function::{ArgBytesLike, ArgMemoryBuffer, OptionalArg, OptionalOption},
|
||||
@@ -5864,8 +5861,8 @@ mod fileio {
|
||||
if zelf.fd.load() >= 0 && zelf.closefd.load() {
|
||||
let repr = source
|
||||
.repr(vm)
|
||||
.map(|s| s.as_str().to_owned())
|
||||
.unwrap_or_else(|_| "<file>".to_owned());
|
||||
.map(|s| s.as_wtf8().to_owned())
|
||||
.unwrap_or_else(|_| Wtf8Buf::from("<file>"));
|
||||
if let Err(e) = crate::stdlib::warnings::warn(
|
||||
vm.ctx.exceptions.resource_warning,
|
||||
format!("unclosed file {repr}"),
|
||||
@@ -5902,7 +5899,7 @@ mod winconsoleio {
|
||||
use crate::{
|
||||
AsObject, Py, PyObject, PyObjectRef, PyRef, PyResult, TryFromObject, VirtualMachine,
|
||||
builtins::{PyBaseExceptionRef, PyUtf8StrRef},
|
||||
common::lock::PyMutex,
|
||||
common::{lock::PyMutex, wtf8::Wtf8Buf},
|
||||
convert::{IntoPyException, ToPyException},
|
||||
function::{ArgBytesLike, ArgMemoryBuffer, OptionalArg},
|
||||
types::{Constructor, DefaultConstructor, Destructor, Initializer, Representable},
|
||||
@@ -6187,8 +6184,8 @@ mod winconsoleio {
|
||||
|
||||
let name_str = nameobj.str(vm)?;
|
||||
let wide: Vec<u16> = name_str
|
||||
.as_str()
|
||||
.encode_utf16()
|
||||
.as_wtf8()
|
||||
.encode_wide()
|
||||
.chain(core::iter::once(0))
|
||||
.collect();
|
||||
|
||||
@@ -6417,8 +6414,8 @@ mod winconsoleio {
|
||||
if zelf.fd.load() >= 0 && zelf.closefd.load() {
|
||||
let repr = source
|
||||
.repr(vm)
|
||||
.map(|s| s.as_str().to_owned())
|
||||
.unwrap_or_else(|_| "<file>".to_owned());
|
||||
.map(|s| s.as_wtf8().to_owned())
|
||||
.unwrap_or_else(|_| Wtf8Buf::from("<file>"));
|
||||
if let Err(e) = crate::stdlib::warnings::warn(
|
||||
vm.ctx.exceptions.resource_warning,
|
||||
format!("unclosed file {repr}"),
|
||||
|
||||
@@ -20,6 +20,7 @@ mod decl {
|
||||
use crossbeam_utils::atomic::AtomicCell;
|
||||
use malachite_bigint::BigInt;
|
||||
use num_traits::One;
|
||||
use rustpython_common::wtf8::Wtf8Buf;
|
||||
|
||||
use alloc::fmt;
|
||||
use num_traits::{Signed, ToPrimitive};
|
||||
@@ -220,13 +221,17 @@ mod decl {
|
||||
|
||||
impl Representable for PyItertoolsCount {
|
||||
#[inline]
|
||||
fn repr_str(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<String> {
|
||||
let cur = format!("{}", zelf.cur.read().clone().repr(vm)?);
|
||||
fn repr_wtf8(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<Wtf8Buf> {
|
||||
let cur_repr = zelf.cur.read().clone().repr(vm)?;
|
||||
let step = &zelf.step;
|
||||
if vm.bool_eq(step, vm.ctx.new_int(1).as_object())? {
|
||||
return Ok(format!("count({cur})"));
|
||||
let mut result = Wtf8Buf::from("count(");
|
||||
result.push_wtf8(cur_repr.as_wtf8());
|
||||
if !vm.bool_eq(step, vm.ctx.new_int(1).as_object())? {
|
||||
result.push_str(", ");
|
||||
result.push_wtf8(step.repr(vm)?.as_wtf8());
|
||||
}
|
||||
Ok(format!("count({}, {})", cur, step.repr(vm)?))
|
||||
result.push_char(')');
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -345,13 +350,15 @@ mod decl {
|
||||
|
||||
impl Representable for PyItertoolsRepeat {
|
||||
#[inline]
|
||||
fn repr_str(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<String> {
|
||||
let mut fmt = format!("{}", &zelf.object.repr(vm)?);
|
||||
fn repr_wtf8(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<Wtf8Buf> {
|
||||
let mut result = Wtf8Buf::from("repeat(");
|
||||
result.push_wtf8(zelf.object.repr(vm)?.as_wtf8());
|
||||
if let Some(ref times) = zelf.times {
|
||||
fmt.push_str(", ");
|
||||
fmt.push_str(×.read().to_string());
|
||||
result.push_str(", ");
|
||||
result.push_str(×.read().to_string());
|
||||
}
|
||||
Ok(format!("repeat({fmt})"))
|
||||
result.push_char(')');
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -226,7 +226,7 @@ mod decl {
|
||||
marshal::deserialize_value(&mut &buf[..], PyMarshalBag(vm)).map_err(|e| match e {
|
||||
marshal::MarshalError::Eof => vm.new_exception_msg(
|
||||
vm.ctx.exceptions.eof_error.to_owned(),
|
||||
"marshal data too short".to_owned(),
|
||||
"marshal data too short".into(),
|
||||
),
|
||||
marshal::MarshalError::InvalidBytecode => {
|
||||
vm.new_value_error("Couldn't deserialize python bytecode")
|
||||
|
||||
@@ -82,7 +82,7 @@ mod msvcrt {
|
||||
#[pyfunction]
|
||||
fn putwch(s: PyStrRef, vm: &VirtualMachine) -> PyResult<()> {
|
||||
let c = s
|
||||
.as_str()
|
||||
.expect_str()
|
||||
.chars()
|
||||
.exactly_one()
|
||||
.map_err(|_| vm.new_type_error("putch() argument must be a string of length 1"))?;
|
||||
@@ -107,7 +107,7 @@ mod msvcrt {
|
||||
#[pyfunction]
|
||||
fn ungetwch(s: PyStrRef, vm: &VirtualMachine) -> PyResult<()> {
|
||||
let c =
|
||||
s.as_str().chars().exactly_one().map_err(|_| {
|
||||
s.expect_str().chars().exactly_one().map_err(|_| {
|
||||
vm.new_type_error("ungetwch() argument must be a string of length 1")
|
||||
})?;
|
||||
let ret = unsafe { suppress_iph!(_ungetwch(c as u32)) };
|
||||
|
||||
+13
-23
@@ -7,7 +7,9 @@ pub use module::raw_set_handle_inheritable;
|
||||
pub(crate) mod module {
|
||||
use crate::{
|
||||
Py, PyResult, TryFromObject, VirtualMachine,
|
||||
builtins::{PyBaseExceptionRef, PyDictRef, PyListRef, PyStrRef, PyTupleRef},
|
||||
builtins::{
|
||||
PyBaseExceptionRef, PyBytes, PyDictRef, PyListRef, PyStr, PyStrRef, PyTupleRef,
|
||||
},
|
||||
common::{crt_fd, suppress_iph, windows::ToWideString},
|
||||
convert::ToPyException,
|
||||
exceptions::OSErrorBuilder,
|
||||
@@ -15,9 +17,9 @@ pub(crate) mod module {
|
||||
ospath::{OsPath, OsPathOrFd},
|
||||
stdlib::os::{_os, DirFd, SupportFunc, TargetIsDirectory},
|
||||
};
|
||||
|
||||
use core::mem::MaybeUninit;
|
||||
use libc::intptr_t;
|
||||
use rustpython_common::wtf8::Wtf8Buf;
|
||||
use std::os::windows::io::AsRawHandle;
|
||||
use std::{env, io, os::windows::ffi::OsStringExt};
|
||||
use windows_sys::Win32::{
|
||||
@@ -1188,7 +1190,7 @@ pub(crate) mod module {
|
||||
|
||||
let argv = vm.extract_elements_with(argv.as_ref(), |obj| {
|
||||
let arg = PyStrRef::try_from_object(vm, obj)?;
|
||||
make_widestring(arg.as_str())
|
||||
make_widestring(arg.expect_str())
|
||||
})?;
|
||||
|
||||
let first = argv
|
||||
@@ -1229,7 +1231,7 @@ pub(crate) mod module {
|
||||
|
||||
let argv = vm.extract_elements_with(argv.as_ref(), |obj| {
|
||||
let arg = PyStrRef::try_from_object(vm, obj)?;
|
||||
make_widestring(arg.as_str())
|
||||
make_widestring(arg.expect_str())
|
||||
})?;
|
||||
|
||||
let first = argv
|
||||
@@ -1252,8 +1254,8 @@ pub(crate) mod module {
|
||||
for (key, value) in env.into_iter() {
|
||||
let key = PyStrRef::try_from_object(vm, key)?;
|
||||
let value = PyStrRef::try_from_object(vm, value)?;
|
||||
let key_str = key.as_str();
|
||||
let value_str = value.as_str();
|
||||
let key_str = key.expect_str();
|
||||
let value_str = value.expect_str();
|
||||
|
||||
// Validate: no null characters in key or value
|
||||
if key_str.contains('\0') || value_str.contains('\0') {
|
||||
@@ -1463,9 +1465,6 @@ pub(crate) mod module {
|
||||
|
||||
#[pyfunction]
|
||||
fn _path_splitroot_ex(path: crate::PyObjectRef, vm: &VirtualMachine) -> PyResult<PyTupleRef> {
|
||||
use crate::builtins::{PyBytes, PyStr};
|
||||
use rustpython_common::wtf8::Wtf8Buf;
|
||||
|
||||
// Handle path-like objects via os.fspath, but without null check (non_strict=True)
|
||||
let path = if let Some(fspath) = vm.get_method(path.clone(), identifier!(vm, __fspath__)) {
|
||||
fspath?.call((), vm)?
|
||||
@@ -1487,7 +1486,8 @@ pub(crate) mod module {
|
||||
"'utf-8' codec can't decode byte {:#x} in position {}: invalid start byte",
|
||||
b.as_bytes().get(e.valid_up_to()).copied().unwrap_or(0),
|
||||
e.valid_up_to()
|
||||
),
|
||||
)
|
||||
.into(),
|
||||
)
|
||||
})?;
|
||||
let wide: Vec<u16> = s.encode_utf16().collect();
|
||||
@@ -1535,15 +1535,7 @@ pub(crate) mod module {
|
||||
}
|
||||
|
||||
#[pyfunction]
|
||||
fn _path_splitroot(
|
||||
path: OsPath,
|
||||
_vm: &VirtualMachine,
|
||||
) -> (
|
||||
rustpython_common::wtf8::Wtf8Buf,
|
||||
rustpython_common::wtf8::Wtf8Buf,
|
||||
) {
|
||||
use rustpython_common::wtf8::Wtf8Buf;
|
||||
|
||||
fn _path_splitroot(path: OsPath, _vm: &VirtualMachine) -> (Wtf8Buf, Wtf8Buf) {
|
||||
let orig: Vec<_> = path.path.to_wide();
|
||||
if orig.is_empty() {
|
||||
return (Wtf8Buf::new(), Wtf8Buf::new());
|
||||
@@ -1706,9 +1698,6 @@ pub(crate) mod module {
|
||||
|
||||
#[pyfunction]
|
||||
fn _path_normpath(path: crate::PyObjectRef, vm: &VirtualMachine) -> PyResult {
|
||||
use crate::builtins::{PyBytes, PyStr};
|
||||
use rustpython_common::wtf8::Wtf8Buf;
|
||||
|
||||
// Handle path-like objects via os.fspath
|
||||
let path = if let Some(fspath) = vm.get_method(path.clone(), identifier!(vm, __fspath__)) {
|
||||
fspath?.call((), vm)?
|
||||
@@ -1727,7 +1716,8 @@ pub(crate) mod module {
|
||||
"'utf-8' codec can't decode byte {:#x} in position {}: invalid start byte",
|
||||
b.as_bytes().get(e.valid_up_to()).copied().unwrap_or(0),
|
||||
e.valid_up_to()
|
||||
),
|
||||
)
|
||||
.into(),
|
||||
)
|
||||
})?;
|
||||
let wide: Vec<u16> = s.encode_utf16().collect();
|
||||
|
||||
@@ -4,8 +4,8 @@ pub(crate) use _operator::module_def;
|
||||
mod _operator {
|
||||
use crate::{
|
||||
AsObject, Py, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine,
|
||||
builtins::{PyInt, PyIntRef, PyStr, PyStrRef, PyTupleRef, PyType, PyTypeRef},
|
||||
common::wtf8::Wtf8,
|
||||
builtins::{PyInt, PyIntRef, PyStr, PyStrRef, PyTupleRef, PyType, PyTypeRef, PyUtf8StrRef},
|
||||
common::wtf8::{Wtf8, Wtf8Buf},
|
||||
function::{ArgBytesLike, Either, FuncArgs, KwArgs, OptionalArg},
|
||||
protocol::PyIter,
|
||||
recursion::ReprGuard,
|
||||
@@ -432,17 +432,22 @@ mod _operator {
|
||||
|
||||
impl Representable for PyAttrGetter {
|
||||
#[inline]
|
||||
fn repr_str(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<String> {
|
||||
let fmt = if let Some(_guard) = ReprGuard::enter(vm, zelf.as_object()) {
|
||||
let mut parts = Vec::with_capacity(zelf.attrs.len());
|
||||
fn repr_wtf8(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<Wtf8Buf> {
|
||||
let mut result = Wtf8Buf::from("operator.attrgetter(");
|
||||
if let Some(_guard) = ReprGuard::enter(vm, zelf.as_object()) {
|
||||
let mut first = true;
|
||||
for part in &zelf.attrs {
|
||||
parts.push(part.as_object().repr(vm)?.as_str().to_owned());
|
||||
if !first {
|
||||
result.push_str(", ");
|
||||
}
|
||||
first = false;
|
||||
result.push_wtf8(part.as_object().repr(vm)?.as_wtf8());
|
||||
}
|
||||
parts.join(", ")
|
||||
} else {
|
||||
"...".to_owned()
|
||||
};
|
||||
Ok(format!("operator.attrgetter({fmt})"))
|
||||
result.push_str("...");
|
||||
}
|
||||
result.push_char(')');
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -505,17 +510,22 @@ mod _operator {
|
||||
|
||||
impl Representable for PyItemGetter {
|
||||
#[inline]
|
||||
fn repr_str(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<String> {
|
||||
let fmt = if let Some(_guard) = ReprGuard::enter(vm, zelf.as_object()) {
|
||||
let mut items = Vec::with_capacity(zelf.items.len());
|
||||
fn repr_wtf8(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<Wtf8Buf> {
|
||||
let mut result = Wtf8Buf::from("operator.itemgetter(");
|
||||
if let Some(_guard) = ReprGuard::enter(vm, zelf.as_object()) {
|
||||
let mut first = true;
|
||||
for item in &zelf.items {
|
||||
items.push(item.repr(vm)?.as_str().to_owned());
|
||||
if !first {
|
||||
result.push_str(", ");
|
||||
}
|
||||
first = false;
|
||||
result.push_wtf8(item.repr(vm)?.as_wtf8());
|
||||
}
|
||||
items.join(", ")
|
||||
} else {
|
||||
"...".to_owned()
|
||||
};
|
||||
Ok(format!("operator.itemgetter({fmt})"))
|
||||
result.push_str("...");
|
||||
}
|
||||
result.push_char(')');
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -530,7 +540,7 @@ mod _operator {
|
||||
#[pyclass(name = "methodcaller")]
|
||||
#[derive(Debug, PyPayload)]
|
||||
struct PyMethodCaller {
|
||||
name: PyStrRef,
|
||||
name: PyUtf8StrRef,
|
||||
args: FuncArgs,
|
||||
}
|
||||
|
||||
@@ -570,11 +580,10 @@ mod _operator {
|
||||
(name, args): Self::Args,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<Self> {
|
||||
if let Ok(name) = name.try_into_value(vm) {
|
||||
Ok(Self { name, args })
|
||||
} else {
|
||||
Err(vm.new_type_error("method name must be a string"))
|
||||
}
|
||||
let name = name
|
||||
.try_into_value(vm)
|
||||
.map_err(|_| vm.new_type_error("method name must be a string"))?;
|
||||
Ok(Self { name, args })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -589,32 +598,27 @@ mod _operator {
|
||||
|
||||
impl Representable for PyMethodCaller {
|
||||
#[inline]
|
||||
fn repr_str(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<String> {
|
||||
let fmt = if let Some(_guard) = ReprGuard::enter(vm, zelf.as_object()) {
|
||||
fn repr_wtf8(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<Wtf8Buf> {
|
||||
let mut result = Wtf8Buf::from("operator.methodcaller(");
|
||||
if let Some(_guard) = ReprGuard::enter(vm, zelf.as_object()) {
|
||||
let args = &zelf.args.args;
|
||||
let kwargs = &zelf.args.kwargs;
|
||||
let mut fmt = vec![zelf.name.as_object().repr(vm)?.as_str().to_owned()];
|
||||
if !args.is_empty() {
|
||||
let mut parts = Vec::with_capacity(args.len());
|
||||
for v in args {
|
||||
parts.push(v.repr(vm)?.as_str().to_owned());
|
||||
}
|
||||
fmt.push(parts.join(", "));
|
||||
result.push_wtf8(zelf.name.as_object().repr(vm)?.as_wtf8());
|
||||
for v in args {
|
||||
result.push_str(", ");
|
||||
result.push_wtf8(v.repr(vm)?.as_wtf8());
|
||||
}
|
||||
// build name=value pairs from KwArgs.
|
||||
if !kwargs.is_empty() {
|
||||
let mut parts = Vec::with_capacity(kwargs.len());
|
||||
for (key, value) in kwargs {
|
||||
let value_repr = value.repr(vm)?;
|
||||
parts.push(format!("{key}={value_repr}"));
|
||||
}
|
||||
fmt.push(parts.join(", "));
|
||||
for (key, value) in kwargs {
|
||||
result.push_str(", ");
|
||||
result.push_str(key);
|
||||
result.push_char('=');
|
||||
result.push_wtf8(value.repr(vm)?.as_wtf8());
|
||||
}
|
||||
fmt.join(", ")
|
||||
} else {
|
||||
"...".to_owned()
|
||||
};
|
||||
Ok(format!("operator.methodcaller({fmt})"))
|
||||
result.push_str("...");
|
||||
}
|
||||
result.push_char(')');
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -179,6 +179,7 @@ pub(super) mod _os {
|
||||
use core::time::Duration;
|
||||
use crossbeam_utils::atomic::AtomicCell;
|
||||
use itertools::Itertools;
|
||||
use rustpython_common::wtf8::Wtf8Buf;
|
||||
use std::{env, fs, fs::OpenOptions, io, path::PathBuf, time::SystemTime};
|
||||
|
||||
const OPEN_DIR_FD: bool = cfg!(not(any(windows, target_os = "redox")));
|
||||
@@ -357,7 +358,8 @@ pub(super) mod _os {
|
||||
|
||||
#[pyfunction]
|
||||
fn mkdirs(path: PyStrRef, vm: &VirtualMachine) -> PyResult<()> {
|
||||
fs::create_dir_all(path.as_str()).map_err(|err| err.into_pyexception(vm))
|
||||
let os_path = vm.fsencode(&path)?;
|
||||
fs::create_dir_all(&*os_path).map_err(|err| err.into_pyexception(vm))
|
||||
}
|
||||
|
||||
#[cfg(not(windows))]
|
||||
@@ -491,8 +493,8 @@ pub(super) mod _os {
|
||||
#[cfg(windows)]
|
||||
#[pyfunction]
|
||||
fn putenv(key: PyStrRef, value: PyStrRef, vm: &VirtualMachine) -> PyResult<()> {
|
||||
let key_str = key.as_str();
|
||||
let value_str = value.as_str();
|
||||
let key_str = key.expect_str();
|
||||
let value_str = value.expect_str();
|
||||
// Search from index 1 because on Windows starting '=' is allowed for
|
||||
// defining hidden environment variables.
|
||||
if key_str.is_empty()
|
||||
@@ -539,7 +541,7 @@ pub(super) mod _os {
|
||||
#[cfg(windows)]
|
||||
#[pyfunction]
|
||||
fn unsetenv(key: PyStrRef, vm: &VirtualMachine) -> PyResult<()> {
|
||||
let key_str = key.as_str();
|
||||
let key_str = key.expect_str();
|
||||
// Search from index 1 because on Windows starting '=' is allowed for
|
||||
// defining hidden environment variables.
|
||||
if key_str.is_empty()
|
||||
@@ -868,7 +870,7 @@ pub(super) mod _os {
|
||||
|
||||
impl Representable for DirEntry {
|
||||
#[inline]
|
||||
fn repr_str(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<String> {
|
||||
fn repr_wtf8(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<Wtf8Buf> {
|
||||
let name = match zelf.as_object().get_attr("name", vm) {
|
||||
Ok(name) => Some(name),
|
||||
Err(e)
|
||||
@@ -882,7 +884,10 @@ pub(super) mod _os {
|
||||
if let Some(name) = name {
|
||||
if let Some(_guard) = ReprGuard::enter(vm, zelf.as_object()) {
|
||||
let repr = name.repr(vm)?;
|
||||
Ok(format!("<{} {}>", zelf.class(), repr))
|
||||
let mut result = Wtf8Buf::from(format!("<{} ", zelf.class()));
|
||||
result.push_wtf8(repr.as_wtf8());
|
||||
result.push_char('>');
|
||||
Ok(result)
|
||||
} else {
|
||||
Err(vm.new_runtime_error(format!(
|
||||
"reentrant call inside {}.__repr__",
|
||||
@@ -890,7 +895,7 @@ pub(super) mod _os {
|
||||
)))
|
||||
}
|
||||
} else {
|
||||
Ok(format!("<{}>", zelf.class()))
|
||||
Ok(Wtf8Buf::from(format!("<{}>", zelf.class())))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ pub fn set_inheritable(fd: BorrowedFd<'_>, inheritable: bool) -> nix::Result<()>
|
||||
pub mod module {
|
||||
use crate::{
|
||||
AsObject, Py, PyObjectRef, PyResult, VirtualMachine,
|
||||
builtins::{PyDictRef, PyInt, PyListRef, PyStr, PyTupleRef},
|
||||
builtins::{PyDictRef, PyInt, PyListRef, PyTupleRef, PyUtf8Str},
|
||||
convert::{IntoPyException, ToPyObject, TryFromObject},
|
||||
exceptions::OSErrorBuilder,
|
||||
function::{ArgMapping, Either, KwArgs, OptionalArg},
|
||||
@@ -44,18 +44,22 @@ pub mod module {
|
||||
target_os = "linux",
|
||||
target_os = "openbsd"
|
||||
))]
|
||||
use crate::{builtins::PyStrRef, utils::ToCString};
|
||||
use crate::{builtins::PyUtf8StrRef, utils::ToCString};
|
||||
use alloc::ffi::CString;
|
||||
use bitflags::bitflags;
|
||||
use core::ffi::CStr;
|
||||
use nix::{
|
||||
errno::Errno,
|
||||
fcntl,
|
||||
sys::signal,
|
||||
unistd::{self, Gid, Pid, Uid},
|
||||
};
|
||||
use rustpython_common::os::ffi::OsStringExt;
|
||||
use std::{
|
||||
env, fs, io,
|
||||
os::fd::{AsFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd},
|
||||
};
|
||||
use strum::IntoEnumIterator;
|
||||
use strum_macros::{EnumIter, EnumString};
|
||||
|
||||
#[cfg(any(target_os = "android", target_os = "linux"))]
|
||||
@@ -379,7 +383,7 @@ pub mod module {
|
||||
fn getgroups_impl() -> nix::Result<Vec<Gid>> {
|
||||
use core::ptr;
|
||||
use libc::{c_int, gid_t};
|
||||
use nix::errno::Errno;
|
||||
|
||||
let ret = unsafe { libc::getgroups(0, ptr::null_mut()) };
|
||||
let mut groups = Vec::<Gid>::with_capacity(Errno::result(ret)? as usize);
|
||||
let ret = unsafe {
|
||||
@@ -449,8 +453,6 @@ pub mod module {
|
||||
|
||||
#[pyattr]
|
||||
fn environ(vm: &VirtualMachine) -> PyDictRef {
|
||||
use rustpython_common::os::ffi::OsStringExt;
|
||||
|
||||
let environ = vm.ctx.new_dict();
|
||||
for (key, value) in env::vars_os() {
|
||||
let key: PyObjectRef = vm.ctx.new_bytes(key.into_vec()).into();
|
||||
@@ -463,8 +465,6 @@ pub mod module {
|
||||
|
||||
#[pyfunction]
|
||||
fn _create_environ(vm: &VirtualMachine) -> PyDictRef {
|
||||
use rustpython_common::os::ffi::OsStringExt;
|
||||
|
||||
let environ = vm.ctx.new_dict();
|
||||
for (key, value) in env::vars_os() {
|
||||
let key: PyObjectRef = vm.ctx.new_bytes(key.into_vec()).into();
|
||||
@@ -909,7 +909,6 @@ pub mod module {
|
||||
#[cfg(not(target_os = "redox"))]
|
||||
#[pyfunction]
|
||||
fn nice(increment: i32, vm: &VirtualMachine) -> PyResult<i32> {
|
||||
use nix::errno::Errno;
|
||||
Errno::clear();
|
||||
let res = unsafe { libc::nice(increment) };
|
||||
if res == -1 && Errno::last_raw() != 0 {
|
||||
@@ -1428,7 +1427,7 @@ pub mod module {
|
||||
target_os = "openbsd"
|
||||
))]
|
||||
#[pyfunction]
|
||||
fn initgroups(user_name: PyStrRef, gid: Gid, vm: &VirtualMachine) -> PyResult<()> {
|
||||
fn initgroups(user_name: PyUtf8StrRef, gid: Gid, vm: &VirtualMachine) -> PyResult<()> {
|
||||
let user = user_name.to_cstring(vm)?;
|
||||
unistd::initgroups(&user, gid).map_err(|err| err.into_pyexception(vm))
|
||||
}
|
||||
@@ -1584,7 +1583,6 @@ pub mod module {
|
||||
let mut flags = nix::spawn::PosixSpawnFlags::empty();
|
||||
|
||||
if let Some(sigs) = self.setsigdef {
|
||||
use nix::sys::signal;
|
||||
let mut set = signal::SigSet::empty();
|
||||
for sig in sigs.iter(vm)? {
|
||||
let sig = sig?;
|
||||
@@ -1630,7 +1628,6 @@ pub mod module {
|
||||
}
|
||||
|
||||
if let Some(sigs) = self.setsigmask {
|
||||
use nix::sys::signal;
|
||||
let mut set = signal::SigSet::empty();
|
||||
for sig in sigs.iter(vm)? {
|
||||
let sig = sig?;
|
||||
@@ -1669,7 +1666,7 @@ pub mod module {
|
||||
envp_from_dict(env_dict, vm)?
|
||||
} else {
|
||||
// env=None means use the current environment
|
||||
use rustpython_common::os::ffi::OsStringExt;
|
||||
|
||||
env::vars_os()
|
||||
.map(|(k, v)| {
|
||||
let mut entry = k.into_vec();
|
||||
@@ -1920,8 +1917,12 @@ pub mod module {
|
||||
target_os = "openbsd"
|
||||
))]
|
||||
#[pyfunction]
|
||||
fn getgrouplist(user: PyStrRef, group: u32, vm: &VirtualMachine) -> PyResult<Vec<PyObjectRef>> {
|
||||
let user = CString::new(user.as_str()).unwrap();
|
||||
fn getgrouplist(
|
||||
user: PyUtf8StrRef,
|
||||
group: u32,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<Vec<PyObjectRef>> {
|
||||
let user = user.to_cstring(vm)?;
|
||||
let gid = Gid::from_raw(group);
|
||||
let group_ids = unistd::getgrouplist(&user, gid).map_err(|err| err.into_pyexception(vm))?;
|
||||
Ok(group_ids
|
||||
@@ -1954,7 +1955,6 @@ pub mod module {
|
||||
who: PriorityWhoType,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult {
|
||||
use nix::errno::Errno;
|
||||
Errno::clear();
|
||||
let retval = unsafe { libc::getpriority(which, who) };
|
||||
if Errno::last_raw() != 0 {
|
||||
@@ -1987,7 +1987,7 @@ pub mod module {
|
||||
let i = match obj.downcast::<PyInt>() {
|
||||
Ok(int) => int.try_to_primitive(vm)?,
|
||||
Err(obj) => {
|
||||
let s = obj.downcast::<PyStr>().map_err(|_| {
|
||||
let s = obj.downcast::<PyUtf8Str>().map_err(|_| {
|
||||
vm.new_type_error(
|
||||
"configuration names must be strings or integers".to_owned(),
|
||||
)
|
||||
@@ -2176,8 +2176,6 @@ pub mod module {
|
||||
PathconfName(name): PathconfName,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<Option<libc::c_long>> {
|
||||
use nix::errno::Errno;
|
||||
|
||||
Errno::clear();
|
||||
debug_assert_eq!(Errno::last_raw(), 0);
|
||||
let raw = match &path {
|
||||
@@ -2214,7 +2212,6 @@ pub mod module {
|
||||
|
||||
#[pyattr]
|
||||
fn pathconf_names(vm: &VirtualMachine) -> PyDictRef {
|
||||
use strum::IntoEnumIterator;
|
||||
let pathname = vm.ctx.new_dict();
|
||||
for variant in PathconfVar::iter() {
|
||||
// get the name of variant as a string to use as the dictionary key
|
||||
@@ -2385,18 +2382,21 @@ pub mod module {
|
||||
let i = match obj.downcast::<PyInt>() {
|
||||
Ok(int) => int.try_to_primitive(vm)?,
|
||||
Err(obj) => {
|
||||
let s = obj.downcast::<PyStr>().map_err(|_| {
|
||||
let s = obj.downcast::<PyUtf8Str>().map_err(|_| {
|
||||
vm.new_type_error(
|
||||
"configuration names must be strings or integers".to_owned(),
|
||||
)
|
||||
})?;
|
||||
s.as_str().parse::<SysconfVar>().or_else(|_| {
|
||||
if s.as_str() == "SC_PAGESIZE" {
|
||||
Ok(SysconfVar::SC_PAGESIZE)
|
||||
} else {
|
||||
Err(vm.new_value_error("unrecognized configuration name"))
|
||||
}
|
||||
})? as i32
|
||||
{
|
||||
let name = s.as_str();
|
||||
name.parse::<SysconfVar>().or_else(|_| {
|
||||
if name == "SC_PAGESIZE" {
|
||||
Ok(SysconfVar::SC_PAGESIZE)
|
||||
} else {
|
||||
Err(vm.new_value_error("unrecognized configuration name"))
|
||||
}
|
||||
})? as i32
|
||||
}
|
||||
}
|
||||
};
|
||||
Ok(Self(i))
|
||||
@@ -2415,7 +2415,6 @@ pub mod module {
|
||||
|
||||
#[pyattr]
|
||||
fn sysconf_names(vm: &VirtualMachine) -> PyDictRef {
|
||||
use strum::IntoEnumIterator;
|
||||
let names = vm.ctx.new_dict();
|
||||
for variant in SysconfVar::iter() {
|
||||
// get the name of variant as a string to use as the dictionary key
|
||||
|
||||
@@ -323,7 +323,7 @@ pub(crate) mod _signal {
|
||||
if ret != 0 {
|
||||
let err = std::io::Error::last_os_error();
|
||||
let itimer_error = itimer_error(vm);
|
||||
return Err(vm.new_exception_msg(itimer_error, err.to_string()));
|
||||
return Err(vm.new_exception_msg(itimer_error, err.to_string().into()));
|
||||
}
|
||||
let old = unsafe { old.assume_init() };
|
||||
Ok(itimerval_to_tuple(&old))
|
||||
@@ -340,7 +340,7 @@ pub(crate) mod _signal {
|
||||
if ret != 0 {
|
||||
let err = std::io::Error::last_os_error();
|
||||
let itimer_error = itimer_error(vm);
|
||||
return Err(vm.new_exception_msg(itimer_error, err.to_string()));
|
||||
return Err(vm.new_exception_msg(itimer_error, err.to_string().into()));
|
||||
}
|
||||
let old = unsafe { old.assume_init() };
|
||||
Ok(itimerval_to_tuple(&old))
|
||||
|
||||
+20
-16
@@ -9,7 +9,7 @@ mod _sre {
|
||||
PyCallableIterator, PyDictRef, PyGenericAlias, PyInt, PyList, PyListRef, PyStr,
|
||||
PyStrRef, PyTuple, PyTupleRef, PyTypeRef,
|
||||
},
|
||||
common::wtf8::{Wtf8, Wtf8Buf},
|
||||
common::wtf8::{Wtf8, Wtf8Buf, wtf8_concat},
|
||||
common::{ascii, hash::PyHash},
|
||||
convert::ToPyObject,
|
||||
function::{ArgCallable, OptionalArg, PosArgs, PyComparisonValue},
|
||||
@@ -552,7 +552,7 @@ mod _sre {
|
||||
|
||||
impl Representable for Pattern {
|
||||
#[inline]
|
||||
fn repr_str(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<String> {
|
||||
fn repr_wtf8(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<Wtf8Buf> {
|
||||
let flag_names = [
|
||||
("re.IGNORECASE", SreFlag::IGNORECASE),
|
||||
("re.LOCALE", SreFlag::LOCALE),
|
||||
@@ -580,19 +580,19 @@ mod _sre {
|
||||
.join("|");
|
||||
|
||||
let pattern = zelf.pattern.repr(vm)?;
|
||||
let truncated: String;
|
||||
let s = if pattern.char_len() > 200 {
|
||||
truncated = pattern.as_str().chars().take(200).collect();
|
||||
&truncated
|
||||
let mut result = Wtf8Buf::from("re.compile(");
|
||||
let pat = if pattern.char_len() > 200 {
|
||||
pattern.as_wtf8().code_points().take(200).collect()
|
||||
} else {
|
||||
pattern.as_str()
|
||||
pattern.as_wtf8().to_owned()
|
||||
};
|
||||
|
||||
if flags.is_empty() {
|
||||
Ok(format!("re.compile({s})"))
|
||||
} else {
|
||||
Ok(format!("re.compile({s}, {flags})"))
|
||||
result.push_wtf8(&pat);
|
||||
if !flags.is_empty() {
|
||||
result.push_str(", ");
|
||||
result.push_str(&flags);
|
||||
}
|
||||
result.push_char(')');
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -851,13 +851,17 @@ mod _sre {
|
||||
|
||||
impl Representable for Match {
|
||||
#[inline]
|
||||
fn repr_str(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<String> {
|
||||
fn repr_wtf8(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<Wtf8Buf> {
|
||||
with_sre_str!(zelf.pattern, &zelf.string, vm, |str_drive| {
|
||||
Ok(format!(
|
||||
"<re.Match object; span=({}, {}), match={}>",
|
||||
let match_repr = zelf.get_slice(0, str_drive, vm).unwrap().repr(vm)?;
|
||||
Ok(wtf8_concat!(
|
||||
"<re.Match object; span=(",
|
||||
zelf.regs[0].0,
|
||||
", ",
|
||||
zelf.regs[0].1,
|
||||
zelf.get_slice(0, str_drive, vm).unwrap().repr(vm)?
|
||||
"), match=",
|
||||
match_repr.as_wtf8(),
|
||||
'>',
|
||||
))
|
||||
})
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ pub(crate) use _symtable::module_def;
|
||||
mod _symtable {
|
||||
use crate::{
|
||||
Py, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine,
|
||||
builtins::{PyDictRef, PyStrRef},
|
||||
builtins::{PyDictRef, PyUtf8StrRef},
|
||||
compiler,
|
||||
types::Representable,
|
||||
};
|
||||
@@ -111,9 +111,9 @@ mod _symtable {
|
||||
|
||||
#[pyfunction]
|
||||
fn symtable(
|
||||
source: PyStrRef,
|
||||
filename: PyStrRef,
|
||||
mode: PyStrRef,
|
||||
source: PyUtf8StrRef,
|
||||
filename: PyUtf8StrRef,
|
||||
mode: PyUtf8StrRef,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<PyRef<PySymbolTable>> {
|
||||
let mode = mode
|
||||
|
||||
@@ -34,7 +34,7 @@ mod sys {
|
||||
AsObject, PyObject, PyObjectRef, PyPayload, PyRef, PyRefExact, PyResult,
|
||||
builtins::{
|
||||
PyBaseExceptionRef, PyDictRef, PyFrozenSet, PyNamespace, PyStr, PyStrRef, PyTuple,
|
||||
PyTupleRef, PyTypeRef,
|
||||
PyTupleRef, PyTypeRef, PyUtf8StrRef,
|
||||
},
|
||||
common::{
|
||||
ascii,
|
||||
@@ -974,7 +974,7 @@ mod sys {
|
||||
}
|
||||
|
||||
#[pyfunction]
|
||||
fn getfilesystemencodeerrors(vm: &VirtualMachine) -> PyStrRef {
|
||||
fn getfilesystemencodeerrors(vm: &VirtualMachine) -> PyUtf8StrRef {
|
||||
vm.fs_encode_errors().to_owned()
|
||||
}
|
||||
|
||||
@@ -1250,7 +1250,7 @@ mod sys {
|
||||
// Print module name (if not builtins or __main__)
|
||||
let module_name = unraisable.exc_type.__module__(vm);
|
||||
if let Ok(module_str) = module_name.downcast::<PyStr>() {
|
||||
let module = module_str.as_str();
|
||||
let module = module_str.as_wtf8();
|
||||
if module != "builtins" && module != "__main__" {
|
||||
write!(stderr, "{}.", module);
|
||||
}
|
||||
@@ -1261,7 +1261,7 @@ mod sys {
|
||||
// Print qualname
|
||||
let qualname = unraisable.exc_type.__qualname__(vm);
|
||||
if let Ok(qualname_str) = qualname.downcast::<PyStr>() {
|
||||
write!(stderr, "{}", qualname_str.as_str());
|
||||
write!(stderr, "{}", qualname_str.as_wtf8());
|
||||
} else {
|
||||
write!(stderr, "{}", unraisable.exc_type.name());
|
||||
}
|
||||
@@ -1270,7 +1270,7 @@ mod sys {
|
||||
if !vm.is_none(&unraisable.exc_value) {
|
||||
write!(stderr, ": ");
|
||||
if let Ok(str) = unraisable.exc_value.str(vm) {
|
||||
write!(stderr, "{}", str.to_str().unwrap_or("<str with surrogate>"));
|
||||
write!(stderr, "{}", str.as_wtf8());
|
||||
} else {
|
||||
write!(stderr, "<exception str() failed>");
|
||||
}
|
||||
|
||||
@@ -12,7 +12,8 @@ pub(crate) use _thread::{
|
||||
pub(crate) mod _thread {
|
||||
use crate::{
|
||||
AsObject, Py, PyPayload, PyRef, PyResult, VirtualMachine,
|
||||
builtins::{PyDictRef, PyStr, PyStrRef, PyTupleRef, PyType, PyTypeRef},
|
||||
builtins::{PyDictRef, PyStr, PyTupleRef, PyType, PyTypeRef, PyUtf8StrRef},
|
||||
common::wtf8::Wtf8Buf,
|
||||
frame::FrameRef,
|
||||
function::{ArgCallable, Either, FuncArgs, KwArgs, OptionalArg, PySetterValue},
|
||||
types::{Constructor, GetAttr, Representable, SetAttr},
|
||||
@@ -318,7 +319,7 @@ pub(crate) mod _thread {
|
||||
|
||||
/// Set the name of the current thread
|
||||
#[pyfunction]
|
||||
fn set_name(name: PyStrRef) {
|
||||
fn set_name(name: PyUtf8StrRef) {
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
use alloc::ffi::CString;
|
||||
@@ -710,11 +711,11 @@ pub(crate) mod _thread {
|
||||
.get_attr("name", vm)
|
||||
.ok()
|
||||
.and_then(|n| n.str(vm).ok())
|
||||
.map(|s| s.as_str().to_owned())
|
||||
.map(|s| s.as_wtf8().to_owned())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let name = thread_name.unwrap_or_else(|| format!("{}", get_ident()));
|
||||
let name = thread_name.unwrap_or_else(|| Wtf8Buf::from(format!("{}", get_ident())));
|
||||
|
||||
let _ = vm.call_method(
|
||||
&file,
|
||||
@@ -836,7 +837,7 @@ pub(crate) mod _thread {
|
||||
impl GetAttr for Local {
|
||||
fn getattro(zelf: &Py<Self>, attr: &Py<PyStr>, vm: &VirtualMachine) -> PyResult {
|
||||
let l_dict = zelf.l_dict(vm);
|
||||
if attr.as_str() == "__dict__" {
|
||||
if attr.as_bytes() == b"__dict__" {
|
||||
Ok(l_dict.into())
|
||||
} else {
|
||||
zelf.as_object()
|
||||
@@ -859,7 +860,7 @@ pub(crate) mod _thread {
|
||||
value: PySetterValue,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<()> {
|
||||
if attr.as_str() == "__dict__" {
|
||||
if attr.as_bytes() == b"__dict__" {
|
||||
Err(vm.new_attribute_error(format!(
|
||||
"{} attribute '__dict__' is read-only",
|
||||
zelf.class().name()
|
||||
|
||||
@@ -101,7 +101,7 @@ mod decl {
|
||||
let dur = seconds.try_into_value::<Duration>(vm).map_err(|e| {
|
||||
if e.class().is(vm.ctx.exceptions.value_error)
|
||||
&& let Some(s) = e.args().first().and_then(|arg| arg.str(vm).ok())
|
||||
&& s.as_str() == "negative duration"
|
||||
&& s.as_bytes() == b"negative duration"
|
||||
{
|
||||
return vm.new_value_error("sleep length must be non-negative");
|
||||
}
|
||||
@@ -434,10 +434,11 @@ mod decl {
|
||||
|
||||
#[cfg(unix)]
|
||||
{
|
||||
use crate::builtins::PyUtf8StrRef;
|
||||
let zone = if t.tm_zone.is(&vm.ctx.none) {
|
||||
None
|
||||
} else {
|
||||
let zone: PyStrRef = t
|
||||
let zone: PyUtf8StrRef = t
|
||||
.tm_zone
|
||||
.clone()
|
||||
.try_into_value(vm)
|
||||
@@ -1047,7 +1048,7 @@ mod platform {
|
||||
#[cfg_attr(target_os = "macos", allow(unused_imports))]
|
||||
use crate::{
|
||||
PyObject, PyRef, PyResult, TryFromBorrowedObject, VirtualMachine,
|
||||
builtins::{PyNamespace, PyStrRef},
|
||||
builtins::{PyNamespace, PyUtf8StrRef},
|
||||
convert::IntoPyException,
|
||||
};
|
||||
use core::time::Duration;
|
||||
@@ -1216,8 +1217,8 @@ mod platform {
|
||||
target_os = "linux",
|
||||
))]
|
||||
#[pyfunction]
|
||||
fn get_clock_info(name: PyStrRef, vm: &VirtualMachine) -> PyResult<PyRef<PyNamespace>> {
|
||||
let (adj, imp, mono, res) = match name.as_ref() {
|
||||
fn get_clock_info(name: PyUtf8StrRef, vm: &VirtualMachine) -> PyResult<PyRef<PyNamespace>> {
|
||||
let (adj, imp, mono, res) = match name.as_str() {
|
||||
"monotonic" | "perf_counter" => (
|
||||
false,
|
||||
"time.clock_gettime(CLOCK_MONOTONIC)",
|
||||
@@ -1263,7 +1264,7 @@ mod platform {
|
||||
target_os = "linux",
|
||||
)))]
|
||||
#[pyfunction]
|
||||
fn get_clock_info(_name: PyStrRef, vm: &VirtualMachine) -> PyResult<PyRef<PyNamespace>> {
|
||||
fn get_clock_info(_name: PyUtf8StrRef, vm: &VirtualMachine) -> PyResult<PyRef<PyNamespace>> {
|
||||
Err(vm.new_not_implemented_error("get_clock_info unsupported on this system"))
|
||||
}
|
||||
|
||||
@@ -1330,7 +1331,7 @@ mod platform {
|
||||
use super::decl::{MS_TO_NS, SEC_TO_NS, StructTimeData, get_tz_info, time_muldiv};
|
||||
use crate::{
|
||||
PyRef, PyResult, VirtualMachine,
|
||||
builtins::{PyNamespace, PyStrRef},
|
||||
builtins::{PyNamespace, PyUtf8StrRef},
|
||||
};
|
||||
use core::time::Duration;
|
||||
use windows_sys::Win32::{
|
||||
@@ -1500,8 +1501,8 @@ mod platform {
|
||||
}
|
||||
|
||||
#[pyfunction]
|
||||
fn get_clock_info(name: PyStrRef, vm: &VirtualMachine) -> PyResult<PyRef<PyNamespace>> {
|
||||
let (adj, imp, mono, res) = match name.as_ref() {
|
||||
fn get_clock_info(name: PyUtf8StrRef, vm: &VirtualMachine) -> PyResult<PyRef<PyNamespace>> {
|
||||
let (adj, imp, mono, res) = match name.as_str() {
|
||||
"monotonic" => (
|
||||
false,
|
||||
"GetTickCount64()",
|
||||
|
||||
@@ -30,7 +30,7 @@ pub(crate) mod typevar {
|
||||
contravariant: bool,
|
||||
) -> String {
|
||||
if infer_variance {
|
||||
return name.to_string();
|
||||
return name.to_owned();
|
||||
}
|
||||
let prefix = if covariant {
|
||||
'+'
|
||||
@@ -62,23 +62,19 @@ pub(crate) mod typevar {
|
||||
fn set_module_from_caller(obj: &PyObject, vm: &VirtualMachine) -> PyResult<()> {
|
||||
// Note: CPython gets module from frame->f_funcobj, but RustPython's Frame
|
||||
// architecture is different - we use globals['__name__'] instead
|
||||
if let Some(module_name) = caller(vm) {
|
||||
let module_value: PyObjectRef = if let Some(module_name) = caller(vm) {
|
||||
// Special handling for certain module names
|
||||
if let Ok(name_str) = module_name.str(vm) {
|
||||
let name = name_str.as_str();
|
||||
// CPython sets __module__ to None for builtins and <...> modules
|
||||
// Also set to None for exec contexts (no __name__ in globals means exec)
|
||||
if name == "builtins" || name.starts_with('<') {
|
||||
// Don't set __module__ attribute at all (CPython behavior)
|
||||
// This allows the typing module to handle it
|
||||
return Ok(());
|
||||
}
|
||||
if let Ok(name_str) = module_name.str(vm)
|
||||
&& let Some(name) = name_str.to_str()
|
||||
&& (name == "builtins" || name.starts_with('<'))
|
||||
{
|
||||
return Ok(());
|
||||
}
|
||||
obj.set_attr("__module__", module_name, vm)?;
|
||||
module_name
|
||||
} else {
|
||||
// If no module name is found (e.g., in exec context), set __module__ to None
|
||||
obj.set_attr("__module__", vm.ctx.none(), vm)?;
|
||||
}
|
||||
vm.ctx.none()
|
||||
};
|
||||
obj.set_attr("__module__", module_value, vm)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -288,7 +284,7 @@ pub(crate) mod typevar {
|
||||
impl Representable for TypeVar {
|
||||
#[inline(always)]
|
||||
fn repr_str(zelf: &crate::Py<Self>, vm: &VirtualMachine) -> PyResult<String> {
|
||||
let name = zelf.name.str(vm)?;
|
||||
let name = zelf.name.str_utf8(vm)?;
|
||||
Ok(variance_repr(
|
||||
name.as_str(),
|
||||
zelf.infer_variance,
|
||||
@@ -684,7 +680,7 @@ pub(crate) mod typevar {
|
||||
impl Representable for ParamSpec {
|
||||
#[inline(always)]
|
||||
fn repr_str(zelf: &crate::Py<Self>, vm: &VirtualMachine) -> PyResult<String> {
|
||||
let name = zelf.__name__().str(vm)?;
|
||||
let name = zelf.__name__().str_utf8(vm)?;
|
||||
Ok(variance_repr(
|
||||
name.as_str(),
|
||||
zelf.infer_variance,
|
||||
|
||||
@@ -32,6 +32,7 @@ pub(crate) mod decl {
|
||||
use crate::{
|
||||
AsObject, Py, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine, atomic_func,
|
||||
builtins::{PyGenericAlias, PyStrRef, PyTuple, PyTupleRef, PyType, PyTypeRef, type_},
|
||||
common::wtf8::Wtf8Buf,
|
||||
function::FuncArgs,
|
||||
protocol::{PyMappingMethods, PyNumberMethods},
|
||||
types::{AsMapping, AsNumber, Callable, Constructor, Iterable, Representable},
|
||||
@@ -145,10 +146,14 @@ pub(crate) mod decl {
|
||||
&& !vm.is_none(&module)
|
||||
&& let Some(module_str) = module.downcast_ref::<crate::builtins::PyStr>()
|
||||
{
|
||||
if module_str.as_str() == "builtins" {
|
||||
return Ok(qualname.str(vm)?.to_string());
|
||||
if module_str.as_bytes() == b"builtins" {
|
||||
return Ok(qualname.str_utf8(vm)?.as_str().to_owned());
|
||||
}
|
||||
return Ok(format!("{}.{}", module_str.as_str(), qualname.str(vm)?));
|
||||
return Ok(format!(
|
||||
"{}.{}",
|
||||
module_str.as_wtf8(),
|
||||
qualname.str_utf8(vm)?.as_str()
|
||||
));
|
||||
}
|
||||
// Fallback to repr
|
||||
Ok(obj.repr(vm)?.to_string())
|
||||
@@ -426,8 +431,8 @@ pub(crate) mod decl {
|
||||
}
|
||||
|
||||
impl Representable for TypeAliasType {
|
||||
fn repr_str(zelf: &Py<Self>, _vm: &VirtualMachine) -> PyResult<String> {
|
||||
Ok(zelf.name.as_str().to_owned())
|
||||
fn repr_wtf8(zelf: &Py<Self>, _vm: &VirtualMachine) -> PyResult<Wtf8Buf> {
|
||||
Ok(zelf.name.as_wtf8().to_owned())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Loaded 100 of 121 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user