diff --git a/crates/stdlib/src/binascii.rs b/crates/stdlib/src/binascii.rs index 5deae90d5a1..bbeaa331e4c 100644 --- a/crates/stdlib/src/binascii.rs +++ b/crates/stdlib/src/binascii.rs @@ -2,6 +2,8 @@ pub(super) use decl::crc32; pub(crate) use decl::module_def; + +use rustpython_common::wtf8::Wtf8Buf; use rustpython_vm::{VirtualMachine, builtins::PyBaseExceptionRef, convert::ToPyException}; const PAD: u8 = 61u8; @@ -16,6 +18,7 @@ mod decl { convert::ToPyException, function::{ArgAsciiBuffer, ArgBytesLike, OptionalArg}, }; + use base64::Engine; use itertools::Itertools; @@ -33,7 +36,7 @@ mod decl { vm.ctx.new_exception_type("binascii", "Incomplete", None) } - fn hex_nibble(n: u8) -> u8 { + const fn hex_nibble(n: u8) -> u8 { match n { 0..=9 => b'0' + n, 10..=15 => b'a' + (n - 10), @@ -174,10 +177,7 @@ mod decl { fn unhexlify(data: ArgAsciiBuffer, vm: &VirtualMachine) -> PyResult> { data.with_ref(|hex_bytes| { if hex_bytes.len() % 2 != 0 { - return Err(super::new_binascii_error( - "Odd-length string".to_owned(), - vm, - )); + return Err(super::new_binascii_error("Odd-length string", vm)); } let mut unhex = Vec::::with_capacity(hex_bytes.len() / 2); @@ -185,10 +185,7 @@ mod decl { if let (Some(n1), Some(n2)) = (unhex_nibble(*n1), unhex_nibble(*n2)) { unhex.push((n1 << 4) | n2); } else { - return Err(super::new_binascii_error( - "Non-hexadecimal digit found".to_owned(), - vm, - )); + return Err(super::new_binascii_error("Non-hexadecimal digit found", vm)); } } @@ -264,10 +261,10 @@ mod decl { #[pyfunction] fn a2b_base64(args: A2bBase64Args, vm: &VirtualMachine) -> PyResult> { - #[rustfmt::skip] // Converts between ASCII and base-64 characters. The index of a given number yields the // number in ASCII while the value of said index yields the number in base-64. For example // "=" is 61 in ASCII but 0 (since it's the pad character) in base-64, so BASE64_TABLE[61] == 0 + #[rustfmt::skip] const BASE64_TABLE: [i8; 256] = [ -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, @@ -398,7 +395,7 @@ mod decl { if [b'\r', b'\n'].contains(c) { return Ok(0); } - return Err(super::new_binascii_error("Illegal char".to_owned(), vm)); + return Err(super::new_binascii_error("Illegal char", vm)); } Ok((*c - b' ') & 0x3f) } @@ -757,8 +754,7 @@ mod decl { // Allocate the buffer let mut res = Vec::::with_capacity(length); - let trailing_garbage_error = - || Err(super::new_binascii_error("Trailing garbage".to_owned(), vm)); + let trailing_garbage_error = || Err(super::new_binascii_error("Trailing garbage", vm)); for chunk in b.get(1..).unwrap_or_default().chunks(4) { let (char_a, char_b, char_c, char_d) = { @@ -823,10 +819,7 @@ mod decl { data.with_ref(|b| { let length = b.len(); if length > 45 { - return Err(super::new_binascii_error( - "At most 45 bytes at once".to_owned(), - vm, - )); + return Err(super::new_binascii_error("At most 45 bytes at once", vm)); } let mut res = Vec::::with_capacity(2 + length.div_ceil(3) * 4); res.push(uu_b2a(length as u8, backtick)); @@ -850,7 +843,7 @@ mod decl { struct Base64DecodeError(base64::DecodeError); -fn new_binascii_error(msg: String, vm: &VirtualMachine) -> PyBaseExceptionRef { +fn new_binascii_error>(msg: T, vm: &VirtualMachine) -> PyBaseExceptionRef { vm.new_exception_msg(decl::error_type(vm), msg.into()) } diff --git a/crates/stdlib/src/unicodedata.rs b/crates/stdlib/src/unicodedata.rs index 0c43f5a2290..50731e606d7 100644 --- a/crates/stdlib/src/unicodedata.rs +++ b/crates/stdlib/src/unicodedata.rs @@ -274,11 +274,10 @@ mod unicodedata { #[pymethod] fn decomposition(&self, character: PyStrRef, vm: &VirtualMachine) -> PyResult { - let ch = match self.extract_char(character, vm)?.and_then(|c| c.to_char()) { - Some(ch) => ch, - None => return Ok(String::new()), + let Some(ch) = self.extract_char(character, vm)?.and_then(|c| c.to_char()) else { + return Ok(String::new()); }; - let chars: Vec = ch.decomposition_map().collect(); + let chars = ch.decomposition_map().collect::>(); // If decomposition maps to just the character itself, there's no decomposition if chars.len() == 1 && chars[0] == ch { return Ok(String::new()); @@ -356,7 +355,7 @@ mod unicodedata { } } - fn decomposition_type_tag(dt: DecompositionType) -> &'static str { + const fn decomposition_type_tag(dt: DecompositionType) -> &'static str { match dt { DecompositionType::Canonical => "canonical", DecompositionType::Compat => "compat", diff --git a/crates/vm/src/builtins/dict.rs b/crates/vm/src/builtins/dict.rs index 4b2b7c7541e..605322ef07f 100644 --- a/crates/vm/src/builtins/dict.rs +++ b/crates/vm/src/builtins/dict.rs @@ -855,6 +855,7 @@ impl Iterator for DictIntoIter { (l, Some(l)) } } + impl ExactSizeIterator for DictIntoIter { fn len(&self) -> usize { self.dict.entries.len_from_entry_index(self.position) @@ -886,6 +887,7 @@ impl Iterator for DictIter<'_> { (l, Some(l)) } } + impl ExactSizeIterator for DictIter<'_> { fn len(&self) -> usize { self.dict.entries.len_from_entry_index(self.position) @@ -1266,6 +1268,7 @@ trait ViewSetOps: DictView { } impl ViewSetOps for PyDictKeys {} + #[pyclass( flags(DISALLOW_INSTANTIATION), with( diff --git a/crates/vm/src/builtins/list.rs b/crates/vm/src/builtins/list.rs index f4af27ea492..4179f7ef949 100644 --- a/crates/vm/src/builtins/list.rs +++ b/crates/vm/src/builtins/list.rs @@ -533,7 +533,7 @@ impl AsSequence for PyList { } .map_err(|e| { if e.class().is(vm.ctx.exceptions.index_error) { - vm.new_index_error("list assignment index out of range".to_owned()) + vm.new_index_error("list assignment index out of range") } else { e } diff --git a/crates/vm/src/stdlib/_collections.rs b/crates/vm/src/stdlib/_collections.rs index e5127431c8b..4f580ab5dff 100644 --- a/crates/vm/src/stdlib/_collections.rs +++ b/crates/vm/src/stdlib/_collections.rs @@ -365,7 +365,7 @@ mod _collections { }) } else { Err(vm.new_type_error(format!( - "can only concatenate deque (not \"{}\") to deque", + r#"can only concatenate deque (not "{}") to deque"#, other.class().name() ))) } @@ -503,11 +503,13 @@ mod _collections { .concat(other, vm) .map(|x| x.into_ref(&vm.ctx).into()) }), + repeat: atomic_func!(|seq, n, vm| { PyDeque::sequence_downcast(seq) .__mul__(n, vm) .map(|x| x.into_ref(&vm.ctx).into()) }), + item: atomic_func!(|seq, i, vm| PyDeque::sequence_downcast(seq).__getitem__(i, vm)), ass_item: atomic_func!(|seq, i, value, vm| { let zelf = PyDeque::sequence_downcast(seq); @@ -517,14 +519,17 @@ mod _collections { zelf.__delitem__(i, vm) } }), + contains: atomic_func!( |seq, needle, vm| PyDeque::sequence_downcast(seq)._contains(needle, vm) ), + inplace_concat: atomic_func!(|seq, other, vm| { let zelf = PyDeque::sequence_downcast(seq); zelf._extend(other, vm)?; Ok(zelf.to_owned().into()) }), + inplace_repeat: atomic_func!(|seq, n, vm| { let zelf = PyDeque::sequence_downcast(seq); PyDeque::__imul__(zelf.to_owned(), n, vm).map(|x| x.into()) @@ -545,6 +550,7 @@ mod _collections { if let Some(res) = op.identical_optimization(zelf, other) { return Ok(res.into()); } + let other = class_or_notimplemented!(Self, other); let lhs = zelf.borrow_deque(); let rhs = other.borrow_deque(); @@ -573,6 +579,7 @@ mod _collections { 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), diff --git a/crates/vm/src/stdlib/_io.rs b/crates/vm/src/stdlib/_io.rs index 4d16c104694..c6bee82c223 100644 --- a/crates/vm/src/stdlib/_io.rs +++ b/crates/vm/src/stdlib/_io.rs @@ -188,18 +188,18 @@ mod _io { result.map(Some) } - pub(super) fn new_unsupported_operation( + pub(super) fn new_unsupported_operation>( + msg: T, vm: &VirtualMachine, - msg: String, ) -> PyBaseExceptionRef { - vm.new_os_subtype_error(unsupported_operation().to_owned(), None, msg) + vm.new_os_subtype_error(unsupported_operation().to_owned(), None, msg.into()) .upcast() } fn _unsupported(vm: &VirtualMachine, zelf: &PyObject, operation: &str) -> PyResult { Err(new_unsupported_operation( - vm, format!("{}.{}() not supported", zelf.class().name(), operation), + vm, )) } @@ -363,8 +363,8 @@ mod _io { Ok(()) } else { Err(new_unsupported_operation( + "File or stream is not readable", vm, - "File or stream is not readable".to_owned(), )) } } @@ -374,8 +374,8 @@ mod _io { Ok(()) } else { Err(new_unsupported_operation( + "File or stream is not writable.", vm, - "File or stream is not writable.".to_owned(), )) } } @@ -385,8 +385,8 @@ mod _io { Ok(()) } else { Err(new_unsupported_operation( + "File or stream is not seekable", vm, - "File or stream is not seekable".to_owned(), )) } } @@ -1680,7 +1680,7 @@ mod _io { let mut data = zelf.lock(vm)?; data.check_init(vm)?; if !data.writable() { - return Err(new_unsupported_operation(vm, "truncate".to_owned())); + return Err(new_unsupported_operation("truncate", vm)); } data.flush_rewind(vm)?; let res = vm.call_method(data.raw.as_ref().unwrap(), "truncate", (pos,))?; @@ -3063,9 +3063,8 @@ mod _io { || data.decoded_chars_used.chars != 0) { return Err(new_unsupported_operation( + "cannot reconfigure encoding or newline after reading from the stream", vm, - "cannot reconfigure encoding or newline after reading from the stream" - .to_owned(), )); } @@ -3203,8 +3202,8 @@ mod _io { if !textio.seekable { return Err(new_unsupported_operation( + "underlying stream is not seekable", vm, - "underlying stream is not seekable".to_owned(), )); } @@ -3217,8 +3216,8 @@ mod _io { vm.call_method(&textio.buffer, "tell", ())? } else { return Err(new_unsupported_operation( + "can't do nonzero cur-relative seeks", vm, - "can't do nonzero cur-relative seeks".to_owned(), )); } } @@ -3241,8 +3240,8 @@ mod _io { return Ok(res); } return Err(new_unsupported_operation( + "can't do nonzero end-relative seeks", vm, - "can't do nonzero end-relative seeks".to_owned(), )); } _ => { @@ -3312,8 +3311,8 @@ mod _io { let mut textio = zelf.lock(vm)?; if !textio.seekable { return Err(new_unsupported_operation( + "underlying stream is not seekable", vm, - "underlying stream is not seekable".to_owned(), )); } if !textio.telling { @@ -3444,7 +3443,7 @@ mod _io { let decoder = textio .decoder .clone() - .ok_or_else(|| new_unsupported_operation(vm, "not readable".to_owned()))?; + .ok_or_else(|| new_unsupported_operation("not readable", vm))?; textio.write_pending(vm)?; @@ -3495,7 +3494,7 @@ mod _io { let (encoder, encode_func) = textio .encoder .as_ref() - .ok_or_else(|| new_unsupported_operation(vm, "not writable".to_owned()))?; + .ok_or_else(|| new_unsupported_operation("not writable", vm))?; let char_len = obj.char_len(); @@ -3861,7 +3860,7 @@ mod _io { let decoder = self .decoder .as_ref() - .ok_or_else(|| new_unsupported_operation(vm, "not readable".to_owned()))?; + .ok_or_else(|| new_unsupported_operation("not readable", vm))?; let dec_state = if self.telling { let state = vm.call_method(decoder, "getstate", ())?; @@ -5101,8 +5100,8 @@ mod _io { } .ok_or_else(|| { new_unsupported_operation( + "Couldn't get FileIO, io.open likely isn't supported on your platform", vm, - "Couldn't get FileIO, io.open likely isn't supported on your platform".to_owned(), ) })?; let raw = PyType::call( @@ -5586,8 +5585,8 @@ mod fileio { ) -> PyResult>> { if !zelf.mode.load().contains(host_io::FileMode::READABLE) { return Err(new_unsupported_operation( + "File or stream is not readable", vm, - "File or stream is not readable".to_owned(), )); } let handle = zelf.get_fd(vm)?; @@ -5644,8 +5643,8 @@ mod fileio { ) -> PyResult> { if !zelf.mode.load().contains(host_io::FileMode::READABLE) { return Err(new_unsupported_operation( + "File or stream is not readable", vm, - "File or stream is not readable".to_owned(), )); } @@ -5679,8 +5678,8 @@ mod fileio { ) -> PyResult> { if !zelf.mode.load().contains(host_io::FileMode::WRITABLE) { return Err(new_unsupported_operation( + "File or stream is not writable", vm, - "File or stream is not writable".to_owned(), )); } @@ -6219,8 +6218,8 @@ mod winconsoleio { let fd = self.get_fd(vm)?; if !self.readable.load() { return Err(new_unsupported_operation( + "Console buffer does not support reading", vm, - "Console buffer does not support reading".to_owned(), )); } let mut buf_ref = buffer.borrow_buf_mut(); @@ -6267,8 +6266,8 @@ mod winconsoleio { } if !self.readable.load() { return Err(new_unsupported_operation( + "Console buffer does not support reading", vm, - "Console buffer does not support reading".to_owned(), )); } let size = size.unwrap_or(-1); @@ -6310,8 +6309,8 @@ mod winconsoleio { } if !self.writable.load() { return Err(new_unsupported_operation( + "Console buffer does not support writing", vm, - "Console buffer does not support writing".to_owned(), )); } diff --git a/crates/vm/src/stdlib/_thread.rs b/crates/vm/src/stdlib/_thread.rs index 7291026ed58..93d42676b4e 100644 --- a/crates/vm/src/stdlib/_thread.rs +++ b/crates/vm/src/stdlib/_thread.rs @@ -152,6 +152,7 @@ pub(crate) mod _thread { fn acquire(&self, args: AcquireArgs, vm: &VirtualMachine) -> PyResult { acquire_lock_impl!(&self.mu, args, vm) } + #[pymethod] #[pymethod(name = "release_lock")] fn release(&self, vm: &VirtualMachine) -> PyResult<()> { @@ -240,6 +241,7 @@ pub(crate) mod _thread { } Ok(result) } + #[pymethod] #[pymethod(name = "release_lock")] fn release(&self, vm: &VirtualMachine) -> PyResult<()> { @@ -413,34 +415,36 @@ pub(crate) mod _thread { /// Get OS-level thread ID (pthread_self on Unix) /// This is important for fork compatibility - the ID must remain stable after fork - #[cfg(unix)] - fn current_thread_id() -> u64 { - host_thread::current_thread_id() - } - - #[cfg(not(unix))] fn current_thread_id() -> u64 { - thread_to_rust_id(&thread::current()) + cfg_select! { + unix => host_thread::current_thread_id(), + _ => thread_to_rust_id(&thread::current()), + } } /// Convert Rust thread to ID (used for non-unix platforms) #[cfg(not(unix))] fn thread_to_rust_id(t: &thread::Thread) -> u64 { use core::hash::{Hash, Hasher}; + struct U64Hash { v: Option, } + impl Hasher for U64Hash { fn write(&mut self, _: &[u8]) { unreachable!() } + fn write_u64(&mut self, i: u64) { self.v = Some(i); } + fn finish(&self) -> u64 { self.v.expect("should have written a u64") } } + let mut h = U64Hash { v: None }; t.id().hash(&mut h); h.finish() @@ -472,12 +476,14 @@ pub(crate) mod _thread { if !f_args.kwargs.is_empty() { return Err(vm.new_type_error("start_new_thread() takes no keyword arguments")); } + let given = f_args.args.len(); if given < 2 { return Err(vm.new_type_error(format!( "start_new_thread expected at least 2 arguments, got {given}" ))); } + if given > 3 { return Err(vm.new_type_error(format!( "start_new_thread expected at most 3 arguments, got {given}" @@ -491,9 +497,11 @@ pub(crate) mod _thread { if func_obj.to_callable().is_none() { return Err(vm.new_type_error("first arg must be callable")); } + if !args_obj.fast_isinstance(vm.ctx.types.tuple_type) { return Err(vm.new_type_error("2nd arg must be a tuple")); } + if kwargs_obj .as_ref() .is_some_and(|obj| !obj.fast_isinstance(vm.ctx.types.dict_type)) @@ -1537,13 +1545,12 @@ pub(crate) mod _thread { }) .min_by_key(|(distance, _)| *distance) .map(|(_, candidate)| candidate); - let msg = if let Some(suggestion) = suggestion { - format!( - "start_joinable_thread() got an unexpected keyword argument '{unexpected}'. Did you mean '{suggestion}'?" - ) - } else { - format!("start_joinable_thread() got an unexpected keyword argument '{unexpected}'") - }; + + let msg_suffix = + suggestion.map_or_else(String::new, |s| format!(". Did you mean '{s}'?")); + let msg = format!( + "start_joinable_thread() got an unexpected keyword argument '{unexpected}'{msg_suffix}" + ); return Err(vm.new_type_error(msg)); } diff --git a/crates/vm/src/stdlib/_weakref.rs b/crates/vm/src/stdlib/_weakref.rs index e7e030b2b01..4db8970ba70 100644 --- a/crates/vm/src/stdlib/_weakref.rs +++ b/crates/vm/src/stdlib/_weakref.rs @@ -17,18 +17,22 @@ mod _weakref { fn ref_(vm: &VirtualMachine) -> PyTypeRef { vm.ctx.types.weakref_type.to_owned() } + #[pyattr] fn proxy(vm: &VirtualMachine) -> PyTypeRef { vm.ctx.types.weakproxy_type.to_owned() } + #[pyattr(name = "ReferenceType")] fn reference_type(vm: &VirtualMachine) -> PyTypeRef { vm.ctx.types.weakref_type.to_owned() } + #[pyattr(name = "ProxyType")] fn proxy_type(vm: &VirtualMachine) -> PyTypeRef { vm.ctx.types.weakproxy_type.to_owned() } + #[pyattr(name = "CallableProxyType")] fn callable_proxy_type(vm: &VirtualMachine) -> PyTypeRef { vm.ctx.types.weakproxy_type.to_owned() @@ -41,10 +45,8 @@ mod _weakref { #[pyfunction] fn getweakrefs(obj: PyObjectRef) -> Vec { - match obj.get_weak_references() { - Some(v) => v.into_iter().map(Into::into).collect(), - None => vec![], - } + obj.get_weak_references() + .map_or_else(Vec::new, |v| v.into_iter().map(Into::into).collect()) } #[pyfunction] diff --git a/crates/vm/src/stdlib/builtins.rs b/crates/vm/src/stdlib/builtins.rs index cfb9562b793..0bd04f332b5 100644 --- a/crates/vm/src/stdlib/builtins.rs +++ b/crates/vm/src/stdlib/builtins.rs @@ -128,7 +128,7 @@ mod builtins { let hash_pos = line.iter().position(|&b| b == b'#')?; if !line[..hash_pos] .iter() - .all(|&b| b == b' ' || b == b'\t' || b == b'\x0c' || b == b'\r') + .all(|&b| matches!(b, b' ' | b'\t' | b'\x0c' | b'\r')) { return None; } @@ -139,8 +139,7 @@ mod builtins { let after_coding = &after_hash[coding_pos + 6..]; // Next char must be ':' or '=' - let rest = if after_coding.first() == Some(&b':') || after_coding.first() == Some(&b'=') - { + let rest = if matches!(after_coding.first(), Some(b':' | b'=')) { &after_coding[1..] } else { return None; @@ -150,15 +149,15 @@ mod builtins { let rest = rest .iter() .copied() - .skip_while(|&b| b == b' ' || b == b'\t') + .skip_while(|&b| matches!(b, b' ' | b'\t')) .collect::>(); // Read encoding name: [-\w.]+ - let name: String = rest + let name = rest .iter() - .take_while(|&&b| b.is_ascii_alphanumeric() || b == b'-' || b == b'_' || b == b'.') + .take_while(|&&b| b.is_ascii_alphanumeric() || matches!(b, b'-' | b'_' | b'.')) .map(|&b| b as char) - .collect(); + .collect::(); if name.is_empty() { None @@ -179,10 +178,10 @@ mod builtins { // Only check second line if first line is blank or a comment let trimmed = first .iter() - .skip_while(|&&b| b == b' ' || b == b'\t' || b == b'\x0c' || b == b'\r') - .copied() - .collect::>(); - if !trimmed.is_empty() && trimmed[0] != b'#' { + .find(|&&b| !matches!(b, b' ' | b'\t' | b'\x0c' | b'\r')) + .copied(); + + if trimmed.is_some_and(|b| b != b'#') { return None; } } @@ -350,7 +349,7 @@ mod builtins { #[cfg(not(feature = "rustpython-codegen"))] { - return Err(vm.new_type_error(CODEGEN_NOT_SUPPORTED.to_owned())); + return Err(vm.new_type_error(CODEGEN_NOT_SUPPORTED)); } #[cfg(feature = "rustpython-codegen")] { @@ -368,10 +367,10 @@ mod builtins { } #[cfg(not(feature = "parser"))] - { - const PARSER_NOT_SUPPORTED: &str = "can't compile() source code when the `parser` feature of rustpython is disabled"; - Err(vm.new_type_error(PARSER_NOT_SUPPORTED.to_owned())) - } + return Err(vm.new_type_error( + "can't compile() source code when the `parser` feature of rustpython is disabled", + )); + #[cfg(feature = "parser")] { use crate::convert::ToPyException; @@ -398,7 +397,7 @@ mod builtins { if (flags & _ast::PY_CF_ONLY_AST).is_zero() { #[cfg(not(feature = "compiler"))] { - Err(vm.new_value_error(CODEGEN_NOT_SUPPORTED.to_owned())) + Err(vm.new_value_error(CODEGEN_NOT_SUPPORTED)) } #[cfg(feature = "compiler")] { @@ -470,23 +469,22 @@ mod builtins { feature_version: OptionalArg, vm: &VirtualMachine, ) -> PyResult> { - let minor = match feature_version.into_option() { - Some(minor) => minor, - None => return Ok(None), + let Some(minor) = feature_version.into_option() else { + return Ok(None); }; if minor < 0 { return Ok(None); } - let minor = u8::try_from(minor) - .map_err(|_| vm.new_value_error("compile() _feature_version out of range"))?; - Ok(Some(ruff_python_ast::PythonVersion { major: 3, minor })) + u8::try_from(minor) + .map(|v| Some(ruff_python_ast::PythonVersion { major: 3, minor: v })) + .map_err(|_| vm.new_value_error("compile() _feature_version out of range")) } #[pyfunction] fn delattr(obj: PyObjectRef, attr: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> { - let attr = attr.try_to_ref::(vm).map_err(|_e| { + let attr = attr.try_to_ref::(vm).map_err(|_| { vm.new_type_error(format!( "attribute name must be string, not '{}'", attr.class().name() @@ -514,40 +512,42 @@ mod builtins { } impl ScopeArgs { + fn validate_globals_dict( + globals: &PyObject, + vm: &VirtualMachine, + func_name: &'static str, + ) -> PyResult<()> { + if globals.fast_isinstance(vm.ctx.types.dict_type) { + return Ok(()); + } + + let msg = match func_name { + "eval" => { + let is_mapping = globals.mapping_unchecked().check(); + if is_mapping { + "globals must be a real dict; try eval(expr, {}, mapping)".into() + } else { + "globals must be a dict".into() + } + } + "exec" => format!( + "exec() globals must be a dict, not {}", + globals.class().name() + ), + _ => "globals must be a dict".into(), + }; + + Err(vm.new_type_error(msg)) + } + fn make_scope( self, vm: &VirtualMachine, func_name: &'static str, ) -> PyResult { - fn validate_globals_dict( - globals: &PyObject, - vm: &VirtualMachine, - func_name: &'static str, - ) -> PyResult<()> { - if !globals.fast_isinstance(vm.ctx.types.dict_type) { - return Err(match func_name { - "eval" => { - let is_mapping = globals.mapping_unchecked().check(); - vm.new_type_error(if is_mapping { - "globals must be a real dict; try eval(expr, {}, mapping)" - .to_owned() - } else { - "globals must be a dict".to_owned() - }) - } - "exec" => vm.new_type_error(format!( - "exec() globals must be a dict, not {}", - globals.class().name() - )), - _ => vm.new_type_error("globals must be a dict"), - }); - } - Ok(()) - } - let (globals, locals) = match self.globals { Some(globals) => { - validate_globals_dict(&globals, vm, func_name)?; + Self::validate_globals_dict(&globals, vm, func_name)?; let globals = PyDictRef::try_from_object(vm, globals)?; if !globals.contains_key(identifier!(vm, __builtins__), vm) { @@ -636,7 +636,7 @@ mod builtins { .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::A(_) => return Err(vm.new_type_error(CODEGEN_NOT_SUPPORTED)), Either::B(code_obj) => code_obj, }; diff --git a/crates/vm/src/stdlib/itertools.rs b/crates/vm/src/stdlib/itertools.rs index 2cb23902ec9..8a78c698ed3 100644 --- a/crates/vm/src/stdlib/itertools.rs +++ b/crates/vm/src/stdlib/itertools.rs @@ -109,12 +109,11 @@ mod decl { } } }; - match next { - Err(_) | Ok(PyIterReturn::StopIteration(_)) => { - *zelf.source.write() = None; - } - _ => {} + + if matches!(next, Err(_) | Ok(PyIterReturn::StopIteration(_))) { + *zelf.source.write() = None; }; + next } } diff --git a/crates/vm/src/stdlib/typevar.rs b/crates/vm/src/stdlib/typevar.rs index 81d32f852bc..da018b552e1 100644 --- a/crates/vm/src/stdlib/typevar.rs +++ b/crates/vm/src/stdlib/typevar.rs @@ -81,7 +81,6 @@ pub(crate) mod typevar { #[pyattr] #[pyclass(name = "TypeVar", module = "typing")] #[derive(Debug, PyPayload)] - #[allow(dead_code)] pub struct TypeVar { name: PyObjectRef, // TODO PyStrRef? bound: PyMutex, @@ -94,6 +93,7 @@ pub(crate) mod typevar { contravariant: bool, infer_variance: bool, } + #[pyclass( flags(HAS_DICT, HAS_WEAKREF), with(AsNumber, Constructor, Representable) @@ -267,7 +267,7 @@ pub(crate) mod typevar { // Check if we have enough arguments if args_tuple.len() <= index && zelf.has_default(vm) { // Need to add default value - let mut new_args: Vec = args_tuple.iter().cloned().collect(); + let mut new_args = args_tuple.iter().cloned().collect::>(); // Add default value at the correct position while new_args.len() <= index { @@ -367,7 +367,7 @@ pub(crate) mod typevar { // Check for unexpected keyword arguments if !kwargs.is_empty() { - let unexpected_keys: Vec = kwargs.keys().map(|s| s.to_string()).collect(); + let unexpected_keys = kwargs.keys().map(|s| s.to_string()).collect::>(); return Err(vm.new_type_error(format!( "TypeVar() got unexpected keyword argument(s): {}", unexpected_keys.join(", ") @@ -459,7 +459,6 @@ pub(crate) mod typevar { #[pyattr] #[pyclass(name = "ParamSpec", module = "typing")] #[derive(Debug, PyPayload)] - #[allow(dead_code)] pub struct ParamSpec { name: PyObjectRef, bound: Option, @@ -651,7 +650,7 @@ pub(crate) mod typevar { // Check for unexpected keyword arguments if !kwargs.is_empty() { - let unexpected_keys: Vec = kwargs.keys().map(|s| s.to_string()).collect(); + let unexpected_keys = kwargs.keys().map(|s| s.to_string()).collect::>(); return Err(vm.new_type_error(format!( "ParamSpec() got unexpected keyword argument(s): {}", unexpected_keys.join(", ") @@ -721,12 +720,12 @@ pub(crate) mod typevar { #[pyattr] #[pyclass(name = "TypeVarTuple", module = "typing")] #[derive(Debug, PyPayload)] - #[allow(dead_code)] pub struct TypeVarTuple { name: PyObjectRef, default_value: PyMutex, evaluate_default: PyMutex, } + #[pyclass( flags(HAS_DICT, HAS_WEAKREF), with(Constructor, Representable, Iterable) @@ -845,7 +844,7 @@ pub(crate) mod typevar { // Check for unexpected keyword arguments if !kwargs.is_empty() { - let unexpected_keys: Vec = kwargs.keys().map(|s| s.to_string()).collect(); + let unexpected_keys = kwargs.keys().map(|s| s.to_string()).collect::>(); return Err(vm.new_type_error(format!( "TypeVarTuple() got unexpected keyword argument(s): {}", unexpected_keys.join(", ") @@ -898,10 +897,10 @@ pub(crate) mod typevar { #[pyattr] #[pyclass(name = "ParamSpecArgs", module = "typing")] #[derive(Debug, PyPayload)] - #[allow(dead_code)] pub struct ParamSpecArgs { __origin__: PyObjectRef, } + #[pyclass(with(Constructor, Representable, Comparable), flags(HAS_WEAKREF))] impl ParamSpecArgs { #[pymethod] @@ -961,10 +960,10 @@ pub(crate) mod typevar { #[pyattr] #[pyclass(name = "ParamSpecKwargs", module = "typing")] #[derive(Debug, PyPayload)] - #[allow(dead_code)] pub struct ParamSpecKwargs { __origin__: PyObjectRef, } + #[pyclass(with(Constructor, Representable, Comparable), flags(HAS_WEAKREF))] impl ParamSpecKwargs { #[pymethod] @@ -1048,7 +1047,6 @@ pub(crate) mod typevar { #[pyattr] #[pyclass(name = "Generic", module = "typing")] #[derive(Debug, PyPayload)] - #[allow(dead_code)] pub struct Generic; #[pyclass(flags(BASETYPE, HEAPTYPE))]