This is an automated email from the ASF dual-hosted git repository.

szaszm pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/nifi-minifi-cpp.git

commit c92b0b8841bad7bfa823d1f80196fd4c432b0040
Author: Adam Debreceni <[email protected]>
AuthorDate: Mon Apr 13 20:21:41 2026 +0200

    MINIFICPP-2770 - Handle exceptions at usage side, add test
    
    Closes #2156
    
    Signed-off-by: Marton Szasz <[email protected]>
---
 extensions/python/PythonBindings.cpp               |  4 +--
 extensions/python/PythonBindings.h                 | 18 +++++-----
 .../TestExecuteScriptProcessorWithPythonScript.cpp | 30 ++++++++++++++++
 extensions/python/types/PyDataConverter.cpp        |  4 ---
 extensions/python/types/PyInputStream.cpp          |  4 +--
 extensions/python/types/PyLogger.cpp               | 20 +++--------
 extensions/python/types/PyOutputStream.cpp         |  4 +--
 extensions/python/types/PyProcessContext.cpp       | 40 ++++++----------------
 extensions/python/types/PyProcessSession.cpp       | 40 ++++++----------------
 extensions/python/types/PyProcessor.cpp            | 16 +++------
 extensions/python/types/PyRecordSetReader.cpp      |  4 +--
 extensions/python/types/PyRecordSetWriter.cpp      |  4 +--
 extensions/python/types/PyRelationship.cpp         |  8 ++---
 extensions/python/types/PySSLContextService.cpp    | 16 +++------
 extensions/python/types/PyScriptFlowFile.cpp       | 28 ++++-----------
 extensions/python/types/PyStateManager.cpp         | 16 +++------
 16 files changed, 92 insertions(+), 164 deletions(-)

