Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion Lib/test/test_tstring.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@


class TestTString(unittest.TestCase, TStringBaseCase):
@unittest.expectedFailure # TODO: RUSTPYTHON; + Template(strings=('Hello',), interpolations=())
def test_string_representation(self):
# Test __repr__
t = t"Hello"
Expand Down
11 changes: 6 additions & 5 deletions crates/common/src/str.rs
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,7 @@ impl StrData {
&self.data
}

// TODO: rename to to_str
#[inline]
pub fn as_str(&self) -> Option<&str> {
self.kind
Expand Down Expand Up @@ -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 {
Expand Down
90 changes: 43 additions & 47 deletions crates/stdlib/src/_asyncio.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::{
Expand Down Expand Up @@ -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)?;
Expand Down Expand Up @@ -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)
Expand All @@ -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),
}
Expand Down Expand Up @@ -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} ...>")))
}
}
}
Expand Down Expand Up @@ -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)?;
Expand Down Expand Up @@ -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)?;
Expand Down
12 changes: 6 additions & 6 deletions crates/stdlib/src/_sqlite3.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down Expand Up @@ -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)
}

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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)
}
Expand Down Expand Up @@ -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]] = &[
Expand Down
12 changes: 6 additions & 6 deletions crates/stdlib/src/array.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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},
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -625,7 +625,7 @@ mod array {
#[derive(FromArgs)]
pub struct ArrayNewArgs {
#[pyarg(positional)]
spec: PyStrRef,
spec: PyUtf8StrRef,
#[pyarg(positional, optional)]
init: OptionalArg<PyObjectRef>,
}
Expand Down Expand Up @@ -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(())
Expand Down Expand Up @@ -1425,7 +1425,7 @@ mod array {
#[pyarg(positional)]
arraytype: PyTypeRef,
#[pyarg(positional)]
typecode: PyStrRef,
typecode: PyUtf8StrRef,
#[pyarg(positional)]
mformat_code: MachineFormatCode,
#[pyarg(positional)]
Expand Down Expand Up @@ -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",
Expand Down
2 changes: 1 addition & 1 deletion crates/stdlib/src/binascii.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
15 changes: 9 additions & 6 deletions crates/stdlib/src/contextvars.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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},
Expand Down Expand Up @@ -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));
}

Expand Down Expand Up @@ -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))
}
Expand Down Expand Up @@ -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)
}
}

Expand Down
Loading
Loading