Files
RustPython/crates/stdlib/src/cmath.rs
T
Hanjeong Lee df653cea69 Name generated signature parameters the way CPython names them (#8725)
* Leave Rust-only name decoration out of a generated signature

A leading underscore marks an argument Rust sees as unused and `r#`
escapes a Rust keyword. Neither describes the parameter a Python caller
passes, and `r#` is not valid Python, so `inspect.signature()` rejects
any signature carrying one.

Reporting the bare name fixes _sre.template, select.epoll.__exit__ and
bytearray.__reduce_ex__ without touching them, and lets a method name a
parameter after a Rust keyword.

A parameter CPython itself names with a leading underscore, such as
compile's `_feature_version`, reaches Python through a FromArgs field,
which this never sees.

Assisted-by: Claude Code:claude-opus-5

* Name builtin type parameters the way CPython names them

A generated __text_signature__ reports the Rust parameter name, so a
name chosen for Rust reaches anyone reading help() or inspect. Rename
the ones CPython describes differently: list.pop's `i` becomes `index`,
list.count's `needle` becomes `value`, __format__'s `spec` becomes
`format_spec`, bytes.strip's `chars` becomes `bytes`, and
object.__reduce_ex__'s `proto` becomes `protocol`.

Left alone are the parameters CPython reports as `object` or `unused`.
Those are synthesised from the METH_* flags rather than named: CPython
reports slice.indices as ($self, object, /) while its own docstring
calls the argument len.

Assisted-by: Claude Code:claude-opus-5

* Name os parameters the way CPython names them

os.read's `n` becomes `length`, os.lseek's `how` becomes `whence`,
os.kill's `sig` becomes `signal`, os.stat's `file` becomes `path`, and
os.putenv's `key` becomes `name`, among others.

os.chmod is left alone. It reports (path, dir_fd, mode, follow_symlinks)
while the call binds mode second and takes follow_symlinks by keyword, so
the order and the kind are wrong rather than the names, which a rename
cannot fix.

os.fstatvfs is left alone too: it shares one Rust function with statvfs,
which CPython names `path`, so the two aliases cannot report different
names.

Assisted-by: Claude Code:claude-opus-5

* Name sys parameters the way CPython names them

sys.excepthook's (exc_type, exc_val, exc_tb) become (exctype, value,
traceback), sys._getframe's `offset` becomes `depth`, sys.intern's `s`
becomes `string`, and sys.settrace's `tracefunc` becomes `function`.

sys._clear_type_descriptors takes the name CPython gives it, which
Rust spells r#type.

Assisted-by: Claude Code:claude-opus-5

* Name vm stdlib parameters the way CPython names them

deque.rotate's `mid` becomes `n`, deque.extend's `iter` becomes
`iterable`, _sre.ascii_tolower's `ch` becomes `character`,
_operator.neg's `pos` becomes `a`, signal.alarm's `time` becomes
`seconds`, and codecs.register_error's `name` becomes `errors`.

gc.collect and charmap_build keep theirs. Their argument is a FromArgs
struct or a PosArgs bundle, so the reported name is the binding rather
than a parameter, and renaming the binding would only disguise that.

Assisted-by: Claude Code:claude-opus-5

* Name stdlib crate parameters the way CPython names them

unicodedata.category's `character` becomes `chr`, math.isqrt's `x`
becomes `n`, cmath.polar's `x` becomes `z`, struct.calcsize's `fmt`
becomes `format`, binascii.crc32's `init` becomes `crc`, zlib.adler32's
`begin_state` becomes `value`, and array.append's `x` becomes `v`.

array.fromunicode's parameter is `ustr`, which cspell has to be told
about.

array.__deepcopy__ keeps `_memo`, which the generator now reports as
`memo`; CPython calls it `unused`, a name synthesised from METH_O rather
than chosen.

Assisted-by: Claude Code:claude-opus-5

* Name BaseExceptionGroup.split's parameter the way CPython names it