diff --git a/extensions/python/PythonBindings.cpp 
b/extensions/python/PythonBindings.cpp
index 8895c4a94..ed2394ec3 100644
--- a/extensions/python/PythonBindings.cpp
+++ b/extensions/python/PythonBindings.cpp
@@ -35,8 +35,8 @@ namespace org::apache::nifi::minifi::extensions::python {
 extern "C" {
 
 static PyMethodDef minifi_native_methods[] = {  // 
NOLINT(cppcoreguidelines-avoid-c-arrays)
-    {"timePeriodStringToMilliseconds", (PyCFunction) 
timePeriodStringToMilliseconds, METH_VARARGS, nullptr},
-    {"dataSizeStringToBytes", (PyCFunction) dataSizeStringToBytes, 
METH_VARARGS, nullptr},
+    {"timePeriodStringToMilliseconds", 
safePyFunction<timePeriodStringToMilliseconds>, METH_VARARGS, nullptr},
+    {"dataSizeStringToBytes", safePyFunction<dataSizeStringToBytes>, 
METH_VARARGS, nullptr},
     {}  /* Sentinel */
 };
 
diff --git a/extensions/python/PythonBindings.h 
b/extensions/python/PythonBindings.h
index eeae3fd74..c4413c057 100644
--- a/extensions/python/PythonBindings.h
+++ b/extensions/python/PythonBindings.h
@@ -23,20 +23,22 @@
 
 #include "types/Types.h"
 
-#define PYTHON_METHOD_BEGIN \
+namespace org::apache::nifi::minifi::extensions::python {
+
+PyMODINIT_FUNC
+PyInit_minifi_native(void);
+
+template<auto Fn>
+PyObject* safePyFunction(PyObject* self, PyObject* args) {
   try {
-#define PYTHON_METHOD_END \
+    return ((PyCFunction)Fn)(self, args);
   } catch (const std::exception& e) { \
-    PyErr_Format(PyExc_RuntimeError, "C++ binding error: %s", e.what()); \
+    PyErr_Format(PyExc_RuntimeError, "C++ exception: %s", e.what()); \
     return nullptr; \
   } catch (...) { \
     PyErr_SetString(PyExc_Exception, "Unknown C++ exception"); \
     return nullptr; \
   }
-
-namespace org::apache::nifi::minifi::extensions::python {
-
-PyMODINIT_FUNC
-PyInit_minifi_native(void);
+}
 
 }  // namespace org::apache::nifi::minifi::extensions::python
diff --git 
a/extensions/python/tests/TestExecuteScriptProcessorWithPythonScript.cpp 
b/extensions/python/tests/TestExecuteScriptProcessorWithPythonScript.cpp
index f9162a7e5..472905e83 100644
--- a/extensions/python/tests/TestExecuteScriptProcessorWithPythonScript.cpp
+++ b/extensions/python/tests/TestExecuteScriptProcessorWithPythonScript.cpp
@@ -288,4 +288,34 @@ def onTrigger(context, session):
   }
 }
 
+TEST_CASE("Python: Test exception handling", "[pythonException]") {
+  minifi::test::SingleProcessorTestController 
controller{minifi::test::utils::make_processor<ExecuteScript>("ExecuteScript")};
+  const auto execute_script = controller.getProcessor();
+  LogTestController::getInstance().setTrace<ExecuteScript>();
+
+  REQUIRE(execute_script->setProperty(ExecuteScript::ScriptEngine.name, 
"python"));
+  REQUIRE(execute_script->setProperty(ExecuteScript::ScriptBody.name, R"(
+class ErroringWriteCallback(object):
+    def process(self, output_stream):
+        return -1
+
+
+def onTrigger(context, session):
+    flow_file = session.create()
+    try:
+        session.write(flow_file, ErroringWriteCallback())
+        log.error(f'Got no exceptions from c++')
+    except Exception as e:
+        log.error(f'Got exception from c++: {e}')
+    except:
+        log.error(f'Got unknown exception from c++')
+    finally:
+        session.remove(flow_file)
+
+  )"));
+
+  auto result = controller.trigger();
+  REQUIRE(LogTestController::getInstance().contains("Got exception from c++: 
C++ exception: File Operation: Failed to process flowfile content"));
+}
+
 }  // namespace org::apache::nifi::minifi::processors::test
diff --git a/extensions/python/types/PyDataConverter.cpp 
b/extensions/python/types/PyDataConverter.cpp
index 90d6ef3b6..a97902c5d 100644
--- a/extensions/python/types/PyDataConverter.cpp
+++ b/extensions/python/types/PyDataConverter.cpp
@@ -23,7 +23,6 @@
 namespace org::apache::nifi::minifi::extensions::python {
 
 PyObject* timePeriodStringToMilliseconds(PyObject* /*self*/, PyObject* args) {
-  PYTHON_METHOD_BEGIN
   const char* time_period_str = nullptr;
   if (!PyArg_ParseTuple(args, "s", &time_period_str)) {
     return nullptr;
@@ -32,11 +31,9 @@ PyObject* timePeriodStringToMilliseconds(PyObject* /*self*/, 
PyObject* args) {
   auto milliseconds = 
core::TimePeriodValue(std::string(time_period_str)).getMilliseconds().count();
 
   return object::returnReference(milliseconds);
-  PYTHON_METHOD_END
 }
 
 PyObject* dataSizeStringToBytes(PyObject* /*self*/, PyObject* args) {
-  PYTHON_METHOD_BEGIN
   const char* data_size_str = nullptr;
   if (!PyArg_ParseTuple(args, "s", &data_size_str)) {
     return nullptr;
@@ -45,7 +42,6 @@ PyObject* dataSizeStringToBytes(PyObject* /*self*/, PyObject* 
args) {
   uint64_t bytes = core::DataSizeValue(std::string(data_size_str)).getValue();
 
   return object::returnReference(bytes);
-  PYTHON_METHOD_END
 }
 
 }  // namespace org::apache::nifi::minifi::extensions::python
diff --git a/extensions/python/types/PyInputStream.cpp 
b/extensions/python/types/PyInputStream.cpp
index 6a5b6649b..c76eae3be 100644
--- a/extensions/python/types/PyInputStream.cpp
+++ b/extensions/python/types/PyInputStream.cpp
@@ -26,7 +26,7 @@ extern "C" {
 namespace org::apache::nifi::minifi::extensions::python {
 
 static PyMethodDef PyInputStream_methods[] = {  // 
NOLINT(cppcoreguidelines-avoid-c-arrays)
-    {"read", (PyCFunction) PyInputStream::read, METH_VARARGS, nullptr},
+    {"read", safePyFunction<PyInputStream::read>, METH_VARARGS, nullptr},
     {}  /* Sentinel */
 };
 
@@ -60,7 +60,6 @@ int PyInputStream::init(PyInputStream* self, PyObject* args, 
PyObject*) {
 }
 
 PyObject* PyInputStream::read(PyInputStream* self, PyObject* args) {
-  PYTHON_METHOD_BEGIN
   auto input_stream = self->input_stream_.lock();
   if (!input_stream) {
     PyErr_SetString(PyExc_AttributeError, "tried reading FlowFile outside 
'on_trigger'");
@@ -80,7 +79,6 @@ PyObject* PyInputStream::read(PyInputStream* self, PyObject* 
args) {
 
   const auto read = input_stream->read(buffer);
   return 
object::returnReference(OwnedBytes::fromStringAndSize(std::string_view(reinterpret_cast<const
 char*>(buffer.data()), read)));
-  PYTHON_METHOD_END
 }
 
 PyTypeObject* PyInputStream::typeObject() {
diff --git a/extensions/python/types/PyLogger.cpp 
b/extensions/python/types/PyLogger.cpp
index 2525eeb97..af149915d 100644
--- a/extensions/python/types/PyLogger.cpp
+++ b/extensions/python/types/PyLogger.cpp
@@ -22,11 +22,11 @@ extern "C" {
 namespace org::apache::nifi::minifi::extensions::python {
 
 static PyMethodDef PyLogger_methods[] = {  // 
NOLINT(cppcoreguidelines-avoid-c-arrays)
-    {"error", (PyCFunction) PyLogger::error, METH_VARARGS, nullptr},
-    {"warn", (PyCFunction) PyLogger::warn, METH_VARARGS, nullptr},
-    {"info", (PyCFunction) PyLogger::info, METH_VARARGS, nullptr},
-    {"debug", (PyCFunction) PyLogger::debug, METH_VARARGS, nullptr},
-    {"trace", (PyCFunction) PyLogger::trace, METH_VARARGS, nullptr},
+    {"error", safePyFunction<PyLogger::error>, METH_VARARGS, nullptr},
+    {"warn", safePyFunction<PyLogger::warn>, METH_VARARGS, nullptr},
+    {"info", safePyFunction<PyLogger::info>, METH_VARARGS, nullptr},
+    {"debug", safePyFunction<PyLogger::debug>, METH_VARARGS, nullptr},
+    {"trace", safePyFunction<PyLogger::trace>, METH_VARARGS, nullptr},
     {}  /* Sentinel */
 };
 
@@ -60,7 +60,6 @@ int PyLogger::init(PyLogger* self, PyObject* args, PyObject*) 
{
 }
 
 PyObject* PyLogger::error(PyLogger* self, PyObject* args) {
-  PYTHON_METHOD_BEGIN
   auto logger = self->logger_.lock();
   if (logger == nullptr) {
     PyErr_SetString(PyExc_AttributeError, "internal 'logger' instance is 
null");
@@ -73,11 +72,9 @@ PyObject* PyLogger::error(PyLogger* self, PyObject* args) {
   }
   logger->log_error("{}", message);
   Py_RETURN_NONE;
-  PYTHON_METHOD_END
 }
 
 PyObject* PyLogger::warn(PyLogger* self, PyObject* args) {
-  PYTHON_METHOD_BEGIN
   auto logger = self->logger_.lock();
   if (logger == nullptr) {
     PyErr_SetString(PyExc_AttributeError, "internal 'logger' instance is 
null");
@@ -90,11 +87,9 @@ PyObject* PyLogger::warn(PyLogger* self, PyObject* args) {
   }
   logger->log_warn("{}", message);
   Py_RETURN_NONE;
-  PYTHON_METHOD_END
 }
 
 PyObject* PyLogger::info(PyLogger* self, PyObject* args) {
-  PYTHON_METHOD_BEGIN
   auto logger = self->logger_.lock();
   if (logger == nullptr) {
     PyErr_SetString(PyExc_AttributeError, "internal 'logger' instance is 
null");
@@ -107,11 +102,9 @@ PyObject* PyLogger::info(PyLogger* self, PyObject* args) {
   }
   logger->log_info("{}", message);
   Py_RETURN_NONE;
-  PYTHON_METHOD_END
 }
 
 PyObject* PyLogger::debug(PyLogger* self, PyObject* args) {
-  PYTHON_METHOD_BEGIN
   auto logger = self->logger_.lock();
   if (logger == nullptr) {
     PyErr_SetString(PyExc_AttributeError, "internal 'logger' instance is 
null");
@@ -124,11 +117,9 @@ PyObject* PyLogger::debug(PyLogger* self, PyObject* args) {
   }
   logger->log_debug("{}", message);
   Py_RETURN_NONE;
-  PYTHON_METHOD_END
 }
 
 PyObject* PyLogger::trace(PyLogger* self, PyObject* args) {
-  PYTHON_METHOD_BEGIN
   auto logger = self->logger_.lock();
   if (logger == nullptr) {
     PyErr_SetString(PyExc_AttributeError, "internal 'logger' instance is 
null");
@@ -141,7 +132,6 @@ PyObject* PyLogger::trace(PyLogger* self, PyObject* args) {
   }
   logger->log_trace("{}", message);
   Py_RETURN_NONE;
-  PYTHON_METHOD_END
 }
 
 PyTypeObject* PyLogger::typeObject() {
diff --git a/extensions/python/types/PyOutputStream.cpp 
b/extensions/python/types/PyOutputStream.cpp
index 521d918c7..5e845496c 100644
--- a/extensions/python/types/PyOutputStream.cpp
+++ b/extensions/python/types/PyOutputStream.cpp
@@ -22,7 +22,7 @@ extern "C" {
 namespace org::apache::nifi::minifi::extensions::python {
 
 static PyMethodDef PyOutputStream_methods[] = {  // 
NOLINT(cppcoreguidelines-avoid-c-arrays)
-    {"write", (PyCFunction) PyOutputStream::write, METH_VARARGS, nullptr},
+    {"write", safePyFunction<PyOutputStream::write>, METH_VARARGS, nullptr},
     {}  /* Sentinel */
 };
 
@@ -56,7 +56,6 @@ int PyOutputStream::init(PyOutputStream* self, PyObject* 
args, PyObject*) {
 }
 
 PyObject* PyOutputStream::write(PyOutputStream* self, PyObject* args) {
-  PYTHON_METHOD_BEGIN
   auto output_stream = self->output_stream_.lock();
   if (!output_stream) {
     PyErr_SetString(PyExc_AttributeError, "tried reading FlowFile outside 
'on_trigger'");
@@ -74,7 +73,6 @@ PyObject* PyOutputStream::write(PyOutputStream* self, 
PyObject* args) {
     return nullptr;
   }
   return object::returnReference(output_stream->write(gsl::make_span(buffer, 
length).as_span<const std::byte>()));
-  PYTHON_METHOD_END
 }
 
 PyTypeObject* PyOutputStream::typeObject() {
diff --git a/extensions/python/types/PyProcessContext.cpp 
b/extensions/python/types/PyProcessContext.cpp
index bbebe2e4c..2ccef8ba5 100644
--- a/extensions/python/types/PyProcessContext.cpp
+++ b/extensions/python/types/PyProcessContext.cpp
@@ -29,16 +29,16 @@ extern "C" {
 namespace org::apache::nifi::minifi::extensions::python {
 
 static PyMethodDef PyProcessContext_methods[] = {  // 
NOLINT(cppcoreguidelines-avoid-c-arrays)
-    {"getProperty", (PyCFunction) PyProcessContext::getProperty, METH_VARARGS, 
nullptr},
-    {"getRawProperty", (PyCFunction) PyProcessContext::getRawProperty, 
METH_VARARGS, nullptr},
-    {"getDynamicProperty", (PyCFunction) PyProcessContext::getDynamicProperty, 
METH_VARARGS, nullptr},
-    {"getRawDynamicProperty", (PyCFunction) 
PyProcessContext::getRawDynamicProperty, METH_VARARGS, nullptr},
-    {"getDynamicPropertyKeys", (PyCFunction) 
PyProcessContext::getDynamicPropertyKeys, METH_VARARGS, nullptr},
-    {"getStateManager", (PyCFunction) PyProcessContext::getStateManager, 
METH_VARARGS, nullptr},
-    {"getControllerService", (PyCFunction) 
PyProcessContext::getControllerService, METH_VARARGS, nullptr},
-    {"getName", (PyCFunction) PyProcessContext::getName, METH_VARARGS, 
nullptr},
-    {"getProperties", (PyCFunction) PyProcessContext::getProperties, 
METH_VARARGS, nullptr},
-    {"yieldResources", (PyCFunction) PyProcessContext::yieldResources, 
METH_VARARGS, nullptr},
+    {"getProperty", safePyFunction<PyProcessContext::getProperty>, 
METH_VARARGS, nullptr},
+    {"getRawProperty", safePyFunction<PyProcessContext::getRawProperty>, 
METH_VARARGS, nullptr},
+    {"getDynamicProperty", 
safePyFunction<PyProcessContext::getDynamicProperty>, METH_VARARGS, nullptr},
+    {"getRawDynamicProperty", 
safePyFunction<PyProcessContext::getRawDynamicProperty>, METH_VARARGS, nullptr},
+    {"getDynamicPropertyKeys", 
safePyFunction<PyProcessContext::getDynamicPropertyKeys>, METH_VARARGS, 
nullptr},
+    {"getStateManager", safePyFunction<PyProcessContext::getStateManager>, 
METH_VARARGS, nullptr},
+    {"getControllerService", 
safePyFunction<PyProcessContext::getControllerService>, METH_VARARGS, nullptr},
+    {"getName", safePyFunction<PyProcessContext::getName>, METH_VARARGS, 
nullptr},
+    {"getProperties", safePyFunction<PyProcessContext::getProperties>, 
METH_VARARGS, nullptr},
+    {"yieldResources", safePyFunction<PyProcessContext::yieldResources>, 
METH_VARARGS, nullptr},
     {}  /* Sentinel */
 };
 
@@ -72,7 +72,6 @@ int PyProcessContext::init(PyProcessContext* self, PyObject* 
args, PyObject*) {
 }
 
 PyObject* PyProcessContext::getProperty(PyProcessContext* self, PyObject* 
args) {
-  PYTHON_METHOD_BEGIN
   auto context = self->process_context_;
   if (!context) {
     PyErr_SetString(PyExc_AttributeError, "tried reading process context 
outside 'on_trigger'");
@@ -103,11 +102,9 @@ PyObject* PyProcessContext::getProperty(PyProcessContext* 
self, PyObject* args)
     return object::returnReference(*property_value);
   }
   Py_RETURN_NONE;
-  PYTHON_METHOD_END
 }
 
 PyObject* PyProcessContext::getRawProperty(PyProcessContext* self, PyObject* 
args) {
-  PYTHON_METHOD_BEGIN
   auto context = self->process_context_;
   if (!context) {
     PyErr_SetString(PyExc_AttributeError, "tried reading process context 
outside 'on_trigger'");
@@ -124,11 +121,9 @@ PyObject* 
PyProcessContext::getRawProperty(PyProcessContext* self, PyObject* arg
     return object::returnReference(*property_value);
   }
   Py_RETURN_NONE;
-  PYTHON_METHOD_END
 }
 
 PyObject* PyProcessContext::getDynamicProperty(PyProcessContext* self, 
PyObject* args) {
-  PYTHON_METHOD_BEGIN
   auto context = self->process_context_;
   if (!context) {
     PyErr_SetString(PyExc_AttributeError, "tried reading process context 
outside 'on_trigger'");
@@ -158,11 +153,9 @@ PyObject* 
PyProcessContext::getDynamicProperty(PyProcessContext* self, PyObject*
     return object::returnReference(*property_value);
   }
   Py_RETURN_NONE;
-  PYTHON_METHOD_END
 }
 
 PyObject* PyProcessContext::getRawDynamicProperty(PyProcessContext* self, 
PyObject* args) {
-  PYTHON_METHOD_BEGIN
   auto context = self->process_context_;
   if (!context) {
     PyErr_SetString(PyExc_AttributeError, "tried reading process context 
outside 'on_trigger'");
@@ -179,11 +172,9 @@ PyObject* 
PyProcessContext::getRawDynamicProperty(PyProcessContext* self, PyObje
     return object::returnReference(*property_value);
   }
   Py_RETURN_NONE;
-  PYTHON_METHOD_END
 }
 
 PyObject* PyProcessContext::getDynamicPropertyKeys(PyProcessContext* self, 
PyObject*) {
-  PYTHON_METHOD_BEGIN
   auto context = self->process_context_;
   if (!context) {
     PyErr_SetString(PyExc_AttributeError, "tried reading process context 
outside 'on_trigger'");
@@ -197,11 +188,9 @@ PyObject* 
PyProcessContext::getDynamicPropertyKeys(PyProcessContext* self, PyObj
   }
 
   return object::returnReference(py_properties);
-  PYTHON_METHOD_END
 }
 
 PyObject* PyProcessContext::getStateManager(PyProcessContext* self, PyObject*) 
{
-  PYTHON_METHOD_BEGIN
   auto context = self->process_context_;
   if (!context) {
     PyErr_SetString(PyExc_AttributeError, "tried reading process context 
outside 'on_trigger'");
@@ -209,11 +198,9 @@ PyObject* 
PyProcessContext::getStateManager(PyProcessContext* self, PyObject*) {
   }
 
   return object::returnReference(context->getStateManager());
-  PYTHON_METHOD_END
 }
 
 PyObject* PyProcessContext::getControllerService(PyProcessContext* self, 
PyObject* args) {
-  PYTHON_METHOD_BEGIN
   auto context = self->process_context_;
   if (!context) {
     PyErr_SetString(PyExc_AttributeError, "tried reading process context 
outside 'on_trigger'");
@@ -241,11 +228,9 @@ PyObject* 
PyProcessContext::getControllerService(PyProcessContext* self, PyObjec
   }
 
   Py_RETURN_NONE;
-  PYTHON_METHOD_END
 }
 
 PyObject* PyProcessContext::getName(PyProcessContext* self, PyObject*) {
-  PYTHON_METHOD_BEGIN
   auto context = self->process_context_;
   if (!context) {
     PyErr_SetString(PyExc_AttributeError, "tried reading process context 
outside 'on_trigger'");
@@ -253,11 +238,9 @@ PyObject* PyProcessContext::getName(PyProcessContext* 
self, PyObject*) {
   }
 
   return object::returnReference(context->getProcessorInfo().getName());
-  PYTHON_METHOD_END
 }
 
 PyObject* PyProcessContext::getProperties(PyProcessContext* self, PyObject*) {
-  PYTHON_METHOD_BEGIN
   auto context = self->process_context_;
   if (!context) {
     PyErr_SetString(PyExc_AttributeError, "tried reading process context 
outside 'on_trigger'");
@@ -273,11 +256,9 @@ PyObject* 
PyProcessContext::getProperties(PyProcessContext* self, PyObject*) {
   }
 
   return object::returnReference(py_properties);
-  PYTHON_METHOD_END
 }
 
 PyObject* PyProcessContext::yieldResources(PyProcessContext* self, PyObject*) {
-  PYTHON_METHOD_BEGIN
   auto context = self->process_context_;
   if (!context) {
     PyErr_SetString(PyExc_AttributeError, "tried reading process context 
outside 'on_trigger'");
@@ -287,7 +268,6 @@ PyObject* 
PyProcessContext::yieldResources(PyProcessContext* self, PyObject*) {
   context->yield();
 
   Py_RETURN_NONE;
-  PYTHON_METHOD_END
 }
 
 PyTypeObject* PyProcessContext::typeObject() {
diff --git a/extensions/python/types/PyProcessSession.cpp 
b/extensions/python/types/PyProcessSession.cpp
index b0170cafb..9391c93ff 100644
--- a/extensions/python/types/PyProcessSession.cpp
+++ b/extensions/python/types/PyProcessSession.cpp
@@ -125,16 +125,16 @@ void PyProcessSession::putAttribute(const 
std::shared_ptr<core::FlowFile>& flow_
 extern "C" {
 
 static PyMethodDef PyProcessSessionObject_methods[] = {  // 
NOLINT(cppcoreguidelines-avoid-c-arrays)
-    {"get", (PyCFunction) PyProcessSessionObject::get, METH_NOARGS, nullptr},
-    {"create", (PyCFunction) PyProcessSessionObject::create, METH_VARARGS, 
nullptr},
-    {"clone", (PyCFunction) PyProcessSessionObject::clone, METH_VARARGS, 
nullptr},
-    {"read", (PyCFunction) PyProcessSessionObject::read, METH_VARARGS, 
nullptr},
-    {"write", (PyCFunction) PyProcessSessionObject::write, METH_VARARGS, 
nullptr},
-    {"transfer", (PyCFunction) PyProcessSessionObject::transfer, METH_VARARGS, 
nullptr},
-    {"transferToCustomRelationship", (PyCFunction) 
PyProcessSessionObject::transferToCustomRelationship, METH_VARARGS, nullptr},
-    {"remove", (PyCFunction) PyProcessSessionObject::remove, METH_VARARGS, 
nullptr},
-    {"getContentsAsBytes", (PyCFunction) 
PyProcessSessionObject::getContentsAsBytes, METH_VARARGS, nullptr},
-    {"putAttribute", (PyCFunction) PyProcessSessionObject::putAttribute, 
METH_VARARGS, nullptr},
+    {"get", safePyFunction<PyProcessSessionObject::get>, METH_NOARGS, nullptr},
+    {"create", safePyFunction<PyProcessSessionObject::create>, METH_VARARGS, 
nullptr},
+    {"clone", safePyFunction<PyProcessSessionObject::clone>, METH_VARARGS, 
nullptr},
+    {"read", safePyFunction<PyProcessSessionObject::read>, METH_VARARGS, 
nullptr},
+    {"write", safePyFunction<PyProcessSessionObject::write>, METH_VARARGS, 
nullptr},
+    {"transfer", safePyFunction<PyProcessSessionObject::transfer>, 
METH_VARARGS, nullptr},
+    {"transferToCustomRelationship", 
safePyFunction<PyProcessSessionObject::transferToCustomRelationship>, 
METH_VARARGS, nullptr},
+    {"remove", safePyFunction<PyProcessSessionObject::remove>, METH_VARARGS, 
nullptr},
+    {"getContentsAsBytes", 
safePyFunction<PyProcessSessionObject::getContentsAsBytes>, METH_VARARGS, 
nullptr},
+    {"putAttribute", safePyFunction<PyProcessSessionObject::putAttribute>, 
METH_VARARGS, nullptr},
     {}  /* Sentinel */
 };
 
@@ -168,7 +168,6 @@ int PyProcessSessionObject::init(PyProcessSessionObject* 
self, PyObject* args, P
 }
 
 PyObject* PyProcessSessionObject::get(PyProcessSessionObject* self, PyObject*) 
{
-  PYTHON_METHOD_BEGIN
   auto session = self->process_session_.lock();
   if (!session) {
     PyErr_SetString(PyExc_AttributeError, "tried reading process session 
outside 'on_trigger'");
@@ -177,11 +176,9 @@ PyObject* 
PyProcessSessionObject::get(PyProcessSessionObject* self, PyObject*) {
   if (auto flow_file = session->get())
     return object::returnReference(std::weak_ptr(flow_file));
   return object::returnReference(nullptr);
-  PYTHON_METHOD_END
 }
 
 PyObject* PyProcessSessionObject::create(PyProcessSessionObject* self, 
PyObject* args) {
-  PYTHON_METHOD_BEGIN
   auto session = self->process_session_.lock();
   if (!session) {
     PyErr_SetString(PyExc_AttributeError, "tried reading process session 
outside 'on_trigger'");
@@ -201,11 +198,9 @@ PyObject* 
PyProcessSessionObject::create(PyProcessSessionObject* self, PyObject*
   if (auto flow_file = session->create(parent_flow_file))
     return object::returnReference(std::weak_ptr(flow_file));
   return object::returnReference(nullptr);
-  PYTHON_METHOD_END
 }
 
 PyObject* PyProcessSessionObject::clone(PyProcessSessionObject* self, 
PyObject* args) {
-  PYTHON_METHOD_BEGIN
   auto session = self->process_session_.lock();
   if (!session) {
     PyErr_SetString(PyExc_AttributeError, "tried reading process session 
outside 'on_trigger'");
@@ -220,11 +215,9 @@ PyObject* 
PyProcessSessionObject::clone(PyProcessSessionObject* self, PyObject*
   if (auto cloned_flow_file = session->clone(flow_file))
     return object::returnReference(std::weak_ptr(cloned_flow_file));
   return object::returnReference(nullptr);
-  PYTHON_METHOD_END
 }
 
 PyObject* PyProcessSessionObject::remove(PyProcessSessionObject* self, 
PyObject* args) {
-  PYTHON_METHOD_BEGIN
   auto session = self->process_session_.lock();
   if (!session) {
     PyErr_SetString(PyExc_AttributeError, "tried reading process session 
outside 'on_trigger'");
@@ -237,11 +230,9 @@ PyObject* 
PyProcessSessionObject::remove(PyProcessSessionObject* self, PyObject*
   const auto flow_file = 
reinterpret_cast<PyScriptFlowFile*>(script_flow_file)->script_flow_file_.lock();
   session->remove(flow_file);
   Py_RETURN_NONE;
-  PYTHON_METHOD_END
 }
 
 PyObject* PyProcessSessionObject::read(PyProcessSessionObject* self, PyObject* 
args) {
-  PYTHON_METHOD_BEGIN
   auto session = self->process_session_.lock();
   if (!session) {
     PyErr_SetString(PyExc_AttributeError, "tried reading process session 
outside 'on_trigger'");
@@ -261,11 +252,9 @@ PyObject* 
PyProcessSessionObject::read(PyProcessSessionObject* self, PyObject* a
   }
   session->read(flow_file, BorrowedObject(callback));
   Py_RETURN_NONE;
-  PYTHON_METHOD_END
 }
 
 PyObject* PyProcessSessionObject::write(PyProcessSessionObject* self, 
PyObject* args) {
-  PYTHON_METHOD_BEGIN
   auto session = self->process_session_.lock();
   if (!session) {
     PyErr_SetString(PyExc_AttributeError, "tried reading process session 
outside 'on_trigger'");
@@ -285,11 +274,9 @@ PyObject* 
PyProcessSessionObject::write(PyProcessSessionObject* self, PyObject*
   }
   session->write(flow_file, BorrowedObject(callback));
   Py_RETURN_NONE;
-  PYTHON_METHOD_END
 }
 
 PyObject* PyProcessSessionObject::transfer(PyProcessSessionObject* self, 
PyObject* args) {
-  PYTHON_METHOD_BEGIN
   auto session = self->process_session_.lock();
   if (!session) {
     PyErr_SetString(PyExc_AttributeError, "tried reading process session 
outside 'on_trigger'");
@@ -309,11 +296,9 @@ PyObject* 
PyProcessSessionObject::transfer(PyProcessSessionObject* self, PyObjec
   }
   session->transfer(flow_file, 
reinterpret_cast<PyRelationship*>(relationship)->relationship_);
   Py_RETURN_NONE;
-  PYTHON_METHOD_END
 }
 
 PyObject* 
PyProcessSessionObject::transferToCustomRelationship(PyProcessSessionObject* 
self, PyObject* args) {
-  PYTHON_METHOD_BEGIN
   auto session = self->process_session_.lock();
   if (!session) {
     PyErr_SetString(PyExc_AttributeError, "tried reading process session 
outside 'on_trigger'");
@@ -346,11 +331,9 @@ PyObject* 
PyProcessSessionObject::transferToCustomRelationship(PyProcessSessionO
   BorrowedStr name = BorrowedStr::fromTuple(args, 0);
   session->transferToCustomRelationship(flow_file, relationship_name_str);
   Py_RETURN_NONE;
-  PYTHON_METHOD_END
 }
 
 PyObject* PyProcessSessionObject::getContentsAsBytes(PyProcessSessionObject* 
self, PyObject* args) {
-  PYTHON_METHOD_BEGIN
   auto session = self->process_session_.lock();
   if (!session) {
     PyErr_SetString(PyExc_AttributeError, "tried reading process session 
outside 'on_trigger'");
@@ -364,11 +347,9 @@ PyObject* 
PyProcessSessionObject::getContentsAsBytes(PyProcessSessionObject* sel
   auto content = session->getContentsAsString(flow_file);
 
   return PyBytes_FromStringAndSize(content.c_str(), 
gsl::narrow<Py_ssize_t>(content.size()));
-  PYTHON_METHOD_END
 }
 
 PyObject* PyProcessSessionObject::putAttribute(PyProcessSessionObject* self, 
PyObject* args) {
-  PYTHON_METHOD_BEGIN
   auto session = self->process_session_.lock();
   if (!session) {
     PyErr_SetString(PyExc_AttributeError, "tried reading process session 
outside 'on_trigger'");
@@ -407,7 +388,6 @@ PyObject* 
PyProcessSessionObject::putAttribute(PyProcessSessionObject* self, PyO
 
   session->putAttribute(flow_file, attribute_key_str, attribute_value_str);
   Py_RETURN_NONE;
-  PYTHON_METHOD_END
 }
 
 PyTypeObject* PyProcessSessionObject::typeObject() {
diff --git a/extensions/python/types/PyProcessor.cpp 
b/extensions/python/types/PyProcessor.cpp
index 73d2d6267..a539568ae 100644
--- a/extensions/python/types/PyProcessor.cpp
+++ b/extensions/python/types/PyProcessor.cpp
@@ -42,10 +42,10 @@ bool getBoolFromTuple(PyObject* tuple, Py_ssize_t location) 
{
 extern "C" {
 
 static PyMethodDef PyProcessor_methods[] = {  // 
NOLINT(cppcoreguidelines-avoid-c-arrays)
-    {"setSupportsDynamicProperties", (PyCFunction) 
PyProcessor::setSupportsDynamicProperties, METH_VARARGS, nullptr},
-    {"setDescription", (PyCFunction) PyProcessor::setDescription, 
METH_VARARGS, nullptr},
-    {"setVersion", (PyCFunction) PyProcessor::setVersion, METH_VARARGS, 
nullptr},
-    {"addProperty", (PyCFunction) PyProcessor::addProperty, METH_VARARGS, 
nullptr},
+    {"setSupportsDynamicProperties", 
safePyFunction<PyProcessor::setSupportsDynamicProperties>, METH_VARARGS, 
nullptr},
+    {"setDescription", safePyFunction<PyProcessor::setDescription>, 
METH_VARARGS, nullptr},
+    {"setVersion", safePyFunction<PyProcessor::setVersion>, METH_VARARGS, 
nullptr},
+    {"addProperty", safePyFunction<PyProcessor::addProperty>, METH_VARARGS, 
nullptr},
     {}  /* Sentinel */
 };
 
@@ -79,7 +79,6 @@ int PyProcessor::init(PyProcessor* self, PyObject* args, 
PyObject*) {
 }
 
 PyObject* PyProcessor::setSupportsDynamicProperties(PyProcessor* self, 
PyObject*) {
-  PYTHON_METHOD_BEGIN
   auto processor = self->processor_.lock();
   if (!processor) {
     PyErr_SetString(PyExc_AttributeError, "tried reading processor outside 
'on_trigger'");
@@ -88,11 +87,9 @@ PyObject* 
PyProcessor::setSupportsDynamicProperties(PyProcessor* self, PyObject*
 
   processor->setSupportsDynamicProperties();
   Py_RETURN_NONE;
-  PYTHON_METHOD_END
 }
 
 PyObject* PyProcessor::setDescription(PyProcessor* self, PyObject* args) {
-  PYTHON_METHOD_BEGIN
   auto processor = self->processor_.lock();
   if (!processor) {
     PyErr_SetString(PyExc_AttributeError, "tried reading processor outside 
'on_trigger'");
@@ -105,11 +102,9 @@ PyObject* PyProcessor::setDescription(PyProcessor* self, 
PyObject* args) {
   }
   processor->setDescription(std::string(description));
   Py_RETURN_NONE;
-  PYTHON_METHOD_END
 }
 
 PyObject* PyProcessor::setVersion(PyProcessor* self, PyObject* args) {
-  PYTHON_METHOD_BEGIN
   auto processor = self->processor_.lock();
   if (!processor) {
     PyErr_SetString(PyExc_AttributeError, "tried reading processor outside 
'on_trigger'");
@@ -122,11 +117,9 @@ PyObject* PyProcessor::setVersion(PyProcessor* self, 
PyObject* args) {
   }
   processor->setVersion(std::string(version));
   Py_RETURN_NONE;
-  PYTHON_METHOD_END
 }
 
 PyObject* PyProcessor::addProperty(PyProcessor* self, PyObject* args) {
-  PYTHON_METHOD_BEGIN
   auto processor = self->processor_.lock();
   if (!processor) {
     PyErr_SetString(PyExc_AttributeError, "tried reading processor outside 
'on_trigger'");
@@ -197,7 +190,6 @@ PyObject* PyProcessor::addProperty(PyProcessor* self, 
PyObject* args) {
   processor->addProperty(name.toUtf8String(), description.toUtf8String(), 
default_value, is_required, supports_expression_language, sensitive,
       validator_value, allowable_values, controller_service_type_name);
   Py_RETURN_NONE;
-  PYTHON_METHOD_END
 }
 
 PyTypeObject* PyProcessor::typeObject() {
diff --git a/extensions/python/types/PyRecordSetReader.cpp 
b/extensions/python/types/PyRecordSetReader.cpp
index 6d58b9bf0..666843074 100644
--- a/extensions/python/types/PyRecordSetReader.cpp
+++ b/extensions/python/types/PyRecordSetReader.cpp
@@ -26,7 +26,7 @@ extern "C" {
 namespace org::apache::nifi::minifi::extensions::python {
 
 static PyMethodDef PyRecordSetReader_methods[] = {  // 
NOLINT(cppcoreguidelines-avoid-c-arrays)
-    {"read", (PyCFunction) PyRecordSetReader::read, METH_VARARGS, nullptr},
+    {"read", safePyFunction<PyRecordSetReader::read>, METH_VARARGS, nullptr},
     {}  /* Sentinel */
 };
 
@@ -61,7 +61,6 @@ int PyRecordSetReader::init(PyRecordSetReader* self, 
PyObject* args, PyObject*)
 }
 
 PyObject* PyRecordSetReader::read(PyRecordSetReader* self, PyObject* args) {
-  PYTHON_METHOD_BEGIN
   gsl_Expects(self && args);
   auto record_set_reader = self->record_set_reader_.lock();
   if (!record_set_reader) {
@@ -107,7 +106,6 @@ PyObject* PyRecordSetReader::read(PyRecordSetReader* self, 
PyObject* args) {
     records.append(std::string{buffer.GetString(), buffer.GetSize()});
   }
   return object::returnReference(records);
-  PYTHON_METHOD_END
 }
 
 PyTypeObject* PyRecordSetReader::typeObject() {
diff --git a/extensions/python/types/PyRecordSetWriter.cpp 
b/extensions/python/types/PyRecordSetWriter.cpp
index 6a8239892..7e70d8554 100644
--- a/extensions/python/types/PyRecordSetWriter.cpp
+++ b/extensions/python/types/PyRecordSetWriter.cpp
@@ -26,7 +26,7 @@ extern "C" {
 namespace org::apache::nifi::minifi::extensions::python {
 
 static PyMethodDef PyRecordSetWriter_methods[] = {  // 
NOLINT(cppcoreguidelines-avoid-c-arrays)
-    {"write", (PyCFunction) PyRecordSetWriter::write, METH_VARARGS, nullptr},
+    {"write", safePyFunction<PyRecordSetWriter::write>, METH_VARARGS, nullptr},
     {}  /* Sentinel */
 };
 
@@ -61,7 +61,6 @@ int PyRecordSetWriter::init(PyRecordSetWriter* self, 
PyObject* args, PyObject*)
 }
 
 PyObject* PyRecordSetWriter::write(PyRecordSetWriter* self, PyObject* args) {
-  PYTHON_METHOD_BEGIN
   gsl_Expects(self && args);
   auto record_set_writer = self->record_set_writer_.lock();
   if (!record_set_writer) {
@@ -104,7 +103,6 @@ PyObject* PyRecordSetWriter::write(PyRecordSetWriter* self, 
PyObject* args) {
 
   record_set_writer->write(record_set, flow_file, 
process_session->getSession());
   Py_RETURN_NONE;
-  PYTHON_METHOD_END
 }
 
 PyTypeObject* PyRecordSetWriter::typeObject() {
diff --git a/extensions/python/types/PyRelationship.cpp 
b/extensions/python/types/PyRelationship.cpp
index fdb23004e..cb369ba31 100644
--- a/extensions/python/types/PyRelationship.cpp
+++ b/extensions/python/types/PyRelationship.cpp
@@ -21,8 +21,8 @@ extern "C" {
 namespace org::apache::nifi::minifi::extensions::python {
 
 static PyMethodDef PyRelationship_methods[] = {  // 
NOLINT(cppcoreguidelines-avoid-c-arrays)
-    {"getName", (PyCFunction) PyRelationship::getName, METH_VARARGS, nullptr},
-    {"getDescription", (PyCFunction) PyRelationship::getDescription, 
METH_VARARGS, nullptr},
+    {"getName", safePyFunction<PyRelationship::getName>, METH_VARARGS, 
nullptr},
+    {"getDescription", safePyFunction<PyRelationship::getDescription>, 
METH_VARARGS, nullptr},
     {}  /* Sentinel */
 };
 
@@ -56,15 +56,11 @@ int PyRelationship::init(PyRelationship* self, PyObject* 
args, PyObject*) {
 }
 
 PyObject* PyRelationship::getName(PyRelationship* self, PyObject*) {
-  PYTHON_METHOD_BEGIN
   return object::returnReference(self->relationship_.getName());
-  PYTHON_METHOD_END
 }
 
 PyObject* PyRelationship::getDescription(PyRelationship* self, PyObject*) {
-  PYTHON_METHOD_BEGIN
   return object::returnReference(self->relationship_.getDescription());
-  PYTHON_METHOD_END
 }
 
 PyTypeObject* PyRelationship::typeObject() {
diff --git a/extensions/python/types/PySSLContextService.cpp 
b/extensions/python/types/PySSLContextService.cpp
index faf9a2774..5fec7afdf 100644
--- a/extensions/python/types/PySSLContextService.cpp
+++ b/extensions/python/types/PySSLContextService.cpp
@@ -21,10 +21,10 @@ extern "C" {
 namespace org::apache::nifi::minifi::extensions::python {
 
 static PyMethodDef PySSLContextService_methods[] = {  // 
NOLINT(cppcoreguidelines-avoid-c-arrays)
-    {"getCertificateFile", (PyCFunction) 
PySSLContextService::getCertificateFile, METH_VARARGS, nullptr},
-    {"getPassphrase", (PyCFunction) PySSLContextService::getPassphrase, 
METH_VARARGS, nullptr},
-    {"getPrivateKeyFile", (PyCFunction) 
PySSLContextService::getPrivateKeyFile, METH_VARARGS, nullptr},
-    {"getCACertificate", (PyCFunction) PySSLContextService::getCACertificate, 
METH_VARARGS, nullptr},
+    {"getCertificateFile", 
safePyFunction<PySSLContextService::getCertificateFile>, METH_VARARGS, nullptr},
+    {"getPassphrase", safePyFunction<PySSLContextService::getPassphrase>, 
METH_VARARGS, nullptr},
+    {"getPrivateKeyFile", 
safePyFunction<PySSLContextService::getPrivateKeyFile>, METH_VARARGS, nullptr},
+    {"getCACertificate", 
safePyFunction<PySSLContextService::getCACertificate>, METH_VARARGS, nullptr},
     {}  /* Sentinel */
 };
 
@@ -58,47 +58,39 @@ int PySSLContextService::init(PySSLContextService* self, 
PyObject* args, PyObjec
 }
 
 PyObject* PySSLContextService::getCertificateFile(PySSLContextService* self, 
PyObject* /*args*/) {
-  PYTHON_METHOD_BEGIN
   auto ssl_context_service = self->ssl_context_service_.lock();
   if (!ssl_context_service) {
     PyErr_SetString(PyExc_AttributeError, "tried reading ssl context service 
outside 'on_trigger'");
     return nullptr;
   }
   return 
object::returnReference(ssl_context_service->getCertificateFile().string());
-  PYTHON_METHOD_END
 }
 
 PyObject* PySSLContextService::getPassphrase(PySSLContextService* self, 
PyObject* /*args*/) {
-  PYTHON_METHOD_BEGIN
   auto ssl_context_service = self->ssl_context_service_.lock();
   if (!ssl_context_service) {
     PyErr_SetString(PyExc_AttributeError, "tried reading ssl context service 
outside 'on_trigger'");
     return nullptr;
   }
   return object::returnReference(ssl_context_service->getPassphrase());
-  PYTHON_METHOD_END
 }
 
 PyObject* PySSLContextService::getPrivateKeyFile(PySSLContextService* self, 
PyObject* /*args*/) {
-  PYTHON_METHOD_BEGIN
   auto ssl_context_service = self->ssl_context_service_.lock();
   if (!ssl_context_service) {
     PyErr_SetString(PyExc_AttributeError, "tried reading ssl context service 
outside 'on_trigger'");
     return nullptr;
   }
   return 
object::returnReference(ssl_context_service->getPrivateKeyFile().string());
-  PYTHON_METHOD_END
 }
 
 PyObject* PySSLContextService::getCACertificate(PySSLContextService* self, 
PyObject* /*args*/) {
-  PYTHON_METHOD_BEGIN
   auto ssl_context_service = self->ssl_context_service_.lock();
   if (!ssl_context_service) {
     PyErr_SetString(PyExc_AttributeError, "tried reading ssl context service 
outside 'on_trigger'");
     return nullptr;
   }
   return 
object::returnReference(ssl_context_service->getCACertificate().string());
-  PYTHON_METHOD_END
 }
 
 PyTypeObject* PySSLContextService::typeObject() {
diff --git a/extensions/python/types/PyScriptFlowFile.cpp 
b/extensions/python/types/PyScriptFlowFile.cpp
index c22399f2d..b38a999fc 100644
--- a/extensions/python/types/PyScriptFlowFile.cpp
+++ b/extensions/python/types/PyScriptFlowFile.cpp
@@ -22,13 +22,13 @@ extern "C" {
 namespace org::apache::nifi::minifi::extensions::python {
 
 static PyMethodDef PyScriptFlowFile_methods[] = {  // 
NOLINT(cppcoreguidelines-avoid-c-arrays)
-    {"getAttribute", (PyCFunction) PyScriptFlowFile::getAttribute, 
METH_VARARGS, nullptr},
-    {"addAttribute", (PyCFunction) PyScriptFlowFile::addAttribute, 
METH_VARARGS, nullptr},
-    {"updateAttribute", (PyCFunction) PyScriptFlowFile::updateAttribute, 
METH_VARARGS, nullptr},
-    {"removeAttribute", (PyCFunction) PyScriptFlowFile::removeAttribute, 
METH_VARARGS, nullptr},
-    {"setAttribute", (PyCFunction) PyScriptFlowFile::setAttribute, 
METH_VARARGS, nullptr},
-    {"getSize", (PyCFunction) PyScriptFlowFile::getSize, METH_VARARGS, 
nullptr},
-    {"getAttributes", (PyCFunction) PyScriptFlowFile::getAttributes, 
METH_VARARGS, nullptr},
+    {"getAttribute", safePyFunction<PyScriptFlowFile::getAttribute>, 
METH_VARARGS, nullptr},
+    {"addAttribute", safePyFunction<PyScriptFlowFile::addAttribute>, 
METH_VARARGS, nullptr},
+    {"updateAttribute", safePyFunction<PyScriptFlowFile::updateAttribute>, 
METH_VARARGS, nullptr},
+    {"removeAttribute", safePyFunction<PyScriptFlowFile::removeAttribute>, 
METH_VARARGS, nullptr},
+    {"setAttribute", safePyFunction<PyScriptFlowFile::setAttribute>, 
METH_VARARGS, nullptr},
+    {"getSize", safePyFunction<PyScriptFlowFile::getSize>, METH_VARARGS, 
nullptr},
+    {"getAttributes", safePyFunction<PyScriptFlowFile::getAttributes>, 
METH_VARARGS, nullptr},
     {}  /* Sentinel */
 };
 
@@ -63,7 +63,6 @@ int PyScriptFlowFile::init(PyScriptFlowFile* self, PyObject* 
args, PyObject*) {
 }
 
 PyObject* PyScriptFlowFile::getAttribute(PyScriptFlowFile* self, PyObject* 
args) {
-  PYTHON_METHOD_BEGIN
   auto flow_file = self->script_flow_file_.lock();
   if (!flow_file) {
     PyErr_SetString(PyExc_AttributeError, "tried reading FlowFile outside 
'on_trigger'");
@@ -75,11 +74,9 @@ PyObject* PyScriptFlowFile::getAttribute(PyScriptFlowFile* 
self, PyObject* args)
     return nullptr;
   }
   return 
object::returnReference(flow_file->getAttribute(attribute).value_or(""));
-  PYTHON_METHOD_END
 }
 
 PyObject* PyScriptFlowFile::addAttribute(PyScriptFlowFile* self, PyObject* 
args) {
-  PYTHON_METHOD_BEGIN
   auto flow_file = self->script_flow_file_.lock();
   if (!flow_file) {
     PyErr_SetString(PyExc_AttributeError, "tried reading FlowFile outside 
'on_trigger'");
@@ -93,11 +90,9 @@ PyObject* PyScriptFlowFile::addAttribute(PyScriptFlowFile* 
self, PyObject* args)
   }
 
   return object::returnReference(flow_file->addAttribute(key, 
std::string(value)));
-  PYTHON_METHOD_END
 }
 
 PyObject* PyScriptFlowFile::updateAttribute(PyScriptFlowFile* self, PyObject* 
args) {
-  PYTHON_METHOD_BEGIN
   auto flow_file = self->script_flow_file_.lock();
   if (!flow_file) {
     PyErr_SetString(PyExc_AttributeError, "tried reading FlowFile outside 
'on_trigger'");
@@ -111,11 +106,9 @@ PyObject* 
PyScriptFlowFile::updateAttribute(PyScriptFlowFile* self, PyObject* ar
   }
 
   return object::returnReference(flow_file->updateAttribute(key, 
std::string(value)));
-  PYTHON_METHOD_END
 }
 
 PyObject* PyScriptFlowFile::removeAttribute(PyScriptFlowFile* self, PyObject* 
args) {
-  PYTHON_METHOD_BEGIN
   auto flow_file = self->script_flow_file_.lock();
   if (!flow_file) {
     PyErr_SetString(PyExc_AttributeError, "tried reading FlowFile outside 
'on_trigger'");
@@ -127,11 +120,9 @@ PyObject* 
PyScriptFlowFile::removeAttribute(PyScriptFlowFile* self, PyObject* ar
     return nullptr;
   }
   return object::returnReference(flow_file->removeAttribute(attribute));
-  PYTHON_METHOD_END
 }
 
 PyObject* PyScriptFlowFile::setAttribute(PyScriptFlowFile* self, PyObject* 
args) {
-  PYTHON_METHOD_BEGIN
   auto flow_file = self->script_flow_file_.lock();
   if (!flow_file) {
     PyErr_SetString(PyExc_AttributeError, "tried reading FlowFile outside 
'on_trigger'");
@@ -146,11 +137,9 @@ PyObject* PyScriptFlowFile::setAttribute(PyScriptFlowFile* 
self, PyObject* args)
 
   flow_file->setAttribute(key, value);
   Py_RETURN_NONE;
-  PYTHON_METHOD_END
 }
 
 PyObject* PyScriptFlowFile::getSize(PyScriptFlowFile* self, PyObject* 
/*args*/) {
-  PYTHON_METHOD_BEGIN
   auto flow_file = self->script_flow_file_.lock();
   if (!flow_file) {
     PyErr_SetString(PyExc_AttributeError, "tried reading FlowFile outside 
'on_trigger'");
@@ -158,11 +147,9 @@ PyObject* PyScriptFlowFile::getSize(PyScriptFlowFile* 
self, PyObject* /*args*/)
   }
 
   return object::returnReference(flow_file->getSize());
-  PYTHON_METHOD_END
 }
 
 PyObject* PyScriptFlowFile::getAttributes(PyScriptFlowFile* self, PyObject* 
/*args*/) {
-  PYTHON_METHOD_BEGIN
   auto flow_file = self->script_flow_file_.lock();
   if (!flow_file) {
     PyErr_SetString(PyExc_AttributeError, "tried reading FlowFile outside 
'on_trigger'");
@@ -175,7 +162,6 @@ PyObject* PyScriptFlowFile::getAttributes(PyScriptFlowFile* 
self, PyObject* /*ar
   }
 
   return object::returnReference(attributes);
-  PYTHON_METHOD_END
 }
 
 PyTypeObject* PyScriptFlowFile::typeObject() {
diff --git a/extensions/python/types/PyStateManager.cpp 
b/extensions/python/types/PyStateManager.cpp
index bf0f3adb4..e4d94e89d 100644
--- a/extensions/python/types/PyStateManager.cpp
+++ b/extensions/python/types/PyStateManager.cpp
@@ -21,10 +21,10 @@ extern "C" {
 namespace org::apache::nifi::minifi::extensions::python {
 
 static PyMethodDef PyStateManager_methods[] = {  // 
NOLINT(cppcoreguidelines-avoid-c-arrays)
-    {"get", (PyCFunction) PyStateManager::get, METH_VARARGS, nullptr},
-    {"set", (PyCFunction) PyStateManager::set, METH_VARARGS, nullptr},
-    {"clear", (PyCFunction) PyStateManager::clear, METH_VARARGS, nullptr},
-    {"replace", (PyCFunction) PyStateManager::replace, METH_VARARGS, nullptr},
+    {"get", safePyFunction<PyStateManager::get>, METH_VARARGS, nullptr},
+    {"set", safePyFunction<PyStateManager::set>, METH_VARARGS, nullptr},
+    {"clear", safePyFunction<PyStateManager::clear>, METH_VARARGS, nullptr},
+    {"replace", safePyFunction<PyStateManager::replace>, METH_VARARGS, 
nullptr},
     {}  /* Sentinel */
 };
 
@@ -58,7 +58,6 @@ int PyStateManager::init(PyStateManager* self, PyObject* 
args, PyObject*) {
 }
 
 PyObject* PyStateManager::set(PyStateManager* self, PyObject* args) {
-  PYTHON_METHOD_BEGIN
   if (!self->state_manager_) {
     PyErr_SetString(PyExc_AttributeError, "tried reading state manager outside 
'on_trigger'");
     return nullptr;
@@ -77,11 +76,9 @@ PyObject* PyStateManager::set(PyStateManager* self, 
PyObject* args) {
   }
 
   return object::returnReference(self->state_manager_->set(cpp_state));
-  PYTHON_METHOD_END
 }
 
 PyObject* PyStateManager::get(PyStateManager* self, PyObject*) {
-  PYTHON_METHOD_BEGIN
   if (!self->state_manager_) {
     PyErr_SetString(PyExc_AttributeError, "tried reading state manager outside 
'on_trigger'");
     return nullptr;
@@ -95,11 +92,9 @@ PyObject* PyStateManager::get(PyStateManager* self, 
PyObject*) {
   } else {
     Py_RETURN_NONE;
   }
-  PYTHON_METHOD_END
 }
 
 PyObject* PyStateManager::clear(PyStateManager* self, PyObject* /*args*/) {
-  PYTHON_METHOD_BEGIN
   if (!self->state_manager_) {
     PyErr_SetString(PyExc_AttributeError, "tried reading state manager outside 
'on_trigger'");
     return nullptr;
@@ -107,11 +102,9 @@ PyObject* PyStateManager::clear(PyStateManager* self, 
PyObject* /*args*/) {
 
   self->state_manager_->clear();
   Py_RETURN_NONE;
-  PYTHON_METHOD_END
 }
 
 PyObject* PyStateManager::replace(PyStateManager* self, PyObject* args) {
-  PYTHON_METHOD_BEGIN
   if (!self->state_manager_) {
     PyErr_SetString(PyExc_AttributeError, "tried reading state manager outside 
'on_trigger'");
     return nullptr;
@@ -148,7 +141,6 @@ PyObject* PyStateManager::replace(PyStateManager* self, 
PyObject* args) {
   }
 
   return object::returnReference(self->state_manager_->set(new_cpp_state));
-  PYTHON_METHOD_END
 }
 
 PyTypeObject* PyStateManager::typeObject() {


Reply via email to