aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFriedemann Kleint <Friedemann.Kleint@qt.io>2019-05-24 09:47:11 +0200
committerFriedemann Kleint <Friedemann.Kleint@qt.io>2019-05-24 09:47:11 +0200
commit235092d72f57d42de04501b04f6a3ff4d77adf74 (patch)
treef887244a022ea8508055412cb3e85ba8158cf509
parent6ba23a245449aaa9c1a7ab8e954d93f5f4366530 (diff)
parent8c772c12612b408b42e66ad0627d37477f42255a (diff)
Merge remote-tracking branch 'origin/5.13' into dev
-rw-r--r--sources/pyside2/PySide2/QtGui/CMakeLists.txt1
-rw-r--r--sources/pyside2/PySide2/QtGui/typesystem_gui_common.xml5
-rw-r--r--sources/pyside2/PySide2/support/generate_pyi.py211
-rw-r--r--sources/pyside2/cmake/Macros/PySideModules.cmake31
-rw-r--r--sources/pyside2/tests/QtGui/CMakeLists.txt1
-rw-r--r--sources/pyside2/tests/QtGui/qtextdocument_functions.py47
-rw-r--r--sources/shiboken2/ApiExtractor/abstractmetabuilder.cpp56
-rw-r--r--sources/shiboken2/ApiExtractor/abstractmetabuilder_p.h6
-rw-r--r--sources/shiboken2/ApiExtractor/abstractmetalang.h8
-rw-r--r--sources/shiboken2/ApiExtractor/clangparser/clangbuilder.cpp14
-rw-r--r--sources/shiboken2/ApiExtractor/messages.cpp29
-rw-r--r--sources/shiboken2/ApiExtractor/messages.h9
-rw-r--r--sources/shiboken2/ApiExtractor/parser/codemodel.cpp16
-rw-r--r--sources/shiboken2/ApiExtractor/parser/codemodel.h4
-rw-r--r--sources/shiboken2/ApiExtractor/typedatabase.cpp34
-rw-r--r--sources/shiboken2/ApiExtractor/typedatabase.h3
-rw-r--r--sources/shiboken2/ApiExtractor/typedatabase_typedefs.h2
-rw-r--r--sources/shiboken2/ApiExtractor/typesystem.cpp66
-rw-r--r--sources/shiboken2/ApiExtractor/typesystem.h20
-rw-r--r--sources/shiboken2/ApiExtractor/typesystem_p.h6
-rw-r--r--sources/shiboken2/generator/shiboken2/cppgenerator.cpp12
-rw-r--r--sources/shiboken2/generator/shiboken2/shibokengenerator.cpp10
22 files changed, 389 insertions, 202 deletions
diff --git a/sources/pyside2/PySide2/QtGui/CMakeLists.txt b/sources/pyside2/PySide2/QtGui/CMakeLists.txt
index 48354987d..18d80f647 100644
--- a/sources/pyside2/PySide2/QtGui/CMakeLists.txt
+++ b/sources/pyside2/PySide2/QtGui/CMakeLists.txt
@@ -197,6 +197,7 @@ ${QtGui_GEN_DIR}/qtouchdevice_wrapper.cpp
${QtGui_GEN_DIR}/qtouchevent_touchpoint_wrapper.cpp
${QtGui_GEN_DIR}/qtouchevent_wrapper.cpp
${QtGui_GEN_DIR}/qtransform_wrapper.cpp
+${QtGui_GEN_DIR}/qt_wrapper.cpp
${QtGui_GEN_DIR}/qvalidator_wrapper.cpp
${QtGui_GEN_DIR}/qvector2d_wrapper.cpp
${QtGui_GEN_DIR}/qvector3d_wrapper.cpp
diff --git a/sources/pyside2/PySide2/QtGui/typesystem_gui_common.xml b/sources/pyside2/PySide2/QtGui/typesystem_gui_common.xml
index a496238b8..b42391148 100644
--- a/sources/pyside2/PySide2/QtGui/typesystem_gui_common.xml
+++ b/sources/pyside2/PySide2/QtGui/typesystem_gui_common.xml
@@ -202,6 +202,11 @@
<rejection class="QActionGroup" function-name="selected"/>
<rejection class="QPaintEngine" function-name="fix_neg_rect"/>
+ <!-- For Qt::mightBeRichText(QString), Qt::convertFromPlainText(QString,Qt::WhiteSpaceMode)
+ Match on files from '/QtGui/' (shadow build) or '/gui/' (developer build)
+ or '/QtGui.framework' (macOS) -->
+ <namespace-type name="Qt" files="^.*/(gui|QtGui)[/.].*\.h$" extends="PySide2.QtCore"/>
+
<primitive-type name="WId" target-lang-api-name="PyLong">
<conversion-rule>
<native-to-target file="../glue/qtgui.cpp" snippet="return-pylong-voidptr"/>
diff --git a/sources/pyside2/PySide2/support/generate_pyi.py b/sources/pyside2/PySide2/support/generate_pyi.py
index 377a53331..294cdc91b 100644
--- a/sources/pyside2/PySide2/support/generate_pyi.py
+++ b/sources/pyside2/PySide2/support/generate_pyi.py
@@ -1,7 +1,7 @@
# This Python file uses the following encoding: utf-8
#############################################################################
##
-## Copyright (C) 2018 The Qt Company Ltd.
+## Copyright (C) 2019 The Qt Company Ltd.
## Contact: https://www.qt.io/licensing/
##
## This file is part of Qt for Python.
@@ -52,16 +52,9 @@ import io
import re
import subprocess
import argparse
-import glob
-import math
from contextlib import contextmanager
from textwrap import dedent
-import traceback
-
-
import logging
-logging.basicConfig(level=logging.INFO)
-logger = logging.getLogger("generate_pyi")
# Make sure not to get .pyc in Python2.
@@ -73,6 +66,10 @@ USE_PEP563 = sys.version_info[:2] >= (3, 7)
indent = " " * 4
is_py3 = sys.version_info[0] == 3
is_ci = os.environ.get("QTEST_ENVIRONMENT", "") == "ci"
+is_debug = is_ci or os.environ.get("QTEST_ENVIRONMENT")
+
+logging.basicConfig(level=logging.DEBUG if is_debug else logging.INFO)
+logger = logging.getLogger("generate_pyi")
class Writer(object):
@@ -118,7 +115,7 @@ class Formatter(Writer):
self.print("import shiboken2 as Shiboken")
self.print("Shiboken.Object = Object")
self.print()
- # This line will be replaced by the missing imports.
+ # This line will be replaced by the missing imports postprocess.
self.print("IMPORTS")
yield
@@ -184,83 +181,36 @@ def find_imports(text):
return [imp for imp in PySide2.__all__ if imp + "." in text]
-_cache = {}
-
-def check_if_skipable(outfilepath):
- # A file can be skipped if it exists, and if it's file time is not
- # older than this script or any of its dependencies.
- def _do_find_newest_module():
- newest = 0
- for obj in sys.modules.values():
- if getattr(obj, "__file__", None) and os.path.isfile(obj.__file__):
- sourcepath = os.path.splitext(obj.__file__)[0] + ".py"
- if os.path.exists(sourcepath):
- newest = max(os.path.getmtime(sourcepath), newest)
- return newest
-
- def find_newest_module():
- cache_name = "newest_module"
- if cache_name not in _cache:
- _cache[cache_name] = _do_find_newest_module()
- return _cache[cache_name]
-
- if os.path.exists(outfilepath):
- stamp = os.path.getmtime(outfilepath)
- if stamp >= find_newest_module():
- return True
- return False
-
-
def generate_pyi(import_name, outpath, options):
"""
Generates a .pyi file.
-
- Returns 1 If the result is valid, -1 if the result existed already
- and was skipped, else 0.
-
- This function will get called during a PySide build, and many concurrent
- process might try to create .pyi files. We let only one process at a
- time work on these files, but it will still be different processes which
- do the work.
"""
- pid = os.getpid()
plainname = import_name.split(".")[-1]
outfilepath = os.path.join(outpath, plainname + ".pyi")
- if options.skip and check_if_skipable(outfilepath):
- logger.debug("{pid}:Skipped existing: {op}"
- .format(op=os.path.basename(outfilepath), **locals()))
- return -1
-
- try:
- top = __import__(import_name)
- obj = getattr(top, plainname)
- if not getattr(obj, "__file__", None) or os.path.isdir(obj.__file__):
- raise ImportError("We do not accept a namespace as module {plainname}"
- .format(**locals()))
- module = sys.modules[import_name]
-
- outfile = io.StringIO()
- fmt = Formatter(outfile)
- enu = HintingEnumerator(fmt)
- fmt.print(get_license_text()) # which has encoding, already
- need_imports = not USE_PEP563
- if USE_PEP563:
- fmt.print("from __future__ import annotations")
- fmt.print()
- fmt.print(dedent('''\
- """
- This file contains the exact signatures for all functions in module
- {import_name}, except for defaults which are replaced by "...".
- """
- '''.format(**locals())))
- enu.module(import_name)
+ top = __import__(import_name)
+ obj = getattr(top, plainname)
+ if not getattr(obj, "__file__", None) or os.path.isdir(obj.__file__):
+ raise ModuleNotFoundError("We do not accept a namespace as module "
+ "{plainname}".format(**locals()))
+ module = sys.modules[import_name]
+
+ outfile = io.StringIO()
+ fmt = Formatter(outfile)
+ fmt.print(get_license_text()) # which has encoding, already
+ need_imports = not USE_PEP563
+ if USE_PEP563:
+ fmt.print("from __future__ import annotations")
fmt.print()
- fmt.print("# eof")
-
- except ImportError as e:
- logger.debug("{pid}:Import problem with module {plainname}: {e}".format(**locals()))
- return 0
-
+ fmt.print(dedent('''\
+ """
+ This file contains the exact signatures for all functions in module
+ {import_name}, except for defaults which are replaced by "...".
+ """
+ '''.format(**locals())))
+ HintingEnumerator(fmt).module(import_name)
+ fmt.print()
+ fmt.print("# eof")
+ # Postprocess: resolve the imports
with open(outfilepath, "w") as realfile:
wr = Writer(realfile)
outfile.seek(0)
@@ -282,23 +232,9 @@ def generate_pyi(import_name, outpath, options):
else:
wr.print(line)
logger.info("Generated: {outfilepath}".format(**locals()))
- if is_py3:
+ if is_py3 and (options.check or is_ci):
# Python 3: We can check the file directly if the syntax is ok.
subprocess.check_output([sys.executable, outfilepath])
- return 1
-
-
-@contextmanager
-def single_process(lockdir):
- try:
- os.mkdir(lockdir)
- try:
- yield lockdir
- finally:
- # make sure to cleanup, even if we leave with CTRL-C
- os.rmdir(lockdir)
- except OSError:
- yield None
def generate_all_pyi(outpath, options):
@@ -316,69 +252,40 @@ def generate_all_pyi(outpath, options):
from PySide2.support.signature import inspect
from PySide2.support.signature.lib.enum_sig import HintingEnumerator
- valid = check = 0
- if not outpath:
- outpath = os.path.dirname(PySide2.__file__)
- lockdir = os.path.join(outpath, "generate_pyi.lockdir")
-
- pyi_var = "GENERATE_PYI_RECURSE {}".format(math.pi) # should not be set by anybody
- if not os.environ.get(pyi_var, ""):
- # To catch a possible crash, we run as a subprocess:
- os.environ[pyi_var] = "yes"
- ret = subprocess.call([sys.executable] + sys.argv)
- if ret and os.path.exists(lockdir):
- os.rmdir(lockdir)
- sys.exit(ret)
- # We are the subprocess. Do the real work.
- with single_process(lockdir) as locked:
- if locked:
- if is_ci:
- # When COIN is running, we sometimes get racing conditions with
- # the windows manifest tool which wants access to a module that
- # we already have imported. But when we wait until all binaries
- # are created, that cannot happen, because we are then the last
- # process, and the tool has already been run.
- bin_pattern = "Qt*.pyd" if sys.platform == "win32" else "Qt*.so"
- search = os.path.join(PySide2.__path__[0], bin_pattern)
- if len(glob.glob(search)) < len(PySide2.__all__):
- return
- for mod_name in PySide2.__all__:
- import_name = "PySide2." + mod_name
- step = generate_pyi(import_name, outpath, options)
- valid += abs(step)
- check += step
-
- npyi = len(PySide2.__all__)
- # Prevent too many messages when '--reuse-build' is used. We check that
- # all files are created, but at least one was really computed.
- if valid == npyi and check != -npyi:
- logger.info("+++ All {npyi} .pyi files have been created.".format(**locals()))
+ outpath = outpath or os.path.dirname(PySide2.__file__)
+ name_list = PySide2.__all__ if options.modules == ["all"] else options.modules
+ errors = ", ".join(set(name_list) - set(PySide2.__all__))
+ if errors:
+ raise ImportError("The module(s) '{errors}' do not exist".format(**locals()))
+ quirk1, quirk2 = "QtMultimedia", "QtMultimediaWidgets"
+ if name_list == [quirk1]:
+ logger.debug("Note: We must defer building of {quirk1}.pyi until {quirk2} "
+ "is available".format(**locals()))
+ name_list = []
+ elif name_list == [quirk2]:
+ name_list = [quirk1, quirk2]
+ for mod_name in name_list:
+ import_name = "PySide2." + mod_name
+ generate_pyi(import_name, outpath, options)
if __name__ == "__main__":
- parser = argparse.ArgumentParser()
- subparsers = parser.add_subparsers(dest="command")
- # create the parser for the "run" command
- parser_run = subparsers.add_parser("run",
- help="run the generation",
+ parser = argparse.ArgumentParser(
description="This script generates the .pyi file for all PySide modules.")
- parser_run.add_argument("--skip", action="store_true",
- help="skip existing files")
- parser_run.add_argument("--quiet", action="store_true", help="Run quietly")
- parser_run.add_argument("--outpath",
+ parser.add_argument("modules", nargs="+",
+ help="'all' or the names of modules to build (QtCore QtGui etc.)")
+ parser.add_argument("--quiet", action="store_true", help="Run quietly")
+ parser.add_argument("--check", action="store_true", help="Test the output if on Python 3")
+ parser.add_argument("--outpath",
help="the output directory (default = binary location)")
- parser_run.add_argument("--sys-path", nargs="+",
+ parser.add_argument("--sys-path", nargs="+",
help="a list of strings prepended to sys.path")
options = parser.parse_args()
- if options.command == "run":
- if options.quiet:
- logger.setLevel(logging.WARNING)
- outpath = options.outpath
- if outpath and not os.path.exists(outpath):
- os.makedirs(outpath)
- logger.info("+++ Created path {outpath}".format(**locals()))
- generate_all_pyi(outpath, options=options)
- else:
- parser_run.print_help()
- sys.exit(1)
+ if options.quiet:
+ logger.setLevel(logging.WARNING)
+ outpath = options.outpath
+ if outpath and not os.path.exists(outpath):
+ os.makedirs(outpath)
+ logger.info("+++ Created path {outpath}".format(**locals()))
+ generate_all_pyi(outpath, options=options)
# eof
diff --git a/sources/pyside2/cmake/Macros/PySideModules.cmake b/sources/pyside2/cmake/Macros/PySideModules.cmake
index 77dc8c8ac..dca00ec11 100644
--- a/sources/pyside2/cmake/Macros/PySideModules.cmake
+++ b/sources/pyside2/cmake/Macros/PySideModules.cmake
@@ -162,27 +162,38 @@ macro(create_pyside_module)
# Need to set the LD_ env vars before invoking the script, because it might use build-time
# libraries instead of install time libraries.
if (WIN32)
- set(ld_prefix "PATH=")
+ set(ld_prefix_var_name "PATH")
elseif(APPLE)
- set(ld_prefix "DYLD_LIBRARY_PATH=")
+ set(ld_prefix_var_name "DYLD_LIBRARY_PATH")
else()
- set(ld_prefix "LD_LIBRARY_PATH=")
+ set(ld_prefix_var_name "LD_LIBRARY_PATH")
endif()
- set(ld_prefix "${ld_prefix}${pysidebindings_BINARY_DIR}/libpyside${PATH_SEP}${SHIBOKEN_SHARED_LIBRARY_DIR}")
+ set(ld_prefix "${ld_prefix_var_name}=${pysidebindings_BINARY_DIR}/libpyside${PATH_SEP}${SHIBOKEN_SHARED_LIBRARY_DIR}")
- # On Windows we also need to propagate the whole environment PATH value, because pyside modules
- # import Qt, and the Qt modules are found from PATH.
+ # Append any existing ld_prefix values, so existing PATH, LD_LIBRARY_PATH, etc.
+ # On Windows it is needed because pyside modules import Qt,
+ # and the Qt modules are found from PATH.
+ # On Linux and macOS, existing values might be set to find system libraries correctly.
+ # For example on openSUSE when compiling with icc, libimf.so from Intel has to be found.
if(WIN32)
# Get the value of PATH with CMake separators.
- file(TO_CMAKE_PATH "$ENV{PATH}" path_value)
+ file(TO_CMAKE_PATH "$ENV{${ld_prefix_var_name}}" path_value)
# Replace the CMake list separators with "\;"s, to avoid the PATH values being
# interpreted as CMake list elements, we actually want to pass the whole string separated
# by ";" to the command line.
- make_path(path_value "${path_value}")
- string(APPEND ld_prefix "${PATH_SEP}${path_value}")
+ if(path_value)
+ make_path(path_value "${path_value}")
+ string(APPEND ld_prefix "${PATH_SEP}${path_value}")
+ endif()
+ else()
+ # Handles both macOS and Linux.
+ set(env_value "$ENV{${ld_prefix_var_name}}")
+ if(env_value)
+ string(APPEND ld_prefix ":${env_value}")
+ endif()
endif()
- set(generate_pyi_options run --skip --sys-path
+ set(generate_pyi_options ${module_NAME} --sys-path
"${pysidebindings_BINARY_DIR}"
"${SHIBOKEN_PYTHON_MODULE_DIR}")
if (QUIET_BUILD)
diff --git a/sources/pyside2/tests/QtGui/CMakeLists.txt b/sources/pyside2/tests/QtGui/CMakeLists.txt
index 31747659e..927e72468 100644
--- a/sources/pyside2/tests/QtGui/CMakeLists.txt
+++ b/sources/pyside2/tests/QtGui/CMakeLists.txt
@@ -40,6 +40,7 @@ PYSIDE_TEST(qrasterwindow_test.py)
PYSIDE_TEST(qopenglwindow_test.py)
PYSIDE_TEST(qregion_test.py)
PYSIDE_TEST(qstylehints_test.py)
+PYSIDE_TEST(qtextdocument_functions.py)
PYSIDE_TEST(qtextdocument_undoredo_test.py)
PYSIDE_TEST(qtextdocumentwriter_test.py)
PYSIDE_TEST(qtextline_test.py)
diff --git a/sources/pyside2/tests/QtGui/qtextdocument_functions.py b/sources/pyside2/tests/QtGui/qtextdocument_functions.py
new file mode 100644
index 000000000..f1376aa5b
--- /dev/null
+++ b/sources/pyside2/tests/QtGui/qtextdocument_functions.py
@@ -0,0 +1,47 @@
+#############################################################################
+##
+## Copyright (C) 2019 The Qt Company Ltd.
+## Contact: https://www.qt.io/licensing/
+##
+## This file is part of the test suite of Qt for Python.
+##
+## $QT_BEGIN_LICENSE:GPL-EXCEPT$
+## Commercial License Usage
+## Licensees holding valid commercial Qt licenses may use this file in
+## accordance with the commercial license agreement provided with the
+## Software or, alternatively, in accordance with the terms contained in
+## a written agreement between you and The Qt Company. For licensing terms
+## and conditions see https://www.qt.io/terms-conditions. For further
+## information use the contact form at https://www.qt.io/contact-us.
+##
+## GNU General Public License Usage
+## Alternatively, this file may be used under the terms of the GNU
+## General Public License version 3 as published by the Free Software
+## Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+## included in the packaging of this file. Please review the following
+## information to ensure the GNU General Public License requirements will
+## be met: https://www.gnu.org/licenses/gpl-3.0.html.
+##
+## $QT_END_LICENSE$
+##
+#############################################################################
+
+import unittest
+
+from PySide2.QtGui import Qt
+from PySide2.QtCore import QTextCodec
+
+
+class QTextDocumentFunctions(unittest.TestCase):
+
+ def testFunctions(self):
+ self.assertFalse(Qt.mightBeRichText('bla'))
+ self.assertTrue(Qt.mightBeRichText('<html><head/><body><p>bla</p></body></html>'))
+ html = Qt.convertFromPlainText("A & B", Qt.WhiteSpaceNormal)
+ self.assertEqual(html, '<p>A &amp; B</p>')
+ codec = Qt.codecForHtml(b'bla')
+ self.assertTrue(codec)
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/sources/shiboken2/ApiExtractor/abstractmetabuilder.cpp b/sources/shiboken2/ApiExtractor/abstractmetabuilder.cpp
index cfe9d7d89..6cb0d1fe5 100644
--- a/sources/shiboken2/ApiExtractor/abstractmetabuilder.cpp
+++ b/sources/shiboken2/ApiExtractor/abstractmetabuilder.cpp
@@ -452,10 +452,8 @@ void AbstractMetaBuilderPrivate::traverseDom(const FileModelItem &dom)
const auto &namespaceTypeValues = dom->namespaces();
ReportHandler::startProgress("Generating namespace model ("
+ QByteArray::number(namespaceTypeValues.size()) + ")...");
- for (const NamespaceModelItem &item : namespaceTypeValues) {
- if (AbstractMetaClass *metaClass = traverseNamespace(dom, item))
- addAbstractMetaClass(metaClass, item.data());
- }
+ for (const NamespaceModelItem &item : namespaceTypeValues)
+ traverseNamespace(dom, item);
// Go through all typedefs to see if we have defined any
// specific typedefs to be used as classes.
@@ -742,23 +740,38 @@ AbstractMetaClass *AbstractMetaBuilderPrivate::traverseNamespace(const FileModel
if (!namespaceName.isEmpty())
namespaceName.append(colonColon());
namespaceName.append(namespaceItem->name());
- NamespaceTypeEntry *type = TypeDatabase::instance()->findNamespaceType(namespaceName);
if (TypeDatabase::instance()->isClassRejected(namespaceName)) {
m_rejectedClasses.insert(namespaceName, AbstractMetaBuilder::GenerationDisabled);
return 0;
}
+ auto type = TypeDatabase::instance()->findNamespaceType(namespaceName, namespaceItem->fileName());
if (!type) {
qCWarning(lcShiboken).noquote().nospace()
<< QStringLiteral("namespace '%1' does not have a type entry").arg(namespaceName);
return 0;
}
- AbstractMetaClass* metaClass = new AbstractMetaClass;
- metaClass->setTypeEntry(type);
-
- *metaClass += AbstractMetaAttributes::Public;
+ // Continue populating namespace?
+ AbstractMetaClass *metaClass = AbstractMetaClass::findClass(m_metaClasses, type);
+ if (!metaClass) {
+ metaClass = new AbstractMetaClass;
+ metaClass->setTypeEntry(type);
+ *metaClass += AbstractMetaAttributes::Public;
+ addAbstractMetaClass(metaClass, namespaceItem.data());
+ if (auto extendsType = type->extends()) {
+ AbstractMetaClass *extended = AbstractMetaClass::findClass(m_metaClasses, extendsType);
+ if (!extended) {
+ qCWarning(lcShiboken, "%s",
+ qPrintable(msgNamespaceToBeExtendedNotFound(extendsType->name(), extendsType->targetLangPackage())));
+ return nullptr;
+ }
+ metaClass->setExtendedNamespace(extended);
+ }
+ } else {
+ m_itemToClass.insert(namespaceItem.data(), metaClass);
+ }
if (ReportHandler::isDebug(ReportHandler::SparseDebug)) {
qCDebug(lcShiboken)
@@ -797,7 +810,6 @@ AbstractMetaClass *AbstractMetaBuilderPrivate::traverseNamespace(const FileModel
if (mjc) {
metaClass->addInnerClass(mjc);
mjc->setEnclosingClass(metaClass);
- addAbstractMetaClass(mjc, ni.data());
}
}
@@ -3076,6 +3088,30 @@ AbstractMetaClassList AbstractMetaBuilderPrivate::classesTopologicalSorted(const
return result;
}
+void AbstractMetaBuilderPrivate::pushScope(const NamespaceModelItem &item)
+{
+ // For purposes of type lookup, join all namespaces of the same name
+ // within the parent item.
+ QVector<NamespaceModelItem> candidates;
+ const QString name = item->name();
+ if (!m_scopes.isEmpty()) {
+ for (const auto &n : m_scopes.constLast()->namespaces()) {
+ if (n->name() == name)
+ candidates.append(n);
+ }
+ }
+ if (candidates.size() > 1) {
+ NamespaceModelItem joined(new _NamespaceModelItem(m_scopes.constLast()->model(),
+ name, _CodeModelItem::Kind_Namespace));
+ joined->setScope(item->scope());
+ for (const auto &n : candidates)
+ joined->appendNamespace(*n);
+ m_scopes << joined;
+ } else {
+ m_scopes << item;
+ }
+}
+
AbstractMetaClassList AbstractMetaBuilder::classesTopologicalSorted(const AbstractMetaClassList &classList,
const Dependencies &additionalDependencies) const
{
diff --git a/sources/shiboken2/ApiExtractor/abstractmetabuilder_p.h b/sources/shiboken2/ApiExtractor/abstractmetabuilder_p.h
index 3c0039f0e..1fd5f3c34 100644
--- a/sources/shiboken2/ApiExtractor/abstractmetabuilder_p.h
+++ b/sources/shiboken2/ApiExtractor/abstractmetabuilder_p.h
@@ -56,9 +56,9 @@ public:
const Dependencies &additionalDependencies = Dependencies()) const;
ScopeModelItem popScope() { return m_scopes.takeLast(); }
- void pushScope(ScopeModelItem item) { m_scopes << item; }
+ void pushScope(const NamespaceModelItem &item);
- ScopeModelItem currentScope() const { return m_scopes.constLast(); }
+ NamespaceModelItem currentScope() const { return m_scopes.constLast(); }
AbstractMetaClass *argumentToClass(const ArgumentModelItem &,
AbstractMetaClass *currentClass);
@@ -182,7 +182,7 @@ public:
QHash<const TypeEntry *, AbstractMetaEnum *> m_enums;
- QList<ScopeModelItem> m_scopes;
+ QList<NamespaceModelItem> m_scopes;
QSet<AbstractMetaClass *> m_setupInheritanceDone;
diff --git a/sources/shiboken2/ApiExtractor/abstractmetalang.h b/sources/shiboken2/ApiExtractor/abstractmetalang.h
index ef4cef2b4..e8ec21f48 100644
--- a/sources/shiboken2/ApiExtractor/abstractmetalang.h
+++ b/sources/shiboken2/ApiExtractor/abstractmetalang.h
@@ -1486,6 +1486,12 @@ public:
return m_enclosingClass;
}
+ /**
+ * \return the namespace from another package which this namespace extends.
+ */
+ AbstractMetaClass *extendedNamespace() const { return m_extendedNamespace; }
+ void setExtendedNamespace(AbstractMetaClass *e) { m_extendedNamespace = e; }
+
void setEnclosingClass(AbstractMetaClass *cl)
{
m_enclosingClass = cl;
@@ -1729,6 +1735,8 @@ private:
const AbstractMetaClass *m_enclosingClass = nullptr;
AbstractMetaClass *m_baseClass = nullptr;
+ AbstractMetaClass *m_extendedNamespace = nullptr;
+
const AbstractMetaClass *m_templateBaseClass = nullptr;
AbstractMetaFunctionList m_functions;
AbstractMetaFieldList m_fields;
diff --git a/sources/shiboken2/ApiExtractor/clangparser/clangbuilder.cpp b/sources/shiboken2/ApiExtractor/clangparser/clangbuilder.cpp
index 40f915028..3ced0e06c 100644
--- a/sources/shiboken2/ApiExtractor/clangparser/clangbuilder.cpp
+++ b/sources/shiboken2/ApiExtractor/clangparser/clangbuilder.cpp
@@ -885,15 +885,13 @@ BaseVisitor::StartTokenResult Builder::startToken(const CXCursor &cursor)
appendDiagnostic(d);
return Error;
}
- // If possible, continue existing namespace (as otherwise, all headers
- // where a namespace is continued show up in the type database).
+ // Treat namespaces separately to allow for extending namespaces
+ // in subsequent modules.
NamespaceModelItem namespaceItem = parentNamespaceItem->findNamespace(name);
- if (namespaceItem.isNull()) {
- namespaceItem.reset(new _NamespaceModelItem(d->m_model, name));
- setFileName(cursor, namespaceItem.data());
- namespaceItem->setScope(d->m_scope);
- parentNamespaceItem->addNamespace(namespaceItem);
- }
+ namespaceItem.reset(new _NamespaceModelItem(d->m_model, name));
+ setFileName(cursor, namespaceItem.data());
+ namespaceItem->setScope(d->m_scope);
+ parentNamespaceItem->addNamespace(namespaceItem);
d->pushScope(namespaceItem);
}
break;
diff --git a/sources/shiboken2/ApiExtractor/messages.cpp b/sources/shiboken2/ApiExtractor/messages.cpp
index fdae2359b..a6e75aac3 100644
--- a/sources/shiboken2/ApiExtractor/messages.cpp
+++ b/sources/shiboken2/ApiExtractor/messages.cpp
@@ -206,8 +206,6 @@ QString msgCannotTranslateTemplateArgument(int i,
return result;
}
-// abstractmetalang.cpp
-
QString msgDisallowThread(const AbstractMetaFunction *f)
{
QString result;
@@ -219,6 +217,13 @@ QString msgDisallowThread(const AbstractMetaFunction *f)
return result;
}
+QString msgNamespaceToBeExtendedNotFound(const QString &namespaceName, const QString &packageName)
+{
+ return QLatin1String("The namespace '") + namespaceName
+ + QLatin1String("' to be extended cannot be found in package ")
+ + packageName + QLatin1Char('.');
+}
+
// docparser.cpp
QString msgCannotFindDocumentation(const QString &fileName,
@@ -421,6 +426,26 @@ QString msgRejectReason(const TypeRejection &r, const QString &needle)
return result;
}
+// typesystem.cpp
+
+QString msgCannotFindNamespaceToExtend(const QString &name,
+ const QStringRef &extendsPackage)
+{
+ return QLatin1String("Cannot find namespace ") + name
+ + QLatin1String(" in package ") + extendsPackage;
+}
+
+QString msgExtendingNamespaceRequiresPattern(const QString &name)
+{
+ return QLatin1String("Namespace ") + name
+ + QLatin1String(" requires a file pattern since it extends another namespace.");
+}
+
+QString msgInvalidRegularExpression(const QString &pattern, const QString &why)
+{
+ return QLatin1String("Invalid pattern \"") + pattern + QLatin1String("\": ") + why;
+}
+
// qtdocgenerator.cpp
QString msgTagWarning(const QXmlStreamReader &reader, const QString &context,
diff --git a/sources/shiboken2/ApiExtractor/messages.h b/sources/shiboken2/ApiExtractor/messages.h
index 30b13fbf8..ab2bf64b6 100644
--- a/sources/shiboken2/ApiExtractor/messages.h
+++ b/sources/shiboken2/ApiExtractor/messages.h
@@ -85,6 +85,8 @@ QString msgCannotTranslateTemplateArgument(int i,
QString msgDisallowThread(const AbstractMetaFunction *f);
+QString msgNamespaceToBeExtendedNotFound(const QString &namespaceName, const QString &packageName);
+
QString msgCannotFindDocumentation(const QString &fileName,
const char *what, const QString &name,
const QString &query);
@@ -117,6 +119,13 @@ QString msgLeftOverArguments(const QMap<QString, QString> &remainingArgs);
QString msgInvalidVersion(const QString &package, const QString &version);
+QString msgCannotFindNamespaceToExtend(const QString &name,
+ const QStringRef &extendsPackage);
+
+QString msgExtendingNamespaceRequiresPattern(const QString &name);
+
+QString msgInvalidRegularExpression(const QString &pattern, const QString &why);
+
QString msgCyclicDependency(const QString &funcName, const QString &graphName,
const QVector<const AbstractMetaFunction *> &involvedConversions);
diff --git a/sources/shiboken2/ApiExtractor/parser/codemodel.cpp b/sources/shiboken2/ApiExtractor/parser/codemodel.cpp
index 9a845b04a..eb0f44689 100644
--- a/sources/shiboken2/ApiExtractor/parser/codemodel.cpp
+++ b/sources/shiboken2/ApiExtractor/parser/codemodel.cpp
@@ -773,6 +773,16 @@ void _ScopeModelItem::addEnum(EnumModelItem item)
m_enums.append(item);
}
+void _ScopeModelItem::appendScope(const _ScopeModelItem &other)
+{
+ m_classes += other.m_classes;
+ m_enums += other.m_enums;
+ m_typeDefs += other.m_typeDefs;
+ m_variables += other.m_variables;
+ m_functions += other.m_functions;
+ m_enumsDeclarations += other.m_enumsDeclarations;
+}
+
#ifndef QT_NO_DEBUG_STREAM
template <class Hash>
static void formatScopeHash(QDebug &d, const char *prefix, const Hash &h,
@@ -899,6 +909,12 @@ _FileModelItem::~_FileModelItem()
{
}
+void _NamespaceModelItem::appendNamespace(const _NamespaceModelItem &other)
+{
+ appendScope(other);
+ m_namespaces += other.m_namespaces;
+}
+
#ifndef QT_NO_DEBUG_STREAM
void _NamespaceModelItem::formatDebug(QDebug &d) const
{
diff --git a/sources/shiboken2/ApiExtractor/parser/codemodel.h b/sources/shiboken2/ApiExtractor/parser/codemodel.h
index 64415e07b..3bce5e216 100644
--- a/sources/shiboken2/ApiExtractor/parser/codemodel.h
+++ b/sources/shiboken2/ApiExtractor/parser/codemodel.h
@@ -360,6 +360,8 @@ protected:
explicit _ScopeModelItem(CodeModel *model, const QString &name, int kind = __node_kind)
: _CodeModelItem(model, name, kind) {}
+ void appendScope(const _ScopeModelItem &other);
+
#ifndef QT_NO_DEBUG_STREAM
void formatScopeItemsDebug(QDebug &d) const;
#endif
@@ -440,6 +442,8 @@ public:
NamespaceModelItem findNamespace(const QString &name) const;
+ void appendNamespace(const _NamespaceModelItem &other);
+
#ifndef QT_NO_DEBUG_STREAM
void formatDebug(QDebug &d) const override;
#endif
diff --git a/sources/shiboken2/ApiExtractor/typedatabase.cpp b/sources/shiboken2/ApiExtractor/typedatabase.cpp
index a8c69d376..930f85d30 100644
--- a/sources/shiboken2/ApiExtractor/typedatabase.cpp
+++ b/sources/shiboken2/ApiExtractor/typedatabase.cpp
@@ -621,14 +621,33 @@ ObjectTypeEntry* TypeDatabase::findObjectType(const QString& name) const
return 0;
}
-NamespaceTypeEntry* TypeDatabase::findNamespaceType(const QString& name) const
+NamespaceTypeEntryList TypeDatabase::findNamespaceTypes(const QString& name) const
{
+ NamespaceTypeEntryList result;
const auto entries = findTypes(name);
for (TypeEntry *entry : entries) {
- if (entry->isNamespace() && useType(entry))
- return static_cast<NamespaceTypeEntry*>(entry);
+ if (entry->isNamespace())
+ result.append(static_cast<NamespaceTypeEntry*>(entry));
}
- return 0;
+ return result;
+}
+
+NamespaceTypeEntry *TypeDatabase::findNamespaceType(const QString& name,
+ const QString &fileName) const
+{
+ const auto entries = findNamespaceTypes(name);
+ // Preferably check on matching file name first, if a pattern was given.
+ if (!fileName.isEmpty()) {
+ for (NamespaceTypeEntry *entry : entries) {
+ if (entry->hasPattern() && entry->matchesFile(fileName))
+ return entry;
+ }
+ }
+ for (NamespaceTypeEntry *entry : entries) {
+ if (!entry->hasPattern())
+ return entry;
+ }
+ return nullptr;
}
bool TypeDatabase::shouldDropTypeEntry(const QString& fullTypeName) const
@@ -836,6 +855,13 @@ void EnumTypeEntry::formatDebug(QDebug &d) const
d << ", flags=(" << m_flags << ')';
}
+void NamespaceTypeEntry::formatDebug(QDebug &d) const
+{
+ ComplexTypeEntry::formatDebug(d);
+ auto pattern = m_filePattern.pattern();
+ FORMAT_NONEMPTY_STRING("pattern", pattern)
+}
+
void ContainerTypeEntry::formatDebug(QDebug &d) const
{
ComplexTypeEntry::formatDebug(d);
diff --git a/sources/shiboken2/ApiExtractor/typedatabase.h b/sources/shiboken2/ApiExtractor/typedatabase.h
index 0040364bf..7f1b2a3fc 100644
--- a/sources/shiboken2/ApiExtractor/typedatabase.h
+++ b/sources/shiboken2/ApiExtractor/typedatabase.h
@@ -87,7 +87,8 @@ public:
PrimitiveTypeEntry* findPrimitiveType(const QString& name) const;
ComplexTypeEntry* findComplexType(const QString& name) const;
ObjectTypeEntry* findObjectType(const QString& name) const;
- NamespaceTypeEntry* findNamespaceType(const QString& name) const;
+ NamespaceTypeEntryList findNamespaceTypes(const QString& name) const;
+ NamespaceTypeEntry *findNamespaceType(const QString& name, const QString &fileName = QString()) const;
ContainerTypeEntry* findContainerType(const QString& name) const;
FunctionTypeEntry* findFunctionType(const QString& name) const;
const TypeSystemTypeEntry *findTypeSystemType(const QString &name) const;
diff --git a/sources/shiboken2/ApiExtractor/typedatabase_typedefs.h b/sources/shiboken2/ApiExtractor/typedatabase_typedefs.h
index fbbbabe43..f9591609e 100644
--- a/sources/shiboken2/ApiExtractor/typedatabase_typedefs.h
+++ b/sources/shiboken2/ApiExtractor/typedatabase_typedefs.h
@@ -34,6 +34,7 @@
#include <QtCore/QVector>
class ContainerTypeEntry;
+class NamespaceTypeEntry;
class PrimitiveTypeEntry;
class TemplateEntry;
class TypeEntry;
@@ -61,6 +62,7 @@ typedef QMap<QString, TypeEntry *> TypeEntryMap;
typedef QMap<QString, TypedefEntry *> TypedefEntryMap;
typedef QVector<const ContainerTypeEntry *> ContainerTypeEntryList;
+using NamespaceTypeEntryList = QVector<NamespaceTypeEntry *>;
typedef QVector<const PrimitiveTypeEntry *> PrimitiveTypeEntryList;
#endif // TYPEDATABASE_TYPEDEFS_H
diff --git a/sources/shiboken2/ApiExtractor/typesystem.cpp b/sources/shiboken2/ApiExtractor/typesystem.cpp
index 318a52e2e..434134be9 100644
--- a/sources/shiboken2/ApiExtractor/typesystem.cpp
+++ b/sources/shiboken2/ApiExtractor/typesystem.cpp
@@ -126,8 +126,7 @@ static bool setRejectionRegularExpression(const QString &patternIn,
pattern = QLatin1Char('^') + QRegularExpression::escape(patternIn) + QLatin1Char('$');
re->setPattern(pattern);
if (!re->isValid()) {
- *errorMessage = QLatin1String("Invalid pattern \"") + patternIn
- + QLatin1String("\": ") + re->errorString();
+ *errorMessage = msgInvalidRegularExpression(patternIn, re->errorString());
return false;
}
return true;
@@ -1286,6 +1285,47 @@ ObjectTypeEntry *
return otype;
}
+NamespaceTypeEntry *
+ Handler::parseNamespaceTypeEntry(const QXmlStreamReader &reader,
+ const QString &name, const QVersionNumber &since,
+ QXmlStreamAttributes *attributes)
+{
+ QScopedPointer<NamespaceTypeEntry> result(new NamespaceTypeEntry(name, since));
+ applyCommonAttributes(result.data(), attributes);
+ applyComplexTypeAttributes(reader, result.data(), attributes);
+ for (int i = attributes->size() - 1; i >= 0; --i) {
+ const QStringRef attributeName = attributes->at(i).qualifiedName();
+ if (attributeName == QLatin1String("files")) {
+ const QString pattern = attributes->takeAt(i).value().toString();
+ QRegularExpression re(pattern);
+ if (!re.isValid()) {
+ m_error = msgInvalidRegularExpression(pattern, re.errorString());
+ return nullptr;
+ }
+ result->setFilePattern(re);
+ } else if (attributeName == QLatin1String("extends")) {
+ const auto extendsPackageName = attributes->takeAt(i).value();
+ auto allEntries = TypeDatabase::instance()->findNamespaceTypes(name);
+ auto extendsIt = std::find_if(allEntries.cbegin(), allEntries.cend(),
+ [extendsPackageName] (const NamespaceTypeEntry *e) {
+ return e->targetLangPackage() == extendsPackageName;
+ });
+ if (extendsIt == allEntries.cend()) {
+ m_error = msgCannotFindNamespaceToExtend(name, extendsPackageName);
+ return nullptr;
+ }
+ result->setExtends(*extendsIt);
+ }
+ }
+
+ if (result->extends() && !result->hasPattern()) {
+ m_error = msgExtendingNamespaceRequiresPattern(name);
+ return nullptr;
+ }
+
+ return result.take();
+}
+
ValueTypeEntry *
Handler::parseValueTypeEntry(const QXmlStreamReader &,
const QString &name, const QVersionNumber &since,
@@ -2631,7 +2671,7 @@ bool Handler::startElement(const QXmlStreamReader &reader)
if (element->type != StackElement::PrimitiveTypeEntry
&& element->type != StackElement::FunctionTypeEntry) {
TypeEntry *tmp = m_database->findType(name);
- if (tmp)
+ if (tmp && !tmp->isNamespace())
qCWarning(lcShiboken).noquote().nospace()
<< QStringLiteral("Duplicate type entry: '%1'").arg(name);
}
@@ -2710,9 +2750,10 @@ bool Handler::startElement(const QXmlStreamReader &reader)
}
break;
case StackElement::NamespaceTypeEntry:
- element->entry = new NamespaceTypeEntry(name, since);
- applyCommonAttributes(element->entry, &attributes);
- applyComplexTypeAttributes(reader, static_cast<ComplexTypeEntry *>(element->entry), &attributes);
+ if (auto entry = parseNamespaceTypeEntry(reader, name, since, &attributes))
+ element->entry = entry;
+ else
+ return false;
break;
case StackElement::ObjectTypeEntry:
element->entry = new ObjectTypeEntry(name, since);
@@ -3799,8 +3840,21 @@ TypeEntry *NamespaceTypeEntry::clone() const
return new NamespaceTypeEntry(*this);
}
+void NamespaceTypeEntry::setFilePattern(const QRegularExpression &r)
+{
+ m_filePattern = r;
+ m_hasPattern = !m_filePattern.pattern().isEmpty();
+ if (m_hasPattern)
+ m_filePattern.optimize();
+}
+
NamespaceTypeEntry::NamespaceTypeEntry(const NamespaceTypeEntry &) = default;
+bool NamespaceTypeEntry::matchesFile(const QString &needle) const
+{
+ return m_filePattern.match(needle).hasMatch();
+}
+
ValueTypeEntry::ValueTypeEntry(const QString &name, const QVersionNumber &vr) :
ComplexTypeEntry(name, BasicValueType, vr)
{
diff --git a/sources/shiboken2/ApiExtractor/typesystem.h b/sources/shiboken2/ApiExtractor/typesystem.h
index f089bb6e0..2e4578a1d 100644
--- a/sources/shiboken2/ApiExtractor/typesystem.h
+++ b/sources/shiboken2/ApiExtractor/typesystem.h
@@ -1531,10 +1531,28 @@ public:
TypeEntry *clone() const override;
+ const NamespaceTypeEntry *extends() const { return m_extends; }
+ void setExtends(const NamespaceTypeEntry *e) { m_extends = e; }
+
+ const QRegularExpression &filePattern() const { return m_filePattern; } // restrict files
+ void setFilePattern(const QRegularExpression &r);
+
+ bool hasPattern() const { return m_hasPattern; }
+
+ bool matchesFile(const QString &needle) const;
+
+#ifndef QT_NO_DEBUG_STREAM
+ void formatDebug(QDebug &d) const override;
+#endif
+
protected:
NamespaceTypeEntry(const NamespaceTypeEntry &);
-};
+private:
+ QRegularExpression m_filePattern;
+ const NamespaceTypeEntry *m_extends = nullptr;
+ bool m_hasPattern = false;
+};
class ValueTypeEntry : public ComplexTypeEntry
{
diff --git a/sources/shiboken2/ApiExtractor/typesystem_p.h b/sources/shiboken2/ApiExtractor/typesystem_p.h
index f6b0717f4..8a8fcb359 100644
--- a/sources/shiboken2/ApiExtractor/typesystem_p.h
+++ b/sources/shiboken2/ApiExtractor/typesystem_p.h
@@ -175,6 +175,12 @@ private:
parseFlagsEntry(const QXmlStreamReader &, EnumTypeEntry *enumEntry,
const QString &name, QString flagName,
const QVersionNumber &since, QXmlStreamAttributes *);
+
+ NamespaceTypeEntry *
+ parseNamespaceTypeEntry(const QXmlStreamReader &,
+ const QString &name, const QVersionNumber &since,
+ QXmlStreamAttributes *attributes);
+
ObjectTypeEntry *
parseInterfaceTypeEntry(const QXmlStreamReader &, const QString &name,
const QVersionNumber &since, QXmlStreamAttributes *);
diff --git a/sources/shiboken2/generator/shiboken2/cppgenerator.cpp b/sources/shiboken2/generator/shiboken2/cppgenerator.cpp
index 32b219fa2..5fbc6ed3e 100644
--- a/sources/shiboken2/generator/shiboken2/cppgenerator.cpp
+++ b/sources/shiboken2/generator/shiboken2/cppgenerator.cpp
@@ -4892,7 +4892,11 @@ void CppGenerator::writeFlagsUnaryOperator(QTextStream& s, const AbstractMetaEnu
QString CppGenerator::getSimpleClassInitFunctionName(const AbstractMetaClass *metaClass) const
{
- QString initFunctionName = metaClass->qualifiedCppName();
+ QString initFunctionName;
+ // Disambiguate namespaces per module to allow for extending them.
+ if (metaClass->isNamespace())
+ initFunctionName += moduleName();
+ initFunctionName += metaClass->qualifiedCppName();
initFunctionName.replace(QLatin1String("::"), QLatin1String("_"));
return initFunctionName;
}
@@ -4996,9 +5000,11 @@ void CppGenerator::writeClassRegister(QTextStream &s,
}
// 7:baseType
- if (metaClass->baseClass()) {
+ const auto base = metaClass->isNamespace()
+ ? metaClass->extendedNamespace() : metaClass->baseClass();
+ if (base) {
s << INDENT << "reinterpret_cast<SbkObjectType *>("
- << cpythonTypeNameExt(metaClass->baseClass()->typeEntry()) << ")," << endl;
+ << cpythonTypeNameExt(base->typeEntry()) << ")," << endl;
} else {
s << INDENT << "0," << endl;
}
diff --git a/sources/shiboken2/generator/shiboken2/shibokengenerator.cpp b/sources/shiboken2/generator/shiboken2/shibokengenerator.cpp
index 44405c700..2b3b20c75 100644
--- a/sources/shiboken2/generator/shiboken2/shibokengenerator.cpp
+++ b/sources/shiboken2/generator/shiboken2/shibokengenerator.cpp
@@ -2667,8 +2667,14 @@ QString ShibokenGenerator::getTypeIndexVariableName(const TypeEntry* type)
if (trueType->basicReferencedTypeEntry())
type = trueType->basicReferencedTypeEntry();
}
- QString result = QLatin1String("SBK_")
- + _fixedCppTypeName(type->qualifiedCppName()).toUpper();
+ QString result = QLatin1String("SBK_");
+ // Disambiguate namespaces per module to allow for extending them.
+ if (type->isNamespace()) {
+ QString package = type->targetLangPackage();
+ const int dot = package.lastIndexOf(QLatin1Char('.'));
+ result += package.rightRef(package.size() - (dot + 1));
+ }
+ result += _fixedCppTypeName(type->qualifiedCppName()).toUpper();
appendIndexSuffix(&result);
return result;
}