diff --git a/Lib/test/test_pydoc/test_pydoc.py b/Lib/test/test_pydoc/test_pydoc.py index 9774f8a3a..bac96087a 100644 --- a/Lib/test/test_pydoc/test_pydoc.py +++ b/Lib/test/test_pydoc/test_pydoc.py @@ -419,7 +419,6 @@ class PydocDocTest(unittest.TestCase): def tearDown(self): self.assertIs(sys.modules['pydoc'], pydoc) - @unittest.expectedFailure # TODO: RUSTPYTHON @unittest.skipIf(hasattr(sys, 'gettrace') and sys.gettrace(), 'trace function introduces __locals__ unexpectedly') @requires_docstrings @@ -438,7 +437,6 @@ class PydocDocTest(unittest.TestCase): self.assertIn(mod_file, result) self.assertIn(doc_loc, result) - @unittest.expectedFailure # TODO: RUSTPYTHON @unittest.skipIf(hasattr(sys, 'gettrace') and sys.gettrace(), 'trace function introduces __locals__ unexpectedly') @requires_docstrings @@ -708,7 +706,6 @@ class PydocDocTest(unittest.TestCase): expected = missing_pattern % "abd" self.assertEqual(expected, buf.getvalue().strip().replace('\n', os.linesep)) - @unittest.expectedFailure # TODO: RUSTPYTHON @unittest.skipIf(hasattr(sys, 'gettrace') and sys.gettrace(), 'trace function introduces __locals__ unexpectedly') @unittest.mock.patch('pydoc.pager') @@ -1073,7 +1070,6 @@ class PydocDocTest(unittest.TestCase): methods = pydoc.allmethods(TestClass) self.assertDictEqual(methods, expected) - @unittest.expectedFailure # TODO: RUSTPYTHON @requires_docstrings def test_method_aliases(self): class A: @@ -1164,7 +1160,6 @@ class B(A) for expected_line in expected_lines: self.assertIn(expected_line, as_text) - @unittest.expectedFailure # TODO: RUSTPYTHON def test_long_signatures(self): from collections.abc import Callable from typing import Literal, Annotated diff --git a/crates/derive-impl/src/pyclass.rs b/crates/derive-impl/src/pyclass.rs index f172841de..4b613629e 100644 --- a/crates/derive-impl/src/pyclass.rs +++ b/crates/derive-impl/src/pyclass.rs @@ -450,13 +450,20 @@ fn attr_doc_expr(self_ty: Option<&syn::Type>, attr: &str, rust_doc: Option::ATTR_DOCS, #attr, ); - match FOUND { - Some((offset, len)) if len != 0 => ::rustpython_vm::function::ItemDoc { - text: None, - offset, - len, - }, - _ => ::rustpython_vm::function::ItemDoc::NONE, + if let Some((offset, len)) = FOUND { + if len != 0 { + ::rustpython_vm::function::ItemDoc { + text: None, + offset, + len, + } + } else if offset == u32::MAX { + ::rustpython_vm::function::ItemDoc::EMPTY + } else { + ::rustpython_vm::function::ItemDoc::NONE + } + } else { + ::rustpython_vm::function::ItemDoc::NONE } } } else { diff --git a/crates/stdlib/src/_datetime.rs b/crates/stdlib/src/_datetime.rs index 060f18141..309749b72 100644 --- a/crates/stdlib/src/_datetime.rs +++ b/crates/stdlib/src/_datetime.rs @@ -18,7 +18,7 @@ mod _datetime { hash::PyHash, wtf8::{CodePoint, Wtf8Buf}, }, - function::{FuncArgs, OptionalArg, PyComparisonValue}, + function::{ArgumentError, FromArgs, FuncArgs, OptionalArg, Param, PyComparisonValue}, protocol::{PyNumber, PyNumberMethods}, types::{AsNumber, Comparable, Constructor, Hashable, PyComparisonOp, Representable}, }; @@ -1948,7 +1948,8 @@ mod _datetime { } #[pyclassmethod] - fn fromisoformat(cls: PyTypeRef, dtstr: PyObjectRef, vm: &VirtualMachine) -> PyResult { + fn fromisoformat(cls: PyTypeRef, object: PyObjectRef, vm: &VirtualMachine) -> PyResult { + let dtstr = object; let Some(s) = dtstr.downcast_ref::() else { return Err(vm.new_type_error("fromisoformat: argument must be str")); }; @@ -2069,7 +2070,6 @@ mod _datetime { } #[pymethod] - #[pymethod(name = "__replace__")] fn replace(zelf: &Py, args: DateReplaceArgs, vm: &VirtualMachine) -> PyResult { new_date_subclass( args.year.unwrap_or_else(|| zelf.y()), @@ -2080,6 +2080,11 @@ mod _datetime { ) } + #[pymethod] + fn __replace__(zelf: &Py, changes: ReplaceChanges, vm: &VirtualMachine) -> PyResult { + Self::replace(zelf, changes.args.bind_for(vm, "replace")?, vm) + } + #[pymethod] fn __reduce__(zelf: &Py, vm: &VirtualMachine) -> PyRef { let state = vm.ctx.new_bytes(zelf.state_bytes().to_vec()); @@ -2121,13 +2126,29 @@ mod _datetime { day: PyObjectRef, } + /// `**changes` in the text signature. Binding still accepts the same + /// arguments as `replace`, and a failure names `replace`. + struct ReplaceChanges { + args: FuncArgs, + } + + impl FromArgs for ReplaceChanges { + const PARAMS: Option<&'static [Param]> = Some(&[Param::var_keyword("changes")]); + + fn from_args(_vm: &VirtualMachine, args: &mut FuncArgs) -> Result { + Ok(Self { + args: core::mem::take(args), + }) + } + } + #[derive(FromArgs)] struct DateReplaceArgs { - #[pyarg(any, optional)] + #[pyarg(any, optional, py_default = "unchanged")] year: OptionalArg, - #[pyarg(any, optional)] + #[pyarg(any, optional, py_default = "unchanged")] month: OptionalArg, - #[pyarg(any, optional)] + #[pyarg(any, optional, py_default = "unchanged")] day: OptionalArg, } @@ -2327,22 +2348,23 @@ mod _datetime { #[pyclass(with(Constructor), flags(BASETYPE))] impl PyTzInfo { #[pymethod] - fn tzname(&self, _dt: PyObjectRef, vm: &VirtualMachine) -> PyResult { + fn tzname(&self, _object: PyObjectRef, vm: &VirtualMachine) -> PyResult { tzinfo_nogo("tzname", vm) } #[pymethod] - fn utcoffset(&self, _dt: PyObjectRef, vm: &VirtualMachine) -> PyResult { + fn utcoffset(&self, _object: PyObjectRef, vm: &VirtualMachine) -> PyResult { tzinfo_nogo("utcoffset", vm) } #[pymethod] - fn dst(&self, _dt: PyObjectRef, vm: &VirtualMachine) -> PyResult { + fn dst(&self, _object: PyObjectRef, vm: &VirtualMachine) -> PyResult { tzinfo_nogo("dst", vm) } #[pymethod] - fn fromutc(zelf: PyObjectRef, dt: PyObjectRef, vm: &VirtualMachine) -> PyResult { + fn fromutc(zelf: PyObjectRef, object: PyObjectRef, vm: &VirtualMachine) -> PyResult { + let dt = object; let Some(dtobj) = as_datetime(&dt) else { return Err(vm.new_type_error("fromutc: argument must be a datetime")); }; @@ -2530,25 +2552,29 @@ mod _datetime { #[pyclass(with(Constructor, Comparable, Hashable, Representable))] impl PyTimeZone { #[pymethod] - fn tzname(zelf: &Py, dt: PyObjectRef, vm: &VirtualMachine) -> PyResult { + fn tzname(zelf: &Py, object: PyObjectRef, vm: &VirtualMachine) -> PyResult { + let dt = object; timezone_check_argument(&dt, "tzname", vm)?; Ok(Self::name_str(zelf, vm)) } #[pymethod] - fn utcoffset(&self, dt: PyObjectRef, vm: &VirtualMachine) -> PyResult> { + fn utcoffset(&self, object: PyObjectRef, vm: &VirtualMachine) -> PyResult> { + let dt = object; timezone_check_argument(&dt, "utcoffset", vm)?; Ok(self.offset.clone()) } #[pymethod] - fn dst(&self, dt: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> { + fn dst(&self, object: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> { + let dt = object; timezone_check_argument(&dt, "dst", vm)?; Ok(()) } #[pymethod] - fn fromutc(zelf: &Py, dt: PyObjectRef, vm: &VirtualMachine) -> PyResult { + fn fromutc(zelf: &Py, object: PyObjectRef, vm: &VirtualMachine) -> PyResult { + let dt = object; let Some(dtobj) = as_datetime(&dt) else { return Err(vm.new_type_error("fromutc: argument must be a datetime")); }; @@ -2800,17 +2826,17 @@ mod _datetime { #[derive(FromArgs)] struct TimeReplaceArgs { - #[pyarg(any, optional)] + #[pyarg(any, optional, py_default = "unchanged")] hour: OptionalArg, - #[pyarg(any, optional)] + #[pyarg(any, optional, py_default = "unchanged")] minute: OptionalArg, - #[pyarg(any, optional)] + #[pyarg(any, optional, py_default = "unchanged")] second: OptionalArg, - #[pyarg(any, optional)] + #[pyarg(any, optional, py_default = "unchanged")] microsecond: OptionalArg, - #[pyarg(any, optional)] + #[pyarg(any, optional, py_default = "unchanged")] tzinfo: OptionalArg, - #[pyarg(named, optional)] + #[pyarg(named, optional, py_default = "unchanged")] fold: OptionalArg, } @@ -2940,7 +2966,6 @@ mod _datetime { } #[pymethod] - #[pymethod(name = "__replace__")] fn replace(zelf: &Py, args: TimeReplaceArgs, vm: &VirtualMachine) -> PyResult { let tzinfo = match args.tzinfo { OptionalArg::Present(tz) => tzinfo_arg(tz, vm), @@ -2958,8 +2983,14 @@ mod _datetime { ) } + #[pymethod] + fn __replace__(zelf: &Py, changes: ReplaceChanges, vm: &VirtualMachine) -> PyResult { + Self::replace(zelf, changes.args.bind_for(vm, "replace")?, vm) + } + #[pyclassmethod] - fn fromisoformat(cls: PyTypeRef, tstr: PyObjectRef, vm: &VirtualMachine) -> PyResult { + fn fromisoformat(cls: PyTypeRef, object: PyObjectRef, vm: &VirtualMachine) -> PyResult { + let tstr = object; let Some(s) = tstr.downcast_ref::() else { return Err(vm.new_type_error("fromisoformat: argument must be str")); }; @@ -3576,7 +3607,7 @@ mod _datetime { #[derive(FromArgs)] struct NowArgs { - #[pyarg(any, optional)] + #[pyarg(any, optional, py_default = "None")] tz: OptionalArg, } @@ -3608,23 +3639,23 @@ mod _datetime { #[derive(FromArgs)] struct DateTimeReplaceArgs { - #[pyarg(any, optional)] + #[pyarg(any, optional, py_default = "unchanged")] year: OptionalArg, - #[pyarg(any, optional)] + #[pyarg(any, optional, py_default = "unchanged")] month: OptionalArg, - #[pyarg(any, optional)] + #[pyarg(any, optional, py_default = "unchanged")] day: OptionalArg, - #[pyarg(any, optional)] + #[pyarg(any, optional, py_default = "unchanged")] hour: OptionalArg, - #[pyarg(any, optional)] + #[pyarg(any, optional, py_default = "unchanged")] minute: OptionalArg, - #[pyarg(any, optional)] + #[pyarg(any, optional, py_default = "unchanged")] second: OptionalArg, - #[pyarg(any, optional)] + #[pyarg(any, optional, py_default = "unchanged")] microsecond: OptionalArg, - #[pyarg(any, optional)] + #[pyarg(any, optional, py_default = "unchanged")] tzinfo: OptionalArg, - #[pyarg(named, optional)] + #[pyarg(named, optional, py_default = "unchanged")] fold: OptionalArg, } @@ -3788,7 +3819,8 @@ mod _datetime { } #[pyclassmethod] - fn fromisoformat(cls: PyTypeRef, dtstr: PyObjectRef, vm: &VirtualMachine) -> PyResult { + fn fromisoformat(cls: PyTypeRef, object: PyObjectRef, vm: &VirtualMachine) -> PyResult { + let dtstr = object; let Some(s) = dtstr.downcast_ref::() else { return Err(vm.new_type_error("fromisoformat: argument must be str")); }; @@ -4053,7 +4085,6 @@ mod _datetime { } #[pymethod] - #[pymethod(name = "__replace__")] fn replace(zelf: &Py, args: DateTimeReplaceArgs, vm: &VirtualMachine) -> PyResult { let tzinfo = match args.tzinfo { OptionalArg::Present(tz) => tzinfo_arg(tz, vm), @@ -4075,6 +4106,11 @@ mod _datetime { ) } + #[pymethod] + fn __replace__(zelf: &Py, changes: ReplaceChanges, vm: &VirtualMachine) -> PyResult { + Self::replace(zelf, changes.args.bind_for(vm, "replace")?, vm) + } + #[pymethod] fn astimezone(zelf: &Py, args: AstimezoneArgs, vm: &VirtualMachine) -> PyResult { let tz = args.tz.unwrap_or_none(vm); diff --git a/crates/stdlib/src/csv.rs b/crates/stdlib/src/csv.rs index 529dc02e1..5c210b4ec 100644 --- a/crates/stdlib/src/csv.rs +++ b/crates/stdlib/src/csv.rs @@ -74,7 +74,7 @@ mod _csv { } #[pyattr] - #[pyclass(module = "csv", name = "Dialect")] + #[pyclass(module = "_csv", name = "Dialect")] #[derive(Debug, PyPayload, Clone)] struct PyDialect { delimiter: u8, diff --git a/crates/stdlib/src/lzma.rs b/crates/stdlib/src/lzma.rs index 3401bae2d..a5cd619f3 100644 --- a/crates/stdlib/src/lzma.rs +++ b/crates/stdlib/src/lzma.rs @@ -79,7 +79,7 @@ mod _lzma { #[pyattr(once, name = "LZMAError")] fn error(vm: &VirtualMachine) -> PyTypeRef { vm.ctx.new_exception_type_with_doc( - "lzma", + "_lzma", "LZMAError", Some(vec![vm.ctx.exceptions.exception_type.to_owned()]), const { ItemDoc::db("_lzma.LZMAError") }, diff --git a/crates/stdlib/src/openssl.rs b/crates/stdlib/src/openssl.rs index 022a8c617..384d8b563 100644 --- a/crates/stdlib/src/openssl.rs +++ b/crates/stdlib/src/openssl.rs @@ -841,7 +841,7 @@ mod _ssl { } #[pyattr] - #[pyclass(module = "ssl", name = "_SSLContext")] + #[pyclass(module = "_ssl", name = "_SSLContext")] #[derive(PyPayload)] struct PySslContext { ctx: PyRwLock, @@ -2482,7 +2482,7 @@ mod _ssl { } #[pyattr] - #[pyclass(module = "ssl", name = "_SSLSocket", traverse)] + #[pyclass(module = "_ssl", name = "_SSLSocket", traverse)] #[derive(PyPayload)] struct PySslSocket { ctx: PyRwLock>, @@ -3298,7 +3298,7 @@ mod _ssl { } #[pyattr] - #[pyclass(module = "ssl", name = "SSLSession", unhashable = true)] + #[pyclass(module = "_ssl", name = "SSLSession", unhashable = true)] #[derive(PyPayload)] struct PySslSession { session: *mut sys::SSL_SESSION, @@ -3358,7 +3358,7 @@ mod _ssl { } #[pyattr] - #[pyclass(module = "ssl", name = "MemoryBIO")] + #[pyclass(module = "_ssl", name = "MemoryBIO")] #[derive(PyPayload)] struct PySslMemoryBio { bio: *mut sys::BIO, diff --git a/crates/stdlib/src/pickle.rs b/crates/stdlib/src/pickle.rs index 30e7d1e1a..d4c38c333 100644 --- a/crates/stdlib/src/pickle.rs +++ b/crates/stdlib/src/pickle.rs @@ -68,7 +68,7 @@ mod _pickle { // PickleBuffer #[pyattr] - #[pyclass(module = "_pickle", name = "PickleBuffer")] + #[pyclass(module = "pickle", name = "PickleBuffer")] #[derive(Debug, PyPayload)] pub(super) struct PyPickleBuffer { buffer: PyRwLock>, diff --git a/crates/stdlib/src/ssl.rs b/crates/stdlib/src/ssl.rs index 14701109e..465c79f2f 100644 --- a/crates/stdlib/src/ssl.rs +++ b/crates/stdlib/src/ssl.rs @@ -319,7 +319,7 @@ mod _ssl { // SSLContext - manages TLS configuration #[pyattr] - #[pyclass(name = "_SSLContext", module = "ssl", traverse)] + #[pyclass(name = "_SSLContext", module = "_ssl", traverse)] #[derive(Debug, PyPayload)] struct PySSLContext { #[pytraverse(skip)] @@ -2058,7 +2058,7 @@ mod _ssl { // SSLSocket - represents a TLS-wrapped socket #[pyattr] - #[pyclass(name = "_SSLSocket", module = "ssl", traverse)] + #[pyclass(name = "_SSLSocket", module = "_ssl", traverse)] #[derive(Debug, PyPayload)] pub(crate) struct PySSLSocket { io: SocketOrBio, @@ -3898,7 +3898,7 @@ mod _ssl { // MemoryBIO - provides in-memory buffer for SSL/TLS I/O #[pyattr] - #[pyclass(name = "MemoryBIO", module = "ssl")] + #[pyclass(name = "MemoryBIO", module = "_ssl")] #[derive(Debug, PyPayload)] struct PyMemoryBIO { inner: PyMutex, @@ -3979,7 +3979,7 @@ mod _ssl { // SSLSession - represents a cached SSL session // NOTE: This is an EMULATION - actual session data is managed by Rustls internally #[pyattr] - #[pyclass(name = "SSLSession", module = "ssl", unhashable = true)] + #[pyclass(name = "SSLSession", module = "_ssl", unhashable = true)] #[derive(Debug, PyPayload)] struct PySSLSession { context_identity: Arc<()>, diff --git a/crates/stdlib/src/ssl_wasm.rs b/crates/stdlib/src/ssl_wasm.rs index a9328c952..03dc253e8 100644 --- a/crates/stdlib/src/ssl_wasm.rs +++ b/crates/stdlib/src/ssl_wasm.rs @@ -179,7 +179,7 @@ mod _ssl { } #[pyattr] - #[pyclass(name = "_SSLContext", module = "ssl", traverse)] + #[pyclass(name = "_SSLContext", module = "_ssl", traverse)] #[derive(Debug, PyPayload)] struct PySSLContext { #[pytraverse(skip)] @@ -665,7 +665,7 @@ mod _ssl { } #[pyattr] - #[pyclass(name = "MemoryBIO", module = "ssl")] + #[pyclass(name = "MemoryBIO", module = "_ssl")] #[derive(Debug, PyPayload)] struct PyMemoryBIO { inner: PyMutex, @@ -737,7 +737,7 @@ mod _ssl { } #[pyattr] - #[pyclass(name = "SSLSession", module = "ssl", unhashable = true)] + #[pyclass(name = "SSLSession", module = "_ssl", unhashable = true)] #[derive(Debug, PyPayload)] struct PySSLSession { session_id: Vec, diff --git a/crates/stdlib/src/unicodedata.rs b/crates/stdlib/src/unicodedata.rs index e2d9883c2..4c9777bf1 100644 --- a/crates/stdlib/src/unicodedata.rs +++ b/crates/stdlib/src/unicodedata.rs @@ -61,7 +61,9 @@ mod unicodedata { "normalize", "numeric", ] { - module.set_attr(attr, ucd.get_attr(attr, vm)?, vm)?; + let func = ucd.get_attr(attr, vm)?; + func.set_attr("__module__", vm.ctx.new_str("unicodedata"), vm)?; + module.set_attr(attr, func, vm)?; } Ok(()) diff --git a/crates/vm/src/builtins/descriptor.rs b/crates/vm/src/builtins/descriptor.rs index e91a966b2..2582c075e 100644 --- a/crates/vm/src/builtins/descriptor.rs +++ b/crates/vm/src/builtins/descriptor.rs @@ -1507,7 +1507,7 @@ impl Representable for PyMemberDescriptor { Ok(format!( "", zelf.common.name, - zelf.common.typ.name(), + zelf.common.typ.slot_name(), )) } } diff --git a/crates/vm/src/builtins/type.rs b/crates/vm/src/builtins/type.rs index e2d421e51..903c7ed34 100644 --- a/crates/vm/src/builtins/type.rs +++ b/crates/vm/src/builtins/type.rs @@ -2822,9 +2822,11 @@ impl Constructor for PyType { .iter() .any(|base| base.attributes.contains(__dict__)); if !typ.attributes.contains(__dict__) && !has_inherited_dict { - let descriptor = - vm.ctx - .new_getset("__dict__", &typ, subtype_get_dict, subtype_set_dict); + let getset = super::PyGetSet::new("__dict__", &typ, &vm.ctx) + .with_get(subtype_get_dict) + .with_set(subtype_set_dict) + .with_doc(ItemDoc::static_text("dictionary for instance variables")); + let descriptor = PyRef::new_ref(getset, vm.ctx.types.getset_type.to_owned(), None); typ.attributes.set(__dict__, descriptor.into()); } } @@ -2838,12 +2840,13 @@ impl Constructor for PyType { .iter() .any(|base| base.attributes.contains(__weakref__)); if !typ.attributes.contains(__weakref__) && !has_inherited_weakref { - let descriptor = vm.ctx.new_getset( - "__weakref__", - &typ, - subtype_get_weakref, - subtype_set_weakref, - ); + let getset = super::PyGetSet::new("__weakref__", &typ, &vm.ctx) + .with_get(subtype_get_weakref) + .with_set(subtype_set_weakref) + .with_doc(ItemDoc::static_text( + "list of weak references to the object", + )); + let descriptor = PyRef::new_ref(getset, vm.ctx.types.getset_type.to_owned(), None); typ.attributes.set(__weakref__, descriptor.into()); } } diff --git a/crates/vm/src/builtins/weakproxy.rs b/crates/vm/src/builtins/weakproxy.rs index a45ce4750..a47002c0f 100644 --- a/crates/vm/src/builtins/weakproxy.rs +++ b/crates/vm/src/builtins/weakproxy.rs @@ -13,7 +13,7 @@ use crate::{ }, }; -#[pyclass(module = false, name = "weakproxy", unhashable = true)] +#[pyclass(module = "weakref", name = "weakproxy", unhashable = true)] #[derive(Debug)] #[repr(transparent)] pub struct PyWeakProxy(PyWeak); @@ -78,7 +78,7 @@ impl PyWeakProxy { } #[pyclass( - module = false, + module = "weakref", name = "weakcallableproxy", base = PyWeakProxy, ctx = "weakcallableproxy_type", @@ -123,6 +123,7 @@ impl PyWeakProxy { self.0.upgrade().ok_or_else(|| new_reference_error(vm)) } + /// Return str(self). #[pymethod] fn __str__(zelf: &Py, vm: &VirtualMachine) -> PyResult { zelf.try_upgrade(vm)?.str(vm) diff --git a/crates/vm/src/class.rs b/crates/vm/src/class.rs index 04c0fe9e2..e31bd2803 100644 --- a/crates/vm/src/class.rs +++ b/crates/vm/src/class.rs @@ -231,14 +231,17 @@ pub const fn attr_name_present(table: &[&str], name: &str) -> bool { #[must_use] pub fn class_attr_item_doc(name: &str) -> ItemDoc { #[cfg(feature = "doc")] - if let Some((offset, len)) = attr_doc(T::ATTR_DOCS, name) - && len != 0 - { - return ItemDoc { - text: None, - offset, - len, - }; + if let Some((offset, len)) = attr_doc(T::ATTR_DOCS, name) { + if len != 0 { + return ItemDoc { + text: None, + offset, + len, + }; + } + if offset == u32::MAX { + return ItemDoc::EMPTY; + } } #[cfg(not(feature = "doc"))] let _ = name; diff --git a/crates/vm/src/function/doctext.rs b/crates/vm/src/function/doctext.rs index bd7a3b6a5..523fe5a9f 100644 --- a/crates/vm/src/function/doctext.rs +++ b/crates/vm/src/function/doctext.rs @@ -23,6 +23,14 @@ impl ItemDoc { len: 0, }; + /// An explicit empty docstring. `offset == u32::MAX` distinguishes it from + /// [`NONE`](Self::NONE), which means the item has no docstring at all. + pub const EMPTY: Self = Self { + text: None, + offset: u32::MAX, + len: 0, + }; + #[must_use] pub const fn static_text(text: &'static str) -> Self { Self { @@ -73,6 +81,9 @@ pub fn db_doc(offset: u32, len: u32) -> Option<&'static str> { #[inline(never)] #[must_use] pub fn plain_doc(doc: ItemDoc) -> Option<&'static str> { + if doc.offset == u32::MAX && doc.len == 0 { + return Some(""); + } if doc.len != 0 { return db_doc(doc.offset, doc.len); } @@ -109,3 +120,16 @@ fn docs() -> &'static str { }) .as_ref() } + +#[cfg(test)] +mod tests { + use super::{ItemDoc, plain_doc}; + + #[test] + fn explicit_empty_doc_is_empty_string() { + assert_eq!(plain_doc(ItemDoc::EMPTY), Some("")); + assert_eq!(plain_doc(ItemDoc::NONE), None); + assert_eq!(plain_doc(ItemDoc::static_text("")), None); + assert_eq!(plain_doc(ItemDoc::static_text("a")), Some("a")); + } +} diff --git a/crates/vm/src/stdlib/_collections.rs b/crates/vm/src/stdlib/_collections.rs index e5bed88dc..9ef69e4c0 100644 --- a/crates/vm/src/stdlib/_collections.rs +++ b/crates/vm/src/stdlib/_collections.rs @@ -737,7 +737,7 @@ mod _collections { } #[pyattr] - #[pyclass(name = "_deque_iterator", traverse)] + #[pyclass(name = "_deque_iterator", module = "collections", traverse)] #[derive(Debug, PyPayload)] struct PyDequeIterator { #[pytraverse(skip)] @@ -873,7 +873,7 @@ mod _collections { } #[pyattr] - #[pyclass(name = "_deque_reverse_iterator", traverse)] + #[pyclass(name = "_deque_reverse_iterator", module = "collections", traverse)] #[derive(Debug, PyPayload)] struct PyReverseDequeIterator { #[pytraverse(skip)] diff --git a/crates/vm/src/stdlib/_ctypes.rs b/crates/vm/src/stdlib/_ctypes.rs index 84dbcd139..190e257fb 100644 --- a/crates/vm/src/stdlib/_ctypes.rs +++ b/crates/vm/src/stdlib/_ctypes.rs @@ -257,7 +257,7 @@ pub(crate) mod _ctypes { #[pyattr(name = "ArgumentError", once)] fn argument_error(vm: &VirtualMachine) -> PyTypeRef { vm.ctx.new_exception_type( - "_ctypes", + "ctypes", "ArgumentError", Some(vec![vm.ctx.exceptions.exception_type.to_owned()]), ) diff --git a/crates/vm/src/stdlib/_ctypes/base.rs b/crates/vm/src/stdlib/_ctypes/base.rs index 60ad7ee7a..51940cbbc 100644 --- a/crates/vm/src/stdlib/_ctypes/base.rs +++ b/crates/vm/src/stdlib/_ctypes/base.rs @@ -1250,7 +1250,7 @@ impl PyCData { // PyCField - Field descriptor for Structure/Union types -#[pyclass(name = "CField", module = "_ctypes")] +#[pyclass(name = "CField", module = "ctypes")] #[derive(Debug, PyPayload)] pub struct PyCField { /// Field name diff --git a/crates/vm/src/stdlib/_operator.rs b/crates/vm/src/stdlib/_operator.rs index 535bef0b7..9f708bd5e 100644 --- a/crates/vm/src/stdlib/_operator.rs +++ b/crates/vm/src/stdlib/_operator.rs @@ -351,7 +351,7 @@ mod _operator { } #[pyattr] - #[pyclass(name = "attrgetter")] + #[pyclass(name = "attrgetter", module = "operator")] #[derive(Debug, PyPayload)] struct PyAttrGetter { attrs: Vec, @@ -468,7 +468,7 @@ mod _operator { } #[pyattr] - #[pyclass(name = "itemgetter")] + #[pyclass(name = "itemgetter", module = "operator")] #[derive(Debug, PyPayload)] struct PyItemGetter { items: Vec, @@ -554,7 +554,7 @@ mod _operator { } #[pyattr] - #[pyclass(name = "methodcaller")] + #[pyclass(name = "methodcaller", module = "operator")] #[derive(Debug, PyPayload)] struct PyMethodCaller { name: PyUtf8StrRef, diff --git a/crates/vm/src/stdlib/_testinternalcapi.rs b/crates/vm/src/stdlib/_testinternalcapi.rs index 33d3b172e..06b6c8cd5 100644 --- a/crates/vm/src/stdlib/_testinternalcapi.rs +++ b/crates/vm/src/stdlib/_testinternalcapi.rs @@ -291,6 +291,7 @@ mod _testinternalcapi { } } + /// C implementation of inspect.cleandoc(). #[pyfunction] fn compiler_cleandoc(doc: PyStrRef) -> String { clean_doc(doc.to_str().unwrap_or("")) @@ -342,6 +343,7 @@ mod _testinternalcapi { dict_inner::peek_next_keys_version() } + /// forcefully assign type->tp_version_tag #[pyfunction] fn type_assign_specific_version_unsafe(ty: PyTypeRef, version: u32) { ty.assign_specific_version(version); @@ -800,12 +802,18 @@ mod _testinternalcapi { } } + /// Return a new, empty InstructionSequence. #[cfg(feature = "codegen")] #[pyfunction] fn new_instruction_sequence(vm: &VirtualMachine) -> PyRef { PyInstructionSequence::empty().into_ref(&vm.ctx) } + /// Apply compiler code generation to an AST. + /// + /// Return (instruction_sequence, metadata). metadata maps "argcount", + /// "posonlyargcount", "kwonlyargcount" to ints and "consts" to the list of + /// constants in LOAD_CONST index order (for use with optimize_cfg). #[cfg(feature = "codegen")] #[pyfunction] fn compiler_codegen( @@ -856,6 +864,10 @@ mod _testinternalcapi { Ok(vm.ctx.new_tuple(vec![seq.into(), metadata.into()]).into()) } + /// Apply compiler optimizations to an instruction list. + /// + /// consts must be a list aligned with LOAD_CONST opargs (the "consts" entry + /// from the metadata dict returned by compiler_codegen for the same unit). #[cfg(feature = "codegen")] #[pyfunction] fn optimize_cfg( @@ -893,6 +905,7 @@ mod _testinternalcapi { Ok(PyInstructionSequence::from_rust(optimized, vm)) } + /// Create a code object for the given instructions. #[cfg(feature = "codegen")] #[pyfunction] fn assemble_code_object( diff --git a/crates/vm/src/stdlib/os.rs b/crates/vm/src/stdlib/os.rs index 3b72cd7d0..e52779ad7 100644 --- a/crates/vm/src/stdlib/os.rs +++ b/crates/vm/src/stdlib/os.rs @@ -1944,9 +1944,14 @@ pub(super) mod _os { pub elapsed: f64, } - #[cfg(all(any(unix, windows), not(target_os = "redox")))] + #[cfg(all(windows, not(target_os = "redox")))] #[pyattr] - #[pystruct_sequence(name = "times_result", module = "os", data = "TimesResultData")] + #[pystruct_sequence(name = "times_result", module = "nt", data = "TimesResultData")] + struct PyTimesResult; + + #[cfg(all(unix, not(windows), not(target_os = "redox")))] + #[pyattr] + #[pystruct_sequence(name = "times_result", module = "posix", data = "TimesResultData")] struct PyTimesResult; #[cfg(all(any(unix, windows), not(target_os = "redox")))] @@ -2171,8 +2176,14 @@ pub(super) mod _os { pub machine: String, } + #[cfg(windows)] #[pyattr] - #[pystruct_sequence(name = "uname_result", module = "os", data = "UnameResultData")] + #[pystruct_sequence(name = "uname_result", module = "nt", data = "UnameResultData")] + pub(crate) struct PyUnameResult; + + #[cfg(not(windows))] + #[pyattr] + #[pystruct_sequence(name = "uname_result", module = "posix", data = "UnameResultData")] pub(crate) struct PyUnameResult; #[pyclass(with(PyStructSequence))] diff --git a/crates/wasm/src/ssl.rs b/crates/wasm/src/ssl.rs index b9210a4b7..26d49e51a 100644 --- a/crates/wasm/src/ssl.rs +++ b/crates/wasm/src/ssl.rs @@ -243,7 +243,7 @@ mod _ssl { } #[pyattr] - #[pyclass(name = "MemoryBIO", module = "ssl")] + #[pyclass(name = "MemoryBIO", module = "_ssl")] #[derive(Debug, PyPayload)] struct PyMemoryBIO { inner: PyMutex, @@ -299,7 +299,7 @@ mod _ssl { } #[pyattr] - #[pyclass(name = "SSLSession", module = "ssl")] + #[pyclass(name = "SSLSession", module = "_ssl")] #[derive(Debug, PyPayload)] struct PySSLSession {} @@ -332,7 +332,7 @@ mod _ssl { } #[pyattr] - #[pyclass(name = "_SSLContext", module = "ssl")] + #[pyclass(name = "_SSLContext", module = "_ssl")] #[derive(Debug, PyPayload)] struct PySSLContext { protocol: i32, @@ -640,7 +640,7 @@ mod _ssl { } #[pyattr] - #[pyclass(name = "_SSLSocket", module = "ssl")] + #[pyclass(name = "_SSLSocket", module = "_ssl")] #[derive(Debug, PyPayload)] struct PySSLSocket { incoming: PyRef,