diff options
14 files changed, 135 insertions, 57 deletions
diff --git a/examples/xml/dombookmarks/dombookmarks.py b/examples/xml/dombookmarks/dombookmarks.py index f5afdeef8..f02251849 100644 --- a/examples/xml/dombookmarks/dombookmarks.py +++ b/examples/xml/dombookmarks/dombookmarks.py @@ -7,7 +7,7 @@ from __future__ import annotations import sys -from PySide6.QtCore import QDir, QFile, Qt, QTextStream +from PySide6.QtCore import QDir, QFile, QObject, Qt, QTextStream from PySide6.QtGui import QAction, QIcon, QKeySequence from PySide6.QtWidgets import (QApplication, QFileDialog, QHeaderView, QMainWindow, QMessageBox, QStyle, QTreeWidget, @@ -93,6 +93,7 @@ class XbelTree(QTreeWidget): def __init__(self, parent=None): super().__init__(parent) + self._update_conn_id = None self.header().setSectionResizeMode(QHeaderView.ResizeMode.Stretch) self.setHeaderLabels(("Title", "Location")) @@ -111,11 +112,12 @@ class XbelTree(QTreeWidget): self._bookmark_icon.addPixmap(style.standardPixmap(QStyle.StandardPixmap.SP_FileIcon)) def read(self, device): - ok, errorStr, errorLine, errorColumn = self._dom_document.setContent(device, True) - if not ok: + result = self._dom_document.setContent(device, + QDomDocument.ParseOption.UseNamespaceProcessing) + if not result: QMessageBox.information(self.window(), "DOM Bookmarks", - f"Parse error at line {errorLine}, " - f"column {errorColumn}:\n{errorStr}") + f"Parse error at line {result.errorLine}, " + f"column {result.errorColumn}:\n{result.errorMessage}") return False root = self._dom_document.documentElement() @@ -131,17 +133,15 @@ class XbelTree(QTreeWidget): self.clear() # It might not be connected. - try: - self.itemChanged.disconnect(self.update_dom_element) - except RuntimeError: - pass + if self._update_conn_id: + QObject.disconnect(self._update_conn_id) child = root.firstChildElement('folder') while not child.isNull(): self.parse_folder_element(child) child = child.nextSiblingElement('folder') - self.itemChanged.connect(self.update_dom_element) + self._update_conn_id = self.itemChanged.connect(self.update_dom_element) return True diff --git a/sources/pyside6/PySide6/QtXml/typesystem_xml.xml b/sources/pyside6/PySide6/QtXml/typesystem_xml.xml index 089978b6d..93d3c1f56 100644 --- a/sources/pyside6/PySide6/QtXml/typesystem_xml.xml +++ b/sources/pyside6/PySide6/QtXml/typesystem_xml.xml @@ -20,7 +20,7 @@ <enum-type name="ParseOption" flags="ParseOptions" since="6.5"/> <!-- will be replaced in inject code --> - <value-type name="ParseResult"/> + <value-type name="ParseResult" operator-bool="yes"/> <modify-function signature="setContent(const QByteArray&,bool,QString*,int*,int*)"> <modify-argument index="3"> @@ -130,6 +130,8 @@ </modify-argument> <inject-code class="target" position="beginning" file="../glue/qtxml.cpp" snippet="qdomdocument-setcontent" /> </modify-function> + <declare-function signature="setContent(const QByteArray&@data@, QDomDocument::ParseOptions@options@=QDomDocument::ParseOption::Default)" + return-type="QDomDocument::ParseResult" since="6.8" /> </value-type> <value-type name="QDomDocumentFragment"/> diff --git a/sources/pyside6/tests/QtXml/qdomdocument_test.py b/sources/pyside6/tests/QtXml/qdomdocument_test.py index 8fe4f6e17..b321b1bdf 100644 --- a/sources/pyside6/tests/QtXml/qdomdocument_test.py +++ b/sources/pyside6/tests/QtXml/qdomdocument_test.py @@ -44,18 +44,20 @@ class QDomDocumentTest(unittest.TestCase): def testQDomDocumentSetContentWithBadXmlData(self): '''Sets invalid xml as the QDomDocument contents.''' - ok, errorStr, errorLine, errorColumn = self.dom.setContent(self.badXmlData, True) - self.assertFalse(ok) - self.assertEqual(errorStr, 'Opening and ending tag mismatch.') - self.assertEqual(errorLine, 4) + parseResult = self.dom.setContent(self.badXmlData, + QDomDocument.ParseOption.UseNamespaceProcessing) + self.assertFalse(parseResult) + self.assertEqual(parseResult.errorMessage, 'Opening and ending tag mismatch.') + self.assertEqual(parseResult.errorLine, 4) def testQDomDocumentSetContentWithGoodXmlData(self): '''Sets valid xml as the QDomDocument contents.''' - ok, errorStr, errorLine, errorColumn = self.dom.setContent(self.goodXmlData, True) - self.assertTrue(ok) - self.assertEqual(errorStr, '') - self.assertEqual(errorLine, 0) - self.assertEqual(errorColumn, 0) + parseResult = self.dom.setContent(self.goodXmlData, + QDomDocument.ParseOption.UseNamespaceProcessing) + self.assertTrue(parseResult) + self.assertEqual(parseResult.errorMessage, '') + self.assertEqual(parseResult.errorLine, 0) + self.assertEqual(parseResult.errorColumn, 0) def testQDomDocumentData(self): '''Checks the QDomDocument elements for the valid xml contents.''' @@ -66,7 +68,8 @@ class QDomDocumentTest(unittest.TestCase): self.assertTrue(element.hasAttribute(attribute)) self.assertEqual(element.attribute(attribute), value) - ok, errorStr, errorLine, errorColumn = self.dom.setContent(self.goodXmlData, True) + parseResult = self.dom.setContent(self.goodXmlData, # noqa F:841 + QDomDocument.ParseOption.UseNamespaceProcessing) root = self.dom.documentElement() self.assertEqual(root.tagName(), 'typesystem') checkAttribute(root, 'package', 'PySide6.QtXml') diff --git a/sources/shiboken6/ApiExtractor/abstractmetabuilder.cpp b/sources/shiboken6/ApiExtractor/abstractmetabuilder.cpp index 27570ec4f..fb5399baa 100644 --- a/sources/shiboken6/ApiExtractor/abstractmetabuilder.cpp +++ b/sources/shiboken6/ApiExtractor/abstractmetabuilder.cpp @@ -2664,7 +2664,7 @@ std::optional<AbstractMetaType> qsizetype i = d ? d->m_scopes.size() - 1 : -1; while (i >= 0) { typeInfo = TypeInfo::resolveType(_typei, d->m_scopes.at(i--)); - if (typeInfo.qualifiedName().join(u"::"_s) != _typei.qualifiedName().join(u"::"_s)) + if (typeInfo.qualifiedName() != _typei.qualifiedName()) break; } @@ -2757,12 +2757,22 @@ std::optional<AbstractMetaType> // 4. Special case QFlags (include instantiation in name) if (qualifiedName == u"QFlags") { - qualifiedName = typeInfo.toString(); + qualifiedName = typeInfo.qualifiedInstantationName(); typeInfo.clearInstantiations(); } TypeEntryCList types = findTypeEntries(qualifiedName, name, flags, currentClass, d, errorMessageIn); + if (types.isEmpty() && !typeInfo.instantiations().isEmpty()) { + // Allow for specifying template specializations as primitive types + // with converters ('std::optional<int>' or similar). + auto pt = TypeDatabase::instance()->findPrimitiveType(typeInfo.qualifiedInstantationName()); + if (pt) { + types.append(pt); + typeInfo.clearInstantiations(); + } + } + if (!flags.testFlag(AbstractMetaBuilder::TemplateArgument)) { // Avoid clashes between QByteArray and enum value QMetaType::QByteArray // unless we are looking for template arguments. @@ -2796,7 +2806,7 @@ std::optional<AbstractMetaType> // For non-type template parameters, create a dummy type entry on the fly // as is done for classes. if (!targType.has_value()) { - const QString value = ti.qualifiedName().join(u"::"_s); + const QString value = ti.qualifiedNameString(); if (isNumber(value)) { auto module = typeSystemTypeEntry(type); TypeDatabase::instance()->addConstantValueTypeEntry(value, module); @@ -3082,7 +3092,7 @@ AbstractMetaClassPtr QString prefix = i > 0 ? QStringList(scope.mid(0, i)).join(u"::"_s) + u"::"_s : QString(); QString completeName = prefix + name; const TypeInfo parsed = TypeParser::parse(completeName, &errorMessage); - QString qualifiedName = parsed.qualifiedName().join(u"::"_s); + QString qualifiedName = parsed.qualifiedNameString(); if (qualifiedName.isEmpty()) { qWarning().noquote().nospace() << "Unable to parse type \"" << completeName << "\" while looking for template \"" << name << "\": " << errorMessage; @@ -3196,7 +3206,7 @@ static std::optional<AbstractMetaType> const AbstractMetaClassCPtr &templateClass, const TypeInfo &info, QString *errorMessage) { - QString typeName = info.qualifiedName().join("::"_L1); + QString typeName = info.qualifiedNameString(); TypeDatabase *typeDb = TypeDatabase::instance(); TypeEntryPtr t; // Check for a non-type template integer parameter, that is, for a base diff --git a/sources/shiboken6/ApiExtractor/parser/typeinfo.cpp b/sources/shiboken6/ApiExtractor/parser/typeinfo.cpp index 845a0f916..93627e6d5 100644 --- a/sources/shiboken6/ApiExtractor/parser/typeinfo.cpp +++ b/sources/shiboken6/ApiExtractor/parser/typeinfo.cpp @@ -110,6 +110,28 @@ QStringList TypeInfo::qualifiedName() const return d->m_qualifiedName; } +QString TypeInfo::qualifiedNameString() const +{ + return d->m_qualifiedName.join("::"_L1); +} + +QString TypeInfo::qualifiedInstantationName() const +{ + QString result = qualifiedNameString(); + if (const auto instantiationCount = d->m_instantiations.size()) { + result += u'<'; + for (qsizetype i = 0; i < instantiationCount; ++i) { + if (i) + result += ", "_L1; + result += d->m_instantiations.at(i).toString(); + } + if (result.endsWith(u'>')) + result += u' '; + result += u'>'; + } + return result; +} + void TypeInfo::setQualifiedName(const QStringList &qualified_name) { if (d->m_qualifiedName != qualified_name) @@ -300,7 +322,7 @@ TypeInfo TypeInfo::resolveType(const CodeModelItem &__item, TypeInfo const &__ty // typedef struct xcb_connection_t xcb_connection_t; if (nextItem.get() ==__item.get()) { std::cerr << "** WARNING Bailing out recursion of " << __FUNCTION__ - << "() on " << qPrintable(__type.qualifiedName().join(u"::"_s)) + << "() on " << qPrintable(__type.qualifiedNameString()) << '\n'; return otherType; } @@ -385,19 +407,7 @@ QString TypeInfo::toString() const if (isVolatile()) tmp += u"volatile "_s; - tmp += d->m_qualifiedName.join(u"::"_s); - - if (const auto instantiationCount = d->m_instantiations.size()) { - tmp += u'<'; - for (qsizetype i = 0; i < instantiationCount; ++i) { - if (i) - tmp += u", "_s; - tmp += d->m_instantiations.at(i).toString(); - } - if (tmp.endsWith(u'>')) - tmp += u' '; - tmp += u'>'; - } + tmp += qualifiedInstantationName(); for (Indirection i : d->m_indirections) tmp.append(indirectionKeyword(i)); diff --git a/sources/shiboken6/ApiExtractor/parser/typeinfo.h b/sources/shiboken6/ApiExtractor/parser/typeinfo.h index 9cd746e85..6f75b5737 100644 --- a/sources/shiboken6/ApiExtractor/parser/typeinfo.h +++ b/sources/shiboken6/ApiExtractor/parser/typeinfo.h @@ -39,6 +39,11 @@ public: QStringList qualifiedName() const; void setQualifiedName(const QStringList &qualified_name); + // Returns "std::list" + QString qualifiedNameString() const; + // Returns qualifiedName() with instantions ("std::list<int>") + QString qualifiedInstantationName() const; + void addName(const QString &); bool isVoid() const; diff --git a/sources/shiboken6/doc/typesystem_converters.rst b/sources/shiboken6/doc/typesystem_converters.rst index ab6fba930..f34f5b829 100644 --- a/sources/shiboken6/doc/typesystem_converters.rst +++ b/sources/shiboken6/doc/typesystem_converters.rst @@ -16,7 +16,8 @@ C++ and vice-versa. // C++ class struct Complex { - Complex(double real, double imag); + explicit Complex(double real, double imag); + double real() const; double imag() const; }; @@ -82,8 +83,9 @@ Here's how to do it: <!-- Code injection at module level. --> <inject-code class="native" position="beginning"> - static bool Check2TupleOfNumbers(PyObject* pyIn) { - if (!PySequence_Check(pyIn) || !(PySequence_Size(pyIn) == 2)) + static bool Check2TupleOfNumbers(PyObject *pyIn) + { + if (PySequence_Check(pyIn) == 0 || PySequence_Size(pyIn) != 2) return false; Shiboken::AutoDecRef pyReal(PySequence_GetItem(pyIn, 0)); if (!PyNumber_Check(pyReal)) @@ -134,7 +136,8 @@ Container Conversions Converters for :ref:`container-type <container-type>` are pretty much the same as for other type, except that they make use of the type system variables -:ref:`%INTYPE_# <intype_n>` and :ref:`%OUTTYPE_# <outtype_n>`. +:ref:`%INTYPE_# <intype_n>` and :ref:`%OUTTYPE_# <outtype_n>` denoting the +template parameters. |project| combines the conversion code for containers with the conversion defined (or automatically generated) for the containers. @@ -147,13 +150,12 @@ defined (or automatically generated) for the containers. <native-to-target> PyObject* %out = PyDict_New(); - %INTYPE::const_iterator it = %in.begin(); - for (; it != %in.end(); ++it) { - %INTYPE_0 key = it->first; - %INTYPE_1 value = it->second; - PyDict_SetItem(%out, + for (auto it = %in.cbegin(), end = %in.cend(); it != end; ++it) { + const auto &key = it->first; + const auto &value = it->second; + PyDict_SetItem(%out, %CONVERTTOPYTHON[%INTYPE_0](key), - %CONVERTTOPYTHON[%INTYPE_1](value)); + %CONVERTTOPYTHON[%INTYPE_1](value)); } return %out; </native-to-target> @@ -161,8 +163,8 @@ defined (or automatically generated) for the containers. <target-to-native> <add-conversion type="PyDict"> - PyObject* key; - PyObject* value; + PyObject *key{}; + PyObject *value{}; Py_ssize_t pos = 0; while (PyDict_Next(%in, &pos, &key, &value)) { %OUTTYPE_0 cppKey = %CONVERTTOCPP[%OUTTYPE_0](key); @@ -183,10 +185,10 @@ defined (or automatically generated) for the containers. For this case, a number of pre-defined conversion templates are provided (see :ref:`predefined_templates`). -.. _variables_and_functions: +.. _converter_variables_and_functions: -Variables & Functions -===================== +Converter Variables & Functions +=============================== .. _in: @@ -212,7 +214,7 @@ Variables & Functions .. _intype_n: **%INTYPE_#** - Replaced by the name of the #th type used in a container. + Replaced by the name of the #th template parameter type used in a container. .. _outtype: @@ -225,7 +227,7 @@ Variables & Functions .. _outtype_n: **%OUTTYPE_#** - Replaced by the name of the #th type used in a container. + Replaced by the name of the #th template parameter type used in a container. .. _checktype: diff --git a/sources/shiboken6/doc/typesystem_specifying_types.rst b/sources/shiboken6/doc/typesystem_specifying_types.rst index c03d203b9..60ac5c6d9 100644 --- a/sources/shiboken6/doc/typesystem_specifying_types.rst +++ b/sources/shiboken6/doc/typesystem_specifying_types.rst @@ -208,6 +208,9 @@ can be generated for them. Instead, an instance of the viewed class should be instantiated and passed to functions using the view class for argument types. +It is also possible to specify template specializations +like "std::optional<int>" as primitive types with converters. + See :ref:`predefined_templates` for built-in templates for standard type conversion rules. diff --git a/sources/shiboken6/doc/typesystem_variables.rst b/sources/shiboken6/doc/typesystem_variables.rst index 5eb5d5abe..6dfd1f801 100644 --- a/sources/shiboken6/doc/typesystem_variables.rst +++ b/sources/shiboken6/doc/typesystem_variables.rst @@ -16,6 +16,8 @@ implementation specifics. Variables ========= +In addition to the below listed variables, there are some variables specific to type +conversion code (see :ref:`converter_variables_and_functions`). .. _cpp_return_argument: diff --git a/sources/shiboken6/shibokenmodule/files.dir/shibokensupport/signature/layout.py b/sources/shiboken6/shibokenmodule/files.dir/shibokensupport/signature/layout.py index 109562a98..8eea431c3 100644 --- a/sources/shiboken6/shibokenmodule/files.dir/shibokensupport/signature/layout.py +++ b/sources/shiboken6/shibokenmodule/files.dir/shibokensupport/signature/layout.py @@ -352,6 +352,7 @@ def create_signature_union(props, key): if last == _VAR_POSITIONAL: kind = _KEYWORD_ONLY if default is None: + ann = typing.Union[ann] ann = typing.Optional[ann] if default is not _empty and layout.ellipsis: default = ellipsis @@ -373,6 +374,7 @@ def create_signature_union(props, key): ret_anno = annotations.get('return', _empty) if ret_anno is not _empty and props["fullname"] in missing_optional_return: + ret_anno = typing.Union[ret_anno] ret_anno = typing.Optional[ret_anno] return inspect.Signature(params, return_annotation=ret_anno, __validate_parameters__=False) diff --git a/sources/shiboken6/tests/libsample/samplenamespace.cpp b/sources/shiboken6/tests/libsample/samplenamespace.cpp index 18a18d28d..3836f43aa 100644 --- a/sources/shiboken6/tests/libsample/samplenamespace.cpp +++ b/sources/shiboken6/tests/libsample/samplenamespace.cpp @@ -97,6 +97,15 @@ int passReferenceToObjectType(const ObjectType &obj, int multiplier) return obj.objectName().size() * multiplier; } +// Exercise specifying complete template specializations as primitive types. +std::optional<long> optionalMultiply(const std::optional<long> &v1, + const std::optional<long> &v2) +{ + if (!v1.has_value() || !v2.has_value()) + return std::nullopt; + return v1.value() * v2.value(); +} + int variableInNamespace = 42; } // namespace SampleNamespace diff --git a/sources/shiboken6/tests/libsample/samplenamespace.h b/sources/shiboken6/tests/libsample/samplenamespace.h index 99a0787ee..63dc2f316 100644 --- a/sources/shiboken6/tests/libsample/samplenamespace.h +++ b/sources/shiboken6/tests/libsample/samplenamespace.h @@ -9,6 +9,7 @@ #include "point.h" #include "objecttype.h" +#include <optional> #include <list> // Anonymous global enum @@ -157,6 +158,9 @@ LIBSAMPLE_API double passReferenceToValueType(const Point &point, double multipl // Add a new signature on type system with only a ObjectType pointer as parameter. LIBSAMPLE_API int passReferenceToObjectType(const ObjectType &obj, int multiplier); +LIBSAMPLE_API std::optional<long> optionalMultiply(const std::optional<long> &v1, + const std::optional<long> &v2); + extern LIBSAMPLE_API int variableInNamespace; } // namespace SampleNamespace diff --git a/sources/shiboken6/tests/samplebinding/sample_test.py b/sources/shiboken6/tests/samplebinding/sample_test.py index c003ad398..43b84d36e 100644 --- a/sources/shiboken6/tests/samplebinding/sample_test.py +++ b/sources/shiboken6/tests/samplebinding/sample_test.py @@ -79,6 +79,13 @@ class ModuleTest(unittest.TestCase): mo2 = sample.MoveOnlyHandler.passMoveOnly(mo) self.assertEqual(mo2.value(), v) + def testOptionalLong(self): + v1 = 2 + v2 = 3 + self.assertEqual(sample.SampleNamespace.optionalMultiply(v1, v2), 6) + self.assertIsNone(sample.SampleNamespace.optionalMultiply(v1, None)) + self.assertIsNone(sample.SampleNamespace.optionalMultiply(None, v2)) + if __name__ == '__main__': unittest.main() diff --git a/sources/shiboken6/tests/samplebinding/typesystem_sample.xml b/sources/shiboken6/tests/samplebinding/typesystem_sample.xml index 3f1b2e96f..391a0804d 100644 --- a/sources/shiboken6/tests/samplebinding/typesystem_sample.xml +++ b/sources/shiboken6/tests/samplebinding/typesystem_sample.xml @@ -120,6 +120,25 @@ </conversion-rule> </primitive-type> + <primitive-type name="std::optional<long>" target-langapi-name="PyObject"> + <conversion-rule> + <native-to-target> + if (!%in.has_value()) + Py_RETURN_NONE; + return PyLong_FromLong(%in.value()); + </native-to-target> + <target-to-native> + <add-conversion type="Py_None"> + SBK_UNUSED(%in) + %out = %OUTTYPE(); + </add-conversion> + <add-conversion type="PyLong" check="PyLong_CheckExact(%in)"> + %out = %OUTTYPE(PyLong_AsLong(%in)); + </add-conversion> + </target-to-native> + </conversion-rule> + </primitive-type> + <value-type name="MoveOnly"/> <object-type name="MoveOnlyHandler"/> |