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
7 changes: 4 additions & 3 deletions crates/stdlib/src/_asyncio.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,9 @@ pub(crate) mod _asyncio {
struct AddDoneCallbackArgs {
#[pyarg(positional)]
r#fn: PyObjectRef,
// Omission copies the context now; explicit None is forwarded to call_soon.
#[pyarg(named, optional)]
context: Option<PyObjectRef>,
context: OptionalArg<PyObjectRef>,
}

#[derive(FromArgs)]
Expand Down Expand Up @@ -364,8 +365,8 @@ pub(crate) mod _asyncio {
return Err(vm.new_runtime_error("Future object is not initialized."));
}
let ctx = match args.context {
Some(c) => c,
None => get_copy_context(vm)?,
OptionalArg::Present(c) => c,
OptionalArg::Missing => get_copy_context(vm)?,
};

if self.fut_state.load() != FutureState::Pending {
Expand Down
2 changes: 1 addition & 1 deletion crates/stdlib/src/array.rs
Original file line number Diff line number Diff line change
Expand Up @@ -953,7 +953,7 @@ pub mod array {
struct ArrayIndexArgs {
#[pyarg(positional, optional, py_default = "0")]
start: OptionalArg<ArgIndex>,
#[pyarg(positional, optional, py_default = "9223372036854775807")]
#[pyarg(positional, optional, py_default = "sys.maxsize")]
stop: OptionalArg<ArgIndex>,
}

Expand Down
2 changes: 1 addition & 1 deletion crates/stdlib/src/csv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ mod _csv {
fn register_dialect(name: PyObjectRef, opts: DialectArg, vm: &VirtualMachine) -> PyResult<()> {
let name = name
.downcast::<PyStr>()
.map_err(|_| vm.new_type_error("argument 0 must be a string"))?;
.map_err(|_| vm.new_type_error("dialect name must be a string"))?;

let dialect = opts.result(vm)?;
validate_dialect(vm, &dialect)?;
Expand Down
18 changes: 9 additions & 9 deletions crates/stdlib/src/faulthandler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ mod decl {

#[derive(FromArgs)]
struct DumpTracebackArgs {
#[pyarg(any, default)]
#[pyarg(any, default, py_default = "sys.stderr")]
file: OptionalArg<PyObjectRef>,
#[pyarg(any, default = ArgIntoBool::TRUE)]
all_threads: ArgIntoBool,
Expand Down Expand Up @@ -266,7 +266,7 @@ mod decl {
#[cfg(any(unix, windows))]
#[derive(FromArgs)]
struct DumpCStackArgs {
#[pyarg(any, default)]
#[pyarg(any, default, py_default = "sys.stderr")]
file: OptionalArg<PyObjectRef>,
}

Expand Down Expand Up @@ -368,7 +368,7 @@ mod decl {
#[derive(FromArgs)]
#[allow(unused)]
struct EnableArgs {
#[pyarg(any, default)]
#[pyarg(any, default, py_default = "sys.stderr")]
file: OptionalArg<PyObjectRef>,
#[pyarg(any, default = ArgIntoBool::TRUE)]
all_threads: ArgIntoBool,
Expand Down Expand Up @@ -777,11 +777,11 @@ mod decl {
struct DumpTracebackLaterArgs {
#[pyarg(positional, error_msg = "timeout must be a number (int or float)")]
timeout: ArgIntoFloat,
#[pyarg(any, default)]
#[pyarg(any, default = ArgIntoBool::FALSE)]
repeat: ArgIntoBool,
#[pyarg(any, default)]
#[pyarg(any, default, py_default = "sys.stderr")]
file: OptionalArg<PyObjectRef>,
#[pyarg(any, default)]
#[pyarg(any, default = ArgIntoBool::FALSE)]
exit: ArgIntoBool,
}

Expand Down Expand Up @@ -902,11 +902,11 @@ mod decl {
struct RegisterArgs {
#[pyarg(positional)]
signum: i32,
#[pyarg(any, default)]
#[pyarg(any, default, py_default = "sys.stderr")]
file: OptionalArg<PyObjectRef>,
#[pyarg(any, default = ArgIntoBool::TRUE)]
all_threads: ArgIntoBool,
#[pyarg(any, default)]
#[pyarg(any, default = ArgIntoBool::FALSE)]
chain: ArgIntoBool,
}

Expand Down Expand Up @@ -959,7 +959,7 @@ mod decl {
#[derive(FromArgs)]
#[allow(dead_code)]
struct SigsegvArgs {
#[pyarg(positional, default)]
#[pyarg(positional, default = ArgIntoBool::FALSE)]
release_gil: ArgIntoBool,
}

Expand Down
4 changes: 2 additions & 2 deletions crates/stdlib/src/hashlib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,8 +138,8 @@ pub(crate) mod _hashlib {
};
}

blake_args!(Blake2bArgs, 64, "64");
blake_args!(Blake2sArgs, 32, "32");
blake_args!(Blake2bArgs, 64, "_blake2.blake2b.MAX_DIGEST_SIZE");
blake_args!(Blake2sArgs, 32, "_blake2.blake2s.MAX_DIGEST_SIZE");

#[derive(FromArgs)]
struct BlakeOptions {
Expand Down
6 changes: 5 additions & 1 deletion crates/stdlib/src/select.rs
Original file line number Diff line number Diff line change
Expand Up @@ -476,7 +476,11 @@ mod decl {
struct EpollRegisterArgs {
#[pyarg(any)]
fd: PyObjectRef,
#[pyarg(any, optional)]
#[pyarg(
any,
optional,
py_default = "select.EPOLLIN | select.EPOLLPRI | select.EPOLLOUT"
)]
eventmask: OptionalArg<PyObjectRef>,
}

Expand Down
2 changes: 1 addition & 1 deletion crates/vm/src/stdlib/_codecs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -673,7 +673,7 @@ mod _codecs {
struct CharmapDecodeArgs {
#[pyarg(positional)]
data: ArgBytesLike,
#[pyarg(positional, optional)]
#[pyarg(positional, optional, py_default = "None")]
errors: OptionalOption<PyUtf8StrRef>,
#[pyarg(positional, optional)]
mapping: Option<PyObjectRef>,
Expand Down
3 changes: 2 additions & 1 deletion crates/vm/src/stdlib/_imp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -335,7 +335,8 @@ mod _imp {
#[pyarg(positional)]
spec: PyObjectRef,
#[pyarg(positional, optional)]
_file: crate::function::OptionalArg<PyObjectRef>,
#[allow(dead_code)]
file: crate::function::OptionalArg<PyObjectRef>,
}

#[pyfunction]
Expand Down
10 changes: 5 additions & 5 deletions crates/vm/src/stdlib/_io.rs
Original file line number Diff line number Diff line change
Expand Up @@ -310,10 +310,10 @@ mod _io {
pub whence: i32,
}

// IOBase and TextIOWrapper spell the same zero default symbolically.
#[derive(FromArgs)]
#[allow(dead_code)]
struct IgnoredWhence {
#[pyarg(positional, default)]
struct SeekWhenceArg {
#[pyarg(positional, default, py_default = "os.SEEK_SET")]
whence: i32,
}

Expand Down Expand Up @@ -547,7 +547,7 @@ mod _io {
fn seek(
zelf: PyObjectRef,
_offset: PyObjectRef,
_whence: IgnoredWhence,
_whence: SeekWhenceArg,
vm: &VirtualMachine,
) -> PyResult {
_unsupported(vm, &zelf, "seek")
Expand Down Expand Up @@ -3633,7 +3633,7 @@ mod _io {
fn seek(
zelf: PyRef<TextIOWrapper>,
cookie: PyObjectRef,
whence: HowArg,
whence: SeekWhenceArg,
vm: &VirtualMachine,
) -> PyResult {
let how = whence.whence;
Expand Down
6 changes: 3 additions & 3 deletions crates/vm/src/stdlib/_warnings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -166,11 +166,11 @@ mod _warnings {
lineno: usize,
#[pyarg(any, optional)]
module: OptionalArg<PyObjectRef>,
#[pyarg(any, optional)]
#[pyarg(any, optional, py_default = "None")]
registry: OptionalArg<PyObjectRef>,
#[pyarg(any, optional)]
#[pyarg(any, optional, py_default = "None")]
module_globals: OptionalArg<PyObjectRef>,
#[pyarg(any, optional)]
#[pyarg(any, optional, py_default = "None")]
source: OptionalArg<PyObjectRef>,
}

Expand Down
4 changes: 2 additions & 2 deletions crates/vm/src/stdlib/marshal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ mod decl {
struct DumpsArgs {
#[pyarg(positional)]
value: PyObjectRef,
#[pyarg(positional, default = 6)]
#[pyarg(positional, default = 6, py_default = "version")]
version: i32,
#[pyarg(named, default = ArgIntoBool::TRUE)]
allow_code: ArgIntoBool,
Expand Down Expand Up @@ -525,7 +525,7 @@ mod decl {
value: PyObjectRef,
#[pyarg(positional)]
file: PyObjectRef,
#[pyarg(positional, default = 6)]
#[pyarg(positional, default = 6, py_default = "version")]
version: i32,
#[pyarg(named, default = ArgIntoBool::TRUE)]
allow_code: ArgIntoBool,
Expand Down
4 changes: 2 additions & 2 deletions crates/vm/src/types/structseq.rs
Original file line number Diff line number Diff line change
Expand Up @@ -372,11 +372,11 @@ pub trait PyStructSequence: StaticType + PyClassImpl + Sized + 'static {
}

#[pymethod]
fn __getitem__(zelf: PyRef<PyTuple>, needle: PyObjectRef, vm: &VirtualMachine) -> PyResult {
fn __getitem__(zelf: PyRef<PyTuple>, key: PyObjectRef, vm: &VirtualMachine) -> PyResult {
let n_seq = get_visible_len(zelf.as_ref(), vm)?;
let visible_elements = &zelf.as_slice()[..n_seq];

match SequenceIndex::try_from_borrowed_object::<NameTuple>(vm, &needle)? {
match SequenceIndex::try_from_borrowed_object::<NameTuple>(vm, &key)? {
SequenceIndex::Int(i) => visible_elements.getitem_by_index(vm, i),
SequenceIndex::Slice(slice) => visible_elements
.getitem_by_slice(vm, slice)
Expand Down
89 changes: 89 additions & 0 deletions extra_tests/snippets/builtin_signature.py
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,95 @@
== _io.DEFAULT_BUFFER_SIZE
)

# Module defaults retain their symbolic spelling without changing conversion.
import _codecs
import _imp
import _warnings
import array
import faulthandler
import marshal
import time

assert array.array.index.__text_signature__ == (
"($self, v, start=0, stop=sys.maxsize, /)"
)
assert _codecs.charmap_decode.__text_signature__ == (
"($module, data, errors=None, mapping=None, /)"
)
assert _imp.create_dynamic.__text_signature__ == (
"($module, spec, file=<unrepresentable>, /)"
)
assert " ".join(_warnings.warn_explicit.__text_signature__.split()) == (
"($module, /, message, category, filename, lineno, "
"module=<unrepresentable>, registry=None, module_globals=None, source=None)"
)
assert marshal.dumps.__text_signature__ == (
"($module, value, version=version, /, *, allow_code=True)"
)
assert marshal.dump.__text_signature__ == (
"($module, value, file, version=version, /, *, allow_code=True)"
)
assert inspect.signature(marshal.dumps).parameters["version"].default == marshal.version
assert faulthandler._sigsegv.__text_signature__ == ("($module, release_gil=False, /)")
assert " ".join(faulthandler.dump_traceback.__text_signature__.split()) == (
"($module, /, file=sys.stderr, all_threads=True, *, max_threads=100)"
)
if hasattr(faulthandler, "dump_c_stack"):
assert (
faulthandler.dump_c_stack.__text_signature__ == "($module, /, file=sys.stderr)"
)

try:
import _blake2
except ImportError:
pass
else:
for hash_type in (_blake2.blake2b, _blake2.blake2s):
assert (
"digest_size=_blake2." + hash_type.__name__ + ".MAX_DIGEST_SIZE"
in hash_type.__text_signature__
)

try:
import select
except ImportError:
pass
else:
if hasattr(select, "epoll"):
assert " ".join(select.epoll.register.__text_signature__.split()) == (
"($self, /, fd, eventmask=select.EPOLLIN | select.EPOLLPRI | select.EPOLLOUT)"
)

for sequence_type in (os.stat_result, os.terminal_size, time.struct_time):
assert sequence_type.__getitem__.__text_signature__ == "($self, key, /)"

# Symbolic defaults and omitted callback contexts carry distinct introspection
# metadata even when the Rust-side converter is shared with other callables.
import _asyncio
import _io

for _future_type in (_asyncio.Future, _asyncio.Task):
assert _future_type.add_done_callback.__text_signature__ == (
"($self, fn, /, *, context=<unrepresentable>)"
)
try:
inspect.signature(_future_type.add_done_callback)
except ValueError:
pass
else:
raise AssertionError("an omitted context is not a representable default")

assert _io._IOBase.seek.__text_signature__ == ("($self, offset, whence=os.SEEK_SET, /)")
assert _io.TextIOWrapper.seek.__text_signature__ == (
"($self, cookie, whence=os.SEEK_SET, /)"
)
assert inspect.signature(_io.TextIOWrapper.seek).parameters["whence"].default == (
os.SEEK_SET
)
# Other seek methods keep their literal defaults.
assert _io.BytesIO.seek.__text_signature__ == "($self, pos, whence=0, /)"
assert _io.BufferedReader.seek.__text_signature__ == "($self, target, whence=0, /)"

# CPython's tp_new_wrapper hides its bound defining type, but still requires
# the caller to supply the subtype as the first positional argument. The raw
# signature intentionally has no slash, unlike other slot descriptors.
Expand Down
40 changes: 40 additions & 0 deletions extra_tests/snippets/stdlib_asyncio.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,46 @@
timeout=10,
)

# An omitted callback context captures registration context; an explicit None
# delegates context selection to call_soon at completion. These are different
# states even though an Option-based Rust argument used to collapse them.
import asyncio
import contextvars

callback_value = contextvars.ContextVar("callback_value", default="unset")
callback_loop = asyncio.new_event_loop()
try:
for already_done in (False, True):
callback_events = []
callback_future = callback_loop.create_future()
if already_done:
callback_future.set_result(None)
callback_value.set("registration")
callback_future.add_done_callback(
lambda f: callback_events.append(("omitted", callback_value.get()))
)
callback_future.add_done_callback(
lambda f: callback_events.append(("none", callback_value.get())),
context=None,
)
explicit_context = contextvars.copy_context()
explicit_context.run(callback_value.set, "explicit")
callback_future.add_done_callback(
lambda f: callback_events.append(("explicit", callback_value.get())),
context=explicit_context,
)
callback_value.set("completion")
if not already_done:
callback_future.set_result(None)
callback_loop.run_until_complete(asyncio.sleep(0))
assert callback_events == [
("omitted", "registration"),
("none", "registration" if already_done else "completion"),
("explicit", "explicit"),
], callback_events
finally:
callback_loop.close()

if sys.implementation.name != "rustpython":
sys.exit(0)

Expand Down
6 changes: 6 additions & 0 deletions extra_tests/snippets/stdlib_csv.py
Original file line number Diff line number Diff line change
Expand Up @@ -665,6 +665,12 @@ def test_non_ascii_dialect_chars():
test_non_ascii_dialect_chars()


# Reject the name before validating conflicting dialect arguments.
with assert_raises(TypeError) as error:
_csv.register_dialect(1, None, dialect=None)
assert str(error.exception) == "dialect name must be a string"


def test_dialect_name_hashing():
class UnhashableName:
def __hash__(self):
Expand Down
Loading