diff options
33 files changed, 422 insertions, 293 deletions
diff --git a/build_scripts/main.py b/build_scripts/main.py index 8aeabcd63..c7761e2dc 100644 --- a/build_scripts/main.py +++ b/build_scripts/main.py @@ -119,6 +119,7 @@ from distutils.spawn import find_executable from distutils.command.build import build as _build from distutils.command.build_ext import build_ext as _build_ext from distutils.util import get_platform +from distutils.cmd import Command from setuptools import Extension from setuptools.command.install import install as _install @@ -1023,6 +1024,9 @@ class PysideBuild(_build): log.info("Output format will be qthelp") cmake_cmd.append("-DDOC_OUTPUT_FORMAT=qthelp") + # Build the whole documentation (rst + API) by default + cmake_cmd.append("-DFULLDOCSBUILD=1") + if not OPTION["SKIP_CMAKE"]: log.info("Configuring module {} ({})...".format(extension, module_src_dir)) if run_process(cmake_cmd) != 0: @@ -1273,6 +1277,83 @@ class PysideBuild(_build): log.info("Patched rpath to '$ORIGIN/' (Linux) or " "updated rpath (OS/X) in {}.".format(srcpath)) +class PysideRstDocs(Command): + description = "Build .rst documentation only" + user_options = [] + def initialize_options(self): + log.info("-- This build process will not include the API documentation." + "API documentation requires a full build of pyside/shiboken.") + self.skip = False + if config.is_internal_shiboken_generator_build(): + self.skip = True + if not self.skip: + self.name = config.package_name().lower() + self.doc_dir = os.path.join(config.setup_script_dir, "sources") + self.doc_dir = os.path.join(self.doc_dir, self.name) + self.doc_dir = os.path.join(self.doc_dir, "doc") + try: + # Check if sphinx is installed to proceed. + import sphinx + if self.name == "shiboken2": + log.info("-- Generating Shiboken documentation") + log.info("-- Documentation directory: 'html/pyside2/shiboken2/'") + elif self.name == "pyside2": + log.info("-- Generating PySide documentation") + log.info("-- Documentation directory: 'html/pyside2/'") + except ImportError: + raise DistutilsSetupError("Sphinx not found - aborting") + self.html_dir = "html" + + # creating directories html/pyside2/shiboken2 + try: + if not os.path.isdir(self.html_dir): + os.mkdir(self.html_dir) + if self.name == "shiboken2": + out_pyside = os.path.join(self.html_dir, "pyside2") + if not os.path.isdir(out_pyside): + os.mkdir(out_pyside) + out_shiboken = os.path.join(out_pyside, "shiboken2") + if not os.path.isdir(out_shiboken): + os.mkdir(out_shiboken) + self.out_dir = out_shiboken + # We know that on the shiboken step, we already created the + # 'pyside2' directory + elif self.name == "pyside2": + self.out_dir = os.path.join(self.html_dir, "pyside2") + except: + raise DistutilsSetupError("Error while creating directories for {}".format(self.doc_dir)) + + def run(self): + if not self.skip: + cmake_cmd = [OPTION["CMAKE"]] + cmake_cmd += [ + "-S", self.doc_dir, + "-B", self.out_dir, + "-DDOC_OUTPUT_FORMAT=html", + "-DFULLDOCSBUILD=0", + ] + if run_process(cmake_cmd) != 0: + raise DistutilsSetupError("Error running CMake for {}".format(self.doc_dir)) + + if self.name == "pyside2": + self.sphinx_src = os.path.join(self.out_dir, "rst") + elif self.name == "shiboken2": + self.sphinx_src = self.out_dir + + sphinx_cmd = ["sphinx-build", + "-b", "html", + "-c", self.sphinx_src, + self.doc_dir, + self.out_dir + ] + if run_process(sphinx_cmd) != 0: + raise DistutilsSetupError("Error running CMake for {}".format(self.doc_dir)) + # Last message + if not self.skip and self.name == "pyside2": + log.info("-- The documentation was built. Check html/pyside2/index.html") + + def finalize_options(self): + pass cmd_class_dict = { 'build': PysideBuild, @@ -1281,7 +1362,8 @@ cmd_class_dict = { 'bdist_egg': PysideBdistEgg, 'develop': PysideDevelop, 'install': PysideInstall, - 'install_lib': PysideInstallLib + 'install_lib': PysideInstallLib, + 'build_rst_docs': PysideRstDocs, } if wheel_module_exists: params = {} diff --git a/examples/samplebinding/README.md b/examples/samplebinding/README.md index 85e96ddbe..e84d1eff4 100644 --- a/examples/samplebinding/README.md +++ b/examples/samplebinding/README.md @@ -62,6 +62,19 @@ and the `clone()` and `addIcecreamFlavor(Icecream*)` need additional info about who owns the parameter objects when passing them across language boundaries (in this case C++ will delete the objects). +The `Truck` has getters and setters for the string `arrivalMessage`. +In the type system file, we declare this to be a property in Python: + +``` +<property type="std::string" name="arrivalMessage" get="getArrivalMessage" set="setArrivalMessage"/> +``` + +It can then be used in a more pythonic way: + +``` +special_truck.arrivalMessage = "A new SPECIAL icecream truck has arrived!\n" +``` + After shiboken generates the C++ code and CMake makes an extension module from the code, the types can be accessed in Python simply by importing them using the original C++ names. diff --git a/examples/samplebinding/bindings.xml b/examples/samplebinding/bindings.xml index f08243694..9be9f1afa 100644 --- a/examples/samplebinding/bindings.xml +++ b/examples/samplebinding/bindings.xml @@ -70,6 +70,7 @@ <value-type name="Truck"> <!-- Same ownership caveat applies here. --> + <property type="std::string" name="arrivalMessage" get="getArrivalMessage" set="setArrivalMessage"/> <modify-function signature="addIcecreamFlavor(Icecream*)"> <modify-argument index="1"> <define-ownership owner="c++"/> diff --git a/examples/samplebinding/main.py b/examples/samplebinding/main.py index bd1d10b8e..dc727c5d8 100644 --- a/examples/samplebinding/main.py +++ b/examples/samplebinding/main.py @@ -92,7 +92,7 @@ if __name__ == '__main__': del truck print("") - special_truck.setArrivalMessage("A new SPECIAL icecream truck has arrived!\n") + special_truck.arrivalMessage = "A new SPECIAL icecream truck has arrived!\n" special_truck.arrive() special_truck.addIcecreamFlavor(Icecream("SPECIAL *magical* icecream")) special_truck.printAvailableFlavors() diff --git a/examples/samplebinding/truck.cpp b/examples/samplebinding/truck.cpp index 6e24bdc87..056abfcd6 100644 --- a/examples/samplebinding/truck.cpp +++ b/examples/samplebinding/truck.cpp @@ -114,6 +114,11 @@ void Truck::setArrivalMessage(const std::string &message) m_arrivalMessage = message; } +std::string Truck::getArrivalMessage() const +{ + return m_arrivalMessage; +} + bool Truck::deliver() const { std::random_device rd; diff --git a/examples/samplebinding/truck.h b/examples/samplebinding/truck.h index 02e304a82..3f213f9ac 100644 --- a/examples/samplebinding/truck.h +++ b/examples/samplebinding/truck.h @@ -71,7 +71,9 @@ public: void leave() const; void setLeaveOnDestruction(bool value); + void setArrivalMessage(const std::string &message); + std::string getArrivalMessage() const; private: void clearFlavors(); diff --git a/sources/pyside2/doc/CMakeLists.txt b/sources/pyside2/doc/CMakeLists.txt index d84bd073f..73b90c059 100644 --- a/sources/pyside2/doc/CMakeLists.txt +++ b/sources/pyside2/doc/CMakeLists.txt @@ -16,84 +16,90 @@ set(TS_ROOT "${ROOT}/PySide2") file(REMOVE ${CMAKE_CURRENT_LIST_DIR}/pyside.qdocconf ${CMAKE_CURRENT_LIST_DIR}/pyside.qdocconf.in) -# For Qt modules that are part of the documentation build: -# - Configure the module docconf file -# - Write shiboken header consisting of pyside2_global.h and module includes -# - Build include path for qdoc for shiboken - -# The last element of the include list is the mkspec directory containing qplatformdefs.h -list(GET Qt${QT_MAJOR_VERSION}Core_INCLUDE_DIRS -1 mkspecInclude) -configure_file("pyside-config.qdocconf.in" "${CMAKE_CURRENT_LIST_DIR}/pyside-config.qdocconf" @ONLY) - -file(READ "${pyside2_BINARY_DIR}/pyside2_global.h" docHeaderContents) -file(READ "typesystem_doc.xml.in" typeSystemDocXmlContents) - +set(SHIBOKEN_INTERSPHINX_FILE "${ROOT}/pyside2/shiboken2/objects.inv") set(HAS_WEBENGINE_WIDGETS 0) - -foreach(moduleIn ${all_module_shortnames}) - string(TOLOWER "${moduleIn}" lowerModuleIn) - set(docConf "${CMAKE_CURRENT_LIST_DIR}/qtmodules/pyside-qt${lowerModuleIn}.qdocconf.in") - if(EXISTS "${docConf}") - string(REGEX REPLACE "(^.*)\.in" "\\1" OUTFILE ${docConf}) - get_filename_component(BASENAME ${OUTFILE} NAME) - configure_file(${docConf} "${CMAKE_CURRENT_LIST_DIR}/qtmodules/${BASENAME}" @ONLY) - file(APPEND "pyside.qdocconf.in" "\@CMAKE_CURRENT_LIST_DIR\@/qtmodules/${BASENAME}\n") - # Handle docconf files in Qt that contain multiple modules - if ("${moduleIn}" STREQUAL "3DExtras") - set(modules 3DCore 3DRender 3DInput 3DLogic 3DAnimation "${moduleIn}") - elseif ("${moduleIn}" STREQUAL "QuickWidgets") - set(modules Qml Quick "${moduleIn}") - elseif ("${moduleIn}" STREQUAL "MultimediaWidgets") - set(modules Multimedia "${moduleIn}") - elseif ("${moduleIn}" STREQUAL "WebEngineWidgets") - set(modules WebEngine WebEngineCore "${moduleIn}") - set(HAS_WEBENGINE_WIDGETS 1) - else() - set(modules "${moduleIn}") - endif() - foreach(module ${modules}) - string(TOLOWER "${module}" lowerModule) - # -- @TODO fix this for macOS frameworks. - file(APPEND "${CMAKE_CURRENT_LIST_DIR}/pyside-config.qdocconf" - " -I ${QT_INCLUDE_DIR}Qt${module} \\\n" - " -I ${QT_INCLUDE_DIR}Qt${module}/${Qt${QT_MAJOR_VERSION}Core_VERSION} \\\n" - " -I ${QT_INCLUDE_DIR}Qt${module}/${Qt${QT_MAJOR_VERSION}Core_VERSION}/Qt${module} \\\n") - - if (${moduleIn} STREQUAL "X11Extras") - set(globalHeader "QX11Info") +set(SKIP_SPHINX_WARNINGS 1) +if (FULLDOCSBUILD) + set(SKIP_SPHINX_WARNINGS 0) + set(SHIBOKEN_INTERSPHINX_FILE "${CMAKE_BINARY_DIR}/doc/html/shiboken2/doc/html/objects.inv") + # For Qt modules that are part of the documentation build: + # - Configure the module docconf file + # - Write shiboken header consisting of pyside2_global.h and module includes + # - Build include path for qdoc for shiboken + + # The last element of the include list is the mkspec directory containing qplatformdefs.h + list(GET Qt${QT_MAJOR_VERSION}Core_INCLUDE_DIRS -1 mkspecInclude) + configure_file("pyside-config.qdocconf.in" "${CMAKE_CURRENT_LIST_DIR}/pyside-config.qdocconf" @ONLY) + + file(READ "${pyside2_BINARY_DIR}/pyside2_global.h" docHeaderContents) + file(READ "typesystem_doc.xml.in" typeSystemDocXmlContents) + + + foreach(moduleIn ${all_module_shortnames}) + string(TOLOWER "${moduleIn}" lowerModuleIn) + set(docConf "${CMAKE_CURRENT_LIST_DIR}/qtmodules/pyside-qt${lowerModuleIn}.qdocconf.in") + if(EXISTS "${docConf}") + string(REGEX REPLACE "(^.*)\.in" "\\1" OUTFILE ${docConf}) + get_filename_component(BASENAME ${OUTFILE} NAME) + configure_file(${docConf} "${CMAKE_CURRENT_LIST_DIR}/qtmodules/${BASENAME}" @ONLY) + file(APPEND "pyside.qdocconf.in" "\@CMAKE_CURRENT_LIST_DIR\@/qtmodules/${BASENAME}\n") + # Handle docconf files in Qt that contain multiple modules + if ("${moduleIn}" STREQUAL "3DExtras") + set(modules 3DCore 3DRender 3DInput 3DLogic 3DAnimation "${moduleIn}") + elseif ("${moduleIn}" STREQUAL "QuickWidgets") + set(modules Qml Quick "${moduleIn}") + elseif ("${moduleIn}" STREQUAL "MultimediaWidgets") + set(modules Multimedia "${moduleIn}") + elseif ("${moduleIn}" STREQUAL "WebEngineWidgets") + set(modules WebEngine WebEngineCore "${moduleIn}") + set(HAS_WEBENGINE_WIDGETS 1) else() - set(globalHeader "Qt${module}") + set(modules "${moduleIn}") endif() - set(docHeaderContents "${docHeaderContents}\n#include <Qt${module}/${globalHeader}>") - set(typeSystemDocXmlContents "${typeSystemDocXmlContents}\n<load-typesystem name=\"Qt${module}/typesystem_${lowerModule}.xml\" generate=\"yes\"/>") - endforeach() - endif() -endforeach() + foreach(module ${modules}) + string(TOLOWER "${module}" lowerModule) + # -- @TODO fix this for macOS frameworks. + file(APPEND "${CMAKE_CURRENT_LIST_DIR}/pyside-config.qdocconf" + " -I ${QT_INCLUDE_DIR}Qt${module} \\\n" + " -I ${QT_INCLUDE_DIR}Qt${module}/${Qt${QT_MAJOR_VERSION}Core_VERSION} \\\n" + " -I ${QT_INCLUDE_DIR}Qt${module}/${Qt${QT_MAJOR_VERSION}Core_VERSION}/Qt${module} \\\n") + + if (${moduleIn} STREQUAL "X11Extras") + set(globalHeader "QX11Info") + else() + set(globalHeader "Qt${module}") + endif() + set(docHeaderContents "${docHeaderContents}\n#include <Qt${module}/${globalHeader}>") + set(typeSystemDocXmlContents "${typeSystemDocXmlContents}\n<load-typesystem name=\"Qt${module}/typesystem_${lowerModule}.xml\" generate=\"yes\"/>") + endforeach() + endif() + endforeach() -#Appending the additional qdocconf that describes the pyside-examples -#doc project. -configure_file("qtmodules/pyside-examples.qdocconf.in" "${CMAKE_CURRENT_LIST_DIR}/qtmodules/pyside-examples.qdocconf" @ONLY) -file(APPEND "pyside.qdocconf.in" "\@CMAKE_CURRENT_LIST_DIR\@/qtmodules/pyside-examples.qdocconf\n") + #Appending the additional qdocconf that describes the pyside-examples + #doc project. + configure_file("qtmodules/pyside-examples.qdocconf.in" "${CMAKE_CURRENT_LIST_DIR}/qtmodules/pyside-examples.qdocconf" @ONLY) + file(APPEND "pyside.qdocconf.in" "\@CMAKE_CURRENT_LIST_DIR\@/qtmodules/pyside-examples.qdocconf\n") -set(typeSystemDocXmlContents "${typeSystemDocXmlContents}\n</typesystem>\n") -file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/typesystem_doc.xml" "${typeSystemDocXmlContents}") + set(typeSystemDocXmlContents "${typeSystemDocXmlContents}\n</typesystem>\n") + file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/typesystem_doc.xml" "${typeSystemDocXmlContents}") -set(docHeader "${pyside2_BINARY_DIR}/qdoc.h") -file(WRITE ${docHeader} "${docHeaderContents}") + set(docHeader "${pyside2_BINARY_DIR}/qdoc.h") + file(WRITE ${docHeader} "${docHeaderContents}") + configure_file("pyside.qdocconf.in" "pyside.qdocconf" @ONLY) -configure_file("pyside.qdocconf.in" "pyside.qdocconf" @ONLY) -set(QDOC_TYPESYSTEM_PATH "${pyside2_SOURCE_DIR}${PATH_SEP}${pyside2_BINARY_DIR}") + set(QDOC_TYPESYSTEM_PATH "${pyside2_SOURCE_DIR}${PATH_SEP}${pyside2_BINARY_DIR}") -add_custom_target(qdoc - # Use dummy Qt version information, QDoc needs it but has no effect on WebXML output - COMMAND ${CMAKE_COMMAND} -E env BUILDDIR=${CMAKE_CURRENT_LIST_DIR}/src QT_INSTALL_DOCS=${QT_SRC_DIR}/doc - QT_VERSION=${QT_VERSION_MAJOR}.${QT_VERSION_MINOR}.${QT_VERSION_PATCH} - QT_VER=${QT_VERSION_MAJOR}.${QT_VERSION_MINOR} - QT_VERSION_TAG=${QT_VERSION_MAJOR}.${QT_VERSION_MINOR}.${QT_VERSION_PATCH} - qdoc pyside.qdocconf -single-exec -installdir ${DOC_DATA_DIR} -outputdir ${DOC_DATA_DIR} - COMMENT "Running qdoc against Qt source code..." - SOURCE "pyside.qdocconf") + add_custom_target(qdoc + # Use dummy Qt version information, QDoc needs it but has no effect on WebXML output + COMMAND ${CMAKE_COMMAND} -E env BUILDDIR=${CMAKE_CURRENT_LIST_DIR}/src QT_INSTALL_DOCS=${QT_SRC_DIR}/doc + QT_VERSION=${QT_VERSION_MAJOR}.${QT_VERSION_MINOR}.${QT_VERSION_PATCH} + QT_VER=${QT_VERSION_MAJOR}.${QT_VERSION_MINOR} + QT_VERSION_TAG=${QT_VERSION_MAJOR}.${QT_VERSION_MINOR}.${QT_VERSION_PATCH} + qdoc pyside.qdocconf -single-exec -installdir ${DOC_DATA_DIR} -outputdir ${DOC_DATA_DIR} + COMMENT "Running qdoc against Qt source code..." + SOURCE "pyside.qdocconf") +endif() # conditional tag for sphinx build #string(JOIN "_" SPHINX_TAG ${DOC_OUTPUT_FORMAT} "format") @@ -168,9 +174,11 @@ add_custom_target("licensedocrsts" COMMENT "Creating 3rdparty license documentation..." ) -add_dependencies(apidoc docrsts licensedocrsts) -add_dependencies(licensedocrsts docrsts) -add_dependencies(docrsts qdoc) +if (FULLDOCSBUILD) + add_dependencies(apidoc docrsts licensedocrsts) + add_dependencies(licensedocrsts docrsts) + add_dependencies(docrsts qdoc) +endif() #install files add_custom_target(apidocinstall diff --git a/sources/pyside2/doc/conf.py.in b/sources/pyside2/doc/conf.py.in index e37abcb24..ae1bc6455 100644 --- a/sources/pyside2/doc/conf.py.in +++ b/sources/pyside2/doc/conf.py.in @@ -175,10 +175,16 @@ html_show_sourcelink = False # Link to the shiboken2 sphinx project to enable linking # between the two projects. -intersphinx_mapping = {'shiboken2': ('shiboken2','@CMAKE_BINARY_DIR@/doc/html/shiboken2/doc/html/objects.inv')} +intersphinx_mapping = {'shiboken2': ('shiboken2','@SHIBOKEN_INTERSPHINX_FILE@')} add_module_names = False +# Skip some warnings when building the documentation with +# 'build_rst_docs' due to the lack of qdoc generated files, in charge +# of sphinx modules (autodoc) and references. +if @SKIP_SPHINX_WARNINGS@: + suppress_warnings = ["autodoc", "autodoc.import_object", "ref.ref"] + # -- Options for qthelp output --------------------------------------------------- qthelp_theme = 'pysidedocs_qthelp' diff --git a/sources/pyside2/libpyside/feature_select.cpp b/sources/pyside2/libpyside/feature_select.cpp index a1ba76251..b26810add 100644 --- a/sources/pyside2/libpyside/feature_select.cpp +++ b/sources/pyside2/libpyside/feature_select.cpp @@ -546,40 +546,17 @@ static bool feature_01_addLowerNames(PyTypeObject *type, PyObject *prev_dict, in // Feature 0x02: Use true properties instead of getters and setters // -static PyObject *createProperty(PyObject *getter, PyObject *setter, PyObject *doc) +static PyObject *createProperty(PyObject *getter, PyObject *setter) { assert(getter != nullptr); if (setter == nullptr) setter = Py_None; - PyObject *deleter = Py_None; PyObject *prop = PyObject_CallObject(reinterpret_cast<PyObject *>(&PyProperty_Type), nullptr); - AutoDecRef args(Py_BuildValue("OOOO", getter, setter, deleter, doc)); + AutoDecRef args(Py_BuildValue("OO", getter, setter)); PyProperty_Type.tp_init(prop, args, nullptr); return prop; } -static PyObject *calcPropDocString(PyTypeObject *type, PyObject *getterName, PyObject *setterName) -{ - // To calculate the docstring, we need the __doc__ attribute of the original - // getter and setter. We temporatily switch back to no features. This - // might change when we have full signature support for features. - auto hold = type->tp_dict; - moveToFeatureSet(type, fast_id_array[0]); - auto dict = type->tp_dict; - auto getter = PyDict_GetItem(dict, getterName); - auto setter = setterName ? PyDict_GetItem(dict, setterName) : nullptr; - PyObject *buf = PyObject_GetAttr(getter, PyMagicName::doc()); - type->tp_dict = hold; - - if (setter == nullptr) - return buf; - AutoDecRef nl(Py_BuildValue("s", "\n")); - AutoDecRef wdoc(PyObject_GetAttr(setter, PyMagicName::doc())); - String::concat(&buf, nl); - String::concat(&buf, wdoc); - return buf; -} - static QStringList parseFields(const char *propstr) { /* @@ -639,10 +616,7 @@ static bool feature_02_true_property(PyTypeObject *type, PyObject *prev_dict, in if (setter != nullptr && Py_TYPE(setter) != PepMethodDescr_TypePtr) continue; - PyObject *doc_read = make_snake_case(fields[1], false); - PyObject *doc_write(haveWrite ? make_snake_case(fields[2], false) : nullptr); - AutoDecRef doc(calcPropDocString(type, doc_read, doc_write)); - AutoDecRef PyProperty(createProperty(getter, setter, doc)); + AutoDecRef PyProperty(createProperty(getter, setter)); if (PyProperty.isNull()) return false; if (PyDict_SetItem(prop_dict, name, PyProperty) < 0) diff --git a/sources/shiboken2/ApiExtractor/abstractmetalang.h b/sources/shiboken2/ApiExtractor/abstractmetalang.h index 466464807..648c792b3 100644 --- a/sources/shiboken2/ApiExtractor/abstractmetalang.h +++ b/sources/shiboken2/ApiExtractor/abstractmetalang.h @@ -1413,10 +1413,7 @@ public: AbstractMetaField *findField(const QString &name) const; - AbstractMetaEnumList enums() const - { - return m_enums; - } + const AbstractMetaEnumList &enums() const { return m_enums; } void setEnums(const AbstractMetaEnumList &enums) { m_enums = enums; diff --git a/sources/shiboken2/ApiExtractor/doxygenparser.cpp b/sources/shiboken2/ApiExtractor/doxygenparser.cpp index da6e32fba..cf152720e 100644 --- a/sources/shiboken2/ApiExtractor/doxygenparser.cpp +++ b/sources/shiboken2/ApiExtractor/doxygenparser.cpp @@ -199,8 +199,7 @@ void DoxygenParser::fillDocumentation(AbstractMetaClass* metaClass) } //Enums - const AbstractMetaEnumList &enums = metaClass->enums(); - for (AbstractMetaEnum *meta_enum : enums) { + for (AbstractMetaEnum *meta_enum : metaClass->enums()) { QString query = QLatin1String("/doxygen/compounddef/sectiondef/memberdef[@kind=\"enum\"]/name[text()=\"") + meta_enum->name() + QLatin1String("\"]/.."); QString doc = getDocumentation(xquery, query, DocModificationList()); diff --git a/sources/shiboken2/ApiExtractor/qtdocparser.cpp b/sources/shiboken2/ApiExtractor/qtdocparser.cpp index 40c04a403..d439b3fd5 100644 --- a/sources/shiboken2/ApiExtractor/qtdocparser.cpp +++ b/sources/shiboken2/ApiExtractor/qtdocparser.cpp @@ -285,8 +285,7 @@ void QtDocParser::fillDocumentation(AbstractMetaClass* metaClass) } #endif // Enums - const AbstractMetaEnumList &enums = metaClass->enums(); - for (AbstractMetaEnum *meta_enum : enums) { + for (AbstractMetaEnum *meta_enum : metaClass->enums()) { query.clear(); QTextStream(&query) << classQuery << "/enum[@name=\"" << meta_enum->name() << "\"]/description"; diff --git a/sources/shiboken2/doc/CMakeLists.txt b/sources/shiboken2/doc/CMakeLists.txt index 399dbba7a..5f59bfbf0 100644 --- a/sources/shiboken2/doc/CMakeLists.txt +++ b/sources/shiboken2/doc/CMakeLists.txt @@ -1,5 +1,8 @@ cmake_minimum_required(VERSION 3.16) +if(FULLDOCSBUILD EQUAL 0) + project(shiboken2_doc) +endif() find_program(SPHINX sphinx-build DOC "Path to sphinx-build binary.") if (SPHINX) diff --git a/sources/shiboken2/generator/indentor.h b/sources/shiboken2/generator/indentor.h index 111259f12..3dcec85b6 100644 --- a/sources/shiboken2/generator/indentor.h +++ b/sources/shiboken2/generator/indentor.h @@ -82,4 +82,18 @@ inline QTextStream &operator <<(QTextStream &s, const IndentorBase<tabWidth> &in return s; } +template <int tabWidth> +const char *indent(IndentorBase<tabWidth> &indentor) +{ + ++indentor.indent; + return ""; +} + +template <int tabWidth> +const char *outdent(IndentorBase<tabWidth> &indentor) +{ + --indentor.indent; + return ""; +} + #endif // GENERATOR_H diff --git a/sources/shiboken2/generator/qtdoc/qtdocgenerator.cpp b/sources/shiboken2/generator/qtdoc/qtdocgenerator.cpp index 1a7c195b4..282da6eca 100644 --- a/sources/shiboken2/generator/qtdoc/qtdocgenerator.cpp +++ b/sources/shiboken2/generator/qtdoc/qtdocgenerator.cpp @@ -1762,8 +1762,7 @@ void QtDocGenerator::writeEnums(QTextStream& s, const AbstractMetaClass* cppClas { static const QString section_title = QLatin1String(".. attribute:: "); - const AbstractMetaEnumList &enums = cppClass->enums(); - for (AbstractMetaEnum *en : enums) { + for (AbstractMetaEnum *en : cppClass->enums()) { s << section_title << cppClass->fullName() << '.' << en->name() << Qt::endl << Qt::endl; writeFormattedText(s, en->documentation().value(), cppClass); const auto version = versionOf(en->typeEntry()); @@ -2248,18 +2247,13 @@ void QtDocGenerator::writeModuleDocumentation() writeFancyToc(s, it.value()); - s << INDENT << ".. container:: hide\n\n"; - { - Indentation indentation(INDENT); - s << INDENT << ".. toctree::\n"; - Indentation deeperIndentation(INDENT); - s << INDENT << ":maxdepth: 1\n\n"; - for (const QString &className : qAsConst(it.value())) - s << INDENT << className << Qt::endl; - s << Qt::endl << Qt::endl; - } - - s << "Detailed Description\n--------------------\n\n"; + s << INDENT << ".. container:: hide\n\n" << indent(INDENT) + << INDENT << ".. toctree::\n" << indent(INDENT) + << INDENT << ":maxdepth: 1\n\n"; + for (const QString &className : qAsConst(it.value())) + s << INDENT << className << Qt::endl; + s << "\n\n" << outdent(INDENT) << outdent(INDENT) + << "Detailed Description\n--------------------\n\n"; // module doc is always wrong and C++istic, so go straight to the extra directory! QFile moduleDoc(m_extraSectionDir + QLatin1Char('/') + moduleName.mid(lastIndex + 1) + QLatin1String(".rst")); diff --git a/sources/shiboken2/generator/shiboken2/cppgenerator.cpp b/sources/shiboken2/generator/shiboken2/cppgenerator.cpp index 75633c072..8e9b480cc 100644 --- a/sources/shiboken2/generator/shiboken2/cppgenerator.cpp +++ b/sources/shiboken2/generator/shiboken2/cppgenerator.cpp @@ -1011,11 +1011,8 @@ void CppGenerator::writeVirtualMethodNative(QTextStream &s, s << INDENT << "Shiboken::GilState gil;\n"; // Get out of virtual method call if someone already threw an error. - s << INDENT << "if (PyErr_Occurred())\n"; - { - Indentation indentation(INDENT); - s << INDENT << returnStatement << '\n'; - } + s << INDENT << "if (PyErr_Occurred())\n" << indent(INDENT) + << INDENT << returnStatement << '\n' << outdent(INDENT); //PYSIDE-1019: Add info about properties. int propFlag = 0; @@ -1035,16 +1032,13 @@ void CppGenerator::writeVirtualMethodNative(QTextStream &s, s << INDENT << "static const char *funcName = \"" << propStr << funcName << "\";\n"; s << INDENT << "Shiboken::AutoDecRef " << PYTHON_OVERRIDE_VAR << "(Shiboken::BindingManager::instance().getOverride(this, nameCache, funcName));\n"; - s << INDENT << "if (" << PYTHON_OVERRIDE_VAR << ".isNull()) {\n"; - { - Indentation indentation(INDENT); - s << INDENT << "gil.release();\n"; - if (useOverrideCaching(func->ownerClass())) - s << INDENT << "m_PyMethodCache[" << cacheIndex << "] = true;\n"; - writeVirtualMethodCppCall(s, func, funcName, snips, lastArg, retType, - returnStatement); - } - s << INDENT << "}\n\n"; //WS + s << INDENT << "if (" << PYTHON_OVERRIDE_VAR << ".isNull()) {\n" + << indent(INDENT) << INDENT << "gil.release();\n"; + if (useOverrideCaching(func->ownerClass())) + s << INDENT << "m_PyMethodCache[" << cacheIndex << "] = true;\n"; + writeVirtualMethodCppCall(s, func, funcName, snips, lastArg, retType, + returnStatement); + s << outdent(INDENT) << INDENT << "}\n\n"; //WS writeConversionRule(s, func, TypeSystem::TargetLangCode); @@ -1193,15 +1187,14 @@ void CppGenerator::writeVirtualMethodNative(QTextStream &s, } if (invalidateReturn) { - s << INDENT << "if (invalidateArg0)\n"; - Indentation indentation(INDENT); - s << INDENT << "Shiboken::Object::releaseOwnership(" << PYTHON_RETURN_VAR << ".object());\n"; + s << INDENT << "if (invalidateArg0)\n" << indent(INDENT) + << INDENT << "Shiboken::Object::releaseOwnership(" << PYTHON_RETURN_VAR + << ".object());\n" << outdent(INDENT); } for (int argIndex : qAsConst(invalidateArgs)) { - s << INDENT << "if (invalidateArg" << argIndex << ")\n"; - Indentation indentation(INDENT); - s << INDENT << "Shiboken::Object::invalidate(PyTuple_GET_ITEM(" << PYTHON_ARGS << ", "; - s << (argIndex - 1) << "));\n"; + s << INDENT << "if (invalidateArg" << argIndex << ")\n" << indent(INDENT) + << INDENT << "Shiboken::Object::invalidate(PyTuple_GET_ITEM(" << PYTHON_ARGS + << ", " << (argIndex - 1) << "));\n" << outdent(INDENT); } @@ -1861,30 +1854,20 @@ void CppGenerator::writeConstructorWrapper(QTextStream &s, const AbstractMetaFun } if (metaClass->isAbstract()) { - s << INDENT << "if (type == myType) {\n"; - { - Indentation indent(INDENT); - s << INDENT << "PyErr_SetString(PyExc_NotImplementedError,\n"; - { - Indentation indentation(INDENT); - s << INDENT << "\"'" << metaClass->qualifiedCppName(); - } - s << "' represents a C++ abstract class and cannot be instantiated\");\n"; - s << INDENT << returnStatement(m_currentErrorCode) << Qt::endl; - } - s << INDENT<< "}\n\n"; + s << INDENT << "if (type == myType) {\n" << indent(INDENT) + << INDENT << "PyErr_SetString(PyExc_NotImplementedError,\n" << indent(INDENT) + << INDENT << "\"'" << metaClass->qualifiedCppName() + << "' represents a C++ abstract class and cannot be instantiated\");\n" << outdent(INDENT) + << INDENT << returnStatement(m_currentErrorCode) << '\n' << outdent(INDENT) + << INDENT<< "}\n\n"; } if (metaClass->baseClassNames().size() > 1) { - if (!metaClass->isAbstract()) { - s << INDENT << "if (type != myType) {\n"; - } - { - Indentation indentation(INDENT); - s << INDENT << "Shiboken::ObjectType::copyMultipleInheritance(type, myType);\n"; - } if (!metaClass->isAbstract()) - s << INDENT<< "}\n\n"; + s << INDENT << "if (type != myType)\n" << indent(INDENT); + s << INDENT << "Shiboken::ObjectType::copyMultipleInheritance(type, myType);\n"; + if (!metaClass->isAbstract()) + s << outdent(INDENT) << '\n'; } writeMethodWrapperPreamble(s, overloadData, classContext); @@ -1931,11 +1914,9 @@ void CppGenerator::writeConstructorWrapper(QTextStream &s, const AbstractMetaFun s << Qt::endl << INDENT << "// QObject setup\n"; s << INDENT << "PySide::Signal::updateSourceObject(self);\n"; s << INDENT << "metaObject = cptr->metaObject(); // <- init python qt properties\n"; - s << INDENT << "if (kwds && !PySide::fillQtProperties(self, metaObject, kwds, argNames, " << argNamesSet.count() << "))\n"; - { - Indentation indentation(INDENT); - s << INDENT << returnStatement(m_currentErrorCode) << Qt::endl; - } + s << INDENT << "if (kwds && !PySide::fillQtProperties(self, metaObject, kwds, argNames, " + << argNamesSet.count() << "))\n" << indent(INDENT) + << INDENT << returnStatement(m_currentErrorCode) << '\n' << outdent(INDENT); } // Constructor code injections, position=end @@ -2203,7 +2184,8 @@ void CppGenerator::writeCppSelfDefinition(QTextStream &s, Q_ASSERT(!(cppSelfAsReference && hasStaticOverload)); const AbstractMetaClass *metaClass = context.metaClass(); - bool useWrapperClass = avoidProtectedHack() && metaClass->hasProtectedMembers(); + bool useWrapperClass = avoidProtectedHack() && metaClass->hasProtectedMembers() + && !metaClass->attributes().testFlag(AbstractMetaAttributes::FinalCppClass); Q_ASSERT(!useWrapperClass || context.useWrapper()); QString className; if (!context.forSmartPointer()) { @@ -2919,14 +2901,11 @@ void CppGenerator::writeSingleFunctionCall(QTextStream &s, int numRemovedArgs = OverloadData::numberOfRemovedArguments(func); - s << INDENT << "if (!PyErr_Occurred()) {\n"; - { - Indentation indentation(INDENT); - writeMethodCall(s, func, context, func->arguments().size() - numRemovedArgs); - if (!func->isConstructor()) - writeNoneReturn(s, func, overloadData.hasNonVoidReturnType()); - } - s << INDENT << "}\n"; + s << INDENT << "if (!PyErr_Occurred()) {\n" << indent(INDENT); + writeMethodCall(s, func, context, func->arguments().size() - numRemovedArgs); + if (!func->isConstructor()) + writeNoneReturn(s, func, overloadData.hasNonVoidReturnType()); + s << outdent(INDENT) << INDENT << "}\n"; } QString CppGenerator::cppToPythonFunctionName(const QString &sourceTypeName, QString targetTypeName) @@ -3506,14 +3485,16 @@ void CppGenerator::writeMethodCall(QTextStream &s, const AbstractMetaFunction *f + QLatin1String(" *>(") + QLatin1String(CPP_SELF_VAR) + QLatin1Char(')'); if (func->isConstant()) { if (avoidProtectedHack()) { + auto ownerClass = func->ownerClass(); mc << "const_cast<const ::"; - if (func->ownerClass()->hasProtectedMembers()) { + if (ownerClass->hasProtectedMembers() + && !ownerClass->attributes().testFlag(AbstractMetaAttributes::FinalCppClass)) { // PYSIDE-500: Need a special wrapper cast when inherited - const QString selfWrapCast = func->ownerClass() == func->implementingClass() + const QString selfWrapCast = ownerClass == func->implementingClass() ? QLatin1String(CPP_SELF_VAR) - : QLatin1String("reinterpret_cast<") + wrapperName(func->ownerClass()) + : QLatin1String("reinterpret_cast<") + wrapperName(ownerClass) + QLatin1String(" *>(") + QLatin1String(CPP_SELF_VAR) + QLatin1Char(')'); - mc << wrapperName(func->ownerClass()); + mc << wrapperName(ownerClass); mc << " *>(" << selfWrapCast << ")->"; } else { @@ -4555,26 +4536,20 @@ void CppGenerator::writeSetterFunctionPreamble(QTextStream &s, const QString &na writeCppSelfDefinition(s, context); - s << INDENT << "if (pyIn == " << NULL_PTR << ") {\n"; - { - Indentation indent(INDENT); - s << INDENT << "PyErr_SetString(PyExc_TypeError, \"'"; - s << name << "' may not be deleted\");\n"; - s << INDENT << "return -1;\n"; - } - s << INDENT << "}\n"; + s << INDENT << "if (pyIn == " << NULL_PTR << ") {\n" << indent(INDENT) + << INDENT << "PyErr_SetString(PyExc_TypeError, \"'" + << name << "' may not be deleted\");\n" + << INDENT << "return -1;\n" + << outdent(INDENT) << INDENT << "}\n"; s << INDENT << "PythonToCppFunc " << PYTHON_TO_CPP_VAR << "{nullptr};\n"; s << INDENT << "if (!"; writeTypeCheck(s, type, QLatin1String("pyIn"), isNumber(type->typeEntry())); - s << ") {\n"; - { - Indentation indent(INDENT); - s << INDENT << "PyErr_SetString(PyExc_TypeError, \"wrong type attributed to '"; - s << name << "', '" << type->name() << "' or convertible type expected\");\n"; - s << INDENT << "return -1;\n"; - } - s << INDENT<< "}\n\n"; + s << ") {\n" << indent(INDENT) + << INDENT << "PyErr_SetString(PyExc_TypeError, \"wrong type attributed to '" + << name << "', '" << type->name() << "' or convertible type expected\");\n" + << INDENT << "return -1;\n" + << outdent(INDENT) << INDENT<< "}\n\n"; } void CppGenerator::writeSetterFunction(QTextStream &s, @@ -5430,8 +5405,7 @@ void CppGenerator::writeInitQtMetaTypeFunctionBody(QTextStream &s, const Generat } } - const AbstractMetaEnumList &enums = metaClass->enums(); - for (AbstractMetaEnum *metaEnum : enums) { + for (AbstractMetaEnum *metaEnum : metaClass->enums()) { if (!metaEnum->isPrivate() && !metaEnum->isAnonymous()) { for (const QString &name : qAsConst(nameVariants)) s << INDENT << "qRegisterMetaType< ::" << metaEnum->typeEntry()->qualifiedCppName() << " >(\"" << name << "::" << metaEnum->name() << "\");\n"; @@ -5752,7 +5726,7 @@ bool CppGenerator::finishGeneration() QString signaturesString; QTextStream signatureStream(&signaturesString); - Indentation indent(INDENT); + Indentation indentation(INDENT); const auto functionGroups = getGlobalFunctionGroups(); for (auto it = functionGroups.cbegin(), end = functionGroups.cend(); it != end; ++it) { @@ -5877,19 +5851,13 @@ bool CppGenerator::finishGeneration() s << "void cleanTypesAttributes(void) {\n"; s << INDENT << "if (PY_VERSION_HEX >= 0x03000000 && PY_VERSION_HEX < 0x03060000)\n"; s << INDENT << " return; // PYSIDE-953: testbinding crashes in Python 3.5 when hasattr touches types!\n"; - s << INDENT << "for (int i = 0, imax = SBK_" << moduleName() << "_IDX_COUNT; i < imax; i++) {\n"; - { - Indentation indentation(INDENT); - s << INDENT << "PyObject *pyType = reinterpret_cast<PyObject *>(" << cppApiVariableName() << "[i]);\n"; - s << INDENT << "Shiboken::AutoDecRef attrName(Py_BuildValue(\"s\", \"staticMetaObject\"));\n"; - s << INDENT << "if (pyType && PyObject_HasAttr(pyType, attrName))\n"; - { - Indentation indentation(INDENT); - s << INDENT << "PyObject_SetAttr(pyType, attrName, Py_None);\n"; - } - } - s << INDENT << "}\n"; - s << "}\n"; + s << INDENT << "for (int i = 0, imax = SBK_" << moduleName() + << "_IDX_COUNT; i < imax; i++) {\n" << indent(INDENT) + << INDENT << "PyObject *pyType = reinterpret_cast<PyObject *>(" << cppApiVariableName() << "[i]);\n" + << INDENT << "Shiboken::AutoDecRef attrName(Py_BuildValue(\"s\", \"staticMetaObject\"));\n" + << INDENT << "if (pyType && PyObject_HasAttr(pyType, attrName))\n" << indent(INDENT) + << INDENT << "PyObject_SetAttr(pyType, attrName, Py_None);\n" << outdent(INDENT) + << outdent(INDENT) << INDENT << "}\n" << "}\n"; } s << "// Global functions "; @@ -6003,19 +5971,15 @@ bool CppGenerator::finishGeneration() writeCodeSnips(s, snips, TypeSystem::CodeSnipPositionBeginning, TypeSystem::TargetLangCode); for (const QString &requiredModule : requiredModules) { - s << INDENT << "{\n"; - { - Indentation indentation(INDENT); - s << INDENT << "Shiboken::AutoDecRef requiredModule(Shiboken::Module::import(\"" << requiredModule << "\"));\n"; - s << INDENT << "if (requiredModule.isNull())\n"; - { - Indentation indentation(INDENT); - s << INDENT << "return SBK_MODULE_INIT_ERROR;\n"; - } - s << INDENT << cppApiVariableName(requiredModule) << " = Shiboken::Module::getTypes(requiredModule);\n"; - s << INDENT << convertersVariableName(requiredModule) << " = Shiboken::Module::getTypeConverters(requiredModule);\n"; - } - s << INDENT << "}\n\n"; + s << INDENT << "{\n" << indent(INDENT) + << INDENT << "Shiboken::AutoDecRef requiredModule(Shiboken::Module::import(\"" << requiredModule << "\"));\n" + << INDENT << "if (requiredModule.isNull())\n" << indent(INDENT) + << INDENT << "return SBK_MODULE_INIT_ERROR;\n" << outdent(INDENT) + << INDENT << cppApiVariableName(requiredModule) + << " = Shiboken::Module::getTypes(requiredModule);\n" + << INDENT << convertersVariableName(requiredModule) + << " = Shiboken::Module::getTypeConverters(requiredModule);\n" << outdent(INDENT) + << INDENT << "}\n\n"; } int maxTypeIndex = getMaxTypeIndex() + instantiatedSmartPointers().size(); @@ -6088,13 +6052,10 @@ bool CppGenerator::finishGeneration() s << INDENT << "Shiboken::Module::registerTypes(module, " << cppApiVariableName() << ");\n"; s << INDENT << "Shiboken::Module::registerTypeConverters(module, " << convertersVariableName() << ");\n"; - s << Qt::endl << INDENT << "if (PyErr_Occurred()) {\n"; - { - Indentation indentation(INDENT); - s << INDENT << "PyErr_Print();\n"; - s << INDENT << "Py_FatalError(\"can't initialize module " << moduleName() << "\");\n"; - } - s << INDENT << "}\n"; + s << '\n' << INDENT << "if (PyErr_Occurred()) {\n" << indent(INDENT) + << INDENT << "PyErr_Print();\n" + << INDENT << "Py_FatalError(\"can't initialize module " << moduleName() << "\");\n" + << outdent(INDENT) << INDENT << "}\n"; // module inject-code target/end if (!snips.isEmpty()) diff --git a/sources/shiboken2/generator/shiboken2/headergenerator.cpp b/sources/shiboken2/generator/shiboken2/headergenerator.cpp index 7826e350a..1fdd89af8 100644 --- a/sources/shiboken2/generator/shiboken2/headergenerator.cpp +++ b/sources/shiboken2/generator/shiboken2/headergenerator.cpp @@ -343,8 +343,7 @@ void HeaderGenerator::writeTypeIndexValueLines(QTextStream &s, const AbstractMet if (!typeEntry->generateCode() || !NamespaceTypeEntry::isVisibleScope(typeEntry)) return; writeTypeIndexValueLine(s, metaClass->typeEntry()); - const AbstractMetaEnumList &enums = metaClass->enums(); - for (const AbstractMetaEnum *metaEnum : enums) { + for (const AbstractMetaEnum *metaEnum : metaClass->enums()) { if (metaEnum->isPrivate()) continue; writeTypeIndexValueLine(s, metaEnum->typeEntry()); @@ -503,8 +502,7 @@ bool HeaderGenerator::finishGeneration() const TypeEntry *classType = metaClass->typeEntry(); includes << classType->include(); - const AbstractMetaEnumList &enums = metaClass->enums(); - for (const AbstractMetaEnum *cppEnum : enums) { + for (const AbstractMetaEnum *cppEnum : metaClass->enums()) { if (cppEnum->isAnonymous() || cppEnum->isPrivate()) continue; EnumTypeEntry *enumType = cppEnum->typeEntry(); diff --git a/sources/shiboken2/generator/shiboken2/shibokengenerator.cpp b/sources/shiboken2/generator/shiboken2/shibokengenerator.cpp index 825965b92..1f91609bd 100644 --- a/sources/shiboken2/generator/shiboken2/shibokengenerator.cpp +++ b/sources/shiboken2/generator/shiboken2/shibokengenerator.cpp @@ -339,8 +339,7 @@ void ShibokenGenerator::lookForEnumsInClassesNotToBeGenerated(AbstractMetaEnumLi Q_ASSERT(metaClass); // if a scope is not to be generated, collect its enums into the parent scope if (!NamespaceTypeEntry::isVisibleScope(metaClass->typeEntry())) { - const AbstractMetaEnumList &enums = metaClass->enums(); - for (AbstractMetaEnum *metaEnum : enums) { + for (AbstractMetaEnum *metaEnum : metaClass->enums()) { if (!metaEnum->isPrivate() && metaEnum->typeEntry()->generateCode() && !enumList.contains(metaEnum)) { enumList.append(metaEnum); diff --git a/sources/shiboken2/libshiboken/sbkstaticstrings.cpp b/sources/shiboken2/libshiboken/sbkstaticstrings.cpp index 672be4009..5559d58d6 100644 --- a/sources/shiboken2/libshiboken/sbkstaticstrings.cpp +++ b/sources/shiboken2/libshiboken/sbkstaticstrings.cpp @@ -56,6 +56,8 @@ STATIC_STRING_IMPL(dumps, "dumps") STATIC_STRING_IMPL(fget, "fget") STATIC_STRING_IMPL(fset, "fset") STATIC_STRING_IMPL(loads, "loads") +STATIC_STRING_IMPL(multi, "multi") +STATIC_STRING_IMPL(name, "name") STATIC_STRING_IMPL(result, "result") STATIC_STRING_IMPL(value, "value") STATIC_STRING_IMPL(values, "values") diff --git a/sources/shiboken2/libshiboken/sbkstaticstrings.h b/sources/shiboken2/libshiboken/sbkstaticstrings.h index 09e22b395..b72fa989b 100644 --- a/sources/shiboken2/libshiboken/sbkstaticstrings.h +++ b/sources/shiboken2/libshiboken/sbkstaticstrings.h @@ -55,6 +55,8 @@ LIBSHIBOKEN_API PyObject *fset(); LIBSHIBOKEN_API PyObject *f_code(); LIBSHIBOKEN_API PyObject *f_lineno(); LIBSHIBOKEN_API PyObject *loads(); +LIBSHIBOKEN_API PyObject *multi(); +LIBSHIBOKEN_API PyObject *name(); LIBSHIBOKEN_API PyObject *result(); LIBSHIBOKEN_API PyObject *value(); LIBSHIBOKEN_API PyObject *values(); diff --git a/sources/shiboken2/libshiboken/signature/signature.cpp b/sources/shiboken2/libshiboken/signature/signature.cpp index 76a71b00b..085d751aa 100644 --- a/sources/shiboken2/libshiboken/signature/signature.cpp +++ b/sources/shiboken2/libshiboken/signature/signature.cpp @@ -343,6 +343,9 @@ PyObject *PySide_BuildSignatureProps(PyObject *type_key) empty_dict = PyDict_New(); return empty_dict; } + // PYSIDE-1019: Build snake case versions of the functions. + if (insert_snake_case_variants(dict) < 0) + return nullptr; // We replace the arguments by the result dict. if (PyDict_SetItem(pyside_globals->arg_dict, type_key, dict) < 0) return nullptr; diff --git a/sources/shiboken2/libshiboken/signature/signature_helper.cpp b/sources/shiboken2/libshiboken/signature/signature_helper.cpp index 7e92a9861..2b360c786 100644 --- a/sources/shiboken2/libshiboken/signature/signature_helper.cpp +++ b/sources/shiboken2/libshiboken/signature/signature_helper.cpp @@ -150,6 +150,28 @@ static PyObject *compute_name_key(PyObject *ob) return Py_BuildValue("(OO)", type_key.object(), func_name.object()); } +static PyObject *_func_with_new_name(PyTypeObject *type, + PyMethodDef *meth, + const char *new_name) +{ + /* + * Create a function with a lower case name. + * Note: This is similar to feature_select's methodWithNewName, + * but does not create a descriptor. + * XXX Maybe we can get rid of this, completely? + */ + auto obtype = reinterpret_cast<PyObject *>(type); + int len = strlen(new_name); + auto name = new char[len + 1]; + strcpy(name, new_name); + auto new_meth = new PyMethodDef; + new_meth->ml_name = name; + new_meth->ml_meth = meth->ml_meth; + new_meth->ml_flags = meth->ml_flags; + new_meth->ml_doc = meth->ml_doc; + return PyCFunction_NewEx(new_meth, obtype, nullptr); +} + static int build_name_key_to_func(PyObject *obtype) { auto *type = reinterpret_cast<PyTypeObject *>(obtype); @@ -167,6 +189,17 @@ static int build_name_key_to_func(PyObject *obtype) || PyDict_SetItem(pyside_globals->map_dict, name_key, func) < 0) return -1; } + // PYSIDE-1019: Now we repeat the same for snake case names. + meth = type->tp_methods; + for (; meth->ml_name != nullptr; meth++) { + const char *name = String::toCString(String::getSnakeCaseName(meth->ml_name, true)); + AutoDecRef func(_func_with_new_name(type, meth, name)); + AutoDecRef func_name(get_funcname(func)); + AutoDecRef name_key(Py_BuildValue("(OO)", type_key.object(), func_name.object())); + if (func.isNull() || name_key.isNull() + || PyDict_SetItem(pyside_globals->map_dict, name_key, func) < 0) + return -1; + } return 0; } @@ -198,6 +231,46 @@ PyObject *name_key_to_func(PyObject *ob) return ret; } +static PyObject *_build_new_entry(PyObject *new_name, PyObject *value) +{ + PyObject *new_value = PyDict_Copy(value); + PyObject *multi = PyDict_GetItem(value, PyName::multi()); + if (multi != nullptr && Py_TYPE(multi) == &PyList_Type) { + ssize_t len = PyList_Size(multi); + AutoDecRef list(PyList_New(len)); + if (list.isNull()) + return nullptr; + for (int idx = 0; idx < len; ++idx) { + auto multi_entry = PyList_GetItem(multi, idx); + auto dup = PyDict_Copy(multi_entry); + if (PyDict_SetItem(dup, PyName::name(), new_name) < 0) + return nullptr; + if (PyList_SetItem(list, idx, dup) < 0) + return nullptr; + } + if (PyDict_SetItem(new_value, PyName::multi(), list) < 0) + return nullptr; + } else { + if (PyDict_SetItem(new_value, PyName::name(), new_name) < 0) + return nullptr; + } + return new_value; +} + +int insert_snake_case_variants(PyObject *dict) +{ + AutoDecRef snake_dict(PyDict_New()); + PyObject *key, *value; + Py_ssize_t pos = 0; + while (PyDict_Next(dict, &pos, &key, &value)) { + AutoDecRef name(String::getSnakeCaseName(key, true)); + AutoDecRef new_value(_build_new_entry(name, value)); + if (PyDict_SetItem(snake_dict, name, new_value) < 0) + return -1; + } + return PyDict_Merge(dict, snake_dict, 0); +} + PyObject *_get_class_of_cf(PyObject *ob_cf) { PyObject *selftype = PyCFunction_GET_SELF(ob_cf); diff --git a/sources/shiboken2/libshiboken/signature/signature_p.h b/sources/shiboken2/libshiboken/signature/signature_p.h index 9444f3e9b..ef7846472 100644 --- a/sources/shiboken2/libshiboken/signature/signature_p.h +++ b/sources/shiboken2/libshiboken/signature/signature_p.h @@ -95,6 +95,7 @@ PyObject *pyside_tp_get___doc__(PyObject *tp); PyObject *_get_qualname(PyObject *ob); int add_more_getsets(PyTypeObject *type, PyGetSetDef *gsp, PyObject **doc_descr); PyObject *name_key_to_func(PyObject *ob); +int insert_snake_case_variants(PyObject *dict); PyObject *_get_class_of_cf(PyObject *ob_cf); PyObject *_get_class_of_sm(PyObject *ob_sm); PyObject *_get_class_of_descr(PyObject *ob); diff --git a/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/parser.py b/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/parser.py index 8a814114a..20c791cc1 100644 --- a/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/parser.py +++ b/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/parser.py @@ -349,7 +349,6 @@ def calculate_props(line): props.annotations = annotations props.varnames = varnames = tuple(tup[0] for tup in arglist) funcname = parsed.funcname - props.fullname = funcname shortname = funcname[funcname.rindex(".")+1:] props.name = shortname props.multi = parsed.multi @@ -366,7 +365,6 @@ def fix_variables(props, line): if retvar and isinstance(retvar, (ResultVariable, ArrayLikeVariable)): # Special case: a ResultVariable which is the result will always be an array! annos["return"] = retvar = typing.List[retvar.type] - fullname = props.fullname varnames = list(props.varnames) defaults = list(props.defaults) diff = len(varnames) - len(defaults) @@ -456,8 +454,7 @@ def pyside_type_init(type_key, sig_strings): multi_props.append(props) if multi > 0: continue - fullname = props.pop("fullname") - multi_props = {"multi": multi_props, "fullname": fullname} + multi_props = {"multi": multi_props} ret[shortname] = multi_props dprint(multi_props) multi_props = [] diff --git a/sources/shiboken2/tests/libother/otherobjecttype.cpp b/sources/shiboken2/tests/libother/otherobjecttype.cpp index 1f782ecd8..c7778e7c9 100644 --- a/sources/shiboken2/tests/libother/otherobjecttype.cpp +++ b/sources/shiboken2/tests/libother/otherobjecttype.cpp @@ -40,7 +40,7 @@ int OtherObjectType::enumAsInt(SampleNamespace::SomeClass::PublicScopedEnum valu return static_cast<int>(value); } -int OtherObjectType::enumAsIntForInvisibleNamespace(Invisible::EnumOnNamespace value) +int OtherObjectType::enumAsIntForInvisibleNamespace(RemovedNamespace1::RemovedNamespace1_Enum value) { return static_cast<int>(value); } diff --git a/sources/shiboken2/tests/libother/otherobjecttype.h b/sources/shiboken2/tests/libother/otherobjecttype.h index efd394347..a9362c370 100644 --- a/sources/shiboken2/tests/libother/otherobjecttype.h +++ b/sources/shiboken2/tests/libother/otherobjecttype.h @@ -36,13 +36,14 @@ #include "objecttype.h" #include "collector.h" #include "samplenamespace.h" +#include "removednamespaces.h" class LIBOTHER_API OtherObjectType : public ObjectType { public: static int enumAsInt(SampleNamespace::SomeClass::PublicScopedEnum value); - static int enumAsIntForInvisibleNamespace(Invisible::EnumOnNamespace value); + static int enumAsIntForInvisibleNamespace(RemovedNamespace1::RemovedNamespace1_Enum value); }; diff --git a/sources/shiboken2/tests/libsample/removednamespaces.h b/sources/shiboken2/tests/libsample/removednamespaces.h index c9732f8a6..9ad798bf7 100644 --- a/sources/shiboken2/tests/libsample/removednamespaces.h +++ b/sources/shiboken2/tests/libsample/removednamespaces.h @@ -34,10 +34,18 @@ namespace RemovedNamespace1 { -enum RemovedNamespace1_Enum { RemovedNamespace1_Enum_Value0 }; +enum RemovedNamespace1_Enum { RemovedNamespace1_Enum_Value0 = 0, + RemovedNamespace1_Enum_Value1 = 1 }; enum { RemovedNamespace1_AnonymousEnum_Value0 }; +struct ObjectOnInvisibleNamespace +{ + bool exists() const { return true; } + static int toInt(RemovedNamespace1_Enum e) { return static_cast<int>(e); } + static ObjectOnInvisibleNamespace consume(const ObjectOnInvisibleNamespace &other) { return other; } +}; + namespace RemovedNamespace2 { diff --git a/sources/shiboken2/tests/libsample/samplenamespace.h b/sources/shiboken2/tests/libsample/samplenamespace.h index 0d418de16..5fe269c5e 100644 --- a/sources/shiboken2/tests/libsample/samplenamespace.h +++ b/sources/shiboken2/tests/libsample/samplenamespace.h @@ -41,25 +41,6 @@ enum { AnonymousGlobalEnum_Value1 }; -// Invisible namespace -namespace Invisible -{ - -enum EnumOnNamespace { - Option1 = 1, - Option2 = 2, - Option3 = 3 -}; - -struct ObjectOnInvisibleNamespace -{ - bool exists() const { return true; } - static int toInt(EnumOnNamespace e) { return static_cast<int>(e); } - static ObjectOnInvisibleNamespace consume(const ObjectOnInvisibleNamespace &other) { return other; } -}; - -}; - namespace SampleNamespace { diff --git a/sources/shiboken2/tests/libsample/virtualmethods.h b/sources/shiboken2/tests/libsample/virtualmethods.h index 4937fab0d..4b2d25805 100644 --- a/sources/shiboken2/tests/libsample/virtualmethods.h +++ b/sources/shiboken2/tests/libsample/virtualmethods.h @@ -108,6 +108,10 @@ public: virtual const Str & returnConstRef() const; +protected: + // PYSIDE-1388: Protected hack with final classes (see VirtualFinalDaughter). + void protectedMethod() {} + private: Str m_name; int m_left; diff --git a/sources/shiboken2/tests/samplebinding/CMakeLists.txt b/sources/shiboken2/tests/samplebinding/CMakeLists.txt index ad52565ad..6bbfdb5bc 100644 --- a/sources/shiboken2/tests/samplebinding/CMakeLists.txt +++ b/sources/shiboken2/tests/samplebinding/CMakeLists.txt @@ -97,6 +97,7 @@ ${CMAKE_CURRENT_BINARY_DIR}/sample/rectf_wrapper.cpp ${CMAKE_CURRENT_BINARY_DIR}/sample/reference_wrapper.cpp ${CMAKE_CURRENT_BINARY_DIR}/sample/referentmodelindex_wrapper.cpp ${CMAKE_CURRENT_BINARY_DIR}/sample/toberenamedvalue_wrapper.cpp +${CMAKE_CURRENT_BINARY_DIR}/sample/removednamespace1_objectoninvisiblenamespace_wrapper.cpp ${CMAKE_CURRENT_BINARY_DIR}/sample/renameduser_wrapper.cpp ${CMAKE_CURRENT_BINARY_DIR}/sample/sample_module_wrapper.cpp ${CMAKE_CURRENT_BINARY_DIR}/sample/sample_wrapper.cpp @@ -127,7 +128,6 @@ ${CMAKE_CURRENT_BINARY_DIR}/sample/filter_wrapper.cpp ${CMAKE_CURRENT_BINARY_DIR}/sample/data_wrapper.cpp ${CMAKE_CURRENT_BINARY_DIR}/sample/intersection_wrapper.cpp ${CMAKE_CURRENT_BINARY_DIR}/sample/union_wrapper.cpp -${CMAKE_CURRENT_BINARY_DIR}/sample/invisible_objectoninvisiblenamespace_wrapper.cpp ) configure_file("${CMAKE_CURRENT_SOURCE_DIR}/sample-binding.txt.in" diff --git a/sources/shiboken2/tests/samplebinding/enumfromremovednamespace_test.py b/sources/shiboken2/tests/samplebinding/enumfromremovednamespace_test.py index 4b0866c4e..168a609dc 100644 --- a/sources/shiboken2/tests/samplebinding/enumfromremovednamespace_test.py +++ b/sources/shiboken2/tests/samplebinding/enumfromremovednamespace_test.py @@ -38,15 +38,33 @@ from shiboken_paths import init_paths init_paths() import sample +from shiboken_test_helper import objectFullname + class TestEnumFromRemovedNamespace(unittest.TestCase): def testEnumPromotedToGlobal(self): sample.RemovedNamespace1_Enum - sample.RemovedNamespace1_Enum_Value0 + self.assertEqual(sample.RemovedNamespace1_Enum_Value0, 0) + self.assertEqual(sample.RemovedNamespace1_Enum_Value1, 1) sample.RemovedNamespace1_AnonymousEnum_Value0 sample.RemovedNamespace2_Enum sample.RemovedNamespace2_Enum_Value0 + def testNames(self): + # Test if invisible namespace does not appear on type name + self.assertEqual(objectFullname(sample.RemovedNamespace1_Enum), + "sample.RemovedNamespace1_Enum") + self.assertEqual(objectFullname(sample.ObjectOnInvisibleNamespace), + "sample.ObjectOnInvisibleNamespace") + + # Function arguments + signature = sample.ObjectOnInvisibleNamespace.toInt.__signature__ + self.assertEqual(objectFullname(signature.parameters['e'].annotation), + "sample.RemovedNamespace1_Enum") + signature = sample.ObjectOnInvisibleNamespace.consume.__signature__ + self.assertEqual(objectFullname(signature.parameters['other'].annotation), + "sample.ObjectOnInvisibleNamespace") + def testEnumPromotedToUpperNamespace(self): sample.UnremovedNamespace sample.UnremovedNamespace.RemovedNamespace3_Enum diff --git a/sources/shiboken2/tests/samplebinding/namespace_test.py b/sources/shiboken2/tests/samplebinding/namespace_test.py index 8171e0e43..807e7bfc9 100644 --- a/sources/shiboken2/tests/samplebinding/namespace_test.py +++ b/sources/shiboken2/tests/samplebinding/namespace_test.py @@ -43,20 +43,15 @@ from sample import * from shiboken_test_helper import objectFullname +# For tests of invisible namespaces, see +# enumfromremovednamespace_test.py / removednamespaces.h + + class TestVariablesUnderNamespace(unittest.TestCase): def testIt(self): self.assertEqual(SampleNamespace.variableInNamespace, 42) -class TestEnumUnderNamespace(unittest.TestCase): - def testInvisibleNamespace(self): - o1 = EnumOnNamespace.Option1 - self.assertEqual(o1, 1) - def testTpNames(self): - self.assertEqual(objectFullname(EnumOnNamespace), "sample.EnumOnNamespace") - self.assertEqual(str(EnumOnNamespace.Option1), - "sample.EnumOnNamespace.Option1") - class TestClassesUnderNamespace(unittest.TestCase): def testIt(self): c1 = SampleNamespace.SomeClass() @@ -87,14 +82,7 @@ class TestClassesUnderNamespace(unittest.TestCase): "sample.SampleNamespace.InValue") self.assertEqual(objectFullname(SampleNamespace.enumAsInt.__signature__.parameters['value'].annotation), "sample.SampleNamespace.SomeClass.PublicScopedEnum") - self.assertEqual(objectFullname(ObjectOnInvisibleNamespace.toInt.__signature__.parameters['e'].annotation), - "sample.EnumOnNamespace") - # Test if enum on namespace that was marked as not gerenated does not appear on type name - self.assertEqual(objectFullname(ObjectOnInvisibleNamespace), - "sample.ObjectOnInvisibleNamespace") - self.assertEqual(objectFullname(ObjectOnInvisibleNamespace.consume.__signature__.parameters['other'].annotation), - "sample.ObjectOnInvisibleNamespace") if __name__ == '__main__': unittest.main() diff --git a/sources/shiboken2/tests/samplebinding/typesystem_sample.xml b/sources/shiboken2/tests/samplebinding/typesystem_sample.xml index fdddb80f2..e4963721b 100644 --- a/sources/shiboken2/tests/samplebinding/typesystem_sample.xml +++ b/sources/shiboken2/tests/samplebinding/typesystem_sample.xml @@ -536,11 +536,6 @@ <enum-type identified-by-value="AnonymousGlobalEnum_Value0"/> - <namespace-type name="Invisible" visible="no"> - <enum-type name="EnumOnNamespace" /> - <value-type name="ObjectOnInvisibleNamespace" /> - </namespace-type> - <namespace-type name="SampleNamespace"> <enum-type name="Option"/> <enum-type name="InValue"/> @@ -603,6 +598,7 @@ <namespace-type name="RemovedNamespace1" visible='false'> <enum-type name="RemovedNamespace1_Enum" /> + <value-type name="ObjectOnInvisibleNamespace" /> <namespace-type name="RemovedNamespace2" visible='false'> <enum-type name="RemovedNamespace2_Enum" /> </namespace-type> |