Match hashlib constructor name type diagnostics (#9066)

Use native type names, None spelling, and UTF-8 byte precision for invalid name arguments without changing accepted strings or hashing behavior.

Assisted-by: OpenAI Codex:model identifier unavailable
This commit is contained in:
youknowdot authored and GitHub committed 2026-10-12 02:06:41 +09:00
1 parent 4638b6a5f3
commit 924ef2f434
2 files changed
+43 -3

No files matched your search

+27 -3
View File
@@ -8,7 +8,7 @@ pub(crate) use _hashlib::module_def;
#[pymodule]
pub(crate) mod _hashlib {
use crate::vm::{
Py, PyObject, PyObjectRef, PyPayload, PyResult, VirtualMachine,
Py, PyObject, PyObjectRef, PyPayload, PyResult, TryFromObject, VirtualMachine,
builtins::{
PyBaseExceptionRef, PyBytes, PyFrozenSet, PyStr, PyType, PyTypeRef, PyUtf8StrRef,
PyValueError,
@@ -67,11 +67,35 @@ pub(crate) mod _hashlib {
dict.into()
}
#[derive(Debug)]
struct HashName(PyUtf8StrRef);
impl TryFromObject for HashName {
fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
if !obj.downcastable::<PyStr>() {
let name = if vm.is_none(&obj) {
"None".into()
} else {
obj.class().slot_name()
};
let mut end = name.len().min(50);
while !name.is_char_boundary(end) {
end -= 1;
}
return Err(vm.new_type_error(format!(
"new() argument 'name' must be str, not {}",
&name[..end]
)));
}
PyUtf8StrRef::try_from_object(vm, obj).map(Self)
}
}
#[derive(FromArgs, Debug)]
#[allow(unused)]
struct NewHashArgs {
#[pyarg(any)]
name: PyUtf8StrRef,
name: HashName,
// Missing still allows the string keyword; b'' does not.
#[pyarg(any, optional, py_default = "b''")]
data: OptionalArg<ArgBytesLike>,
@@ -785,7 +809,7 @@ pub(crate) mod _hashlib {
#[pyfunction(name = "new")]
fn hashlib_new(args: NewHashArgs, vm: &VirtualMachine) -> PyResult<PyObjectRef> {
let data = resolve_data(args.data, args.string, vm)?;
match args.name.as_str().to_lowercase().as_str() {
match args.name.0.as_str().to_lowercase().as_str() {
"md5" => Ok(new_fixed_hasher("md5", data).into_pyobject(vm)),
"sha1" => Ok(new_fixed_hasher("sha1", data).into_pyobject(vm)),
"sha224" => Ok(new_fixed_hasher("sha224", data).into_pyobject(vm)),
+16
View File
@@ -1,5 +1,7 @@
import _hashlib
import _md5
import _sha1
import array
import hashlib
from testutils import assert_raises
@@ -64,3 +66,17 @@ assert _sha1.sha1(b"").hexdigest() == "da39a3ee5e6b4b0d3255bfef95601890afd80709"
# narrower than the length asked for, converting the argument fails first.
with assert_raises(OverflowError):
hashlib.pbkdf2_hmac("sha256", b"password", b"salt", 1, 2**62)
# Invalid names use the native constructor's bounded type diagnostic.
for value, name in (
(None, "None"),
(42, "int"),
(b"sha256", "bytes"),
(array.array("b"), "array.array"),
(type("x" * 60, (), {})(), "x" * 50),
(type("€" * 17, (), {})(), "€" * 16),
):
with assert_raises(TypeError) as caught:
_hashlib.new(value)
assert str(caught.exception) == f"new() argument 'name' must be str, not {name}"