@@ -413,6 +413,39 @@ int PythonEngine::numberOfArguments(ScriptObject& methodObject, std::string_view
413413 return numberOfArguments;
414414}
415415
416+ bool PythonEngine::hasMethod (ScriptObject& methodObject, std::string_view methodName, bool overriden_only) {
417+ auto val = std::any_cast<PythonObject>(methodObject.object );
418+ if (PyObject_HasAttrString (val.obj_ , methodName.data ()) == 0 ) {
419+ return false ;
420+ }
421+ PyObject* method = PyObject_GetAttrString (val.obj_ , methodName.data ()); // New reference
422+ if (PyMethod_Check (method) == 0 ) {
423+ // Should never happen with modelOutputRequests since the Base class (C++) has it
424+ return false ;
425+ }
426+ Py_DECREF (method);
427+ if (!overriden_only) {
428+ return true ;
429+ }
430+
431+ // equivalent to getattr(instance_obj.__class__, method_name) == getattr(instance_obj.__class__.__bases__[0], method_name)
432+ PyTypeObject* class_type = Py_TYPE (val.obj_ ); // PyObject_Type returns a strong (New) reference, not needed for us
433+ // cppcheck-suppress cstyleCast
434+ PyObject* class_method = PyObject_GetAttrString ((PyObject*)class_type, methodName.data ()); // New reference
435+
436+ assert (class_type->tp_base != nullptr );
437+ // cppcheck-suppress cstyleCast
438+ auto * base = (PyTypeObject*)class_type->tp_base ;
439+ // cppcheck-suppress cstyleCast
440+ PyObject* base_method = PyObject_GetAttrString ((PyObject*)base, methodName.data ()); // New reference
441+
442+ bool result = class_method != base_method;
443+ Py_DECREF (class_method);
444+ Py_DECREF (base_method);
445+
446+ return result;
447+ }
448+
416449} // namespace openstudio
417450
418451extern " C"
0 commit comments