diff --git a/src/App/Application.cpp b/src/App/Application.cpp index a85b916982..3b7aa459f0 100644 --- a/src/App/Application.cpp +++ b/src/App/Application.cpp @@ -114,6 +114,8 @@ #include "PropertyFile.h" #include "PropertyLinks.h" #include "PropertyPythonObject.h" +#include "StringHasherPy.h" +#include "StringIDPy.h" #include "TextDocument.h" #include "Transactions.h" #include "VRMLObject.h" @@ -311,6 +313,9 @@ void Application::setupPythonTypes() Base::Interpreter().addType(&App::MaterialPy::Type, pAppModule, "Material"); Base::Interpreter().addType(&App::MetadataPy::Type, pAppModule, "Metadata"); + Base::Interpreter().addType(&App::StringHasherPy::Type, pAppModule, "StringHasher"); + Base::Interpreter().addType(&App::StringIDPy::Type, pAppModule, "StringID"); + // Add document types Base::Interpreter().addType(&App::PropertyContainerPy::Type, pAppModule, "PropertyContainer"); Base::Interpreter().addType(&App::ExtensionContainerPy::Type, pAppModule, "ExtensionContainer"); @@ -2109,6 +2114,10 @@ void Application::initTypes() App::RangeExpression ::init(); App::PyObjectExpression ::init(); + // Topological naming classes + App::StringHasher ::init(); + App::StringID ::init(); + // register transaction type new App::TransactionProducer (DocumentObject::getClassTypeId()); diff --git a/src/App/StringHasherPyImp.cpp b/src/App/StringHasherPyImp.cpp index ebd6e74984..582effc79f 100644 --- a/src/App/StringHasherPyImp.cpp +++ b/src/App/StringHasherPyImp.cpp @@ -32,117 +32,135 @@ using namespace App; // returns a string which represent the object e.g. when printed in python std::string StringHasherPy::representation() const { - std::ostringstream str; - str << ""; - return str.str(); + std::ostringstream str; + str << ""; + return str.str(); } PyObject *StringHasherPy::PyMake(struct _typeobject *, PyObject *, PyObject *) // Python wrapper { - return new StringHasherPy(new StringHasher); + return new StringHasherPy(new StringHasher); } // constructor method -int StringHasherPy::PyInit(PyObject* , PyObject* ) +int StringHasherPy::PyInit(PyObject* args, PyObject* kwds) { - return 0; + char* kw[] = {nullptr}; + if (!PyArg_ParseTupleAndKeywords(args, kwds, "", kw)) { + return -1; + } + + return 0; } PyObject* StringHasherPy::isSame(PyObject *args) { - PyObject *other; - if (!PyArg_ParseTuple(args, "O!", &StringHasherPy::Type, &other)){ // convert args: Python->C - return Py::new_reference_to(Py::False()); - } - auto otherHasher = static_cast(other)->getStringHasherPtr(); - return Py::new_reference_to(Py::Boolean(getStringHasherPtr() == otherHasher)); + PyObject *other; + if (!PyArg_ParseTuple(args, "O!", &StringHasherPy::Type, &other)) { + return nullptr; + } + + auto otherHasher = static_cast(other)->getStringHasherPtr(); + bool same = getStringHasherPtr() == otherHasher; + + return PyBool_FromLong(same ? 1 : 0); } PyObject* StringHasherPy::getID(PyObject *args) { - long id = -1; - int index = 0; - PyObject *value = nullptr; - PyObject *base64 = Py_False; - if (!PyArg_ParseTuple(args, "l|i",&id,&index)) { - PyErr_Clear(); - if (!PyArg_ParseTuple(args, "O|O",&value,&base64)) - return nullptr; - } - if(id>0) { - PY_TRY { - auto sid = getStringHasherPtr()->getID(id, index); - if(!sid) Py_Return; - return sid.getPyObject(); - }PY_CATCH; - } - std::string txt; -#if PY_MAJOR_VERSION >= 3 - if (PyUnicode_Check(value)) { - txt = PyUnicode_AsUTF8(value); - } -#else - if (PyUnicode_Check(value)) { - PyObject* unicode = PyUnicode_AsLatin1String(value); - txt = PyString_AsString(unicode); - Py_DECREF(unicode); - } - else if (PyString_Check(value)) { - txt = PyString_AsString(value); - } -#endif - else - throw Py::TypeError("expect argument of type string"); - PY_TRY { - QByteArray data; - StringIDRef sid; - if(PyObject_IsTrue(base64)) { - data = QByteArray::fromBase64(QByteArray::fromRawData(txt.c_str(),txt.size())); - sid = getStringHasherPtr()->getID(data,true); - }else - sid = getStringHasherPtr()->getID(txt.c_str(),txt.size()); - return sid.getPyObject(); - }PY_CATCH; + long id; + int index = 0; + if (PyArg_ParseTuple(args, "l|i", &id, &index)) { + if (id > 0) { + PY_TRY { + auto sid = getStringHasherPtr()->getID(id, index); + if (!sid) { + Py_Return; + } + + return sid.getPyObject(); + } + PY_CATCH; + } + else { + PyErr_SetString(PyExc_ValueError, "Id must be positive integer"); + return nullptr; + } + } + + PyErr_Clear(); + PyObject *value = nullptr; + PyObject *base64 = Py_False; + if (PyArg_ParseTuple(args, "O!|O!", &PyUnicode_Type, &value, &PyBool_Type, &base64)) { + PY_TRY { + std::string txt = PyUnicode_AsUTF8(value); + QByteArray data; + StringIDRef sid; + if (PyObject_IsTrue(base64)) { + data = QByteArray::fromBase64(QByteArray::fromRawData(txt.c_str(),txt.size())); + sid = getStringHasherPtr()->getID(data,true); + } + else { + sid = getStringHasherPtr()->getID(txt.c_str(),txt.size()); + } + + return sid.getPyObject(); + } + PY_CATCH; + } + + PyErr_SetString(PyExc_TypeError, "Positive integer and optional integer or " + "string and optional boolean is required"); + return nullptr; } -Py::Int StringHasherPy::getCount() const { - return Py::Int((long)getStringHasherPtr()->count()); +Py::Long StringHasherPy::getCount() const +{ + return Py::Long(PyLong_FromSize_t(getStringHasherPtr()->count()), true); } -Py::Int StringHasherPy::getSize() const { - return Py::Int((long)getStringHasherPtr()->size()); +Py::Long StringHasherPy::getSize() const +{ + return Py::Long(PyLong_FromSize_t(getStringHasherPtr()->size()), true); } -Py::Boolean StringHasherPy::getSaveAll() const { - return Py::Boolean(getStringHasherPtr()->getSaveAll()); +Py::Boolean StringHasherPy::getSaveAll() const +{ + return Py::Boolean(getStringHasherPtr()->getSaveAll()); } -void StringHasherPy::setSaveAll(Py::Boolean value) { - getStringHasherPtr()->setSaveAll(value); +void StringHasherPy::setSaveAll(Py::Boolean value) +{ + getStringHasherPtr()->setSaveAll(value); } -Py::Int StringHasherPy::getThreshold() const { - return Py::Int((long)getStringHasherPtr()->getThreshold()); +Py::Long StringHasherPy::getThreshold() const +{ + return Py::Long(getStringHasherPtr()->getThreshold()); } -void StringHasherPy::setThreshold(Py::Int value) { - getStringHasherPtr()->setThreshold(value); +void StringHasherPy::setThreshold(Py::Long value) +{ + getStringHasherPtr()->setThreshold(value); } -Py::Dict StringHasherPy::getTable() const { - Py::Dict dict; - for(auto &v : getStringHasherPtr()->getIDMap()) - dict.setItem(Py::Int(v.first),Py::String(v.second.dataToText())); - return dict; +Py::Dict StringHasherPy::getTable() const +{ + Py::Dict dict; + for (const auto &v : getStringHasherPtr()->getIDMap()) { + dict.setItem(Py::Long(v.first), Py::String(v.second.dataToText())); + } + + return dict; } PyObject *StringHasherPy::getCustomAttributes(const char* /*attr*/) const { - return nullptr; + return nullptr; } int StringHasherPy::setCustomAttributes(const char* /*attr*/, PyObject* /*obj*/) { - return 0; + return 0; } diff --git a/src/App/StringIDPyImp.cpp b/src/App/StringIDPyImp.cpp index 9526c5b3ec..10b6563185 100644 --- a/src/App/StringIDPyImp.cpp +++ b/src/App/StringIDPyImp.cpp @@ -32,59 +32,69 @@ using namespace App; // returns a string which represent the object e.g. when printed in python std::string StringIDPy::representation() const { - return getStringIDPtr()->toString(_index); + return getStringIDPtr()->toString(this->_index); } PyObject* StringIDPy::isSame(PyObject *args) { - PyObject *other = nullptr; - if (PyArg_ParseTuple(args, "O!", &StringIDPy::Type, &other) == 0) { // convert args: Python->C - return Py::new_reference_to(Py::False()); - } - auto *otherPy = static_cast(other); - return Py::new_reference_to(Py::Boolean( - otherPy->getStringIDPtr() == this->getStringIDPtr() - && otherPy->_index == this->_index)); + PyObject *other = nullptr; + if (!PyArg_ParseTuple(args, "O!", &StringIDPy::Type, &other)) { + return nullptr; + } + + auto *otherPy = static_cast(other); + bool same = (otherPy->getStringIDPtr() == this->getStringIDPtr()) + && (otherPy->_index == this->_index); + + return PyBool_FromLong(same ? 1 : 0); } -Py::Int StringIDPy::getValue() const { - return Py::Int(getStringIDPtr()->value()); +Py::Long StringIDPy::getValue() const +{ + return Py::Long(getStringIDPtr()->value()); } -Py::List StringIDPy::getRelated() const { - Py::List list; - for (const auto &id : getStringIDPtr()->relatedIDs()) { - list.append(Py::Long(id.value())); -} - return list; +Py::List StringIDPy::getRelated() const +{ + Py::List list; + for (const auto &id : getStringIDPtr()->relatedIDs()) { + list.append(Py::Long(id.value())); + } + + return list; } -Py::String StringIDPy::getData() const { - return {Py::String(getStringIDPtr()->dataToText(this->_index))}; +Py::String StringIDPy::getData() const +{ + return Py::String(getStringIDPtr()->dataToText(this->_index)); } -Py::Boolean StringIDPy::getIsBinary() const { - return {getStringIDPtr()->isBinary()}; +Py::Boolean StringIDPy::getIsBinary() const +{ + return Py::Boolean(getStringIDPtr()->isBinary()); } -Py::Boolean StringIDPy::getIsHashed() const { - return {getStringIDPtr()->isHashed()}; +Py::Boolean StringIDPy::getIsHashed() const +{ + return Py::Boolean(getStringIDPtr()->isHashed()); } -Py::Int StringIDPy::getIndex() const { - return Py::Int(this->_index); +Py::Long StringIDPy::getIndex() const +{ + return Py::Long(this->_index); } -void StringIDPy::setIndex(Py::Int index) { - this->_index = index; +void StringIDPy::setIndex(Py::Long index) +{ + this->_index = index; } PyObject *StringIDPy::getCustomAttributes(const char* /*attr*/) const { - return nullptr; + return nullptr; } int StringIDPy::setCustomAttributes(const char* /*attr*/, PyObject* /*obj*/) { - return 0; + return 0; } diff --git a/src/Mod/Test/CMakeLists.txt b/src/Mod/Test/CMakeLists.txt index 67a293c69f..b4d101a0b8 100644 --- a/src/Mod/Test/CMakeLists.txt +++ b/src/Mod/Test/CMakeLists.txt @@ -5,6 +5,7 @@ SET(Test_SRCS BaseTests.py Document.py Metadata.py + StringHasher.py Menu.py TestApp.py TestGui.py diff --git a/src/Mod/Test/Init.py b/src/Mod/Test/Init.py index b9519da36d..a56f9f72ca 100644 --- a/src/Mod/Test/Init.py +++ b/src/Mod/Test/Init.py @@ -28,5 +28,6 @@ FreeCAD.__unit_test__ += [ "BaseTests", "UnitTests", "Document", "Metadata", + "StringHasher", "UnicodeTests", "TestPythonSyntax" ] diff --git a/src/Mod/Test/StringHasher.py b/src/Mod/Test/StringHasher.py new file mode 100644 index 0000000000..98a82f975f --- /dev/null +++ b/src/Mod/Test/StringHasher.py @@ -0,0 +1,47 @@ +# SPDX-License-Identifier: LGPL-2.1-or-later + +#*************************************************************************** +#* Copyright (c) 2023 Mario Passaglia * +#* * +#* This file is part of FreeCAD. * +#* * +#* FreeCAD is free software: you can redistribute it and/or modify it * +#* under the terms of the GNU Lesser General Public License as * +#* published by the Free Software Foundation, either version 2.1 of the * +#* License, or (at your option) any later version. * +#* * +#* FreeCAD is distributed in the hope that it will be useful, but * +#* WITHOUT ANY WARRANTY; without even the implied warranty of * +#* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * +#* Lesser General Public License for more details. * +#* * +#* You should have received a copy of the GNU Lesser General Public * +#* License along with FreeCAD. If not, see * +#* . * +#* * +#**************************************************************************/ + +import FreeCAD +import unittest + + +class TestStringHasher(unittest.TestCase): + def setUp(self): + self.strHash = FreeCAD.StringHasher() + self.strID = self.strHash.getID("A") + + def testInit(self): + with self.assertRaises(TypeError): + FreeCAD.StringHasher(0) + + def testGetID(self): + with self.assertRaises(ValueError): + self.strHash.getID(0) + + def testStringHasherIsSame(self): + with self.assertRaises(TypeError): + self.strHash.isSame(0) + + def testStringIDIsSame(self): + with self.assertRaises(TypeError): + self.strID.isSame(0)