From 6725e25a326be501dde7111dcf7b73b94af894d6 Mon Sep 17 00:00:00 2001 From: leehanjeong Date: Mon, 10 Aug 2026 16:49:24 +0900 Subject: [PATCH 1/8] Drop the $module marker from generated __text_signature__ CPython's C functions receive the module as their first argument, so PyCFunction.__self__ is the module and inspect strips the $module parameter when building a Signature. A #[pyfunction] takes no such argument, PyNativeFunction::zelf is None, and inspect has nothing to strip, so the marker surfaced as a parameter that does not exist: inspect.signature(len) (module, /, obj) # was (obj) # now All 45 builtins shared with CPython carried it. Methods are unaffected; their $self marker comes from func_sig and both branches now produce the same string. Assisted-by: Claude Code:claude-opus-5 --- crates/derive-impl/src/util.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/crates/derive-impl/src/util.rs b/crates/derive-impl/src/util.rs index 1ee878c1313..4713215670d 100644 --- a/crates/derive-impl/src/util.rs +++ b/crates/derive-impl/src/util.rs @@ -732,13 +732,12 @@ where // Best effort attempt to generate a template from which a // __text_signature__ can be created. +// +// Unlike CPython, a `#[pyfunction]` doesn't take the module as an argument, +// so there's no module to mark with `$module`. pub(crate) fn text_signature(sig: &Signature, name: &str) -> String { let signature = func_sig(sig); - if signature.starts_with("$self") { - format!("{name}({signature})") - } else { - format!("{}({}, {})", name, "$module", signature) - } + format!("{name}({signature})") } pub(crate) fn infer_native_call_flags(sig: &Signature, drop_first_typed: usize) -> TokenStream { From 7978ea46629565f60ae3d479dc7f9c42a5fad485 Mon Sep 17 00:00:00 2001 From: leehanjeong Date: Mon, 10 Aug 2026 16:55:22 +0900 Subject: [PATCH 2/8] Mark generated __text_signature__ parameters positional-only Arguments bind through `FuncArgs::take_positional`, which pops from the positional list and never consults the keyword map, so a #[pyfunction] argument cannot be passed by name: >>> len(obj=[1, 2]) TypeError The generated signature omitted the `/` marker, so inspect reported those parameters as POSITIONAL_OR_KEYWORD, contradicting the call above. Emit the marker, except for `*args`/`**kwargs`, which cannot be followed by `/`, and for empty parameter lists. 14 of the 45 builtins shared with CPython now report an identical signature, up from 0. Assisted-by: Claude Code:claude-opus-5 --- crates/derive-impl/src/util.rs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/crates/derive-impl/src/util.rs b/crates/derive-impl/src/util.rs index 4713215670d..a22165b0a36 100644 --- a/crates/derive-impl/src/util.rs +++ b/crates/derive-impl/src/util.rs @@ -737,7 +737,14 @@ where // so there's no module to mark with `$module`. pub(crate) fn text_signature(sig: &Signature, name: &str) -> String { let signature = func_sig(sig); - format!("{name}({signature})") + // Arguments bind through `FuncArgs::take_positional`, which never consults + // the keyword map, so they are positional-only. `*args`/`**kwargs` cannot be + // followed by `/`, and an empty parameter list has nothing to mark. + if signature.is_empty() || signature.contains('*') { + format!("{name}({signature})") + } else { + format!("{name}({signature}, /)") + } } pub(crate) fn infer_native_call_flags(sig: &Signature, drop_first_typed: usize) -> TokenStream { From 34eab71c78de4c46ab1d7f0ba146e882580ebac7 Mon Sep 17 00:00:00 2001 From: leehanjeong Date: Mon, 10 Aug 2026 17:01:25 +0900 Subject: [PATCH 3/8] Emit no __text_signature__ when an argument has no name Arguments bound by a destructuring pattern, e.g. fn round(RoundArgs { number, ndigits }: RoundArgs, ..) have no name to report, and func_sig stringified the pattern verbatim: >>> round.__text_signature__ '($module, RoundArgs { number, ndigits })' That is not valid Python, so inspect.signature() raised "builtin has invalid signature". Return None instead, which leaves __text_signature__ unset and makes inspect raise "no signature found", the same as for a CPython builtin that has no signature. Affects round, sum, os.pathconf, binascii.b2a_base64 and binascii.b2a_uu. Their docstrings are unchanged; only the signature prefix is dropped. Assisted-by: Claude Code:claude-opus-5 --- crates/derive-impl/src/pyclass.rs | 5 +- crates/derive-impl/src/pymodule.rs | 16 ++++--- crates/derive-impl/src/util.rs | 77 +++++++++++++++++------------- 3 files changed, 56 insertions(+), 42 deletions(-) diff --git a/crates/derive-impl/src/pyclass.rs b/crates/derive-impl/src/pyclass.rs index 809d3164b4a..c612572d3dc 100644 --- a/crates/derive-impl/src/pyclass.rs +++ b/crates/derive-impl/src/pyclass.rs @@ -1096,7 +1096,10 @@ where args.attrs.push(allow_attr); } - let doc = args.attrs.doc().map(|doc| format_doc(&sig_doc, &doc)); + let doc = args.attrs.doc().map(|doc| match &sig_doc { + Some(sig_doc) => format_doc(sig_doc, &doc), + None => doc, + }); args.context.method_items.add_item(MethodNurseryItem { py_name, cfgs: args.cfgs.to_vec(), diff --git a/crates/derive-impl/src/pymodule.rs b/crates/derive-impl/src/pymodule.rs index 32d7a0fa6bf..20c94abe94f 100644 --- a/crates/derive-impl/src/pymodule.rs +++ b/crates/derive-impl/src/pymodule.rs @@ -524,7 +524,7 @@ struct FunctionNurseryItem { py_names: Vec, cfgs: Vec, ident: Ident, - doc: String, + doc: Option, call_flags: TokenStream, } @@ -556,8 +556,10 @@ impl ToTokens for ValidatedFunctionNursery { let cfgs = &item.cfgs; let cfgs = quote!(#(#cfgs)*); let py_names = &item.py_names; - let doc = &item.doc; - let doc = quote!(Some(#doc)); + let doc = match &item.doc { + Some(doc) => quote!(Some(#doc)), + None => quote!(None), + }; let flags = &item.call_flags; inner_tokens.extend(quote![ @@ -671,10 +673,10 @@ impl ModuleItem for FunctionItem { .copied() .map(str::to_owned) }); - let doc = if let Some(doc) = doc { - format_doc(&sig_doc, &doc) - } else { - sig_doc + let doc = match (sig_doc, doc) { + (Some(sig_doc), Some(doc)) => Some(format_doc(&sig_doc, &doc)), + (Some(sig_doc), None) => Some(sig_doc), + (None, doc) => doc, }; let py_names = { diff --git a/crates/derive-impl/src/util.rs b/crates/derive-impl/src/util.rs index a22165b0a36..1e734d94a50 100644 --- a/crates/derive-impl/src/util.rs +++ b/crates/derive-impl/src/util.rs @@ -735,16 +735,17 @@ where // // Unlike CPython, a `#[pyfunction]` doesn't take the module as an argument, // so there's no module to mark with `$module`. -pub(crate) fn text_signature(sig: &Signature, name: &str) -> String { - let signature = func_sig(sig); +pub(crate) fn text_signature(sig: &Signature, name: &str) -> Option { + let signature = func_sig(sig)?; // Arguments bind through `FuncArgs::take_positional`, which never consults // the keyword map, so they are positional-only. `*args`/`**kwargs` cannot be // followed by `/`, and an empty parameter list has nothing to mark. - if signature.is_empty() || signature.contains('*') { + let signature = if signature.is_empty() || signature.contains('*') { format!("{name}({signature})") } else { format!("{name}({signature}, /)") - } + }; + Some(signature) } pub(crate) fn infer_native_call_flags(sig: &Signature, drop_first_typed: usize) -> TokenStream { @@ -818,37 +819,45 @@ pub(crate) fn infer_native_call_flags(sig: &Signature, drop_first_typed: usize) } } -fn func_sig(sig: &Signature) -> String { - sig.inputs - .iter() - .filter_map(|arg| { - let arg = match arg { - FnArg::Typed(typed) => typed, - FnArg::Receiver(_) => return Some("$self".to_owned()), - }; - let ty = arg.ty.as_ref(); - let ty = quote!(#ty).to_string(); - if ty == "FuncArgs" { - return Some("*args, **kwargs".to_owned()); - } - if ty.starts_with('&') && ty.ends_with("VirtualMachine") { - return None; - } - let ident = match arg.pat.as_ref() { - syn::Pat::Ident(p) => p.ident.to_string(), - // FIXME: other => unreachable!("function arg pattern must be ident but found `{}`", quote!(fn #ident(.. #other ..))), - other => quote!(#other).to_string(), - }; - if ident == "zelf" { - return Some("$self".to_owned()); - } - if ident == "vm" { - unreachable!("type &VirtualMachine(`{ty}`) must be filtered already"); +/// Returns None when an argument has no name to report, in which case no +/// signature can be generated for the function. +fn func_sig(sig: &Signature) -> Option { + let mut params = Vec::new(); + for arg in &sig.inputs { + let arg = match arg { + FnArg::Typed(typed) => typed, + FnArg::Receiver(_) => { + params.push("$self".to_owned()); + continue; } - Some(ident) - }) - .collect::>() - .join(", ") + }; + let ty = arg.ty.as_ref(); + let ty = quote!(#ty).to_string(); + if ty == "FuncArgs" { + params.push("*args, **kwargs".to_owned()); + continue; + } + if ty.starts_with('&') && ty.ends_with("VirtualMachine") { + continue; + } + // An argument bound by a destructuring pattern, e.g. + // `fn round(RoundArgs { number, ndigits }: RoundArgs, ..)`, has no name + // to report. Stringifying the pattern would emit Rust syntax, which + // makes inspect.signature() raise "invalid signature". + let syn::Pat::Ident(pat) = arg.pat.as_ref() else { + return None; + }; + let ident = pat.ident.to_string(); + if ident == "zelf" { + params.push("$self".to_owned()); + continue; + } + if ident == "vm" { + unreachable!("type &VirtualMachine(`{ty}`) must be filtered already"); + } + params.push(ident); + } + Some(params.join(", ")) } pub(crate) fn format_doc(sig: &str, doc: &str) -> String { From 7b614ca57cacd58707eb6f428dde6554659727eb Mon Sep 17 00:00:00 2001 From: leehanjeong Date: Mon, 10 Aug 2026 17:07:16 +0900 Subject: [PATCH 4/8] Name builtin parameters after CPython These parameters are positional-only, so their names only ever appear in __text_signature__ and cannot be used at a call site. Naming them after CPython makes the generated signatures directly comparable: bin x -> number ord string -> character divmod a, b -> x, y setattr attr -> name delattr attr -> name hasattr attr -> name isinstance typ -> class_or_tuple issubclass subclass,typ -> cls, class_or_tuple aiter iter_target -> async_iterable 23 of the 45 builtins shared with CPython now report an identical signature, up from 0 before this branch. The remainder need FromArgs to report the parameters of its own structs, which is left for a follow-up. Add extra_tests/snippets/builtin_signature.py covering the phantom module parameter, the positional-only marker, the names above, and the signature-less builtins. Assisted-by: Claude Code:claude-opus-5 --- crates/vm/src/stdlib/builtins.rs | 50 ++++++++++-------- extra_tests/snippets/builtin_signature.py | 64 +++++++++++++++++++++++ 2 files changed, 93 insertions(+), 21 deletions(-) create mode 100644 extra_tests/snippets/builtin_signature.py diff --git a/crates/vm/src/stdlib/builtins.rs b/crates/vm/src/stdlib/builtins.rs index 35f404f0f3b..e5d4854434a 100644 --- a/crates/vm/src/stdlib/builtins.rs +++ b/crates/vm/src/stdlib/builtins.rs @@ -74,8 +74,8 @@ mod builtins { } #[pyfunction] - fn bin(x: PyIntRef) -> String { - let x = x.as_bigint(); + fn bin(number: PyIntRef) -> String { + let x = number.as_bigint(); if x.is_negative() { format!("-0b{:b}", x.abs()) } else { @@ -391,11 +391,11 @@ mod builtins { } #[pyfunction] - fn delattr(obj: PyObjectRef, attr: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> { - let attr = attr.try_to_ref::(vm).map_err(|_e| { + fn delattr(obj: PyObjectRef, name: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> { + let attr = name.try_to_ref::(vm).map_err(|_e| { vm.new_type_error(format!( "attribute name must be string, not '{}'", - attr.class().name() + name.class().name() )) })?; obj.del_attr(attr, vm) @@ -407,8 +407,8 @@ mod builtins { } #[pyfunction] - fn divmod(a: PyObjectRef, b: PyObjectRef, vm: &VirtualMachine) -> PyResult { - vm._divmod(&a, &b) + fn divmod(x: PyObjectRef, y: PyObjectRef, vm: &VirtualMachine) -> PyResult { + vm._divmod(&x, &y) } #[derive(FromArgs)] @@ -712,11 +712,11 @@ mod builtins { } #[pyfunction] - fn hasattr(obj: PyObjectRef, attr: PyObjectRef, vm: &VirtualMachine) -> PyResult { - let attr = attr.try_to_ref::(vm).map_err(|_e| { + fn hasattr(obj: PyObjectRef, name: PyObjectRef, vm: &VirtualMachine) -> PyResult { + let attr = name.try_to_ref::(vm).map_err(|_e| { vm.new_type_error(format!( "attribute name must be string, not '{}'", - attr.class().name() + name.class().name() )) })?; Ok(vm.get_attribute_opt(obj, attr)?.is_some()) @@ -818,13 +818,21 @@ mod builtins { } #[pyfunction] - fn isinstance(obj: PyObjectRef, typ: PyObjectRef, vm: &VirtualMachine) -> PyResult { - obj.is_instance(&typ, vm) + fn isinstance( + obj: PyObjectRef, + class_or_tuple: PyObjectRef, + vm: &VirtualMachine, + ) -> PyResult { + obj.is_instance(&class_or_tuple, vm) } #[pyfunction] - fn issubclass(subclass: PyObjectRef, typ: PyObjectRef, vm: &VirtualMachine) -> PyResult { - subclass.is_subclass(&typ, vm) + fn issubclass( + cls: PyObjectRef, + class_or_tuple: PyObjectRef, + vm: &VirtualMachine, + ) -> PyResult { + cls.is_subclass(&class_or_tuple, vm) } #[pyfunction] @@ -845,8 +853,8 @@ mod builtins { } #[pyfunction] - fn aiter(iter_target: PyObjectRef, vm: &VirtualMachine) -> PyResult { - iter_target.get_aiter(vm) + fn aiter(async_iterable: PyObjectRef, vm: &VirtualMachine) -> PyResult { + async_iterable.get_aiter(vm) } #[pyfunction] @@ -994,8 +1002,8 @@ mod builtins { } #[pyfunction] - fn ord(string: Either, vm: &VirtualMachine) -> PyResult { - match string { + fn ord(character: Either, vm: &VirtualMachine) -> PyResult { + match character { Either::A(bytes) => bytes.with_ref(|bytes| { let bytes_len = bytes.len(); if bytes_len != 1 { @@ -1138,14 +1146,14 @@ mod builtins { #[pyfunction] fn setattr( obj: PyObjectRef, - attr: PyObjectRef, + name: PyObjectRef, value: PyObjectRef, vm: &VirtualMachine, ) -> PyResult<()> { - let attr = attr.try_to_ref::(vm).map_err(|_e| { + let attr = name.try_to_ref::(vm).map_err(|_e| { vm.new_type_error(format!( "attribute name must be string, not '{}'", - attr.class().name() + name.class().name() )) })?; obj.set_attr(attr, value, vm)?; diff --git a/extra_tests/snippets/builtin_signature.py b/extra_tests/snippets/builtin_signature.py new file mode 100644 index 00000000000..53f2ad463e6 --- /dev/null +++ b/extra_tests/snippets/builtin_signature.py @@ -0,0 +1,64 @@ +import inspect + +# __text_signature__ is generated from the Rust parameter list, so it must not +# describe parameters the function does not actually take, and must mark the +# ones it does take as positional-only. + +# No phantom `module` parameter. RustPython's #[pyfunction]s take no module +# argument, so `__self__` is None and inspect has nothing to strip. +for f in (len, abs, hash, id, repr, bin, ord, divmod, hex, oct, chr, callable): + assert "module" not in inspect.signature(f).parameters, f.__name__ + +# Plain arguments bind through take_positional(), so they are positional-only. +try: + len(obj=[1, 2]) +except TypeError: + pass +else: + raise AssertionError("len() should not accept keyword arguments") + +assert str(inspect.signature(len)) == "(obj, /)" +assert str(inspect.signature(abs)) == "(x, /)" +assert str(inspect.signature(hash)) == "(obj, /)" +assert str(inspect.signature(chr)) == "(i, /)" +assert str(inspect.signature(callable)) == "(obj, /)" + +assert ( + inspect.signature(len).parameters["obj"].kind == inspect.Parameter.POSITIONAL_ONLY +) + +# *args/**kwargs cannot be followed by `/`. The parameter names themselves still +# differ from CPython here, which is out of scope. +breakpoint_kinds = [p.kind for p in inspect.signature(breakpoint).parameters.values()] +assert breakpoint_kinds == [ + inspect.Parameter.VAR_POSITIONAL, + inspect.Parameter.VAR_KEYWORD, +], breakpoint_kinds + +# Parameter names follow CPython, so signatures are directly comparable. +assert str(inspect.signature(bin)) == "(number, /)" +assert str(inspect.signature(ord)) == "(character, /)" +assert str(inspect.signature(divmod)) == "(x, y, /)" +assert str(inspect.signature(hasattr)) == "(obj, name, /)" +assert str(inspect.signature(setattr)) == "(obj, name, value, /)" +assert str(inspect.signature(delattr)) == "(obj, name, /)" +assert str(inspect.signature(isinstance)) == "(obj, class_or_tuple, /)" +assert str(inspect.signature(issubclass)) == "(cls, class_or_tuple, /)" +assert str(inspect.signature(aiter)) == "(async_iterable, /)" + +# Functions whose Rust arguments are destructuring patterns rather than plain +# names get no signature at all, instead of emitting text that is not valid +# Python and makes inspect.signature() raise "invalid signature". +# +# CPython does have signatures for these, hand-written via Argument Clinic. We +# cannot derive them until FromArgs reports its own parameters, so until then we +# report no signature, which is at least how CPython behaves for the builtins it +# has no signature for. +for f in (round, sum): + assert f.__text_signature__ is None, f.__name__ + try: + inspect.signature(f) + except ValueError as e: + assert "no signature found" in str(e), str(e) + else: + raise AssertionError(f"{f.__name__} should have no signature") From 811b5b3a69bbff30bdd19a50c9dcb192d8fb9e84 Mon Sep 17 00:00:00 2001 From: leehanjeong Date: Mon, 10 Aug 2026 17:52:28 +0900 Subject: [PATCH 5/8] Drop expectedFailure from test_module_level_callable_noargs pydoc's summary line for time.time was "time(module)" because the generated signature carried a $module parameter that inspect could not strip. It now reads "time()", as the test expects. Assisted-by: Claude Code:claude-opus-5 --- Lib/test/test_pydoc/test_pydoc.py | 1 - 1 file changed, 1 deletion(-) diff --git a/Lib/test/test_pydoc/test_pydoc.py b/Lib/test/test_pydoc/test_pydoc.py index d206e5a910d..f44c56da652 100644 --- a/Lib/test/test_pydoc/test_pydoc.py +++ b/Lib/test/test_pydoc/test_pydoc.py @@ -1582,7 +1582,6 @@ def test_module_level_callable(self): self.assertEqual(self._get_summary_line(os.stat), "stat(path, *, dir_fd=None, follow_symlinks=True)") - @unittest.expectedFailure # TODO: RUSTPYTHON def test_module_level_callable_noargs(self): self.assertEqual(self._get_summary_line(time.time), "time()") From 4e41ace230a27ee4d9f400336aa54c578502ebcf Mon Sep 17 00:00:00 2001 From: leehanjeong Date: Mon, 10 Aug 2026 18:19:59 +0900 Subject: [PATCH 6/8] Guard the signature-less assertions to RustPython test_snippets runs every snippet under CPython as well, and CPython does have Argument Clinic signatures for round and sum, so that block only holds for RustPython. Assisted-by: Claude Code:claude-opus-5 --- extra_tests/snippets/builtin_signature.py | 34 ++++++++++++----------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/extra_tests/snippets/builtin_signature.py b/extra_tests/snippets/builtin_signature.py index 53f2ad463e6..320a395d882 100644 --- a/extra_tests/snippets/builtin_signature.py +++ b/extra_tests/snippets/builtin_signature.py @@ -1,4 +1,5 @@ import inspect +import sys # __text_signature__ is generated from the Rust parameter list, so it must not # describe parameters the function does not actually take, and must mark the @@ -46,19 +47,20 @@ assert str(inspect.signature(issubclass)) == "(cls, class_or_tuple, /)" assert str(inspect.signature(aiter)) == "(async_iterable, /)" -# Functions whose Rust arguments are destructuring patterns rather than plain -# names get no signature at all, instead of emitting text that is not valid -# Python and makes inspect.signature() raise "invalid signature". -# -# CPython does have signatures for these, hand-written via Argument Clinic. We -# cannot derive them until FromArgs reports its own parameters, so until then we -# report no signature, which is at least how CPython behaves for the builtins it -# has no signature for. -for f in (round, sum): - assert f.__text_signature__ is None, f.__name__ - try: - inspect.signature(f) - except ValueError as e: - assert "no signature found" in str(e), str(e) - else: - raise AssertionError(f"{f.__name__} should have no signature") +if sys.implementation.name == "rustpython": + # Functions whose Rust arguments are destructuring patterns rather than + # plain names get no signature at all, instead of emitting text that is not + # valid Python and makes inspect.signature() raise "invalid signature". + # + # CPython does have signatures for these, hand-written via Argument Clinic. + # We cannot derive them until FromArgs reports the parameters of its own + # structs, so until then we report no signature, which is at least how + # CPython behaves for the builtins it has no signature for. + for f in (round, sum): + assert f.__text_signature__ is None, f.__name__ + try: + inspect.signature(f) + except ValueError as e: + assert "no signature found" in str(e), str(e) + else: + raise AssertionError(f"{f.__name__} should have no signature") From ce81d4d0a5130c6ec1398acaf96d2c3f3f6aa250 Mon Sep 17 00:00:00 2001 From: "Jeong, YunWon" <69878+youknowone@users.noreply.github.com> Date: Tue, 18 Aug 2026 13:05:20 +0900 Subject: [PATCH 7/8] Update crates/derive-impl/src/util.rs --- crates/derive-impl/src/util.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/derive-impl/src/util.rs b/crates/derive-impl/src/util.rs index 1e734d94a50..a8b4b6ff49b 100644 --- a/crates/derive-impl/src/util.rs +++ b/crates/derive-impl/src/util.rs @@ -733,7 +733,7 @@ where // Best effort attempt to generate a template from which a // __text_signature__ can be created. // -// Unlike CPython, a `#[pyfunction]` doesn't take the module as an argument, +// Unlike CPython, a `#[pyfunction]` doesn't take the module as an argument yet, // so there's no module to mark with `$module`. pub(crate) fn text_signature(sig: &Signature, name: &str) -> Option { let signature = func_sig(sig)?; From 611744dcae619acb9d68e37e87afa9d6ad0f7697 Mon Sep 17 00:00:00 2001 From: leehanjeong Date: Tue, 18 Aug 2026 15:08:55 +0900 Subject: [PATCH 8/8] Fix ord's parameter reference after the merge The merge of main took ord's signature from this branch, which renamed the parameter to character, and its body from main, which rewrote ord to accept bytes and bytearray through a parameter named c. The body then referenced a name that no longer existed and the build failed. Assisted-by: Claude Code:claude-opus-5 --- crates/vm/src/stdlib/builtins.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/vm/src/stdlib/builtins.rs b/crates/vm/src/stdlib/builtins.rs index 539c2aa1f1d..34be8c6178d 100644 --- a/crates/vm/src/stdlib/builtins.rs +++ b/crates/vm/src/stdlib/builtins.rs @@ -1007,7 +1007,7 @@ mod builtins { #[pyfunction] // builtin_ord fn ord(character: PyObjectRef, vm: &VirtualMachine) -> PyResult { - let bytes = if let Some(string) = c.downcast_ref::() { + let bytes = if let Some(string) = character.downcast_ref::() { return match string.as_wtf8().code_points().exactly_one() { Ok(character) => Ok(character.to_u32()), Err(_) => { @@ -1017,14 +1017,14 @@ mod builtins { ))) } }; - } else if let Some(bytes) = c.downcast_ref::() { + } else if let Some(bytes) = character.downcast_ref::() { bytes.as_bytes().to_vec() - } else if let Some(bytearray) = c.downcast_ref::() { + } else if let Some(bytearray) = character.downcast_ref::() { bytearray.borrow_buf().to_vec() } else { return Err(vm.new_type_error(format!( "ord() expected string of length 1, but {} found", - c.class().name() + character.class().name() ))); }; let bytes_len = bytes.len();