diff --git a/newsfragments/4481.changed.md b/newsfragments/4481.changed.md new file mode 100644 index 00000000000..9ce455c923c --- /dev/null +++ b/newsfragments/4481.changed.md @@ -0,0 +1 @@ +Mention the type name in the exception message when trying to instantiate a class with no constructor defined. diff --git a/pytests/tests/test_pyclasses.py b/pytests/tests/test_pyclasses.py index 0c336ecf2e7..e91e75fa58a 100644 --- a/pytests/tests/test_pyclasses.py +++ b/pytests/tests/test_pyclasses.py @@ -65,13 +65,13 @@ def test_new_classmethod(): _ = AssertingSubClass(expected_type=str) -class ClassWithoutConstructorPy: +class ClassWithoutConstructor: def __new__(cls): - raise TypeError("No constructor defined") + raise TypeError("No constructor defined for ClassWithoutConstructor") @pytest.mark.parametrize( - "cls", [pyclasses.ClassWithoutConstructor, ClassWithoutConstructorPy] + "cls", [pyclasses.ClassWithoutConstructor, ClassWithoutConstructor] ) def test_no_constructor_defined_propagates_cause(cls: Type): original_error = ValueError("Original message") @@ -79,10 +79,12 @@ def test_no_constructor_defined_propagates_cause(cls: Type): try: raise original_error except Exception: - cls() # should raise TypeError("No constructor defined") + cls() # should raise TypeError("No constructor defined for ...") assert exc_info.type is TypeError - assert exc_info.value.args == ("No constructor defined",) + assert exc_info.value.args == ( + "No constructor defined for ClassWithoutConstructor", + ) assert exc_info.value.__context__ is original_error diff --git a/src/pyclass/create_type_object.rs b/src/pyclass/create_type_object.rs index 04d911e2e96..c365b70fb9f 100644 --- a/src/pyclass/create_type_object.rs +++ b/src/pyclass/create_type_object.rs @@ -524,14 +524,19 @@ fn bpo_45315_workaround(py: Python<'_>, class_name: CString) { /// Default new implementation unsafe extern "C" fn no_constructor_defined( - _subtype: *mut ffi::PyTypeObject, + subtype: *mut ffi::PyTypeObject, _args: *mut ffi::PyObject, _kwds: *mut ffi::PyObject, ) -> *mut ffi::PyObject { - trampoline(|_| { - Err(crate::exceptions::PyTypeError::new_err( - "No constructor defined", - )) + trampoline(|py| { + let tpobj = PyType::from_borrowed_type_ptr(py, subtype); + let name = tpobj + .name() + .map_or_else(|_| "".into(), |name| name.to_string()); + Err(crate::exceptions::PyTypeError::new_err(format!( + "No constructor defined for {}", + name + ))) }) }