Fix #122777: The keyframe_insert method throws python exceptions

Just for the `pyrna_struct_keyframe_insert()` function, reduce the
reporting level of keyframe insertion failures from `RPT_ERROR` to
`RPT_WARNING`. This prevents the conversion of these reports to a Python
exception.

`CombinedKeyingResult::generate_reports()` now accepts an option
argument `report_level`, so that the caller is in control over the type
of reports it generates.

Previously only errors were converted to exceptions; warnings were
implicitly cleared and never displayed. To avoid these 'keyframe
insertion failure' reports from becoming invisible, the
`pyrna_struct_keyframe_insert()` function now sends any warnings to
stdout (unless there were errors, in which case the old
error-to-exception behaviour is still there).

Pull Request: https://projects.blender.org/blender/blender/pulls/122827
This commit is contained in:
Sybren A. Stüvel
2024-06-11 11:11:16 +02:00
parent b2a58bbdb4
commit 83311ef96d
4 changed files with 26 additions and 6 deletions

View File

@@ -17,7 +17,10 @@
#include "BLI_bit_span.hh"
#include "BLI_string_ref.hh"
#include "BLI_vector.hh"
#include "DNA_anim_types.h"
#include "DNA_windowmanager_types.h"
#include "RNA_path.hh"
#include "RNA_types.hh"
@@ -71,7 +74,7 @@ class CombinedKeyingResult {
bool has_errors() const;
void generate_reports(ReportList *reports);
void generate_reports(ReportList *reports, eReportType report_level = RPT_ERROR);
};
/**

View File

@@ -82,7 +82,7 @@ bool CombinedKeyingResult::has_errors() const
return false;
}
void CombinedKeyingResult::generate_reports(ReportList *reports)
void CombinedKeyingResult::generate_reports(ReportList *reports, const eReportType report_level)
{
if (!this->has_errors() && this->get_count(SingleKeyingResult::SUCCESS) == 0) {
BKE_reportf(
@@ -152,7 +152,7 @@ void CombinedKeyingResult::generate_reports(ReportList *reports)
}
if (errors.size() == 1) {
BKE_report(reports, RPT_ERROR, errors[0].c_str());
BKE_report(reports, report_level, errors[0].c_str());
return;
}
@@ -160,7 +160,7 @@ void CombinedKeyingResult::generate_reports(ReportList *reports)
for (const std::string &error : errors) {
error_message.append(fmt::format("\n- {}", error));
}
BKE_report(reports, RPT_ERROR, error_message.c_str());
BKE_report(reports, report_level, error_message.c_str());
}
const char *default_channel_group_for_path(const PointerRNA *animated_struct,

View File

@@ -415,16 +415,26 @@ PyObject *pyrna_struct_keyframe_insert(BPy_StructRNA *self, PyObject *args, PyOb
eInsertKeyFlags(options));
const int success_count = combined_result.get_count(SingleKeyingResult::SUCCESS);
if (success_count == 0) {
combined_result.generate_reports(&reports);
/* Ideally this would use the GUI presentation of RPT_ERROR, as the resulting pop-up has more
* vertical space than the single-line warning in the status bar. However, semantically these
* may not be errors at all, as skipping the keying of certain properties due to the 'only
* insert available' flag is not an error.
*
* Furthermore, using RPT_ERROR here would cause this function to raise a Python exception,
* rather than returning a boolean. */
combined_result.generate_reports(&reports, RPT_WARNING);
}
result = success_count != 0;
}
MEM_freeN((void *)path_full);
if (BPy_reports_to_error(&reports, PyExc_RuntimeError, true) == -1) {
if (BPy_reports_to_error(&reports, PyExc_RuntimeError, false) == -1) {
BKE_reports_free(&reports);
return nullptr;
}
BPy_reports_write_stdout(&reports, nullptr);
BKE_reports_free(&reports);
if (result) {
WM_event_add_notifier(C, NC_ANIMATION | ND_ANIMCHAN | NA_EDITED, nullptr);

View File

@@ -464,6 +464,13 @@ class InsertAvailableTest(AbstractKeyframingTest, unittest.TestCase):
for fcurve in action.fcurves:
self.assertEqual(len(fcurve.keyframe_points), 2)
def test_insert_available(self):
keyed_object = _create_animation_object()
self.assertIsNone(keyed_object.animation_data, "Precondition check: test object should not have animdata yet")
keyed_ok = keyed_object.keyframe_insert("location", options={'INSERTKEY_AVAILABLE'})
self.assertFalse(keyed_ok, "Should not key with INSERTKEY_AVAILABLE when no F-Curves are available")
class InsertNeededTest(AbstractKeyframingTest, unittest.TestCase):