Skip to content

Commit 8d6e117

Browse files
authored
Throw earlier PyErr instead of throwing cast_error (#1137)
1 parent 96e1fd4 commit 8d6e117

File tree

7 files changed

+34
-20
lines changed

7 files changed

+34
-20
lines changed

docs/api_core.rst

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1610,7 +1610,10 @@ Casting
16101610
When the `convert` argument is set to ``true`` (the default), the
16111611
implementation may also attempt *implicit conversions* to perform the cast.
16121612

1613-
The function raises a :cpp:type:`cast_error` when the conversion fails.
1613+
The function raises an exception when the conversion fails.
1614+
If the type caster uses the CPython error API (e.g., ``PyErr_SetString()``
1615+
or ``PyErr_Format()``) to report a failure, then :cpp:class:`python_error`
1616+
is raised. Otherwise, :cpp:type:`cast_error` is raised.
16141617
See :cpp:func:`try_cast()` for an alternative that never raises.
16151618

16161619
.. cpp:function:: template <typename T, typename Derived> bool try_cast(const detail::api<Derived> &value, T &out, bool convert = true) noexcept
@@ -1632,7 +1635,10 @@ Casting
16321635
policy `policy` is used to handle ownership-related questions when a new
16331636
Python object must be created.
16341637

1635-
The function raises a :cpp:type:`cast_error` when the conversion fails.
1638+
The function raises an exception when the conversion fails.
1639+
If the type caster uses the CPython error API (e.g., ``PyErr_SetString()``
1640+
or ``PyErr_Format()``) to report a failure, then :cpp:class:`python_error`
1641+
is raised. Otherwise, :cpp:type:`cast_error` is raised.
16361642

16371643
.. cpp:function:: template <typename T> object cast(T &&value, rv_policy policy, handle parent)
16381644

@@ -1641,7 +1647,10 @@ Casting
16411647
Python object must be created. A valid `parent` object is required when
16421648
specifying a `reference_internal` return value policy.
16431649

1644-
The function raises a :cpp:type:`cast_error` when the conversion fails.
1650+
The function raises an exception when the conversion fails.
1651+
If the type caster uses the CPython error API (e.g., ``PyErr_SetString()``
1652+
or ``PyErr_Format()``) to report a failure, then :cpp:class:`python_error`
1653+
is raised. Otherwise, :cpp:type:`cast_error` is raised.
16451654

16461655
.. cpp:function:: template <typename T> object find(const T &value) noexcept
16471656

@@ -1655,7 +1664,10 @@ Casting
16551664
value policy `policy` is used to handle ownership-related questions when a
16561665
new Python objects must be created.
16571666

1658-
The function raises a :cpp:type:`cast_error` when the conversion fails.
1667+
The function raises an exception when the conversion fails.
1668+
If the type caster uses the CPython error API (e.g., ``PyErr_SetString()``
1669+
or ``PyErr_Format()``) to report a failure, then :cpp:class:`python_error`
1670+
is raised. Otherwise, :cpp:type:`cast_error` is raised.
16591671

16601672
Common binding annotations
16611673
--------------------------

docs/porting.rst

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -294,15 +294,16 @@ conditions caused by a conversion should instead be caught *within* the
294294
function body and handled as follows:
295295

296296
- ``from_python()``: return ``false``. That's it. (Failed Python to C++
297-
conversion occur all the time while dispatching calls, causing nanobind
297+
conversions occur all the time while dispatching calls, causing nanobind
298298
to simply move to the next function overload. Dedicated error reporting would
299299
add undesirable overheads). In case of a severe internal error, use the
300300
CPython warning API (e.g., ``PyErr_Warn()``) to notify the user.
301301

302302
- ``from_cpp()``: a failure here is more serious, since a return value of a
303-
successfully evaluated cannot be converted, causing the call to fail. To
304-
provide further detail, use the CPython error API (e.g., ``PyErr_Format()``)
305-
and return an invalid handle (``return nb::handle();``).
303+
successfully evaluated function cannot be converted, causing the call to
304+
fail. To provide further detail, use the CPython error API (e.g.,
305+
``PyErr_SetString()`` or ``PyErr_Format()``) and return an invalid handle
306+
(``return nb::handle();``).
306307

