Index: source/Plugins/ScriptInterpreter/Python/PythonDataObjects.h =================================================================== --- source/Plugins/ScriptInterpreter/Python/PythonDataObjects.h +++ source/Plugins/ScriptInterpreter/Python/PythonDataObjects.h @@ -57,6 +57,7 @@ enum class PyObjectType { Unknown, None, + Boolean, Integer, Dictionary, List, @@ -297,6 +298,29 @@ StructuredData::IntegerSP CreateStructuredInteger() const; }; +class PythonBoolean : public PythonObject { +public: + PythonBoolean() = default; + explicit PythonBoolean(bool value); + PythonBoolean(PyRefType type, PyObject *o); + PythonBoolean(const PythonBoolean &object); + + ~PythonBoolean() override = default; + + static bool Check(PyObject *py_obj); + + // Bring in the no-argument base class version + using PythonObject::Reset; + + void Reset(PyRefType type, PyObject *py_obj) override; + + bool GetValue() const; + + void SetValue(bool value); + + StructuredData::BooleanSP CreateStructuredBoolean() const; +}; + class PythonList : public PythonObject { public: PythonList() {} Index: source/Plugins/ScriptInterpreter/Python/PythonDataObjects.cpp =================================================================== --- source/Plugins/ScriptInterpreter/Python/PythonDataObjects.cpp +++ source/Plugins/ScriptInterpreter/Python/PythonDataObjects.cpp @@ -77,6 +77,8 @@ #endif if (PythonByteArray::Check(m_py_obj)) return PyObjectType::ByteArray; + if (PythonBoolean::Check(m_py_obj)) + return PyObjectType::Boolean; if (PythonInteger::Check(m_py_obj)) return PyObjectType::Integer; if (PythonFile::Check(m_py_obj)) @@ -178,6 +180,9 @@ case PyObjectType::Dictionary: return PythonDictionary(PyRefType::Borrowed, m_py_obj) .CreateStructuredDictionary(); + case PyObjectType::Boolean: + return PythonBoolean(PyRefType::Borrowed, m_py_obj) + .CreateStructuredBoolean(); case PyObjectType::Integer: return PythonInteger(PyRefType::Borrowed, m_py_obj) .CreateStructuredInteger(); @@ -526,6 +531,55 @@ } //---------------------------------------------------------------------- +// PythonBoolean +//---------------------------------------------------------------------- + +PythonBoolean::PythonBoolean(PyRefType type, PyObject *py_obj) + : PythonObject() { + Reset(type, py_obj); // Use "Reset()" to ensure that py_obj is a boolean type +} + +PythonBoolean::PythonBoolean(const PythonBoolean &object) + : PythonObject(object) {} + +PythonBoolean::PythonBoolean(bool value) { + SetValue(value); +} + +bool PythonBoolean::Check(PyObject *py_obj) { + return py_obj ? PyBool_Check(py_obj) : false; +} + +void PythonBoolean::Reset(PyRefType type, PyObject *py_obj) { + // Grab the desired reference type so that if we end up rejecting `py_obj` it + // still gets decremented if necessary. + PythonObject result(type, py_obj); + + if (!PythonBoolean::Check(py_obj)) { + PythonObject::Reset(); + return; + } + + // Calling PythonObject::Reset(const PythonObject&) will lead to stack + // overflow since it calls back into the virtual implementation. + PythonObject::Reset(PyRefType::Borrowed, result.get()); +} + +bool PythonBoolean::GetValue() const { + return m_py_obj ? PyObject_IsTrue(m_py_obj) : false; +} + +void PythonBoolean::SetValue(bool value) { + PythonObject::Reset(PyRefType::Owned, PyBool_FromLong(value)); +} + +StructuredData::BooleanSP PythonBoolean::CreateStructuredBoolean() const { + StructuredData::BooleanSP result(new StructuredData::Boolean); + result->SetValue(GetValue()); + return result; +} + +//---------------------------------------------------------------------- // PythonList //---------------------------------------------------------------------- Index: unittests/ScriptInterpreter/Python/PythonDataObjectsTests.cpp =================================================================== --- unittests/ScriptInterpreter/Python/PythonDataObjectsTests.cpp +++ unittests/ScriptInterpreter/Python/PythonDataObjectsTests.cpp @@ -195,6 +195,31 @@ EXPECT_EQ(7, constructed_int.GetInteger()); } +TEST_F(PythonDataObjectsTest, TestPythonBoolean) { + // Test PythonBoolean constructed from Py_True + EXPECT_TRUE(PythonBoolean::Check(Py_True)); + PythonBoolean python_true(PyRefType::Owned, Py_True); + EXPECT_EQ(PyObjectType::Boolean, python_true.GetObjectType()); + + // Test PythonBoolean constructed from Py_False + EXPECT_TRUE(PythonBoolean::Check(Py_False)); + PythonBoolean python_false(PyRefType::Owned, Py_False); + EXPECT_EQ(PyObjectType::Boolean, python_false.GetObjectType()); + + auto test_from_long = [](long value) { + PyObject *py_bool = PyBool_FromLong(value); + EXPECT_TRUE(PythonBoolean::Check(py_bool)); + PythonBoolean python_boolean(PyRefType::Owned, py_bool); + EXPECT_EQ(PyObjectType::Boolean, python_boolean.GetObjectType()); + EXPECT_EQ(bool(value), python_boolean.GetValue()); + }; + + // Test PythonBoolean constructed from long integer values. + test_from_long(0); // Test 'false' value. + test_from_long(1); // Test 'true' value. + test_from_long(~0); // Any value != 0 is 'true'. +} + TEST_F(PythonDataObjectsTest, TestPythonBytes) { static const char *test_bytes = "PythonDataObjectsTest::TestPythonBytes"; PyObject *py_bytes = PyBytes_FromString(test_bytes);