aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFriedemann Kleint <Friedemann.Kleint@qt.io>2020-07-13 13:03:18 +0200
committerFriedemann Kleint <Friedemann.Kleint@qt.io>2020-07-13 13:07:52 +0200
commit843b476cf4bf7f20c7a3c433e41a76995b0cebca (patch)
tree3f3dc80c8c8c370e0d62462f9d352412b9cb4595
parent3899ed571eb2b453736b3b55a8991df83d33d609 (diff)
parent7211180820d76814c7060d39d7ce0e9902865e14 (diff)
Merge remote-tracking branch 'origin/5.15' into dev
-rw-r--r--examples/corelib/tools/settingseditor/settingseditor.py885
-rw-r--r--sources/pyside2/PySide2/global.h.in7
-rw-r--r--sources/pyside2/libpyside/CMakeLists.txt2
-rw-r--r--sources/pyside2/libpyside/feature_select.cpp395
-rw-r--r--sources/pyside2/libpyside/feature_select.h53
-rw-r--r--sources/pyside2/libpyside/pyside.cpp2
-rw-r--r--sources/pyside2/tests/QtCore/CMakeLists.txt1
-rw-r--r--sources/pyside2/tests/QtCore/feature_test.py105
-rw-r--r--sources/shiboken2/ApiExtractor/abstractmetalang.cpp34
-rw-r--r--sources/shiboken2/ApiExtractor/abstractmetalang.h8
-rw-r--r--sources/shiboken2/ApiExtractor/clangparser/clangbuilder.cpp13
-rw-r--r--sources/shiboken2/ApiExtractor/parser/codemodel.cpp2
-rw-r--r--sources/shiboken2/libshiboken/basewrapper.cpp39
-rw-r--r--sources/shiboken2/libshiboken/basewrapper.h3
-rw-r--r--sources/shiboken2/libshiboken/pep384impl.cpp53
-rw-r--r--sources/shiboken2/libshiboken/pep384impl.h7
-rw-r--r--sources/shiboken2/libshiboken/sbkstaticstrings.cpp3
-rw-r--r--sources/shiboken2/libshiboken/sbkstaticstrings.h3
-rw-r--r--sources/shiboken2/libshiboken/shiboken.h1
-rw-r--r--sources/shiboken2/libshiboken/signature.cpp15
-rw-r--r--sources/shiboken2/libshiboken/signature.h1
-rw-r--r--sources/shiboken2/shibokenmodule/CMakeLists.txt2
-rw-r--r--sources/shiboken2/shibokenmodule/files.dir/shibokensupport/__feature__.py111
-rw-r--r--sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/loader.py16
24 files changed, 1323 insertions, 438 deletions
diff --git a/examples/corelib/tools/settingseditor/settingseditor.py b/examples/corelib/tools/settingseditor/settingseditor.py
index 5b141880a..121d7cb0e 100644
--- a/examples/corelib/tools/settingseditor/settingseditor.py
+++ b/examples/corelib/tools/settingseditor/settingseditor.py
@@ -1,8 +1,9 @@
+# -*- coding: utf-8 -*-
#############################################################################
##
## Copyright (C) 2013 Riverbank Computing Limited.
-## Copyright (C) 2016 The Qt Company Ltd.
+## Copyright (C) 2020 The Qt Company Ltd.
## Contact: http://www.qt.io/licensing/
##
## This file is part of the Qt for Python examples of the Qt Toolkit.
@@ -44,69 +45,191 @@
import sys
-from PySide2 import QtCore, QtGui, QtWidgets
+from PySide2.QtCore import (QByteArray, QDate, QDateTime, QDir, QEvent, QPoint,
+ QRect, QRegularExpression, QSettings, QSize, QTime, QTimer, Qt)
+from PySide2.QtGui import (QAction, QColor, QIcon, QIntValidator,
+ QDoubleValidator, QRegularExpressionValidator, QValidator)
+from PySide2.QtWidgets import (QAbstractItemView, QApplication,
+ QCheckBox, QComboBox, QFileDialog, QDialog, QDialogButtonBox, QGridLayout,
+ QGroupBox, QHeaderView, QInputDialog, QItemDelegate, QLabel, QLineEdit,
+ QMainWindow, QMessageBox, QStyle, QSpinBox, QStyleOptionViewItem,
+ QTableWidget, QTableWidgetItem, QTreeWidget, QTreeWidgetItem, QVBoxLayout)
-class MainWindow(QtWidgets.QMainWindow):
+class TypeChecker:
+ def __init__(self, parent=None):
+ self.bool_exp = QRegularExpression('^(true)|(false)$')
+ assert self.bool_exp.isValid()
+ self.bool_exp.setPatternOptions(QRegularExpression.CaseInsensitiveOption)
+
+ self.byteArray_exp = QRegularExpression(r'^[\x00-\xff]*$')
+ assert self.byteArray_exp.isValid()
+
+ self.char_exp = QRegularExpression('^.$')
+ assert self.char_exp.isValid()
+
+ pattern = r'^[+-]?\d+$'
+ self.int_exp = QRegularExpression(pattern)
+ assert self.int_exp.isValid()
+
+ pattern = r'^\(([0-9]*),([0-9]*),([0-9]*),([0-9]*)\)$'
+ self.color_exp = QRegularExpression(pattern)
+ assert self.color_exp.isValid()
+
+ pattern = r'^\((-?[0-9]*),(-?[0-9]*)\)$'
+ self.point_exp = QRegularExpression(pattern)
+ assert self.point_exp.isValid()
+
+ pattern = r'^\((-?[0-9]*),(-?[0-9]*),(-?[0-9]*),(-?[0-9]*)\)$'
+ self.rect_exp = QRegularExpression(pattern)
+ assert self.rect_exp.isValid()
+
+ self.size_exp = QRegularExpression(self.point_exp)
+
+ date_pattern = '([0-9]{,4})-([0-9]{,2})-([0-9]{,2})'
+ self.date_exp = QRegularExpression('^{}$'.format(date_pattern))
+ assert self.date_exp.isValid()
+
+ time_pattern = '([0-9]{,2}):([0-9]{,2}):([0-9]{,2})'
+ self.time_exp = QRegularExpression('^{}$'.format(time_pattern))
+ assert self.time_exp.isValid()
+
+ pattern = '^{}T{}$'.format(date_pattern, time_pattern)
+ self.dateTime_exp = QRegularExpression(pattern)
+ assert self.dateTime_exp.isValid()
+
+ def type_from_text(self, text):
+ if self.bool_exp.match(text).hasMatch():
+ return bool
+ if self.int_exp.match(text).hasMatch():
+ return int
+ return None
+
+ def create_validator(self, value, parent):
+ if isinstance(value, bool):
+ return QRegularExpressionValidator(self.bool_exp, parent)
+ if isinstance(value, float):
+ return QDoubleValidator(parent)
+ if isinstance(value, int):
+ return QIntValidator(parent)
+ if isinstance(value, QByteArray):
+ return QRegularExpressionValidator(self.byteArray_exp, parent)
+ if isinstance(value, QColor):
+ return QRegularExpressionValidator(self.color_exp, parent)
+ if isinstance(value, QDate):
+ return QRegularExpressionValidator(self.date_exp, parent)
+ if isinstance(value, QDateTime):
+ return QRegularExpressionValidator(self.dateTime_exp, parent)
+ if isinstance(value, QTime):
+ return QRegularExpressionValidator(self.time_exp, parent)
+ if isinstance(value, QPoint):
+ return QRegularExpressionValidator(self.point_exp, parent)
+ if isinstance(value, QRect):
+ return QRegularExpressionValidator(self.rect_exp, parent)
+ if isinstance(value, QSize):
+ return QRegularExpressionValidator(self.size_exp, parent)
+ return None
+
+ def from_string(self, text, original_value):
+ if isinstance(original_value, QColor):
+ match = self.color_exp.match(text)
+ return QColor(min(int(match.captured(1)), 255),
+ min(int(match.captured(2)), 255),
+ min(int(match.captured(3)), 255),
+ min(int(match.captured(4)), 255))
+ if isinstance(original_value, QDate):
+ value = QDate.fromString(text, Qt.ISODate)
+ return value if value.isValid() else None
+ if isinstance(original_value, QDateTime):
+ value = QDateTime.fromString(text, Qt.ISODate)
+ return value if value.isValid() else None
+ if isinstance(original_value, QTime):
+ value = QTime.fromString(text, Qt.ISODate)
+ return value if value.isValid() else None
+ if isinstance(original_value, QPoint):
+ match = self.point_exp.match(text)
+ return QPoint(int(match.captured(1)),
+ int(match.captured(2)))
+ if isinstance(original_value, QRect):
+ match = self.rect_exp.match(text)
+ return QRect(int(match.captured(1)),
+ int(match.captured(2)),
+ int(match.captured(3)),
+ int(match.captured(4)))
+ if isinstance(original_value, QSize):
+ match = self.size_exp.match(text)
+ return QSize(int(match.captured(1)),
+ int(match.captured(2)))
+ if isinstance(original_value, list):
+ return text.split(',')
+ return type(original_value)(text)
+
+
+class MainWindow(QMainWindow):
def __init__(self, parent=None):
super(MainWindow, self).__init__(parent)
- self.settingsTree = SettingsTree()
- self.setCentralWidget(self.settingsTree)
+ self.settings_tree = SettingsTree()
+ self.setCentralWidget(self.settings_tree)
- self.locationDialog = None
+ self.location_dialog = None
- self.createActions()
- self.createMenus()
+ self.create_actions()
+ self.create_menus()
- self.autoRefreshAct.setChecked(True)
- self.fallbacksAct.setChecked(True)
+ self.auto_refresh_action.setChecked(True)
+ self.fallbacks_action.setChecked(True)
self.setWindowTitle("Settings Editor")
self.resize(500, 600)
- def openSettings(self):
- if self.locationDialog is None:
- self.locationDialog = LocationDialog(self)
+ def open_settings(self):
+ if self.location_dialog is None:
+ self.location_dialog = LocationDialog(self)
- if self.locationDialog.exec_():
- settings = QtCore.QSettings(self.locationDialog.format(),
- self.locationDialog.scope(),
- self.locationDialog.organization(),
- self.locationDialog.application())
- self.setSettingsObject(settings)
- self.fallbacksAct.setEnabled(True)
+ if self.location_dialog.exec_():
+ settings = QSettings(self.location_dialog.format(),
+ self.location_dialog.scope(),
+ self.location_dialog.organization(),
+ self.location_dialog.application())
+ self.set_settings_object(settings)
+ self.fallbacks_action.setEnabled(True)
- def openIniFile(self):
- fileName, _ = QtWidgets.QFileDialog.getOpenFileName(self, "Open INI File",
+ def open_inifile(self):
+ file_name, _ = QFileDialog.getOpenFileName(self, "Open INI File",
'', "INI Files (*.ini *.conf)")
- if fileName:
- settings = QtCore.QSettings(fileName, QtCore.QSettings.IniFormat)
- self.setSettingsObject(settings)
- self.fallbacksAct.setEnabled(False)
+ if file_name:
+ self.load_ini_file(file_name)
+
+ def load_ini_file(self, file_name):
+ settings = QSettings(file_name, QSettings.IniFormat)
+ if settings.status() != QSettings.NoError:
+ return
+ self.set_settings_object(settings)
+ self.fallbacks_action.setEnabled(False)
- def openPropertyList(self):
- fileName, _ = QtWidgets.QFileDialog.getOpenFileName(self,
+ def open_property_list(self):
+ file_name, _ = QFileDialog.getOpenFileName(self,
"Open Property List", '', "Property List Files (*.plist)")
- if fileName:
- settings = QtCore.QSettings(fileName, QtCore.QSettings.NativeFormat)
- self.setSettingsObject(settings)
- self.fallbacksAct.setEnabled(False)
+ if file_name:
+ settings = QSettings(file_name, QSettings.NativeFormat)
+ self.set_settings_object(settings)
+ self.fallbacks_action.setEnabled(False)
- def openRegistryPath(self):
- path, ok = QtWidgets.QInputDialog.getText(self, "Open Registry Path",
+ def open_registry_path(self):
+ path, ok = QInputDialog.getText(self, "Open Registry Path",
"Enter the path in the Windows registry:",
- QtWidgets.QLineEdit.Normal, 'HKEY_CURRENT_USER\\')
+ QLineEdit.Normal, 'HKEY_CURRENT_USER\\')
if ok and path != '':
- settings = QtCore.QSettings(path, QtCore.QSettings.NativeFormat)
- self.setSettingsObject(settings)
- self.fallbacksAct.setEnabled(False)
+ settings = QSettings(path, QSettings.NativeFormat)
+ self.set_settings_object(settings)
+ self.fallbacks_action.setEnabled(False)
def about(self):
- QtWidgets.QMessageBox.about(self, "About Settings Editor",
+ QMessageBox.about(self, "About Settings Editor",
"The <b>Settings Editor</b> example shows how to access "
"application settings using Qt.")
@@ -119,185 +242,192 @@ class MainWindow(QtWidgets.QMainWindow):
self.openPropertyListAct = QtGui.QAction("Open macOS &Property List...",
self, shortcut="Ctrl+P", triggered=self.openPropertyList)
+
+ def create_actions(self):
+ self.open_settings_action = QAction("&Open Application Settings...",
+ self, shortcut="Ctrl+O", triggered=self.open_settings)
+
+ self.open_ini_file_action = QAction("Open I&NI File...", self,
+ shortcut="Ctrl+N", triggered=self.open_inifile)
+
+ self.open_property_list_action = QAction("Open macOS &Property List...",
+ self, shortcut="Ctrl+P", triggered=self.open_property_list)
if sys.platform != 'darwin':
- self.openPropertyListAct.setEnabled(False)
+ self.open_property_list_action.setEnabled(False)
- self.openRegistryPathAct = QtGui.QAction(
+ self.open_registry_path_action = QAction(
"Open Windows &Registry Path...", self, shortcut="Ctrl+G",
- triggered=self.openRegistryPath)
+ triggered=self.open_registry_path)
if sys.platform != 'win32':
- self.openRegistryPathAct.setEnabled(False)
+ self.open_registry_path_action.setEnabled(False)
- self.refreshAct = QtGui.QAction("&Refresh", self, shortcut="Ctrl+R",
- enabled=False, triggered=self.settingsTree.refresh)
+ self.refresh_action = QAction("&Refresh", self, shortcut="Ctrl+R",
+ enabled=False, triggered=self.settings_tree.refresh)
- self.exitAct = QtGui.QAction("E&xit", self, shortcut="Ctrl+Q",
+ self.exit_action = QAction("E&xit", self, shortcut="Ctrl+Q",
triggered=self.close)
- self.autoRefreshAct = QtGui.QAction("&Auto-Refresh", self,
+ self.auto_refresh_action = QAction("&Auto-Refresh", self,
shortcut="Ctrl+A", checkable=True, enabled=False)
- self.autoRefreshAct.triggered[bool].connect(self.settingsTree.setAutoRefresh)
- self.autoRefreshAct.triggered[bool].connect(self.refreshAct.setDisabled)
+ self.auto_refresh_action.triggered[bool].connect(self.settings_tree.set_auto_refresh)
+ self.auto_refresh_action.triggered[bool].connect(self.refresh_action.setDisabled)
- self.fallbacksAct = QtGui.QAction("&Fallbacks", self,
+ self.fallbacks_action = QAction("&Fallbacks", self,
shortcut="Ctrl+F", checkable=True, enabled=False)
- self.fallbacksAct.triggered[bool].connect(self.settingsTree.setFallbacksEnabled)
+ self.fallbacks_action.triggered[bool].connect(self.settings_tree.set_fallbacks_enabled)
- self.aboutAct = QtGui.QAction("&About", self, triggered=self.about)
+ self.about_action = QAction("&About", self, triggered=self.about)
- self.aboutQtAct = QtGui.QAction("About &Qt", self,
- triggered=qApp.aboutQt)
+ self.about_Qt_action = QAction("About &Qt", self,
+ triggered=qApp.aboutQt)
- def createMenus(self):
- self.fileMenu = self.menuBar().addMenu("&File")
- self.fileMenu.addAction(self.openSettingsAct)
- self.fileMenu.addAction(self.openIniFileAct)
- self.fileMenu.addAction(self.openPropertyListAct)
- self.fileMenu.addAction(self.openRegistryPathAct)
- self.fileMenu.addSeparator()
- self.fileMenu.addAction(self.refreshAct)
- self.fileMenu.addSeparator()
- self.fileMenu.addAction(self.exitAct)
+ def create_menus(self):
+ self.file_menu = self.menuBar().addMenu("&File")
+ self.file_menu.addAction(self.open_settings_action)
+ self.file_menu.addAction(self.open_ini_file_action)
+ self.file_menu.addAction(self.open_property_list_action)
+ self.file_menu.addAction(self.open_registry_path_action)
+ self.file_menu.addSeparator()
+ self.file_menu.addAction(self.refresh_action)
+ self.file_menu.addSeparator()
+ self.file_menu.addAction(self.exit_action)
- self.optionsMenu = self.menuBar().addMenu("&Options")
- self.optionsMenu.addAction(self.autoRefreshAct)
- self.optionsMenu.addAction(self.fallbacksAct)
+ self.options_menu = self.menuBar().addMenu("&Options")
+ self.options_menu.addAction(self.auto_refresh_action)
+ self.options_menu.addAction(self.fallbacks_action)
self.menuBar().addSeparator()
- self.helpMenu = self.menuBar().addMenu("&Help")
- self.helpMenu.addAction(self.aboutAct)
- self.helpMenu.addAction(self.aboutQtAct)
+ self.help_menu = self.menuBar().addMenu("&Help")
+ self.help_menu.addAction(self.about_action)
+ self.help_menu.addAction(self.about_Qt_action)
- def setSettingsObject(self, settings):
- settings.setFallbacksEnabled(self.fallbacksAct.isChecked())
- self.settingsTree.setSettingsObject(settings)
+ def set_settings_object(self, settings):
+ settings.setFallbacksEnabled(self.fallbacks_action.isChecked())
+ self.settings_tree.set_settings_object(settings)
- self.refreshAct.setEnabled(True)
- self.autoRefreshAct.setEnabled(True)
+ self.refresh_action.setEnabled(True)
+ self.auto_refresh_action.setEnabled(True)
- niceName = settings.fileName()
- niceName.replace('\\', '/')
- niceName = niceName.split('/')[-1]
+ nice_name = QDir.fromNativeSeparators(settings.fileName())
+ nice_name = nice_name.split('/')[-1]
if not settings.isWritable():
- niceName += " (read only)"
+ nice_name += " (read only)"
- self.setWindowTitle("%s - Settings Editor" % niceName)
+ self.setWindowTitle("{} - Settings Editor".format(nice_name))
-class LocationDialog(QtWidgets.QDialog):
+class LocationDialog(QDialog):
def __init__(self, parent=None):
super(LocationDialog, self).__init__(parent)
- self.formatComboBox = QtWidgets.QComboBox()
- self.formatComboBox.addItem("Native")
- self.formatComboBox.addItem("INI")
-
- self.scopeComboBox = QtWidgets.QComboBox()
- self.scopeComboBox.addItem("User")
- self.scopeComboBox.addItem("System")
-
- self.organizationComboBox = QtWidgets.QComboBox()
- self.organizationComboBox.addItem("Trolltech")
- self.organizationComboBox.setEditable(True)
-
- self.applicationComboBox = QtWidgets.QComboBox()
- self.applicationComboBox.addItem("Any")
- self.applicationComboBox.addItem("Application Example")
- self.applicationComboBox.addItem("Assistant")
- self.applicationComboBox.addItem("Designer")
- self.applicationComboBox.addItem("Linguist")
- self.applicationComboBox.setEditable(True)
- self.applicationComboBox.setCurrentIndex(3)
-
- formatLabel = QtWidgets.QLabel("&Format:")
- formatLabel.setBuddy(self.formatComboBox)
-
- scopeLabel = QtWidgets.QLabel("&Scope:")
- scopeLabel.setBuddy(self.scopeComboBox)
-
- organizationLabel = QtWidgets.QLabel("&Organization:")
- organizationLabel.setBuddy(self.organizationComboBox)
-
- applicationLabel = QtWidgets.QLabel("&Application:")
- applicationLabel.setBuddy(self.applicationComboBox)
-
- self.locationsGroupBox = QtWidgets.QGroupBox("Setting Locations")
-
- self.locationsTable = QtWidgets.QTableWidget()
- self.locationsTable.setSelectionMode(QtWidgets.QAbstractItemView.SingleSelection)
- self.locationsTable.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows)
- self.locationsTable.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers)
- self.locationsTable.setColumnCount(2)
- self.locationsTable.setHorizontalHeaderLabels(("Location", "Access"))
- self.locationsTable.horizontalHeader().setSectionResizeMode(0, QtWidgets.QHeaderView.Stretch)
- self.locationsTable.horizontalHeader().resizeSection(1, 180)
-
- self.buttonBox = QtWidgets.QDialogButtonBox(QtWidgets.QDialogButtonBox.Ok | QtWidgets.QDialogButtonBox.Cancel)
-
- self.formatComboBox.activated.connect(self.updateLocationsTable)
- self.scopeComboBox.activated.connect(self.updateLocationsTable)
- self.organizationComboBox.lineEdit().editingFinished.connect(self.updateLocationsTable)
- self.applicationComboBox.lineEdit().editingFinished.connect(self.updateLocationsTable)
- self.buttonBox.accepted.connect(self.accept)
- self.buttonBox.rejected.connect(self.reject)
-
- locationsLayout = QtWidgets.QVBoxLayout()
- locationsLayout.addWidget(self.locationsTable)
- self.locationsGroupBox.setLayout(locationsLayout)
-
- mainLayout = QtWidgets.QGridLayout()
- mainLayout.addWidget(formatLabel, 0, 0)
- mainLayout.addWidget(self.formatComboBox, 0, 1)
- mainLayout.addWidget(scopeLabel, 1, 0)
- mainLayout.addWidget(self.scopeComboBox, 1, 1)
- mainLayout.addWidget(organizationLabel, 2, 0)
- mainLayout.addWidget(self.organizationComboBox, 2, 1)
- mainLayout.addWidget(applicationLabel, 3, 0)
- mainLayout.addWidget(self.applicationComboBox, 3, 1)
- mainLayout.addWidget(self.locationsGroupBox, 4, 0, 1, 2)
- mainLayout.addWidget(self.buttonBox, 5, 0, 1, 2)
- self.setLayout(mainLayout)
-
- self.updateLocationsTable()
+ self.format_combo = QComboBox()
+ self.format_combo.addItem("Native")
+ self.format_combo.addItem("INI")
+
+ self.scope_cCombo = QComboBox()
+ self.scope_cCombo.addItem("User")
+ self.scope_cCombo.addItem("System")
+
+ self.organization_combo = QComboBox()
+ self.organization_combo.addItem("Trolltech")
+ self.organization_combo.setEditable(True)
+
+ self.application_combo = QComboBox()
+ self.application_combo.addItem("Any")
+ self.application_combo.addItem("Application Example")
+ self.application_combo.addItem("Assistant")
+ self.application_combo.addItem("Designer")
+ self.application_combo.addItem("Linguist")
+ self.application_combo.setEditable(True)
+ self.application_combo.setCurrentIndex(3)
+
+ format_label = QLabel("&Format:")
+ format_label.setBuddy(self.format_combo)
+
+ scope_label = QLabel("&Scope:")
+ scope_label.setBuddy(self.scope_cCombo)
+
+ organization_label = QLabel("&Organization:")
+ organization_label.setBuddy(self.organization_combo)
+
+ application_label = QLabel("&Application:")
+ application_label.setBuddy(self.application_combo)
+
+ self.locations_groupbox = QGroupBox("Setting Locations")
+
+ self.locations_table = QTableWidget()
+ self.locations_table.setSelectionMode(QAbstractItemView.SingleSelection)
+ self.locations_table.setSelectionBehavior(QAbstractItemView.SelectRows)
+ self.locations_table.setEditTriggers(QAbstractItemView.NoEditTriggers)
+ self.locations_table.setColumnCount(2)
+ self.locations_table.setHorizontalHeaderLabels(("Location", "Access"))
+ self.locations_table.horizontalHeader().setSectionResizeMode(0, QHeaderView.Stretch)
+ self.locations_table.horizontalHeader().resizeSection(1, 180)
+
+ self.button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
+
+ self.format_combo.activated.connect(self.update_locations)
+ self.scope_cCombo.activated.connect(self.update_locations)
+ self.organization_combo.lineEdit().editingFinished.connect(self.update_locations)
+ self.application_combo.lineEdit().editingFinished.connect(self.update_locations)
+ self.button_box.accepted.connect(self.accept)
+ self.button_box.rejected.connect(self.reject)
+
+ locations_layout = QVBoxLayout(self.locations_groupbox)
+ locations_layout.addWidget(self.locations_table)
+
+ mainLayout = QGridLayout(self)
+ mainLayout.addWidget(format_label, 0, 0)
+ mainLayout.addWidget(self.format_combo, 0, 1)
+ mainLayout.addWidget(scope_label, 1, 0)
+ mainLayout.addWidget(self.scope_cCombo, 1, 1)
+ mainLayout.addWidget(organization_label, 2, 0)
+ mainLayout.addWidget(self.organization_combo, 2, 1)
+ mainLayout.addWidget(application_label, 3, 0)
+ mainLayout.addWidget(self.application_combo, 3, 1)
+ mainLayout.addWidget(self.locations_groupbox, 4, 0, 1, 2)
+ mainLayout.addWidget(self.button_box, 5, 0, 1, 2)
+
+ self.update_locations()
self.setWindowTitle("Open Application Settings")
self.resize(650, 400)
def format(self):
- if self.formatComboBox.currentIndex() == 0:
- return QtCore.QSettings.NativeFormat
+ if self.format_combo.currentIndex() == 0:
+ return QSettings.NativeFormat
else:
- return QtCore.QSettings.IniFormat
+ return QSettings.IniFormat
def scope(self):
- if self.scopeComboBox.currentIndex() == 0:
- return QtCore.QSettings.UserScope
+ if self.scope_cCombo.currentIndex() == 0:
+ return QSettings.UserScope
else:
- return QtCore.QSettings.SystemScope
+ return QSettings.SystemScope
def organization(self):
- return self.organizationComboBox.currentText()
+ return self.organization_combo.currentText()
def application(self):
- if self.applicationComboBox.currentText() == "Any":
+ if self.application_combo.currentText() == "Any":
return ''
- return self.applicationComboBox.currentText()
+ return self.application_combo.currentText()
- def updateLocationsTable(self):
- self.locationsTable.setUpdatesEnabled(False)
- self.locationsTable.setRowCount(0)
+ def update_locations(self):
+ self.locations_table.setUpdatesEnabled(False)
+ self.locations_table.setRowCount(0)
for i in range(2):
if i == 0:
- if self.scope() == QtCore.QSettings.SystemScope:
+ if self.scope() == QSettings.SystemScope:
continue
- actualScope = QtCore.QSettings.UserScope
+ actualScope = QSettings.UserScope
else:
- actualScope = QtCore.QSettings.SystemScope
+ actualScope = QSettings.SystemScope
for j in range(2):
if j == 0:
@@ -308,16 +438,16 @@ class LocationDialog(QtWidgets.QDialog):
else:
actualApplication = ''
- settings = QtCore.QSettings(self.format(), actualScope,
- self.organization(), actualApplication)
+ settings = QSettings(self.format(), actualScope,
+ self.organization(), actualApplication)
- row = self.locationsTable.rowCount()
- self.locationsTable.setRowCount(row + 1)
+ row = self.locations_table.rowCount()
+ self.locations_table.setRowCount(row + 1)
- item0 = QtWidgets.QTableWidgetItem()
+ item0 = QTableWidgetItem()
item0.setText(settings.fileName())
- item1 = QtWidgets.QTableWidgetItem()
+ item1 = QTableWidgetItem()
disable = not (settings.childKeys() or settings.childGroups())
if row == 0:
@@ -326,77 +456,79 @@ class LocationDialog(QtWidgets.QDialog):
disable = False
else:
item1.setText("Read-only")
- self.buttonBox.button(QtWidgets.QDialogButtonBox.Ok).setDisabled(disable)
+ self.button_box.button(QDialogButtonBox.Ok).setDisabled(disable)
else:
item1.setText("Read-only fallback")
if disable:
- item0.setFlags(item0.flags() & ~QtCore.Qt.ItemIsEnabled)
- item1.setFlags(item1.flags() & ~QtCore.Qt.ItemIsEnabled)
+ item0.setFlags(item0.flags() & ~Qt.ItemIsEnabled)
+ item1.setFlags(item1.flags() & ~Qt.ItemIsEnabled)
- self.locationsTable.setItem(row, 0, item0)
- self.locationsTable.setItem(row, 1, item1)
+ self.locations_table.setItem(row, 0, item0)
+ self.locations_table.setItem(row, 1, item1)
- self.locationsTable.setUpdatesEnabled(True)
+ self.locations_table.setUpdatesEnabled(True)
-class SettingsTree(QtWidgets.QTreeWidget):
+class SettingsTree(QTreeWidget):
def __init__(self, parent=None):
super(SettingsTree, self).__init__(parent)
- self.setItemDelegate(VariantDelegate(self))
+ self._type_checker = TypeChecker()
+ self.setItemDelegate(VariantDelegate(self._type_checker, self))
self.setHeaderLabels(("Setting", "Type", "Value"))
- self.header().setSectionResizeMode(0, QtWidgets.QHeaderView.Stretch)
- self.header().setSectionResizeMode(2, QtWidgets.QHeaderView.Stretch)
+ self.header().setSectionResizeMode(0, QHeaderView.Stretch)
+ self.header().setSectionResizeMode(2, QHeaderView.Stretch)
self.settings = None
- self.refreshTimer = QtCore.QTimer()
- self.refreshTimer.setInterval(2000)
- self.autoRefresh = False
-
- self.groupIcon = QtGui.QIcon()
- self.groupIcon.addPixmap(self.style().standardPixmap(QtWidgets.QStyle.SP_DirClosedIcon),
- QtGui.QIcon.Normal, QtGui.QIcon.Off)
- self.groupIcon.addPixmap(self.style().standardPixmap(QtWidgets.QStyle.SP_DirOpenIcon),
- QtGui.QIcon.Normal, QtGui.QIcon.On)
- self.keyIcon = QtGui.QIcon()
- self.keyIcon.addPixmap(self.style().standardPixmap(QtWidgets.QStyle.SP_FileIcon))
-
- self.refreshTimer.timeout.connect(self.maybeRefresh)
-
- def setSettingsObject(self, settings):
+ self.refresh_timer = QTimer()
+ self.refresh_timer.setInterval(2000)
+ self.auto_refresh = False
+
+ self.group_icon = QIcon()
+ style = self.style()
+ self.group_icon.addPixmap(style.standardPixmap(QStyle.SP_DirClosedIcon),
+ QIcon.Normal, QIcon.Off)
+ self.group_icon.addPixmap(style.standardPixmap(QStyle.SP_DirOpenIcon),
+ QIcon.Normal, QIcon.On)
+ self.key_icon = QIcon()
+ self.key_icon.addPixmap(style.standardPixmap(QStyle.SP_FileIcon))
+
+ self.refresh_timer.timeout.connect(self.maybe_refresh)
+
+ def set_settings_object(self, settings):
self.settings = settings
self.clear()
if self.settings is not None:
self.settings.setParent(self)
self.refresh()
- if self.autoRefresh:
- self.refreshTimer.start()
+ if self.auto_refresh:
+ self.refresh_timer.start()
else:
- self.refreshTimer.stop()
+ self.refresh_timer.stop()
def sizeHint(self):
- return QtCore.QSize(800, 600)
+ return QSize(800, 600)
- def setAutoRefresh(self, autoRefresh):
- self.autoRefresh = autoRefresh
+ def set_auto_refresh(self, autoRefresh):
+ self.auto_refresh = autoRefresh
if self.settings is not None:
- if self.autoRefresh:
- self.maybeRefresh()
- self.refreshTimer.start()
+ if self.auto_refresh:
+ self.maybe_refresh()
+ self.refresh_timer.start()
else:
- self.refreshTimer.stop()
+ self.refresh_timer.stop()
- def setFallbacksEnabled(self, enabled):
+ def set_fallbacks_enabled(self, enabled):
if self.settings is not None:
self.settings.setFallbacksEnabled(enabled)
self.refresh()
- def maybeRefresh(self):
- if self.state() != QtWidgets.QAbstractItemView.EditingState:
+ def maybe_refresh(self):
+ if self.state() != QAbstractItemView.EditingState:
self.refresh()
def refresh(self):
@@ -405,23 +537,23 @@ class SettingsTree(QtWidgets.QTreeWidget):
# The signal might not be connected.
try:
- self.itemChanged.disconnect(self.updateSetting)
+ self.itemChanged.disconnect(self.update_setting)
except:
pass
self.settings.sync()
- self.updateChildItems(None)
+ self.update_child_items(None)
- self.itemChanged.connect(self.updateSetting)
+ self.itemChanged.connect(self.update_setting)
def event(self, event):
- if event.type() == QtCore.QEvent.WindowActivate:
- if self.isActiveWindow() and self.autoRefresh:
- self.maybeRefresh()
+ if event.type() == QEvent.WindowActivate:
+ if self.isActiveWindow() and self.auto_refresh:
+ self.maybe_refresh()
return super(SettingsTree, self).event(event)
- def updateSetting(self, item):
+ def update_setting(self, item):
key = item.text(0)
ancestor = item.parent()
@@ -429,154 +561,121 @@ class SettingsTree(QtWidgets.QTreeWidget):
key = ancestor.text(0) + '/' + key
ancestor = ancestor.parent()
- d = item.data(2, QtCore.Qt.UserRole)
- self.settings.setValue(key, item.data(2, QtCore.Qt.UserRole))
+ d = item.data(2, Qt.UserRole)
+ self.settings.setValue(key, item.data(2, Qt.UserRole))
- if self.autoRefresh:
+ if self.auto_refresh:
self.refresh()
- def updateChildItems(self, parent):
- dividerIndex = 0
+ def update_child_items(self, parent):
+ divider_index = 0
for group in self.settings.childGroups():
- childIndex = self.findChild(parent, group, dividerIndex)
- if childIndex != -1:
- child = self.childAt(parent, childIndex)
+ child_index = self.find_child(parent, group, divider_index)
+ if child_index != -1:
+ child = self.child_at(parent, child_index)
child.setText(1, '')
child.setText(2, '')
- child.setData(2, QtCore.Qt.UserRole, None)
- self.moveItemForward(parent, childIndex, dividerIndex)
+ child.setData(2, Qt.UserRole, None)
+ self.move_item_forward(parent, child_index, divider_index)
else:
- child = self.createItem(group, parent, dividerIndex)
+ child = self.create_item(group, parent, divider_index)
- child.setIcon(0, self.groupIcon)
- dividerIndex += 1
+ child.setIcon(0, self.group_icon)
+ divider_index += 1
self.settings.beginGroup(group)
- self.updateChildItems(child)
+ self.update_child_items(child)
self.settings.endGroup()
for key in self.settings.childKeys():
- childIndex = self.findChild(parent, key, 0)
- if childIndex == -1 or childIndex >= dividerIndex:
- if childIndex != -1:
- child = self.childAt(parent, childIndex)
+ child_index = self.find_child(parent, key, 0)
+ if child_index == -1 or child_index >= divider_index:
+ if child_index != -1:
+ child = self.child_at(parent, child_index)
for i in range(child.childCount()):
- self.deleteItem(child, i)
- self.moveItemForward(parent, childIndex, dividerIndex)
+ self.delete_item(child, i)
+ self.move_item_forward(parent, child_index, divider_index)
else:
- child = self.createItem(key, parent, dividerIndex)
- child.setIcon(0, self.keyIcon)
- dividerIndex += 1
+ child = self.create_item(key, parent, divider_index)
+ child.setIcon(0, self.key_icon)
+ divider_index += 1
else:
- child = self.childAt(parent, childIndex)
+ child = self.child_at(parent, child_index)
value = self.settings.value(key)
if value is None:
child.setText(1, 'Invalid')
else:
+ # Try to convert to type unless a QByteArray is received
+ if isinstance(value, str):
+ value_type = self._type_checker.type_from_text(value)
+ if value_type:
+ value = self.settings.value(key, type=value_type)
child.setText(1, value.__class__.__name__)
child.setText(2, VariantDelegate.displayText(value))
- child.setData(2, QtCore.Qt.UserRole, value)
+ child.setData(2, Qt.UserRole, value)
- while dividerIndex < self.childCount(parent):
- self.deleteItem(parent, dividerIndex)
+ while divider_index < self.child_count(parent):
+ self.delete_item(parent, divider_index)
- def createItem(self, text, parent, index):
+ def create_item(self, text, parent, index):
after = None
if index != 0:
- after = self.childAt(parent, index - 1)
+ after = self.child_at(parent, index - 1)
if parent is not None:
- item = QtWidgets.QTreeWidgetItem(parent, after)
+ item = QTreeWidgetItem(parent, after)
else:
- item = QtWidgets.QTreeWidgetItem(self, after)
+ item = QTreeWidgetItem(self, after)
item.setText(0, text)
- item.setFlags(item.flags() | QtCore.Qt.ItemIsEditable)
+ item.setFlags(item.flags() | Qt.ItemIsEditable)
return item
- def deleteItem(self, parent, index):
+ def delete_item(self, parent, index):
if parent is not None:
item = parent.takeChild(index)
else:
item = self.takeTopLevelItem(index)
del item
- def childAt(self, parent, index):
+ def child_at(self, parent, index):
if parent is not None:
return parent.child(index)
else:
return self.topLevelItem(index)
- def childCount(self, parent):
+ def child_count(self, parent):
if parent is not None:
return parent.childCount()
else:
return self.topLevelItemCount()
- def findChild(self, parent, text, startIndex):
- for i in range(self.childCount(parent)):
- if self.childAt(parent, i).text(0) == text:
+ def find_child(self, parent, text, startIndex):
+ for i in range(self.child_count(parent)):
+ if self.child_at(parent, i).text(0) == text:
return i
return -1
- def moveItemForward(self, parent, oldIndex, newIndex):
+ def move_item_forward(self, parent, oldIndex, newIndex):
for int in range(oldIndex - newIndex):
- self.deleteItem(parent, newIndex)
+ self.delete_item(parent, newIndex)
-class VariantDelegate(QtWidgets.QItemDelegate):
- def __init__(self, parent=None):
+class VariantDelegate(QItemDelegate):
+ def __init__(self, type_checker, parent=None):
super(VariantDelegate, self).__init__(parent)
-
- self.boolExp = QtCore.QRegExp()
- self.boolExp.setPattern('true|false')
- self.boolExp.setCaseSensitivity(QtCore.Qt.CaseInsensitive)
-
- self.byteArrayExp = QtCore.QRegExp()
- self.byteArrayExp.setPattern('[\\x00-\\xff]*')
-
- self.charExp = QtCore.QRegExp()
- self.charExp.setPattern('.')
-
- self.colorExp = QtCore.QRegExp()
- self.colorExp.setPattern('\\(([0-9]*),([0-9]*),([0-9]*),([0-9]*)\\)')
-
- self.doubleExp = QtCore.QRegExp()
- self.doubleExp.setPattern('')
-
- self.pointExp = QtCore.QRegExp()
- self.pointExp.setPattern('\\((-?[0-9]*),(-?[0-9]*)\\)')
-
- self.rectExp = QtCore.QRegExp()
- self.rectExp.setPattern('\\((-?[0-9]*),(-?[0-9]*),(-?[0-9]*),(-?[0-9]*)\\)')
-
- self.signedIntegerExp = QtCore.QRegExp()
- self.signedIntegerExp.setPattern('-?[0-9]*')
-
- self.sizeExp = QtCore.QRegExp(self.pointExp)
-
- self.unsignedIntegerExp = QtCore.QRegExp()
- self.unsignedIntegerExp.setPattern('[0-9]*')
-
- self.dateExp = QtCore.QRegExp()
- self.dateExp.setPattern('([0-9]{,4})-([0-9]{,2})-([0-9]{,2})')
-
- self.timeExp = QtCore.QRegExp()
- self.timeExp.setPattern('([0-9]{,2}):([0-9]{,2}):([0-9]{,2})')
-
- self.dateTimeExp = QtCore.QRegExp()
- self.dateTimeExp.setPattern(self.dateExp.pattern() + 'T' + self.timeExp.pattern())
+ self._type_checker = type_checker
def paint(self, painter, option, index):
if index.column() == 2:
- value = index.model().data(index, QtCore.Qt.UserRole)
- if not self.isSupportedType(value):
- myOption = QtWidgets.QStyleOptionViewItem(option)
- myOption.state &= ~QtWidgets.QStyle.State_Enabled
- super(VariantDelegate, self).paint(painter, myOption, index)
+ value = index.model().data(index, Qt.UserRole)
+ if not self.is_supported_type(value):
+ my_option = QStyleOptionViewItem(option)
+ my_option.state &= ~QStyle.State_Enabled
+ super(VariantDelegate, self).paint(painter, my_option, index)
return
super(VariantDelegate, self).paint(painter, option, index)
@@ -585,137 +684,97 @@ class VariantDelegate(QtWidgets.QItemDelegate):
if index.column() != 2:
return None
- originalValue = index.model().data(index, QtCore.Qt.UserRole)
- if not self.isSupportedType(originalValue):
+ original_value = index.model().data(index, Qt.UserRole)
+ if not self.is_supported_type(original_value):
return None
- lineEdit = QtWidgets.QLineEdit(parent)
- lineEdit.setFrame(False)
-
- if isinstance(originalValue, bool):
- regExp = self.boolExp
- elif isinstance(originalValue, float):
- regExp = self.doubleExp
- elif isinstance(originalValue, int):
- regExp = self.signedIntegerExp
- elif isinstance(originalValue, QtCore.QByteArray):
- regExp = self.byteArrayExp
- elif isinstance(originalValue, QtGui.QColor):
- regExp = self.colorExp
- elif isinstance(originalValue, QtCore.QDate):
- regExp = self.dateExp
- elif isinstance(originalValue, QtCore.QDateTime):
- regExp = self.dateTimeExp
- elif isinstance(originalValue, QtCore.QTime):
- regExp = self.timeExp
- elif isinstance(originalValue, QtCore.QPoint):
- regExp = self.pointExp
- elif isinstance(originalValue, QtCore.QRect):
- regExp = self.rectExp
- elif isinstance(originalValue, QtCore.QSize):
- regExp = self.sizeExp
+ editor = None
+ if isinstance(original_value, bool):
+ editor = QCheckBox(parent)
+ if isinstance(original_value, int):
+ editor = QSpinBox(parent)
+ editor.setRange(-32767, 32767)
else:
- regExp = QtCore.QRegExp()
-
- if not regExp.isEmpty():
- validator = QtGui.QRegExpValidator(regExp, lineEdit)
- lineEdit.setValidator(validator)
-
- return lineEdit
+ editor = QLineEdit(parent)
+ editor.setFrame(False)
+ validator = self._type_checker.create_validator(original_value, editor)
+ if validator:
+ editor.setValidator(validator)
+ return editor
def setEditorData(self, editor, index):
- value = index.model().data(index, QtCore.Qt.UserRole)
- if editor is not None:
- editor.setText(self.displayText(value))
-
- def setModelData(self, editor, model, index):
- if not editor.isModified():
+ if not editor:
return
+ value = index.model().data(index, Qt.UserRole)
+ if isinstance(editor, QCheckBox):
+ editor.setCheckState(Qt.Checked if value else Qt.Unchecked)
+ elif isinstance(editor, QSpinBox):
+ editor.setValue(value)
+ else:
+ editor.setText(self.displayText(value))
- text = editor.text()
- validator = editor.validator()
+ def value_from_lineedit(self, lineedit, model, index):
+ if not lineedit.isModified():
+ return None
+ text = lineedit.text()
+ validator = lineedit.validator()
if validator is not None:
state, text, _ = validator.validate(text, 0)
- if state != QtGui.QValidator.Acceptable:
- return
+ if state != QValidator.Acceptable:
+ return None
+ original_value = index.model().data(index, Qt.UserRole)
+ return self._type_checker.from_string(text, original_value)
- originalValue = index.model().data(index, QtCore.Qt.UserRole)
-
- if isinstance(originalValue, QtGui.QColor):
- self.colorExp.exactMatch(text)
- value = QtGui.QColor(min(int(self.colorExp.cap(1)), 255),
- min(int(self.colorExp.cap(2)), 255),
- min(int(self.colorExp.cap(3)), 255),
- min(int(self.colorExp.cap(4)), 255))
- elif isinstance(originalValue, QtCore.QDate):
- value = QtCore.QDate.fromString(text, QtCore.Qt.ISODate)
- if not value.isValid():
- return
- elif isinstance(originalValue, QtCore.QDateTime):
- value = QtCore.QDateTime.fromString(text, QtCore.Qt.ISODate)
- if not value.isValid():
- return
- elif isinstance(originalValue, QtCore.QTime):
- value = QtCore.QTime.fromString(text, QtCore.Qt.ISODate)
- if not value.isValid():
- return
- elif isinstance(originalValue, QtCore.QPoint):
- self.pointExp.exactMatch(text)
- value = QtCore.QPoint(int(self.pointExp.cap(1)),
- int(self.pointExp.cap(2)))
- elif isinstance(originalValue, QtCore.QRect):
- self.rectExp.exactMatch(text)
- value = QtCore.QRect(int(self.rectExp.cap(1)),
- int(self.rectExp.cap(2)),
- int(self.rectExp.cap(3)),
- int(self.rectExp.cap(4)))
- elif isinstance(originalValue, QtCore.QSize):
- self.sizeExp.exactMatch(text)
- value = QtCore.QSize(int(self.sizeExp.cap(1)),
- int(self.sizeExp.cap(2)))
- elif isinstance(originalValue, list):
- value = text.split(',')
+ def setModelData(self, editor, model, index):
+ value = None
+ if isinstance(editor, QCheckBox):
+ value = editor.checkState() == Qt.Checked
+ elif isinstance(editor, QSpinBox):
+ value = editor.value()
else:
- value = type(originalValue)(text)
-
- model.setData(index, self.displayText(value), QtCore.Qt.DisplayRole)
- model.setData(index, value, QtCore.Qt.UserRole)
+ value = self.value_from_lineedit(editor, model, index)
+ if not value is None:
+ model.setData(index, value, Qt.UserRole)
+ model.setData(index, self.displayText(value), Qt.DisplayRole)
@staticmethod
- def isSupportedType(value):
- return isinstance(value, (bool, float, int, QtCore.QByteArray,
- str, QtGui.QColor, QtCore.QDate, QtCore.QDateTime,
- QtCore.QTime, QtCore.QPoint, QtCore.QRect, QtCore.QSize,
- list))
+ def is_supported_type(value):
+ return isinstance(value, (bool, float, int, QByteArray, str, QColor,
+ QDate, QDateTime, QTime, QPoint, QRect,
+ QSize, list))
@staticmethod
def displayText(value):
- if isinstance(value, (bool, int, QtCore.QByteArray)):
- return str(value)
if isinstance(value, str):
return value
- elif isinstance(value, float):
- return '%g' % value
- elif isinstance(value, QtGui.QColor):
- return '(%u,%u,%u,%u)' % (value.red(), value.green(), value.blue(), value.alpha())
- elif isinstance(value, (QtCore.QDate, QtCore.QDateTime, QtCore.QTime)):
- return value.toString(QtCore.Qt.ISODate)
- elif isinstance(value, QtCore.QPoint):
- return '(%d,%d)' % (value.x(), value.y())
- elif isinstance(value, QtCore.QRect):
- return '(%d,%d,%d,%d)' % (value.x(), value.y(), value.width(), value.height())
- elif isinstance(value, QtCore.QSize):
- return '(%d,%d)' % (value.width(), value.height())
- elif isinstance(value, list):
+ if isinstance(value, bool):
+ return '✓' if value else '☐'
+ if isinstance(value, (int, float, QByteArray)):
+ return str(value)
+ if isinstance(value, QColor):
+ return '({},{},{},{})'.format(value.red(), value.green(),
+ value.blue(), value.alpha())
+ if isinstance(value, (QDate, QDateTime, QTime)):
+ return value.toString(Qt.ISODate)
+ if isinstance(value, QPoint):
+ return '({},{})'.format(value.x(), value.y())
+ if isinstance(value, QRect):
+ return '({},{},{},{})'.format(value.x(), value.y(), value.width(),
+ value.height())
+ if isinstance(value, QSize):
+ return '({},{})'.format(value.width(), value.height())
+ if isinstance(value, list):
return ','.join(value)
- elif value is None:
+ if value is None:
return '<Invalid>'
- return '<%s>' % value
+ return '<{}>'.format(value)
if __name__ == '__main__':
- app = QtWidgets.QApplication(sys.argv)
- mainWin = MainWindow()
- mainWin.show()
+ app = QApplication(sys.argv)
+ main_win = MainWindow()
+ if len(sys.argv) > 1:
+ main_win.load_ini_file(sys.argv[1])
+ main_win.show()
sys.exit(app.exec_())
diff --git a/sources/pyside2/PySide2/global.h.in b/sources/pyside2/PySide2/global.h.in
index ae1b103f5..a23b0f332 100644
--- a/sources/pyside2/PySide2/global.h.in
+++ b/sources/pyside2/PySide2/global.h.in
@@ -40,6 +40,13 @@
// Make "signals:", "slots:" visible as access specifiers
#define QT_ANNOTATE_ACCESS_SPECIFIER(a) __attribute__((annotate(#a)))
+// Q_PROPERTY is defined as class annotation which does not work since a
+// sequence of properties will to expand to a sequence of annotations
+// annotating nothing, causing clang to complain. Instead, define it away in a
+// static assert with the stringified argument in a ','-operator (cf qdoc).
+
+#define QT_ANNOTATE_CLASS(type,...) static_assert(sizeof(#__VA_ARGS__),#type);
+
#include <QtCore/qnamespace.h>
#if @ENABLE_X11@
diff --git a/sources/pyside2/libpyside/CMakeLists.txt b/sources/pyside2/libpyside/CMakeLists.txt
index 31f68749a..d6b20c45a 100644
--- a/sources/pyside2/libpyside/CMakeLists.txt
+++ b/sources/pyside2/libpyside/CMakeLists.txt
@@ -40,6 +40,7 @@ endif()
set(libpyside_SRC
dynamicqmetaobject.cpp
+ feature_select.cpp
signalmanager.cpp
globalreceiverv2.cpp
pysideclassinfo.cpp
@@ -125,6 +126,7 @@ endif()
set(libpyside_HEADERS
dynamicqmetaobject.h
+ feature_select.h
pysideclassinfo.h
pysideqenum.h
pysidemacros.h
diff --git a/sources/pyside2/libpyside/feature_select.cpp b/sources/pyside2/libpyside/feature_select.cpp
new file mode 100644
index 000000000..b7234ad75
--- /dev/null
+++ b/sources/pyside2/libpyside/feature_select.cpp
@@ -0,0 +1,395 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt for Python.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** 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-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "feature_select.h"
+
+#include <shiboken.h>
+#include <sbkstaticstrings.h>
+
+#include <QtCore/QtGlobal>
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// PYSIDE-1019: Support switchable extensions
+//
+// This functionality is no longer implemented in the signature module, since
+// the PyCFunction getsets do not have to be modified any longer.
+// Instead, we simply exchange the complete class dicts. This is done in the
+// basewrapper.cpp file.
+//
+// This is the general framework of the switchable extensions.
+// A maximum of eight features is planned so far. This seems to be enough.
+// More features are possible, but then we must somehow register the
+// extra `select_id`s above 255.
+//
+
+/*****************************************************************************
+
+ How Does This Feature Selection Work?
+ -------------------------------------
+
+The basic idea is to replace the `tp_dict` of a QObject derived type.
+This way, we can replace the methods of the dict in no time.
+
+The crucial point to understand is how the `tp_dict` is actually accessed:
+When you type "QObject.__dict__", the descriptor of SbkObjectType_Type
+is called. This descriptor is per default unassigned, so the base class
+PyType_Type provides the tp_getset method `type_dict`:
+
+ static PyObject *
+ type_dict(PyTypeObject *type, void *context)
+ {
+ if (type->tp_dict == NULL) {
+ Py_RETURN_NONE;
+ }
+ return PyDictProxy_New(type->tp_dict);
+ }
+
+In order to change that, we need to insert our own version into SbkObjectType:
+
+ static PyObject *Sbk_TypeGet___dict__(PyTypeObject *type, void *context)
+ {
+ auto dict = type->tp_dict;
+ if (dict == NULL)
+ Py_RETURN_NONE;
+ if (SelectFeatureSet != nullptr)
+ dict = SelectFeatureSet(type);
+ return PyDictProxy_New(dict);
+ }
+
+This way, the Python function `type_ready()` does not fill in the default,
+but uses our modified version. It a similar way, we overwrite type_getattro
+with our own version, again in SbkObjectType, replacing the default of
+PyType_Type.
+
+Now we can exchange the dict with a customized version.
+We have our own derived type `ChameleonDict` with additional attributes.
+These allow us to create a ring of dicts which can be rotated to the actual
+needed dict version:
+
+Every dict has a field `select_id` which is selected by the `from __feature__`
+import. The dicts are cyclic connected by the `dict_ring` field.
+
+When a class dict is required, now always `SelectFeatureSet` is called, which
+looks into the `__name__` attribute of the active module and decides which
+version of `tp_dict` is needed. Then the right dict is searched in the ring
+and created if not already there.
+
+This is everything that the following code does.
+
+*****************************************************************************/
+
+
+namespace PySide { namespace FeatureSelector {
+
+using namespace Shiboken;
+
+static PyObject *getFeatureSelectID()
+{
+ static PyObject *zero = PyInt_FromLong(0);
+ static PyObject *feature_dict = GetFeatureDict();
+ // these things are all borrowed
+ PyObject *globals = PyEval_GetGlobals();
+ if (globals == nullptr)
+ return zero;
+ PyObject *modname = PyDict_GetItem(globals, PyMagicName::name());
+ if (modname == nullptr)
+ return zero;
+ PyObject *flag = PyDict_GetItem(feature_dict, modname);
+ if (flag == nullptr || !PyInt_Check(flag)) // int/long cheating
+ return zero;
+ return flag;
+}
+
+// Create a derived dict class
+static PyTypeObject *
+createDerivedDictType()
+{
+ // It is not easy to create a compatible dict object with the
+ // limited API. Easier is to use Python to create a derived
+ // type and to modify that a bit from the C code.
+ PyObject *ChameleonDict = PepRun_GetResult(R"CPP(if True:
+
+ class ChameleonDict(dict):
+ __slots__ = ("dict_ring", "select_id")
+
+ result = ChameleonDict
+
+ )CPP");
+ return reinterpret_cast<PyTypeObject *>(ChameleonDict);
+}
+
+static PyTypeObject *old_dict_type = Py_TYPE(PyType_Type.tp_dict);
+static PyTypeObject *new_dict_type = nullptr;
+
+static void ensureNewDictType()
+{
+ if (new_dict_type == nullptr) {
+ new_dict_type = createDerivedDictType();
+ if (new_dict_type == nullptr)
+ Py_FatalError("PySide2: Problem creating ChameleonDict");
+ }
+}
+
+static inline PyObject *nextInCircle(PyObject *dict)
+{
+ // returns a borrowed ref
+ assert(Py_TYPE(dict) != old_dict_type);
+ AutoDecRef next_dict(PyObject_GetAttr(dict, PyName::dict_ring()));
+ return next_dict;
+}
+
+static inline void setNextDict(PyObject *dict, PyObject *next_dict)
+{
+ assert(Py_TYPE(dict) != old_dict_type);
+ PyObject_SetAttr(dict, PyName::dict_ring(), next_dict);
+}
+
+static inline void setSelectId(PyObject *dict, PyObject *select_id)
+{
+ assert(Py_TYPE(dict) != old_dict_type);
+ PyObject_SetAttr(dict, PyName::select_id(), select_id);
+}
+
+static inline PyObject *getSelectId(PyObject *dict)
+{
+ assert(Py_TYPE(dict) != old_dict_type);
+ auto select_id = PyObject_GetAttr(dict, PyName::select_id());
+ return select_id;
+}
+
+static bool replaceClassDict(PyTypeObject *type)
+{
+ /*
+ * Replace the type dict by the derived ChameleonDict.
+ * This is mandatory for all type dicts when they are touched.
+ */
+ ensureNewDictType();
+ PyObject *dict = type->tp_dict;
+ auto ob_ndt = reinterpret_cast<PyObject *>(new_dict_type);
+ PyObject *new_dict = PyObject_CallObject(ob_ndt, nullptr);
+ if (new_dict == nullptr || PyDict_Update(new_dict, dict) < 0)
+ return false;
+ // Insert the default id. Cannot fail for small numbers.
+ AutoDecRef select_id(PyInt_FromLong(0));
+ setSelectId(new_dict, select_id);
+ // insert the dict into itself as ring
+ setNextDict(new_dict, new_dict);
+ // We have now an exact copy of the dict with a new type.
+ // Replace `__dict__` which usually has refcount 1 (but see cyclic_test.py)
+ Py_DECREF(type->tp_dict);
+ type->tp_dict = new_dict;
+ return true;
+}
+
+static bool addNewDict(PyTypeObject *type, PyObject *select_id)
+{
+ /*
+ * Add a new dict to the ring and set it as `type->tp_dict`.
+ * A 'false' return is fatal.
+ */
+ auto dict = type->tp_dict;
+ auto ob_ndt = reinterpret_cast<PyObject *>(new_dict_type);
+ auto new_dict = PyObject_CallObject(ob_ndt, nullptr);
+ if (new_dict == nullptr)
+ return false;
+ setSelectId(new_dict, select_id);
+ // insert the dict into the ring
+ auto next_dict = nextInCircle(dict);
+ setNextDict(dict, new_dict);
+ setNextDict(new_dict, next_dict);
+ type->tp_dict = new_dict;
+ return true;
+}
+
+static bool moveToFeatureSet(PyTypeObject *type, PyObject *select_id)
+{
+ /*
+ * Rotate the ring to the given `select_id` and return `true`.
+ * If not found, stay at the current position and return `false`.
+ */
+ auto initial_dict = type->tp_dict;
+ auto dict = initial_dict;
+ do {
+ dict = nextInCircle(dict);
+ AutoDecRef current_id(getSelectId(dict));
+ // This works because small numbers are singleton objects.
+ if (current_id == select_id) {
+ type->tp_dict = dict;
+ return true;
+ }
+ } while (dict != initial_dict);
+ type->tp_dict = initial_dict;
+ return false;
+}
+
+typedef bool(*FeatureProc)(PyTypeObject *type, PyObject *prev_dict);
+
+static FeatureProc *featurePointer = nullptr;
+
+static bool createNewFeatureSet(PyTypeObject *type, PyObject *select_id)
+{
+ /*
+ * Create a new feature set.
+ * A `false` return value is a fatal error.
+ *
+ * A FeatureProc sees an empty `type->tp_dict` and the previous dict
+ * content in `prev_dict`. It is responsible of filling `type->tp_dict`
+ * with modified content.
+ */
+ static auto small_1 = PyInt_FromLong(255);
+ Q_UNUSED(small_1);
+ static auto small_2 = PyInt_FromLong(255);
+ Q_UNUSED(small_2);
+ // make sure that small integers are cached
+ assert(small_1 != nullptr && small_1 == small_2);
+
+ static auto zero = PyInt_FromLong(0);
+ bool ok = moveToFeatureSet(type, zero);
+ Q_UNUSED(ok);
+ assert(ok);
+
+ AutoDecRef prev_dict(type->tp_dict);
+ Py_INCREF(prev_dict);
+ if (!addNewDict(type, select_id))
+ return false;
+ int id = PyInt_AsSsize_t(select_id);
+ if (id == -1)
+ return false;
+ FeatureProc *proc = featurePointer;
+ for (int idx = id; *proc != nullptr; ++proc, idx >>= 1) {
+ if (idx & 1) {
+ // clear the tp_dict that will get new content
+ PyDict_Clear(type->tp_dict);
+ // let the proc re-fill the tp_dict
+ if (!(*proc)(type, prev_dict))
+ return false;
+ // if there is still a step, prepare `prev_dict`
+ if (idx >> 1) {
+ prev_dict.reset(PyDict_Copy(type->tp_dict));
+ if (prev_dict.isNull())
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+static PyObject *SelectFeatureSet(PyTypeObject *type)
+{
+ /*
+ * This is the main function of the module.
+ * It just makes no sense to make the function public, because
+ * Shiboken will assign it via a public hook of `basewrapper.cpp`.
+ */
+ if (Py_TYPE(type->tp_dict) == old_dict_type) {
+ // PYSIDE-1019: On first touch, we initialize the dynamic naming.
+ // The dict type will be replaced after the first call.
+ if (!replaceClassDict(type))
+ return nullptr;
+ }
+ PyObject *select_id = getFeatureSelectID(); // borrowed
+ AutoDecRef current_id(getSelectId(type->tp_dict));
+ if (select_id != current_id) {
+ if (!moveToFeatureSet(type, select_id))
+ if (!createNewFeatureSet(type, select_id)) {
+ Py_FatalError("failed to create a new feature set!");
+ return nullptr;
+ }
+ }
+ return type->tp_dict;
+}
+
+static bool feature_01_addDummyNames(PyTypeObject *type, PyObject *prev_dict);
+static bool feature_02_addDummyNames(PyTypeObject *type, PyObject *prev_dict);
+static bool feature_04_addDummyNames(PyTypeObject *type, PyObject *prev_dict);
+static bool feature_08_addDummyNames(PyTypeObject *type, PyObject *prev_dict);
+static bool feature_10_addDummyNames(PyTypeObject *type, PyObject *prev_dict);
+static bool feature_20_addDummyNames(PyTypeObject *type, PyObject *prev_dict);
+static bool feature_40_addDummyNames(PyTypeObject *type, PyObject *prev_dict);
+static bool feature_80_addDummyNames(PyTypeObject *type, PyObject *prev_dict);
+
+static FeatureProc featureProcArray[] = {
+ feature_01_addDummyNames,
+ feature_02_addDummyNames,
+ feature_04_addDummyNames,
+ feature_08_addDummyNames,
+ feature_10_addDummyNames,
+ feature_20_addDummyNames,
+ feature_40_addDummyNames,
+ feature_80_addDummyNames,
+ nullptr
+};
+
+void init()
+{
+ featurePointer = featureProcArray;
+ initSelectableFeature(SelectFeatureSet);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+//
+// PYSIDE-1019: Support switchable extensions
+//
+// Feature 0x01..0x80: A fake switchable option for testing
+//
+
+#define SIMILAR_FEATURE(xx) \
+static bool feature_##xx##_addDummyNames(PyTypeObject *type, PyObject *prev_dict) \
+{ \
+ PyObject *dict = type->tp_dict; \
+ if (PyDict_Update(dict, prev_dict) < 0) \
+ return false; \
+ Py_INCREF(Py_None); \
+ if (PyDict_SetItemString(dict, "fake_feature_" #xx, Py_None) < 0) \
+ return false; \
+ return true; \
+}
+
+SIMILAR_FEATURE(01)
+SIMILAR_FEATURE(02)
+SIMILAR_FEATURE(04)
+SIMILAR_FEATURE(08)
+SIMILAR_FEATURE(10)
+SIMILAR_FEATURE(20)
+SIMILAR_FEATURE(40)
+SIMILAR_FEATURE(80)
+
+} // namespace PySide
+} // namespace FeatureSelector
diff --git a/sources/pyside2/libpyside/feature_select.h b/sources/pyside2/libpyside/feature_select.h
new file mode 100644
index 000000000..68e29292d
--- /dev/null
+++ b/sources/pyside2/libpyside/feature_select.h
@@ -0,0 +1,53 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt for Python.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** 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 Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** 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-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef FEATURE_SELECT_H
+#define FEATURE_SELECT_H
+
+#include "pysidemacros.h"
+
+namespace PySide {
+namespace FeatureSelector {
+
+PYSIDE_API void init();
+
+} // namespace PySide
+} // namespace FeatureSelector
+
+#endif // FEATURE_SELECT_H
diff --git a/sources/pyside2/libpyside/pyside.cpp b/sources/pyside2/libpyside/pyside.cpp
index 66e931164..074fa764b 100644
--- a/sources/pyside2/libpyside/pyside.cpp
+++ b/sources/pyside2/libpyside/pyside.cpp
@@ -50,6 +50,7 @@
#include "pysidemetafunction_p.h"
#include "pysidemetafunction.h"
#include "dynamicqmetaobject.h"
+#include "feature_select.h"
#include <autodecref.h>
#include <basewrapper.h>
@@ -93,6 +94,7 @@ void init(PyObject *module)
MetaFunction::init(module);
// Init signal manager, so it will register some meta types used by QVariant.
SignalManager::instance();
+ FeatureSelector::init();
initQApp();
}
diff --git a/sources/pyside2/tests/QtCore/CMakeLists.txt b/sources/pyside2/tests/QtCore/CMakeLists.txt
index f3c3ddfdd..c0ec2ef79 100644
--- a/sources/pyside2/tests/QtCore/CMakeLists.txt
+++ b/sources/pyside2/tests/QtCore/CMakeLists.txt
@@ -37,6 +37,7 @@ PYSIDE_TEST(deletelater_test.py)
PYSIDE_TEST(destroysignal_test.py)
PYSIDE_TEST(duck_punching_test.py)
PYSIDE_TEST(emoji_string_test.py)
+PYSIDE_TEST(feature_test.py)
PYSIDE_TEST(hash_test.py)
PYSIDE_TEST(inherits_test.py)
PYSIDE_TEST(max_signals.py)
diff --git a/sources/pyside2/tests/QtCore/feature_test.py b/sources/pyside2/tests/QtCore/feature_test.py
new file mode 100644
index 000000000..cf1e8c3f2
--- /dev/null
+++ b/sources/pyside2/tests/QtCore/feature_test.py
@@ -0,0 +1,105 @@
+#############################################################################
+##
+## Copyright (C) 2020 The Qt Company Ltd.
+## Contact: https://www.qt.io/licensing/
+##
+## This file is part of Qt for Python.
+##
+## $QT_BEGIN_LICENSE:LGPL$
+## 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 Lesser General Public License Usage
+## Alternatively, this file may be used under the terms of the GNU Lesser
+## General Public License version 3 as published by the Free Software
+## Foundation and appearing in the file LICENSE.LGPL3 included in the
+## packaging of this file. Please review the following information to
+## ensure the GNU Lesser General Public License version 3 requirements
+## will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+##
+## GNU General Public License Usage
+## Alternatively, this file may be used under the terms of the GNU
+## General Public License version 2.0 or (at your option) the GNU General
+## Public license version 3 or any later version approved by the KDE Free
+## Qt Foundation. The licenses are as published by the Free Software
+## Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+## 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-2.0.html and
+## https://www.gnu.org/licenses/gpl-3.0.html.
+##
+## $QT_END_LICENSE$
+##
+#############################################################################
+
+import os
+import sys
+import unittest
+
+sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
+from init_paths import init_test_paths
+init_test_paths(False)
+
+from PySide2 import QtCore
+from PySide2.support.__feature__ import _really_all_feature_names
+from textwrap import dedent
+
+"""
+feature_test.py
+--------------
+
+This tests the selectable features in PySide.
+
+There are no real features implemented. They will be added, later.
+"""
+
+class FeaturesTest(unittest.TestCase):
+
+ def testAllFeatureCombinations(self):
+ """
+ Test for all 256 possible combinations of `__feature__` imports.
+ """
+ global __name__
+
+ for bit in range(8):
+ # We are cheating here, since the functions are in the globals.
+ exec(dedent("""
+
+ def tst_bit{0}(flag, self):
+ if flag == 0:
+ with self.assertRaises(AttributeError):
+ QtCore.QCborArray.fake_feature_{1:02x}
+ with self.assertRaises(KeyError):
+ QtCore.QCborArray.__dict__["fake_feature_{1:02x}"]
+ else:
+ QtCore.QCborArray.fake_feature_{1:02x}
+ QtCore.QCborArray.__dict__["fake_feature_{1:02x}"]
+
+ """.format(bit, 1 << bit)), globals(), globals())
+ feature_list = _really_all_feature_names
+ func_list = [tst_bit0, tst_bit1, tst_bit2, tst_bit3,
+ tst_bit4, tst_bit5, tst_bit6, tst_bit7]
+
+ for idx in range(0x100):
+ __name__ = "feature_{:02x}".format(idx)
+ print()
+ print("--- Feature Test Module `{}` ---".format(__name__))
+ print("Imports:")
+ for bit in range(8):
+ if idx & 1 << bit:
+ feature = feature_list[bit]
+ text = "from __feature__ import {}".format(feature)
+ print(text)
+ exec(text)
+ for bit in range(8):
+ value = idx & 1 << bit
+ func_list[bit](value, self=self)
+
+
+if __name__ == '__main__':
+ unittest.main()
diff --git a/sources/shiboken2/ApiExtractor/abstractmetalang.cpp b/sources/shiboken2/ApiExtractor/abstractmetalang.cpp
index e9a2c2b57..a202c42d5 100644
--- a/sources/shiboken2/ApiExtractor/abstractmetalang.cpp
+++ b/sources/shiboken2/ApiExtractor/abstractmetalang.cpp
@@ -2619,6 +2619,15 @@ void AbstractMetaClass::format(QDebug &d) const
d << (i ? ',' : '<') << instantiatedTypes.at(i)->name();
d << ">\"";
}
+ if (const int count = m_propertySpecs.size()) {
+ d << ", properties (" << count << "): [";
+ for (int i = 0; i < count; ++i) {
+ if (i)
+ d << ", ";
+ m_propertySpecs.at(i)->formatDebug(d);
+ }
+ d << ']';
+ }
}
void AbstractMetaClass::formatMembers(QDebug &d) const
@@ -2725,3 +2734,28 @@ QString AbstractMetaEnum::package() const
{
return m_typeEntry->targetLangPackage();
}
+
+#ifndef QT_NO_DEBUG_STREAM
+void QPropertySpec::formatDebug(QDebug &d) const
+{
+ d << '#' << m_index << " \"" << m_name << "\" (" << m_type->qualifiedCppName()
+ << "), read=" << m_read;
+ if (!m_write.isEmpty())
+ d << ", write=" << m_write;
+ if (!m_reset.isEmpty())
+ d << ", reset=" << m_reset;
+ if (!m_designable.isEmpty())
+ d << ", esignable=" << m_designable;
+}
+
+QDebug operator<<(QDebug d, const QPropertySpec &p)
+{
+ QDebugStateSaver s(d);
+ d.noquote();
+ d.nospace();
+ d << "QPropertySpec(";
+ p.formatDebug(d);
+ d << ')';
+ return d;
+}
+#endif // QT_NO_DEBUG_STREAM
diff --git a/sources/shiboken2/ApiExtractor/abstractmetalang.h b/sources/shiboken2/ApiExtractor/abstractmetalang.h
index 830631e68..8169c4a30 100644
--- a/sources/shiboken2/ApiExtractor/abstractmetalang.h
+++ b/sources/shiboken2/ApiExtractor/abstractmetalang.h
@@ -1807,6 +1807,10 @@ public:
m_index = index;
}
+#ifndef QT_NO_DEBUG_STREAM
+ void formatDebug(QDebug &d) const;
+#endif
+
private:
QString m_name;
QString m_read;
@@ -1817,4 +1821,8 @@ private:
int m_index = -1;
};
+#ifndef QT_NO_DEBUG_STREAM
+QDebug operator<<(QDebug d, const QPropertySpec &p);
+#endif
+
#endif // ABSTRACTMETALANG_H
diff --git a/sources/shiboken2/ApiExtractor/clangparser/clangbuilder.cpp b/sources/shiboken2/ApiExtractor/clangparser/clangbuilder.cpp
index 8765f9c3a..ef3471d6a 100644
--- a/sources/shiboken2/ApiExtractor/clangparser/clangbuilder.cpp
+++ b/sources/shiboken2/ApiExtractor/clangparser/clangbuilder.cpp
@@ -1138,6 +1138,19 @@ BaseVisitor::StartTokenResult Builder::startToken(const CXCursor &cursor)
if (!d->m_currentFunction.isNull())
d->m_currentFunction->setOverride(true);
break;
+ case CXCursor_StaticAssert:
+ // Check for Q_PROPERTY() (see PySide2/global.h.in for an explanation
+ // how it is defined, and qdoc).
+ if (clang_isDeclaration(cursor.kind) && !d->m_currentClass.isNull()) {
+ auto snippet = getCodeSnippet(cursor);
+ const auto length = snippet.size();
+ if (length > 12 && *snippet.rbegin() == ')'
+ && snippet.compare(0, 11, "Q_PROPERTY(") == 0) {
+ const QString qProperty = QString::fromUtf8(snippet.data() + 11, length - 12);
+ d->m_currentClass->addPropertyDeclaration(qProperty);
+ }
+ }
+ break;
default:
break;
}
diff --git a/sources/shiboken2/ApiExtractor/parser/codemodel.cpp b/sources/shiboken2/ApiExtractor/parser/codemodel.cpp
index e5a6e074c..3b9521e82 100644
--- a/sources/shiboken2/ApiExtractor/parser/codemodel.cpp
+++ b/sources/shiboken2/ApiExtractor/parser/codemodel.cpp
@@ -789,6 +789,8 @@ void _ClassModelItem::formatDebug(QDebug &d) const
}
formatModelItemList(d, ", templateParameters=", m_templateParameters);
formatScopeItemsDebug(d);
+ if (!m_propertyDeclarations.isEmpty())
+ d << ", Properties=" << m_propertyDeclarations;
}
#endif // !QT_NO_DEBUG_STREAM
diff --git a/sources/shiboken2/libshiboken/basewrapper.cpp b/sources/shiboken2/libshiboken/basewrapper.cpp
index 4c15582e9..5c2dc7807 100644
--- a/sources/shiboken2/libshiboken/basewrapper.cpp
+++ b/sources/shiboken2/libshiboken/basewrapper.cpp
@@ -95,12 +95,31 @@ void Sbk_object_dealloc(PyObject *self)
static void SbkObjectTypeDealloc(PyObject *pyObj);
static PyObject *SbkObjectTypeTpNew(PyTypeObject *metatype, PyObject *args, PyObject *kwds);
+static SelectableFeatureHook SelectFeatureSet = nullptr;
+
+void initSelectableFeature(SelectableFeatureHook func)
+{
+ SelectFeatureSet = func;
+}
+
+// PYSIDE-1019: Switch type's tp_dict to the currently active namespace.
+static PyObject *Sbk_TypeGet___dict__(PyTypeObject *type, void *context)
+{
+ auto dict = type->tp_dict;
+ if (dict == NULL)
+ Py_RETURN_NONE;
+ if (SelectFeatureSet != nullptr)
+ dict = SelectFeatureSet(type);
+ return PyDictProxy_New(dict);
+}
+
// PYSIDE-908: The function PyType_Modified does not work in PySide, so we need to
// explicitly pass __doc__. For __signature__ it _did_ actually work, because
// it was not existing before. We add them both for clarity.
static PyGetSetDef SbkObjectType_Type_getsetlist[] = {
{const_cast<char *>("__signature__"), (getter)Sbk_TypeGet___signature__},
{const_cast<char *>("__doc__"), (getter)Sbk_TypeGet___doc__},
+ {const_cast<char *>("__dict__"), (getter)Sbk_TypeGet___dict__},
{nullptr} // Sentinel
};
@@ -121,8 +140,25 @@ static PyObject *SbkObjectType_repr(PyObject *type)
#endif // PY_VERSION_HEX < 0x03000000
+// PYSIDE-1019: Switch type's tp_dict to the currently active namespace.
+static PyObject *(*type_getattro)(PyObject *type, PyObject *name);
+
+static PyObject *mangled_type_getattro(PyTypeObject *type, PyObject *name)
+{
+ /*
+ * Note: This `type_getattro` version is only the default that comes
+ * from `PyType_Type.tp_getattro`. This does *not* interfere in any way
+ * with the complex `tp_getattro` of `QObject` and other instances.
+ * What we change here is the meta class of `QObject`.
+ */
+ if (SelectFeatureSet != nullptr)
+ type->tp_dict = SelectFeatureSet(type);
+ return type_getattro(reinterpret_cast<PyObject *>(type), name);
+}
+
static PyType_Slot SbkObjectType_Type_slots[] = {
{Py_tp_dealloc, reinterpret_cast<void *>(SbkObjectTypeDealloc)},
+ {Py_tp_getattro, reinterpret_cast<void *>(mangled_type_getattro)},
{Py_tp_setattro, reinterpret_cast<void *>(PyObject_GenericSetAttr)},
{Py_tp_base, static_cast<void *>(&PyType_Type)},
{Py_tp_alloc, reinterpret_cast<void *>(PyType_GenericAlloc)},
@@ -235,6 +271,9 @@ PyTypeObject *SbkObjectType_TypeF(void)
{
static PyTypeObject *type = nullptr;
if (!type) {
+ // PYSIDE-1019: Insert the default tp_getattro explicitly here
+ // so we can overwrite it a bit.
+ type_getattro = PyType_Type.tp_getattro;
SbkObjectType_Type_spec.basicsize =
PepHeapType_SIZE + sizeof(SbkObjectTypePrivate);
type = reinterpret_cast<PyTypeObject *>(SbkType_FromSpec(&SbkObjectType_Type_spec));
diff --git a/sources/shiboken2/libshiboken/basewrapper.h b/sources/shiboken2/libshiboken/basewrapper.h
index 7248103cc..a4a8629fb 100644
--- a/sources/shiboken2/libshiboken/basewrapper.h
+++ b/sources/shiboken2/libshiboken/basewrapper.h
@@ -93,6 +93,9 @@ typedef void (*ObjectDestructor)(void *);
typedef void (*SubTypeInitHook)(SbkObjectType *, PyObject *, PyObject *);
+typedef PyObject *(*SelectableFeatureHook)(PyTypeObject *);
+LIBSHIBOKEN_API void initSelectableFeature(SelectableFeatureHook func);
+
extern LIBSHIBOKEN_API PyTypeObject *SbkObjectType_TypeF(void);
extern LIBSHIBOKEN_API SbkObjectType *SbkObject_TypeF(void);
diff --git a/sources/shiboken2/libshiboken/pep384impl.cpp b/sources/shiboken2/libshiboken/pep384impl.cpp
index 5e0053e2e..4149bbcbf 100644
--- a/sources/shiboken2/libshiboken/pep384impl.cpp
+++ b/sources/shiboken2/libshiboken/pep384impl.cpp
@@ -78,14 +78,20 @@ static PyGetSetDef probe_getseters[] = {
{nullptr} /* Sentinel */
};
+static PyMemberDef probe_members[] = {
+ {nullptr} /* Sentinel */
+};
+
#define probe_tp_dealloc make_dummy(1)
#define probe_tp_repr make_dummy(2)
#define probe_tp_call make_dummy(3)
+#define probe_tp_getattro make_dummy(16)
#define probe_tp_str make_dummy(4)
#define probe_tp_traverse make_dummy(5)
#define probe_tp_clear make_dummy(6)
#define probe_tp_iternext make_dummy(7)
#define probe_tp_methods probe_methoddef
+#define probe_tp_members probe_members
#define probe_tp_getset probe_getseters
#define probe_tp_descr_get make_dummy(10)
#define probe_tp_init make_dummy(11)
@@ -101,11 +107,13 @@ static PyType_Slot typeprobe_slots[] = {
{Py_tp_dealloc, probe_tp_dealloc},
{Py_tp_repr, probe_tp_repr},
{Py_tp_call, probe_tp_call},
+ {Py_tp_getattro, probe_tp_getattro},
{Py_tp_str, probe_tp_str},
{Py_tp_traverse, probe_tp_traverse},
{Py_tp_clear, probe_tp_clear},
{Py_tp_iternext, probe_tp_iternext},
{Py_tp_methods, probe_tp_methods},
+ {Py_tp_members, probe_tp_members},
{Py_tp_getset, probe_tp_getset},
{Py_tp_descr_get, probe_tp_descr_get},
{Py_tp_init, probe_tp_init},
@@ -144,6 +152,7 @@ check_PyTypeObject_valid()
|| probe_tp_dealloc != check->tp_dealloc
|| probe_tp_repr != check->tp_repr
|| probe_tp_call != check->tp_call
+ || probe_tp_getattro != check->tp_getattro
|| probe_tp_str != check->tp_str
|| probe_tp_traverse != check->tp_traverse
|| probe_tp_clear != check->tp_clear
@@ -427,27 +436,6 @@ PyRun_String(const char *str, int start, PyObject *globals, PyObject *locals)
#endif // Py_LIMITED_API
-// This is only a simple local helper that returns a computed variable.
-// Used also in Python 2.
-#if defined(Py_LIMITED_API) || defined(IS_PY2)
-static PyObject *
-PepRun_GetResult(const char *command)
-{
- PyObject *d, *v, *res;
-
- d = PyDict_New();
- if (d == nullptr
- || PyDict_SetItem(d, Shiboken::PyMagicName::builtins(), PyEval_GetBuiltins()) < 0) {
- return nullptr;
- }
- v = PyRun_String(command, Py_file_input, d, d);
- res = v ? PyDict_GetItem(d, Shiboken::PyName::result()) : nullptr;
- Py_XDECREF(v);
- Py_DECREF(d);
- return res;
-}
-#endif // defined(Py_LIMITED_API) || defined(IS_PY2)
-
/*****************************************************************************
*
* Support for classobject.h
@@ -669,6 +657,29 @@ PyImport_GetModule(PyObject *name)
#endif // PY_VERSION_HEX < 0x03070000 || defined(Py_LIMITED_API)
+// 2020-06-16: For simplicity of creating arbitrary things, this function
+// is now made public.
+
+PyObject *
+PepRun_GetResult(const char *command)
+{
+ /*
+ * Evaluate a string and return the variable `result`
+ */
+ PyObject *d, *v, *res;
+
+ d = PyDict_New();
+ if (d == nullptr
+ || PyDict_SetItem(d, Shiboken::PyMagicName::builtins(), PyEval_GetBuiltins()) < 0) {
+ return nullptr;
+ }
+ v = PyRun_String(command, Py_file_input, d, d);
+ res = v ? PyDict_GetItem(d, Shiboken::PyName::result()) : nullptr;
+ Py_XDECREF(v);
+ Py_DECREF(d);
+ return res;
+}
+
/*****************************************************************************
*
* Python 2 incompatibilities
diff --git a/sources/shiboken2/libshiboken/pep384impl.h b/sources/shiboken2/libshiboken/pep384impl.h
index 2bfe52254..2a14d6543 100644
--- a/sources/shiboken2/libshiboken/pep384impl.h
+++ b/sources/shiboken2/libshiboken/pep384impl.h
@@ -98,7 +98,7 @@ typedef struct _typeobject {
void *X13; // hashfunc tp_hash;
ternaryfunc tp_call;
reprfunc tp_str;
- void *X16; // getattrofunc tp_getattro;
+ getattrofunc tp_getattro;
void *X17; // setattrofunc tp_setattro;
void *X18; // PyBufferProcs *tp_as_buffer;
unsigned long tp_flags;
@@ -110,7 +110,7 @@ typedef struct _typeobject {
void *X25; // getiterfunc tp_iter;
iternextfunc tp_iternext;
struct PyMethodDef *tp_methods;
- void *X28; // struct PyMemberDef *tp_members;
+ struct PyMemberDef *tp_members;
struct PyGetSetDef *tp_getset;
struct _typeobject *tp_base;
PyObject *tp_dict;
@@ -531,6 +531,9 @@ extern LIBSHIBOKEN_API PyTypeObject *PepMethodDescr_TypePtr;
LIBSHIBOKEN_API PyObject *PyImport_GetModule(PyObject *name);
#endif // PY_VERSION_HEX < 0x03070000 || defined(Py_LIMITED_API)
+// Evaluate a script and return the variable `result`
+LIBSHIBOKEN_API PyObject *PepRun_GetResult(const char *command);
+
/*****************************************************************************
*
* Python 2 incompatibilities
diff --git a/sources/shiboken2/libshiboken/sbkstaticstrings.cpp b/sources/shiboken2/libshiboken/sbkstaticstrings.cpp
index 541d74918..9dd98eef3 100644
--- a/sources/shiboken2/libshiboken/sbkstaticstrings.cpp
+++ b/sources/shiboken2/libshiboken/sbkstaticstrings.cpp
@@ -52,9 +52,11 @@ namespace Shiboken
{
namespace PyName {
// exported:
+STATIC_STRING_IMPL(dict_ring, "dict_ring")
STATIC_STRING_IMPL(dumps, "dumps")
STATIC_STRING_IMPL(loads, "loads")
STATIC_STRING_IMPL(result, "result")
+STATIC_STRING_IMPL(select_id, "select_id")
STATIC_STRING_IMPL(value, "value")
STATIC_STRING_IMPL(values, "values")
@@ -75,6 +77,7 @@ STATIC_STRING_IMPL(staticmethod, "staticmethod")
namespace PyMagicName {
// exported:
STATIC_STRING_IMPL(class_, "__class__")
+STATIC_STRING_IMPL(dict, "__dict__")
STATIC_STRING_IMPL(ecf, "__ecf__")
STATIC_STRING_IMPL(file, "__file__")
STATIC_STRING_IMPL(members, "__members__")
diff --git a/sources/shiboken2/libshiboken/sbkstaticstrings.h b/sources/shiboken2/libshiboken/sbkstaticstrings.h
index 4078d163c..ebb64579c 100644
--- a/sources/shiboken2/libshiboken/sbkstaticstrings.h
+++ b/sources/shiboken2/libshiboken/sbkstaticstrings.h
@@ -49,11 +49,13 @@ namespace Shiboken
namespace PyName
{
LIBSHIBOKEN_API PyObject *co_name();
+LIBSHIBOKEN_API PyObject *dict_ring();
LIBSHIBOKEN_API PyObject *dumps();
LIBSHIBOKEN_API PyObject *f_code();
LIBSHIBOKEN_API PyObject *f_lineno();
LIBSHIBOKEN_API PyObject *loads();
LIBSHIBOKEN_API PyObject *result();
+LIBSHIBOKEN_API PyObject *select_id();
LIBSHIBOKEN_API PyObject *value();
LIBSHIBOKEN_API PyObject *values();
} // namespace PyName
@@ -61,6 +63,7 @@ LIBSHIBOKEN_API PyObject *values();
namespace PyMagicName
{
LIBSHIBOKEN_API PyObject *class_();
+LIBSHIBOKEN_API PyObject *dict();
LIBSHIBOKEN_API PyObject *ecf();
LIBSHIBOKEN_API PyObject *file();
LIBSHIBOKEN_API PyObject *members();
diff --git a/sources/shiboken2/libshiboken/shiboken.h b/sources/shiboken2/libshiboken/shiboken.h
index 0d2d6b0a6..3e1df5235 100644
--- a/sources/shiboken2/libshiboken/shiboken.h
+++ b/sources/shiboken2/libshiboken/shiboken.h
@@ -55,6 +55,7 @@
#include "sbkstaticstrings.h"
#include "shibokenmacros.h"
#include "shibokenbuffer.h"
+#include "signature.h"
#endif // SHIBOKEN_H
diff --git a/sources/shiboken2/libshiboken/signature.cpp b/sources/shiboken2/libshiboken/signature.cpp
index ba1fc119e..4614a131e 100644
--- a/sources/shiboken2/libshiboken/signature.cpp
+++ b/sources/shiboken2/libshiboken/signature.cpp
@@ -74,7 +74,8 @@ typedef struct safe_globals_struc {
PyObject *helper_module;
PyObject *arg_dict;
PyObject *map_dict;
- PyObject *value_dict; // for writing signatures
+ PyObject *value_dict; // for writing signatures
+ PyObject *feature_dict; // registry for PySide.support.__feature__
// init part 2: run module
PyObject *pyside_type_init_func;
PyObject *create_signature_func;
@@ -572,6 +573,12 @@ init_phase_1(void)
if (p->value_dict == nullptr)
goto error;
+ // PYSIDE-1019: build a __feature__ dict
+ p->feature_dict = PyDict_New();
+ if (p->feature_dict == nullptr
+ || PyObject_SetAttrString(p->helper_module, "pyside_feature_dict", p->feature_dict) < 0)
+ goto error;
+
// This function will be disabled until phase 2 is done.
p->finish_import_func = nullptr;
@@ -1259,4 +1266,10 @@ PyObject *Sbk_TypeGet___doc__(PyObject *ob)
return pyside_tp_get___doc__(ob);
}
+PyObject *GetFeatureDict()
+{
+ init_module_1();
+ return pyside_globals->feature_dict;
+}
+
} //extern "C"
diff --git a/sources/shiboken2/libshiboken/signature.h b/sources/shiboken2/libshiboken/signature.h
index b22a78497..2a69df9cf 100644
--- a/sources/shiboken2/libshiboken/signature.h
+++ b/sources/shiboken2/libshiboken/signature.h
@@ -50,6 +50,7 @@ LIBSHIBOKEN_API void FinishSignatureInitialization(PyObject *, const char *[]);
LIBSHIBOKEN_API void SetError_Argument(PyObject *, const char *);
LIBSHIBOKEN_API PyObject *Sbk_TypeGet___signature__(PyObject *, PyObject *);
LIBSHIBOKEN_API PyObject *Sbk_TypeGet___doc__(PyObject *);
+LIBSHIBOKEN_API PyObject *GetFeatureDict();
} // extern "C"
diff --git a/sources/shiboken2/shibokenmodule/CMakeLists.txt b/sources/shiboken2/shibokenmodule/CMakeLists.txt
index e1eafa12f..b14de5c9e 100644
--- a/sources/shiboken2/shibokenmodule/CMakeLists.txt
+++ b/sources/shiboken2/shibokenmodule/CMakeLists.txt
@@ -42,6 +42,8 @@ install(FILES "${CMAKE_CURRENT_BINARY_DIR}/_config.py"
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/__init__.py.in"
"${CMAKE_CURRENT_BINARY_DIR}/__init__.py" @ONLY)
+configure_file("${CMAKE_CURRENT_SOURCE_DIR}/files.dir/shibokensupport/__feature__.py"
+ "${CMAKE_CURRENT_BINARY_DIR}/files.dir/shibokensupport/__feature__.py" COPYONLY)
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/files.dir/shibokensupport/__init__.py"
"${CMAKE_CURRENT_BINARY_DIR}/files.dir/shibokensupport/__init__.py" COPYONLY)
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/files.dir/shibokensupport/signature/__init__.py"
diff --git a/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/__feature__.py b/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/__feature__.py
new file mode 100644
index 000000000..58c1ff925
--- /dev/null
+++ b/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/__feature__.py
@@ -0,0 +1,111 @@
+#############################################################################
+##
+## Copyright (C) 2020 The Qt Company Ltd.
+## Contact: https://www.qt.io/licensing/
+##
+## This file is part of Qt for Python.
+##
+## $QT_BEGIN_LICENSE:LGPL$
+## 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 Lesser General Public License Usage
+## Alternatively, this file may be used under the terms of the GNU Lesser
+## General Public License version 3 as published by the Free Software
+## Foundation and appearing in the file LICENSE.LGPL3 included in the
+## packaging of this file. Please review the following information to
+## ensure the GNU Lesser General Public License version 3 requirements
+## will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+##
+## GNU General Public License Usage
+## Alternatively, this file may be used under the terms of the GNU
+## General Public License version 2.0 or (at your option) the GNU General
+## Public license version 3 or any later version approved by the KDE Free
+## Qt Foundation. The licenses are as published by the Free Software
+## Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+## 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-2.0.html and
+## https://www.gnu.org/licenses/gpl-3.0.html.
+##
+## $QT_END_LICENSE$
+##
+#############################################################################
+
+from __future__ import print_function, absolute_import
+
+"""
+__feature__.py
+
+This is the feature file for the Qt for Python project. There is some
+similarity to Python's `__future__` file, but also some distinction.
+"""
+
+import sys
+
+all_feature_names = [
+ "_dummy_feature_01",
+ "_dummy_feature_02",
+ "_dummy_feature_04",
+ "_dummy_feature_08",
+ "_dummy_feature_10",
+ "_dummy_feature_20",
+ "_dummy_feature_40",
+ "_dummy_feature_80",
+]
+
+__all__ = ["all_feature_names"] + all_feature_names
+
+_dummy_feature_01 = 0x01
+_dummy_feature_02 = 0x02
+_dummy_feature_04 = 0x04
+_dummy_feature_08 = 0x08
+_dummy_feature_10 = 0x10
+_dummy_feature_20 = 0x20
+_dummy_feature_40 = 0x40
+_dummy_feature_80 = 0x80
+
+# let's remove the dummies for the normal user
+_really_all_feature_names = all_feature_names[:]
+all_feature_names = list(_ for _ in all_feature_names if not _.startswith("_"))
+
+# Install an import hook that controls the `__feature__` import.
+"""
+Note: This are two imports.
+>>> import dis
+>>> def test():
+... from __feature__ import snake_case
+...
+>>> dis.dis(test)
+ 2 0 LOAD_CONST 1 (0)
+ 2 LOAD_CONST 2 (('snake_case',))
+ 4 IMPORT_NAME 0 (__feature__)
+ 6 IMPORT_FROM 1 (snake_case)
+ 8 STORE_FAST 0 (snake_case)
+ 10 POP_TOP
+ 12 LOAD_CONST 0 (None)
+ 14 RETURN_VALUE
+"""
+# XXX build an improved C version
+def _import(name, *args, **kwargs):
+ if name == "__feature__" and args[2]:
+ # This is an `import from` statement that corresponds to `IMPORT_NAME`.
+ # The following `IMPORT_FROM` will handle errors. (Confusing, ofc.)
+ flag = 0
+ for feature in args[2]:
+ if feature in _really_all_feature_names:
+ flag |= globals()[feature]
+ else:
+ raise SyntaxError("PySide feature {} is not defined".format(feature))
+ importing_module = sys._getframe(1).f_globals['__name__']
+ existing = pyside_feature_dict.get(importing_module, 0)
+ if isinstance(existing, int):
+ flag |= existing & 255
+ pyside_feature_dict[importing_module] = flag
+ return sys.modules["__feature__"]
+ return original_import(name, *args, **kwargs)
diff --git a/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/loader.py b/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/loader.py
index 1efc6fde5..6cee54680 100644
--- a/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/loader.py
+++ b/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/loader.py
@@ -114,8 +114,10 @@ def finish_import(module):
import signature_bootstrap
-from shibokensupport import signature
+from shibokensupport import signature, __feature__
signature.get_signature = signature_bootstrap.get_signature
+# PYSIDE-1019: Publish the __feature__ dictionary.
+__feature__.pyside_feature_dict = signature_bootstrap.pyside_feature_dict
del signature_bootstrap
def _get_modname(mod):
@@ -194,6 +196,7 @@ def move_into_pyside_package():
import PySide2.support
except ModuleNotFoundError:
PySide2.support = types.ModuleType("PySide2.support")
+ put_into_package(PySide2.support, __feature__)
put_into_package(PySide2.support, signature)
put_into_package(PySide2.support.signature, mapping)
put_into_package(PySide2.support.signature, errorhandler)
@@ -217,5 +220,16 @@ from shibokensupport.signature.lib import enum_sig
if "PySide2" in sys.modules:
# We publish everything under "PySide2.support.signature", again.
move_into_pyside_package()
+ # PYSIDE-1019: Modify `__import__` to be `__feature__` aware.
+ # __feature__ is already in sys.modules, so this is actually no import
+ try:
+ import PySide2.support.__feature__
+ sys.modules["__feature__"] = PySide2.support.__feature__
+ PySide2.support.__feature__.original_import = __builtins__["__import__"]
+ __builtins__["__import__"] = PySide2.support.__feature__._import
+ # Maybe we should optimize that and change `__import__` from C, instead?
+ except ModuleNotFoundError:
+ print("__feature__ could not be imported. "
+ "This is an unsolved PyInstaller problem.", file=sys.stderr)
# end of file