CPython calls the argument of split and subgroup `matcher_value`.

Assisted-by: Claude Code:claude-opus-5

* Strip a generated parameter name with unraw and one prefix check

syn already removes r# through IdentExt::unraw, the way from_args.rs
does for a field name. The empty-name guard never ran: a bare `_` is
Pat::Wild, which func_sig has already refused.

Assisted-by: Claude Code:claude-fable-5-1

* Take object and unused from CPython's Clinic signatures

CPython also falls back to these names for METH_O, but list.append and
insert, set.add, discard and remove, _weakref.getweakrefcount and
getweakrefs, sys.displayhook and getrefcount, and array.__deepcopy__
get them from an Argument Clinic signature. The earlier commit that
kept array.__deepcopy__'s `_memo` was wrong to call `unused`
synthesised.

test_pydoc's set.add summary-line tests now pass.

Assisted-by: Claude Code:claude-opus-5
2026-09-17 20:10:16 +09:00

171 lines
5.6 KiB
Rust

pub(crate) use cmath::module_def;
#[pymodule]
mod cmath {
use crate::vm::{
PyResult, VirtualMachine,
function::{ArgIntoComplex, ArgIntoFloat, OptionalArg},
};
use num_complex::Complex64;
use crate::math::pymath_exception;
// Constants
#[pyattr(name = "e")]
const E: f64 = pymath::cmath::E;
#[pyattr(name = "pi")]
const PI: f64 = pymath::cmath::PI;
#[pyattr(name = "tau")]
const TAU: f64 = pymath::cmath::TAU;
#[pyattr(name = "inf")]
const INF: f64 = pymath::cmath::INF;
#[pyattr(name = "nan")]
const NAN: f64 = pymath::cmath::NAN;
#[pyattr(name = "infj")]
const INFJ: Complex64 = pymath::cmath::INFJ;
#[pyattr(name = "nanj")]
const NANJ: Complex64 = pymath::cmath::NANJ;
#[pyfunction]
fn phase(z: ArgIntoComplex, vm: &VirtualMachine) -> PyResult<f64> {
pymath::cmath::phase(z.into_complex()).map_err(|err| pymath_exception(err, vm))
}
#[pyfunction]
fn polar(z: ArgIntoComplex, vm: &VirtualMachine) -> PyResult<(f64, f64)> {
pymath::cmath::polar(z.into_complex()).map_err(|err| pymath_exception(err, vm))
}
#[pyfunction]
fn rect(r: ArgIntoFloat, phi: ArgIntoFloat, vm: &VirtualMachine) -> PyResult<Complex64> {
pymath::cmath::rect(r.into_float(), phi.into_float())
.map_err(|err| pymath_exception(err, vm))
}
#[pyfunction]
fn isinf(z: ArgIntoComplex) -> bool {
pymath::cmath::isinf(z.into_complex())
}
#[pyfunction]
fn isfinite(z: ArgIntoComplex) -> bool {
pymath::cmath::isfinite(z.into_complex())
}
#[pyfunction]
fn isnan(z: ArgIntoComplex) -> bool {
pymath::cmath::isnan(z.into_complex())
}
#[pyfunction]
fn exp(z: ArgIntoComplex, vm: &VirtualMachine) -> PyResult<Complex64> {
pymath::cmath::exp(z.into_complex()).map_err(|err| pymath_exception(err, vm))
}
#[pyfunction]
fn sqrt(z: ArgIntoComplex, vm: &VirtualMachine) -> PyResult<Complex64> {
pymath::cmath::sqrt(z.into_complex()).map_err(|err| pymath_exception(err, vm))
}
#[pyfunction]
fn sin(z: ArgIntoComplex, vm: &VirtualMachine) -> PyResult<Complex64> {
pymath::cmath::sin(z.into_complex()).map_err(|err| pymath_exception(err, vm))
}
#[pyfunction]
fn asin(z: ArgIntoComplex, vm: &VirtualMachine) -> PyResult<Complex64> {
pymath::cmath::asin(z.into_complex()).map_err(|err| pymath_exception(err, vm))
}
#[pyfunction]
fn cos(z: ArgIntoComplex, vm: &VirtualMachine) -> PyResult<Complex64> {
pymath::cmath::cos(z.into_complex()).map_err(|err| pymath_exception(err, vm))
}
#[pyfunction]
fn acos(z: ArgIntoComplex, vm: &VirtualMachine) -> PyResult<Complex64> {
pymath::cmath::acos(z.into_complex()).map_err(|err| pymath_exception(err, vm))
}
#[pyfunction]
fn log(
z: ArgIntoComplex,
base: OptionalArg<ArgIntoComplex>,
vm: &VirtualMachine,
) -> PyResult<Complex64> {
pymath::cmath::log(
z.into_complex(),
base.into_option().map(|b| b.into_complex()),
)
.map_err(|err| pymath_exception(err, vm))
}
#[pyfunction]
fn log10(z: ArgIntoComplex, vm: &VirtualMachine) -> PyResult<Complex64> {
pymath::cmath::log10(z.into_complex()).map_err(|err| pymath_exception(err, vm))
}
#[pyfunction]
fn acosh(z: ArgIntoComplex, vm: &VirtualMachine) -> PyResult<Complex64> {
pymath::cmath::acosh(z.into_complex()).map_err(|err| pymath_exception(err, vm))
}
#[pyfunction]
fn atan(z: ArgIntoComplex, vm: &VirtualMachine) -> PyResult<Complex64> {
pymath::cmath::atan(z.into_complex()).map_err(|err| pymath_exception(err, vm))
}
#[pyfunction]
fn atanh(z: ArgIntoComplex, vm: &VirtualMachine) -> PyResult<Complex64> {
pymath::cmath::atanh(z.into_complex()).map_err(|err| pymath_exception(err, vm))
}
#[pyfunction]
fn tan(z: ArgIntoComplex, vm: &VirtualMachine) -> PyResult<Complex64> {
pymath::cmath::tan(z.into_complex()).map_err(|err| pymath_exception(err, vm))
}
#[pyfunction]
fn tanh(z: ArgIntoComplex, vm: &VirtualMachine) -> PyResult<Complex64> {
pymath::cmath::tanh(z.into_complex()).map_err(|err| pymath_exception(err, vm))
}
#[pyfunction]
fn sinh(z: ArgIntoComplex, vm: &VirtualMachine) -> PyResult<Complex64> {
pymath::cmath::sinh(z.into_complex()).map_err(|err| pymath_exception(err, vm))
}
#[pyfunction]
fn cosh(z: ArgIntoComplex, vm: &VirtualMachine) -> PyResult<Complex64> {
pymath::cmath::cosh(z.into_complex()).map_err(|err| pymath_exception(err, vm))
}
#[pyfunction]
fn asinh(z: ArgIntoComplex, vm: &VirtualMachine) -> PyResult<Complex64> {
pymath::cmath::asinh(z.into_complex()).map_err(|err| pymath_exception(err, vm))
}
#[derive(FromArgs)]
struct IsCloseArgs {
#[pyarg(positional)]
a: ArgIntoComplex,
#[pyarg(positional)]
b: ArgIntoComplex,
#[pyarg(named, optional)]
rel_tol: OptionalArg<ArgIntoFloat>,
#[pyarg(named, optional)]
abs_tol: OptionalArg<ArgIntoFloat>,
}
#[pyfunction]
fn isclose(args: IsCloseArgs, vm: &VirtualMachine) -> PyResult<bool> {
let a = args.a.into_complex();
let b = args.b.into_complex();
let rel_tol = args.rel_tol.into_option().map(|v| v.into_float());
let abs_tol = args.abs_tol.into_option().map(|v| v.into_float());
pymath::cmath::isclose(a, b, rel_tol, abs_tol)
.map_err(|_| vm.new_value_error("tolerances must be non-negative"))
}
}