307308
The ``std::pair<T1, T2>`` type caster (`link
308309
<https://github.com/wjakob/nanobind/blob/master/include/nanobind/stl/pair.h>`_)

include/nanobind/nb_cast.h

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -573,12 +573,12 @@ T cast_impl(handle h) {
573573
((uint8_t) cast_flags::manual),
574574
&cleanup.list);
575575
if (!rv)
576-
detail::raise_cast_error();
576+
detail::raise_python_or_cast_error();
577577
return caster.operator cast_t<T>();
578578
} else {
579579
rv = caster.from_python(h.ptr(), (uint8_t) cast_flags::manual, nullptr);
580580
if (!rv)
581-
detail::raise_cast_error();
581+
detail::raise_python_or_cast_error();
582582
return caster.operator cast_t<T>();
583583
}
584584
}
@@ -647,7 +647,7 @@ object cast(T &&value, rv_policy policy = rv_policy::automatic_reference) {
647647
handle h = detail::make_caster<T>::from_cpp((detail::forward_t<T>) value,
648648
policy, nullptr);
649649
if (!h.is_valid())
650-
detail::raise_cast_error();
650+
detail::raise_python_or_cast_error();
651651

652652
return steal(h);
653653
}
@@ -661,7 +661,7 @@ object cast(T &&value, rv_policy policy, handle parent) {
661661
cleanup.release();
662662

663663
if (!h.is_valid())
664-
detail::raise_cast_error();
664+
detail::raise_python_or_cast_error();
665665

666666
return steal(h);
667667
}

include/nanobind/nb_class.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -415,7 +415,7 @@ namespace detail {
415415
cpp_function_def(
416416
[](handle type) {
417417
if (!type_check(type))
418-
detail::raise_cast_error();
418+
throw cast_error();
419419
return inst_alloc(type);
420420
},
421421
scope(cls), name("__new__"));

include/nanobind/nb_lib.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ NB_CORE void fail(const char *fmt, ...) noexcept;
103103
NB_CORE void raise_next_overload_if_null(void *p);
104104

105105
/// Raise nanobind::cast_error
106-
[[noreturn]] NB_CORE void raise_cast_error();
106+
[[noreturn]] NB_CORE void raise_python_or_cast_error();
107107

108108
// ========================================================================
109109

src/common.cpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,9 @@ void raise_next_overload_if_null(void *p) {
110110
throw next_overload();
111111
}
112112

113-
void raise_cast_error() {
113+
void raise_python_or_cast_error() {
114+
if (PyErr_Occurred())
115+
throw python_error();
114116
throw cast_error();
115117
}
116118

@@ -325,7 +327,7 @@ PyObject *obj_vectorcall(PyObject *base, PyObject *const *args, size_t nargsf,
325327

326328
if (!res) {
327329
if (cast_error)
328-
raise_cast_error();
330+
raise_python_or_cast_error();
329331
else if (gil_error)
330332
raise("nanobind::detail::obj_vectorcall(): PyGILState_Check() failure.");
331333
else
@@ -917,7 +919,7 @@ void property_install_static(PyObject *scope, const char *name,
917919
void tuple_check(PyObject *tuple, size_t nargs) {
918920
for (size_t i = 0; i < nargs; ++i) {
919921
if (!NB_TUPLE_GET_ITEM(tuple, i))
920-
raise_cast_error();
922+
raise_python_or_cast_error();
921923
}
922924
}
923925

src/nb_ndarray.cpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -800,9 +800,8 @@ PyObject *ndarray_export(ndarray_handle *th, int framework,
800800
o = module_::import_(pkg_name).attr("from_dlpack")(o);
801801
}
802802
} catch (const std::exception &e) {
803-
PyErr_Format(PyExc_RuntimeError,
804-
"nanobind::detail::ndarray_export(): could not "
805-
"import ndarray: %s",
803+
PyErr_Format(PyExc_TypeError,
804+
"could not export nb::ndarray: %s",
806805
e.what());
807806
return nullptr;
808807
}

0 commit comments

Comments
 (0)