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:
@@ -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);
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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):
|
||||
|
||||
|
||||
Reference in New Issue
Block a user