aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFriedemann Kleint <Friedemann.Kleint@qt.io>2018-03-06 07:58:51 +0100
committerFriedemann Kleint <Friedemann.Kleint@qt.io>2018-03-06 12:43:06 +0100
commitdf7c72e63c6b7aecee156db2eff726bcfb89977e (patch)
treeb9cecf87e2cd75d11813cd06ef1f2a5ab28384de
parente621f81115cec5089f30755e09b9a59ece39660c (diff)
parentf3139399b273fdf405f20cba32a08262cd933d6e (diff)
Merge remote-tracking branch 'origin/5.9' into dev
-rw-r--r--missing_bindings.py4
-rw-r--r--qtinfo.py145
-rw-r--r--setup.py564
-rw-r--r--sources/pyside2/CMakeLists.txt85
-rw-r--r--sources/pyside2/PySide2/CMakeLists.txt5
-rw-r--r--sources/pyside2/PySide2/QtGui/typesystem_gui_common.xml3
-rw-r--r--sources/pyside2/PySide2/__init__.py.in7
-rw-r--r--sources/pyside2/PySide2/_config.py.in11
-rw-r--r--sources/pyside2/PySide2/typesystem_templates.xml6
-rw-r--r--sources/pyside2/doc/inheritance_diagram.py87
-rw-r--r--sources/pyside2/doc/pysideversion.rst18
-rw-r--r--sources/pyside2/libpyside/pysidesignal.cpp59
-rw-r--r--sources/pyside2/pyside_version.py49
-rw-r--r--sources/pyside2/tests/QtWidgets/qvalidator_test.py27
-rw-r--r--sources/pyside2/tests/pysidetest/version_test.py6
-rw-r--r--sources/pyside2/tests/signals/signal_signature_test.py40
-rw-r--r--sources/shiboken2/ApiExtractor/doc/typesystem_specifying_types.rst5
-rw-r--r--sources/shiboken2/ApiExtractor/typesystem.cpp106
-rw-r--r--sources/shiboken2/ApiExtractor/typesystem.h41
-rw-r--r--sources/shiboken2/ApiExtractor/typesystem_p.h1
-rw-r--r--sources/shiboken2/CMakeLists.txt60
-rw-r--r--sources/shiboken2/generator/qtdoc/qtdocgenerator.cpp71
-rw-r--r--sources/shiboken2/generator/qtdoc/qtdocgenerator.h9
-rw-r--r--sources/shiboken2/generator/shiboken2/cppgenerator.cpp29
-rw-r--r--sources/shiboken2/libshiboken/sbkenum.cpp13
-rw-r--r--sources/shiboken2/libshiboken/sbkenum.h2
-rw-r--r--sources/shiboken2/shiboken_version.py49
-rw-r--r--sources/shiboken2/tests/libsample/objecttype.h6
-rw-r--r--sources/shiboken2/tests/samplebinding/enum_test.py5
-rw-r--r--sources/shiboken2/tests/samplebinding/typesystem_sample.xml1
-rw-r--r--utils.py35
31 files changed, 1049 insertions, 500 deletions
diff --git a/missing_bindings.py b/missing_bindings.py
index 1b7b42441..894fdbd58 100644
--- a/missing_bindings.py
+++ b/missing_bindings.py
@@ -201,7 +201,9 @@ types_to_ignore.add('QSqlDriverPlugin')
qt_documentation_website_prefixes = OrderedDict()
qt_documentation_website_prefixes['5.6'] = 'http://doc.qt.io/qt-5.6/'
qt_documentation_website_prefixes['5.8'] = 'http://doc.qt.io/qt-5.8/'
-qt_documentation_website_prefixes['5.9'] = 'http://doc-snapshots.qt.io/qt5-5.9/'
+qt_documentation_website_prefixes['5.9'] = 'http://doc.qt.io/qt-5.9/'
+qt_documentation_website_prefixes['5.10'] = 'http://doc.qt.io/qt-5.10/'
+qt_documentation_website_prefixes['5.11'] = 'http://doc-snapshots.qt.io/qt5-5.11/'
qt_documentation_website_prefixes['dev'] = 'http://doc-snapshots.qt.io/qt5-dev/'
diff --git a/qtinfo.py b/qtinfo.py
index 0105ba44e..c61e796b0 100644
--- a/qtinfo.py
+++ b/qtinfo.py
@@ -37,25 +37,29 @@
##
#############################################################################
-import os, sys
-import subprocess
+import os, sys, re, subprocess
from distutils.spawn import find_executable
class QtInfo(object):
def __init__(self, qmake_command=None):
+ self.initialized = False
+
if qmake_command:
self._qmake_command = qmake_command
else:
self._qmake_command = [find_executable("qmake"),]
- self._dict = {}
- # bind all variables early at __init__ time.
- for thing in self.__class__.__dict__:
- getattr(self, thing)
+
+ # Dict to cache qmake values.
+ self._query_dict = {}
+ # Dict to cache mkspecs variables.
+ self._mkspecs_dict = {}
+ # Initialize the properties.
+ self._initProperties()
def getQMakeCommand(self):
qmake_command_string = self._qmake_command[0]
for entry in self._qmake_command[1:]:
- qmake_command_string += " %s" %(entry)
+ qmake_command_string += " {}".format(entry)
return qmake_command_string
def getVersion(self):
@@ -91,21 +95,127 @@ class QtInfo(object):
def getQmlPath(self):
return self.getProperty("QT_INSTALL_QML")
- def _getProperty(self, prop_name):
- cmd = self._qmake_command + ["-query", prop_name]
+ def getMacOSMinDeploymentTarget(self):
+ """ Return value is a macOS version or None. """
+ return self.getProperty("QMAKE_MACOSX_DEPLOYMENT_TARGET")
+
+ def getBuildType(self):
+ """ Return value is either debug, release, debug_release, or None. """
+ return self.getProperty("BUILD_TYPE")
+
+ def getSrcDir(self):
+ """ Return path to Qt src dir or None.. """
+ return self.getProperty("QT_INSTALL_PREFIX/src")
+
+ def getProperty(self, prop_name):
+ if prop_name not in self._query_dict:
+ return None
+ return self._query_dict[prop_name]
+
+ def getProperties(self):
+ return self._query_dict
+
+ def getMkspecsVariables(self):
+ return self._mkspecs_dict
+
+ def _getQMakeOutput(self, args_list = []):
+ cmd = self._qmake_command + args_list
proc = subprocess.Popen(cmd, stdout = subprocess.PIPE, shell=False)
- prop = proc.communicate()[0]
+ output = proc.communicate()[0]
proc.wait()
if proc.returncode != 0:
return None
if sys.version_info >= (3,):
- return str(prop, 'ascii').strip()
- return prop.strip()
+ output = str(output, 'ascii').strip()
+ else:
+ output = output.strip()
+ return output
+
+ def _parseQueryProperties(self, process_output):
+ props = {}
+ if not process_output:
+ return props
+ lines = [s.strip() for s in process_output.splitlines()]
+ for line in lines:
+ if line and ':' in line:
+ key, value = line.split(':', 1)
+ props[key] = value
+ return props
+
+ def _getQueryProperties(self):
+ output = self._getQMakeOutput(['-query'])
+ self._query_dict = self._parseQueryProperties(output)
+
+ def _parseQtBuildType(self):
+ key = 'QT_CONFIG'
+ if key not in self._mkspecs_dict:
+ return None
- def getProperty(self, prop_name):
- if prop_name not in self._dict:
- self._dict[prop_name] = self._getProperty(prop_name)
- return self._dict[prop_name]
+ qt_config = self._mkspecs_dict[key]
+ if 'debug_and_release' in qt_config:
+ return 'debug_and_release'
+
+ split = qt_config.split(' ')
+ if 'release' in split and 'debug' in split:
+ return 'debug_and_release'
+
+ if 'release' in split:
+ return 'release'
+
+ if 'debug' in split:
+ return 'debug'
+
+ return None
+
+ def _getOtherProperties(self):
+ # Get the src property separately, because it is not returned by qmake unless explicitly
+ # specified.
+ key = 'QT_INSTALL_PREFIX/src'
+ result = self._getQMakeOutput(['-query', key])
+ self._query_dict[key] = result
+
+ # Get mkspecs variables and cache them.
+ self._getQMakeMkspecsVariables()
+
+ # Get macOS minimum deployment target.
+ key = 'QMAKE_MACOSX_DEPLOYMENT_TARGET'
+ if key in self._mkspecs_dict:
+ self._query_dict[key] = self._mkspecs_dict[key]
+
+ # Figure out how Qt was built: debug mode, release mode, or both.
+ build_type = self._parseQtBuildType()
+ if build_type:
+ self._query_dict['BUILD_TYPE'] = build_type
+
+ def _initProperties(self):
+ self._getQueryProperties()
+ self._getOtherProperties()
+
+ def _getQMakeMkspecsVariables(self):
+ # Create empty temporary qmake project file.
+ temp_file_name = 'qmake_fake_empty_project.txt'
+ open(temp_file_name, 'a').close()
+
+ # Query qmake for all of its mkspecs variables.
+ qmakeOutput = self._getQMakeOutput(['-E', temp_file_name])
+ lines = [s.strip() for s in qmakeOutput.splitlines()]
+ pattern = re.compile(r"^(.+?)=(.+?)$")
+ for line in lines:
+ found = pattern.search(line)
+ if found:
+ key = found.group(1).strip()
+ value = found.group(2).strip()
+ self._mkspecs_dict[key] = value
+
+ # We need to clean up after qmake, which always creates a .qmake.stash file after a -E
+ # invocation.
+ qmake_stash_file = os.path.join(os.getcwd(), ".qmake.stash")
+ if os.path.exists(qmake_stash_file):
+ os.remove(qmake_stash_file)
+
+ # Also clean up the temporary empty project file.
+ if os.path.exists(temp_file_name):
+ os.remove(temp_file_name)
version = property(getVersion)
bins_dir = property(getBinsPath)
@@ -119,3 +229,6 @@ class QtInfo(object):
headers_dir = property(getHeadersPath)
docs_dir = property(getDocsPath)
qml_dir = property(getQmlPath)
+ macos_min_deployment_target = property(getMacOSMinDeploymentTarget)
+ build_type = property(getBuildType)
+ src_dir = property(getSrcDir)
diff --git a/setup.py b/setup.py
index 34cea9c91..ccce48e9c 100644
--- a/setup.py
+++ b/setup.py
@@ -82,6 +82,7 @@ For development purposes the following options might be of use, when using "setu
--skip-packaging will skip creation of the python package,
--ignore-git will skip the fetching and checkout steps for supermodule and all submodules.
--verbose-build will output the compiler invocation with command line arguments, etc.
+ --sanitize-address will build all targets with address sanitizer enabled.
REQUIREMENTS:
- Python: 2.6, 2.7, 3.3, 3.4, 3.5 and 3.6 are supported
@@ -109,78 +110,60 @@ OS X SDK: You can specify which OS X SDK should be used for compilation with the
For e.g. "--osx-sysroot=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.11.sdk/".
OS X Minimum deployment target:
- You can specify the OS X minimum deployment target with the --osx-deployment-target=<x> option.
- For example "--osx-deployment-target=10.10".
+ You can specify a custom OS X minimum deployment target with the --osx-deployment-target=<value>
+ option.
+ For example: "--osx-deployment-target=10.10".
+
+ If the option is not set, the minimum deployment target of the used Qt library will be used
+ instead. Thus it is not necessary to use the option without a good reason.
+ If a new value is specified, it has to be higher or equal to both Python's and Qt's minimum
+ deployment targets.
+
+ Description:
+ OS X allows specifying a minimum OS version on which a binary will be able to run. This implies
+ that an application can be built on a machine with the latest OS X version installed, with
+ latest Xcode version and SDK version and the built application can still run on an older OS
+ version.
+"""
- OS X provides the ability to set what is the minimum OS version on which a binary will run. This
- means that a build can be done on the latest OS X version with latest XCode and SDK versions,
- but the built application / library can run on older OS versions.
+import os
+import time
+from utils import memoize, has_option, get_python_dict
+OPTION_SNAPSHOT_BUILD = has_option("snapshot-build")
+script_dir = os.getcwd()
- Note: if the option is not set, CMake will try to query the MACOSX_DEPLOYMENT_TARGET environment
- variable, and if that is empty, it will try to deduce a value internally (afaik based on
- current OS X version and on the chosen SDK version).
-"""
+@memoize
+def get_package_timestamp():
+ return int(time.time())
-__version__ = "2.0.0.dev0"
+@memoize
+def get_package_version():
+ """ Returns the version string for the PySide2 package. """
+ pyside_version_py = os.path.join(script_dir, "sources", "pyside2", "pyside_version.py")
+ d = get_python_dict(pyside_version_py)
-containedModules = ['shiboken2', 'pyside2']
+ final_version = "{}.{}.{}".format(d['major_version'], d['minor_version'], d['patch_version'])
+ pre_release_version_type = d['pre_release_version_type']
+ pre_release_version = d['pre_release_version']
+ if pre_release_version and pre_release_version:
+ final_version += pre_release_version_type + pre_release_version
-submodules = {
- '2.0.0.dev0': [
- ["pyside2-tools", "dev"]
- ],
- '5.9': [
- ["pyside2-tools", "5.9"]
- ],
- '5.11': [
- ["pyside2-tools", "5.11"]
- ],
- '5.6': [
- ["pyside2-tools", "5.6"],
- ["pyside2-examples", "5.6"],
- ["wiki", "master", ".."]
- ],
-}
-old_submodules = {
- # these are just kept a while for reference but not maintained.
- # if you need an old version, please use the pyside/pyside-setup version.
- '1.3.0dev': [
- ["shiboken", "master"],
- ["pyside", "master"],
- ["pyside-tools", "master"],
- ["pyside-examples", "master"],
- ],
- '1.2.2': [
- ["shiboken", "1.2.2"],
- ["pyside", "1.2.2"],
- ["pyside-tools", "0.2.15"],
- ["pyside-examples", "master"],
- ],
- '1.2.1': [
- ["shiboken", "1.2.1"],
- ["pyside", "1.2.1"],
- ["pyside-tools", "0.2.15"],
- ["pyside-examples", "master"],
- ],
- '1.2.0': [
- ["shiboken", "1.2.0"],
- ["pyside", "1.2.0"],
- ["pyside-tools", "0.2.14"],
- ["pyside-examples", "master"],
- ],
- '1.1.2': [
- ["shiboken", "1.1.2"],
- ["pyside", "1.1.2"],
- ["pyside-tools", "0.2.14"],
- ["pyside-examples", "master"],
- ],
- '1.1.1': [
- ["shiboken", "1.1.1"],
- ["pyside", "1.1.1"],
- ["pyside-tools", "0.2.14"],
- ["pyside-examples", "master"],
- ],
-}
+ # Add the current timestamp to the version number, to suggest it is a development snapshot
+ # build.
+ if OPTION_SNAPSHOT_BUILD:
+ final_version += ".dev{}".format(get_package_timestamp())
+ return final_version
+
+# The __version__ variable is just for PEP compliancy, and shoudn't be used as a value source.
+__version__ = get_package_version()
+
+# Buildable extensions.
+containedModules = ['shiboken2', 'pyside2', 'pyside2-tools']
+
+# Git submodules: ["submodule_name", "location_relative_to_sources_folder"]
+submodules = [["pyside2-tools"],
+ ["pyside2-examples"],
+ ["wiki", ".."]]
pyside_package_dir_name = "pyside_package"
@@ -190,14 +173,13 @@ except ImportError:
from ez_setup import use_setuptools
use_setuptools()
-import os
import sys
import platform
-import time
import re
import fnmatch
import difflib # for a close match of dirname and module
+import functools
from distutils import log
from distutils.errors import DistutilsOptionError
@@ -230,7 +212,6 @@ from utils import makefile
from utils import copyfile
from utils import copydir
from utils import run_process_output, run_process
-from utils import has_option
from utils import option_value
from utils import update_env_path
from utils import init_msvc_env
@@ -239,12 +220,13 @@ from utils import filter_match
from utils import osx_fix_rpaths_for_library
from utils import copy_icu_libs
from utils import find_files_using_glob
+
from textwrap import dedent
# guess a close folder name for extensions
def get_extension_folder(ext):
- candidates = containedModules
- for gitModules in submodules[__version__]:
+ candidates = list(containedModules)
+ for gitModules in submodules:
candidates.append(gitModules[0])
folder = difflib.get_close_matches(ext, candidates)[0]
return folder
@@ -279,8 +261,8 @@ OPTION_CMAKE = option_value("cmake")
OPTION_OPENSSL = option_value("openssl")
OPTION_ONLYPACKAGE = has_option("only-package")
OPTION_STANDALONE = has_option("standalone")
-OPTION_VERSION = option_value("version")
-OPTION_LISTVERSIONS = has_option("list-versions")
+OPTION_VERSION = option_value("version") # Deprecated
+OPTION_LISTVERSIONS = has_option("list-versions") # Deprecated
OPTION_MAKESPEC = option_value("make-spec")
OPTION_IGNOREGIT = has_option("ignore-git")
OPTION_NOEXAMPLES = has_option("no-examples") # don't include pyside2-examples
@@ -303,6 +285,7 @@ OPTION_QT_CONF_PREFIX = option_value("qt-conf-prefix")
OPTION_QT_SRC = option_value("qt-src-dir")
OPTION_ICULIB = option_value("iculib-url") # Deprecated
OPTION_VERBOSE_BUILD = has_option("verbose-build")
+OPTION_SANITIZE_ADDRESS = has_option("sanitize-address")
# This is used automatically by distutils.command.install object, to specify final installation
# location.
@@ -373,14 +356,6 @@ if OPTION_ICULIB:
if not OPTION_STANDALONE:
print("--iculib-url option is a no-op option and will be removed soon.")
-# Show available versions
-if OPTION_LISTVERSIONS:
- for v in submodules:
- print("%s" % (v))
- for m in submodules[v]:
- print(" %s %s" % (m[0], m[1]))
- sys.exit(1)
-
# Change the cwd to our source dir
try:
this_file = __file__
@@ -389,25 +364,16 @@ except NameError:
this_file = os.path.abspath(this_file)
if os.path.dirname(this_file):
os.chdir(os.path.dirname(this_file))
-script_dir = os.getcwd()
-
-# Change package version
-if OPTION_VERSION:
- if OPTION_IGNOREGIT:
- print("Option --version can not be used together with option --ignore-git")
- sys.exit(1)
- if not os.path.isdir(".git"):
- print("Option --version is available only when pyside2-setup was cloned from git repository")
- sys.exit(1)
- if not OPTION_VERSION in submodules:
- print("""Invalid version specified %s
-Use --list-versions option to get list of available versions""" % OPTION_VERSION)
- sys.exit(1)
- __version__ = OPTION_VERSION
def is_debug_python():
return getattr(sys, "gettotalrefcount", None) is not None
+if OPTION_NOEXAMPLES:
+ # Remove pyside2-examples from submodules so they will not be included.
+ for idx, item in enumerate(submodules):
+ if item[0].startswith('pyside2-examples'):
+ del submodules[idx]
+
# Return a prefix suitable for the _install/_build directory
def prefix():
virtualEnvName = os.environ.get('VIRTUAL_ENV', None)
@@ -421,65 +387,56 @@ def prefix():
# Initialize, pull and checkout submodules
def prepareSubModules():
- print("Initializing submodules for PySide2 version %s" % __version__)
+ print("Initializing submodules for PySide2 version: {}".format(get_package_version()))
submodules_dir = os.path.join(script_dir, "sources")
+
# Create list of [name, desired branch, absolute path, desired branch]
# and determine whether all submodules are present
needInitSubModules = False
- modulesList = []
- for m in submodules[__version__]:
+
+ for m in submodules:
module_name = m[0]
- module_version = m[1]
- module_dir = m[2] if len(m) > 2 else ''
+ module_dir = m[1] if len(m) > 1 else ''
module_dir = os.path.join(submodules_dir, module_dir, module_name)
# Check for non-empty directory (repository checked out)
if not os.listdir(module_dir):
needInitSubModules = True
- modulesList.append([module_name, module_version, module_dir])
+ break
+
if needInitSubModules:
git_update_cmd = ["git", "submodule", "update", "--init"]
if run_process(git_update_cmd) != 0:
- raise DistutilsSetupError("Failed to initialize the git submodules")
+ m = "Failed to initialize the git submodules: update --init failed"
+ raise DistutilsSetupError(m)
git_pull_cmd = ["git", "submodule", "foreach", "git", "fetch", "--all"]
if run_process(git_pull_cmd) != 0:
- raise DistutilsSetupError("Failed to initialize the git submodules")
+ m = "Failed to initialize the git submodules: git fetch --all failed"
+ raise DistutilsSetupError(m)
else:
- print("All submodules present...")
- # Ensure all submodules have the correct branch checked out
- for m in modulesList:
- module_name = m[0]
- module_version = m[1]
- module_dir = m[2]
- os.chdir(module_dir)
- currentBranch = ''
- branches = set()
- for line in run_process_output(['git', 'branch']):
- if line.startswith('* '):
- currentBranch = line[2:len(line)]
- else:
- branches.add(line.strip())
- if currentBranch != module_version:
- if not module_version in branches:
- print("Creating tracking branch %s for submodule %s" % \
- (module_version, module_name))
- git_create_branch_cmd = ["git", "branch", "--track", module_version,
- "origin/" + module_version]
- if run_process(git_create_branch_cmd) != 0:
- raise DistutilsSetupError("Failed to create a tracking branch %s for %s" % \
- (module_version, module_name))
- print("Checking out submodule %s to branch %s (from %s)" % (module_name, module_version, currentBranch))
- git_checkout_cmd = ["git", "checkout", module_version]
- if run_process(git_checkout_cmd) != 0:
- raise DistutilsSetupError("Failed to initialize the git submodule %s" % module_name)
- else:
- print("Submodule %s has branch %s checked out" % (module_name, module_version))
- os.chdir(script_dir)
+ print("All submodules present.")
+
+ git_update_cmd = ["git", "submodule", "update"]
+ if run_process(git_update_cmd) != 0:
+ m = "Failed to checkout the correct git submodules SHA1s."
+ raise DistutilsSetupError(m)
+
+# Single global instance of QtInfo to be used later in multiple code paths.
+qtinfo = QtInfo(QMAKE_COMMAND)
+
+def get_qt_version():
+ qt_version = qtinfo.version
+
+ if not qt_version:
+ log.error("Failed to query the Qt version with qmake %s" % self.qtinfo.qmake_command)
+ sys.exit(1)
+
+ return qt_version
def prepareBuild():
if os.path.isdir(".git") and not OPTION_IGNOREGIT and not OPTION_ONLYPACKAGE and not OPTION_REUSE_BUILD:
prepareSubModules()
# Clean up temp and package folders
- for n in [pyside_package_dir_name, "build", "PySide2-%s" % __version__]:
+ for n in [pyside_package_dir_name, "build"]:
d = os.path.join(script_dir, n)
if os.path.isdir(d):
print("Removing %s" % d)
@@ -495,29 +452,14 @@ def prepareBuild():
os.makedirs(pkg_dir)
# locate Qt sources for the documentation
if OPTION_QT_SRC is None:
- qmakeOutput = run_process_output([OPTION_QMAKE, '-query', 'QT_INSTALL_PREFIX'])
- if qmakeOutput:
+ installPrefix = qtinfo.prefix_dir
+ if installPrefix:
global qtSrcDir
- installPrefix = qmakeOutput[0].rstrip()
if installPrefix.endswith("qtbase"): # In-source, developer build
qtSrcDir = installPrefix
else: # SDK: Use 'Src' directory
qtSrcDir = os.path.join(os.path.dirname(installPrefix), 'Src', 'qtbase')
-def get_qt_version(computed_qtinfo = None):
- if not computed_qtinfo:
- qtinfo = QtInfo(QMAKE_COMMAND)
- else:
- qtinfo = computed_qtinfo
-
- qt_version = qtinfo.version
-
- if not qt_version:
- log.error("Failed to query the Qt version with qmake %s" % self.qtinfo.qmake_command)
- sys.exit(1)
-
- return qt_version
-
class pyside_install(_install):
def __init__(self, *args, **kwargs):
_install.__init__(self, *args, **kwargs)
@@ -580,7 +522,8 @@ if wheel_module_exists:
# Example: PySide2-5.6-5.6.4-cp27-cp27m-macosx_10_10_intel.whl
# The PySide2 version is "5.6. The built against Qt version is "5.6.4.
qt_version = get_qt_version()
- wheel_version = "{}-{}".format(__version__, qt_version)
+ package_version = get_package_version()
+ wheel_version = "{}-{}".format(package_version, qt_version)
components = (_safer_name(self.distribution.get_name()),
wheel_version)
if self.build_number:
@@ -671,7 +614,7 @@ class pyside_build(_build):
platform_arch = platform.architecture()[0]
log.info("Python architecture is %s" % platform_arch)
- build_type = OPTION_DEBUG and "Debug" or "Release"
+ build_type = "Debug" if OPTION_DEBUG else "Release"
if OPTION_RELWITHDEBINFO:
build_type = 'RelWithDebInfo'
@@ -826,9 +769,9 @@ class pyside_build(_build):
log.error("Failed to locate a dynamic Python library, using %s"
% py_library)
- self.qtinfo = QtInfo(QMAKE_COMMAND)
+ self.qtinfo = qtinfo
qt_dir = os.path.dirname(OPTION_QMAKE)
- qt_version = get_qt_version(self.qtinfo)
+ qt_version = get_qt_version()
# Update the PATH environment variable
additionalPaths = [py_scripts_dir, qt_dir]
@@ -885,7 +828,7 @@ class pyside_build(_build):
self.shiboken_build_dir = os.path.join(self.build_dir, "shiboken2")
log.info("=" * 30)
- log.info("Package version: %s" % __version__)
+ log.info("Package version: %s" % get_package_version())
log.info("Build type: %s" % self.build_type)
log.info("Build tests: %s" % self.build_tests)
log.info("-" * 3)
@@ -929,6 +872,9 @@ class pyside_build(_build):
log.info("-" * 3)
if sys.platform == 'win32':
log.info("OpenSSL dll directory: %s" % OPTION_OPENSSL)
+ if sys.platform == 'darwin':
+ pyside_macos_deployment_target = pyside_build.macos_pyside_min_deployment_target()
+ log.info("MACOSX_DEPLOYMENT_TARGET set to: {}".format(pyside_macos_deployment_target))
log.info("=" * 30)
# Prepare folders
@@ -944,7 +890,7 @@ class pyside_build(_build):
if not OPTION_ONLYPACKAGE:
# Build extensions
- for ext in containedModules + ['pyside2-tools']:
+ for ext in containedModules:
self.build_extension(ext)
if OPTION_BUILDTESTS:
@@ -973,20 +919,57 @@ class pyside_build(_build):
log.info('*** Build completed')
@staticmethod
- def macos_min_deployment_target():
- # If no explicit minimum deployment target is provided to setup.py, then use the current
- # build OS version. Otherwise use the provided version.
- current_os_version, _, _ = platform.mac_ver()
- current_os_version = '.'.join(current_os_version.split('.')[:2])
- deployment_target = current_os_version
- if OPTION_OSX_DEPLOYMENT_TARGET:
- deployment_target = OPTION_OSX_DEPLOYMENT_TARGET
+ def macos_qt_min_deployment_target():
+ target = qtinfo.macos_min_deployment_target
- return deployment_target
+ if not target:
+ raise DistutilsSetupError("Failed to query for Qt's QMAKE_MACOSX_DEPLOYMENT_TARGET.")
+ return target
@staticmethod
+ @memoize
+ def macos_pyside_min_deployment_target():
+ """
+ Compute and validate PySide2 MACOSX_DEPLOYMENT_TARGET value. Candidate sources that are
+ considered:
+ - setup.py provided value
+ - maximum value between minimum deployment target of the Python interpreter and the
+ minimum deployment target of the Qt libraries.
+ If setup.py value is provided, that takes precedence.
+ Otherwise use the maximum of the above mentioned two values.
+ """
+ python_target = get_config_var('MACOSX_DEPLOYMENT_TARGET') or None
+ qt_target = pyside_build.macos_qt_min_deployment_target()
+ setup_target = OPTION_OSX_DEPLOYMENT_TARGET
+
+ qt_target_split = [int(x) for x in qt_target.split('.')]
+ if python_target:
+ python_target_split = [int(x) for x in python_target.split('.')]
+ if setup_target:
+ setup_target_split = [int(x) for x in setup_target.split('.')]
+
+ message = "Can't set MACOSX_DEPLOYMENT_TARGET value to {} because " \
+ "{} was built with minimum deployment target set to {}."
+ # setup.py provided OPTION_OSX_DEPLOYMENT_TARGET value takes precedence.
+ if setup_target:
+ if python_target and setup_target_split < python_target_split:
+ raise DistutilsSetupError(message.format(setup_target, "Python", python_target))
+ if setup_target_split < qt_target_split:
+ raise DistutilsSetupError(message.format(setup_target, "Qt", qt_target))
+ # All checks clear, use setup.py provided value.
+ return setup_target
+
+ # Setup.py value not provided, use same value as provided by Qt.
+ if python_target:
+ maximum_target = '.'.join([str(e) for e in max(python_target_split, qt_target_split)])
+ else:
+ maximum_target = qt_target
+ return maximum_target
+
+ @staticmethod
+ @memoize
def macos_plat_name():
- deployment_target = pyside_build.macos_min_deployment_target()
+ deployment_target = pyside_build.macos_pyside_min_deployment_target()
# Example triple "macosx-10.12-x86_64".
plat = get_platform().split("-")
plat_name = "{}-{}-{}".format(plat[0], deployment_target, plat[2])
@@ -1071,6 +1054,13 @@ class pyside_build(_build):
if OPTION_VERBOSE_BUILD:
cmake_cmd.append("-DCMAKE_VERBOSE_MAKEFILE:BOOL=ON")
+ if OPTION_SANITIZE_ADDRESS:
+ # Some simple sanity checking. Only use at your own risk.
+ if sys.platform.startswith('linux') or sys.platform.startswith('darwin'):
+ cmake_cmd.append("-DSANITIZE_ADDRESS=ON")
+ else:
+ raise DistutilsSetupError("Address sanitizer can only be used on Linux and macOS.")
+
if extension.lower() == "pyside2":
pyside_qt_conf_prefix = ''
if OPTION_QT_CONF_PREFIX:
@@ -1082,6 +1072,17 @@ class pyside_build(_build):
pyside_qt_conf_prefix = '"."'
cmake_cmd.append("-DPYSIDE_QT_CONF_PREFIX=%s" % pyside_qt_conf_prefix)
+ # Pass package version to CMake, so this string can be embedded into _config.py file.
+ package_version = get_package_version()
+ cmake_cmd.append("-DPYSIDE_SETUP_PY_PACKAGE_VERSION={0}".format(package_version))
+
+ # In case if this is a snapshot build, also pass the timestamp as a separate value,
+ # because it the only version component that is actually generated by setup.py.
+ timestamp = ''
+ if OPTION_SNAPSHOT_BUILD:
+ timestamp = get_package_timestamp()
+ cmake_cmd.append("-DPYSIDE_SETUP_PY_PACKAGE_TIMESTAMP={0}".format(timestamp))
+
if extension.lower() == "shiboken2":
cmake_cmd.append("-DCMAKE_INSTALL_RPATH_USE_LINK_PATH=yes")
if sys.version_info[0] > 2:
@@ -1114,7 +1115,7 @@ class pyside_build(_build):
# set its own minimum deployment target environment variable which is
# based on the python interpreter sysconfig value. Doing so could break the
# detected clang include paths for example.
- deployment_target = pyside_build.macos_min_deployment_target()
+ deployment_target = pyside_build.macos_pyside_min_deployment_target()
cmake_cmd.append("-DCMAKE_OSX_DEPLOYMENT_TARGET={0}".format(deployment_target))
os.environ['MACOSX_DEPLOYMENT_TARGET'] = deployment_target
@@ -1156,8 +1157,6 @@ class pyside_build(_build):
def prepare_packages(self):
try:
log.info("Preparing packages...")
- version_str = "%sqt%s%s" % (__version__, self.qtinfo.version.replace(".", "")[0:3],
- self.debug and "dbg" or "")
vars = {
"site_packages_dir": self.site_packages_dir,
"sources_dir": self.sources_dir,
@@ -1176,7 +1175,6 @@ class pyside_build(_build):
"qt_prefix_dir": self.qtinfo.prefix_dir,
"qt_translations_dir": self.qtinfo.translations_dir,
"qt_qml_dir": self.qtinfo.qml_dir,
- "version": version_str,
}
os.chdir(self.script_dir)
@@ -1193,20 +1191,8 @@ class pyside_build(_build):
# Get config that contains list of built modules, and SOVERSIONs of the built libraries.
pyside_package_dir = vars['pyside_package_dir']
config_path = os.path.join(pyside_package_dir, "PySide2", "_config.py")
-
- try:
- with open(config_path) as f:
- scoped_locals = {}
- code = compile(f.read(), config_path, 'exec')
- exec(code, scoped_locals, scoped_locals)
- config = {}
- config['built_modules'] = scoped_locals['built_modules']
- config['shiboken_library_soversion'] = scoped_locals['shiboken_library_soversion']
- config['pyside_library_soversion'] = scoped_locals['pyside_library_soversion']
- return config
- except IOError as e:
- print("get_built_pyside_config: Couldn't find file: {}.".format(config_path))
- raise
+ config = get_python_dict(config_path)
+ return config
def prepare_packages_posix(self, vars):
executables = []
@@ -1374,7 +1360,7 @@ class pyside_build(_build):
# <qt>/translations/* -> <setup>/PySide2/Qt/translations
copydir("{qt_translations_dir}", "{pyside_package_dir}/PySide2/Qt/translations",
- filter=["*.qm"],
+ filter=["*.qm", "*.pak"],
force=False,
vars=vars)
@@ -1463,12 +1449,16 @@ class pyside_build(_build):
# <qt>/translations/* -> <setup>/PySide2/Qt/translations
copydir("{qt_translations_dir}", "{pyside_package_dir}/PySide2/Qt/translations",
- filter=["*.qm"],
+ filter=["*.qm", "*.pak"],
force=False,
vars=vars)
def prepare_packages_win32(self, vars):
- pdbs = ['*.pdb'] if self.debug or self.build_type == 'RelWithDebInfo' else []
+ # For now, debug symbols will not be shipped into the package.
+ copy_pdbs = False
+ pdbs = []
+ if (self.debug or self.build_type == 'RelWithDebInfo') and copy_pdbs:
+ pdbs = ['*.pdb']
# <install>/lib/site-packages/PySide2/* -> <setup>/PySide2
copydir(
"{site_packages_dir}/PySide2",
@@ -1476,18 +1466,19 @@ class pyside_build(_build):
vars=vars)
built_modules = self.get_built_pyside_config(vars)['built_modules']
- if self.debug or self.build_type == 'RelWithDebInfo':
- # <build>/pyside2/PySide2/*.pdb -> <setup>/PySide2
- copydir(
- "{build_dir}/pyside2/PySide2",
- "{pyside_package_dir}/PySide2",
- filter=pdbs,
- recursive=False, vars=vars)
+ # <build>/pyside2/PySide2/*.pdb -> <setup>/PySide2
+ copydir(
+ "{build_dir}/pyside2/PySide2",
+ "{pyside_package_dir}/PySide2",
+ filter=pdbs,
+ recursive=False, vars=vars)
+
# <build>/shiboken2/doc/html/* -> <setup>/PySide2/docs/shiboken2
copydir(
"{build_dir}/shiboken2/doc/html",
"{pyside_package_dir}/PySide2/docs/shiboken2",
force=False, vars=vars)
+
# <install>/lib/site-packages/shiboken2.pyd -> <setup>/PySide2/shiboken2.pyd
shiboken_module_name = 'shiboken2.pyd'
shiboken_src_path = "{site_packages_dir}".format(**vars)
@@ -1500,12 +1491,14 @@ class pyside_build(_build):
"{site_packages_dir}/{shiboken_module_name}",
"{pyside_package_dir}/PySide2/{shiboken_module_name}",
vars=vars)
- if self.debug or self.build_type == 'RelWithDebInfo':
- copydir(
- "{build_dir}/shiboken2/shibokenmodule",
- "{pyside_package_dir}/PySide2",
- filter=pdbs,
- recursive=False, vars=vars)
+ # @TODO: Fix this .pdb file not to overwrite release {shibokengenerator}.pdb file.
+ # Task-number: PYSIDE-615
+ copydir(
+ "{build_dir}/shiboken2/shibokenmodule",
+ "{pyside_package_dir}/PySide2",
+ filter=pdbs,
+ recursive=False, vars=vars)
+
# <install>/lib/site-packages/pyside2uic/* -> <setup>/pyside2uic
copydir(
"{site_packages_dir}/pyside2uic",
@@ -1515,6 +1508,7 @@ class pyside_build(_build):
rmtree("{pyside_package_dir}/pyside2uic/port_v2".format(**vars))
else:
rmtree("{pyside_package_dir}/pyside2uic/port_v3".format(**vars))
+
# <install>/bin/pyside2-uic -> PySide2/scripts/uic.py
makefile(
"{pyside_package_dir}/PySide2/scripts/__init__.py",
@@ -1523,33 +1517,46 @@ class pyside_build(_build):
"{install_dir}/bin/pyside2-uic",
"{pyside_package_dir}/PySide2/scripts/uic.py",
force=False, vars=vars)
+
# <install>/bin/*.exe,*.dll,*.pdb -> PySide2/
copydir(
"{install_dir}/bin/",
"{pyside_package_dir}/PySide2",
- filter=["*.exe", "*.dll"] + pdbs,
+ filter=["*.exe", "*.dll"],
+ recursive=False, vars=vars)
+ # @TODO: Fix this .pdb file not to overwrite release {shibokenmodule}.pdb file.
+ # Task-number: PYSIDE-615
+ copydir(
+ "{build_dir}/shiboken2/generator",
+ "{pyside_package_dir}/PySide2",
+ filter=pdbs,
recursive=False, vars=vars)
+
# <install>/lib/*.lib -> PySide2/
copydir(
"{install_dir}/lib/",
"{pyside_package_dir}/PySide2",
filter=["*.lib"],
recursive=False, vars=vars)
+
# <install>/share/PySide2/typesystems/* -> <setup>/PySide2/typesystems
copydir(
"{install_dir}/share/PySide2/typesystems",
"{pyside_package_dir}/PySide2/typesystems",
vars=vars)
+
# <install>/include/* -> <setup>/PySide2/include
copydir(
"{install_dir}/include",
"{pyside_package_dir}/PySide2/include",
vars=vars)
+
# <source>/pyside2/PySide2/support/* -> <setup>/PySide2/support/*
copydir(
"{build_dir}/pyside2/PySide2/support",
"{pyside_package_dir}/PySide2/support",
vars=vars)
+
if not OPTION_NOEXAMPLES:
# examples/* -> <setup>/PySide2/examples
copydir(os.path.join(self.script_dir, "examples"),
@@ -1562,6 +1569,7 @@ class pyside_build(_build):
pyside_rcc_options = '-py3'
regenerate_qt_resources(examples_path, pyside_rcc_path,
pyside_rcc_options)
+
# <ssl_libs>/* -> <setup>/PySide2/openssl
copydir("{ssl_libs_dir}", "{pyside_package_dir}/PySide2/openssl",
filter=[
@@ -1569,60 +1577,100 @@ class pyside_build(_build):
"ssleay32.dll"],
force=False, vars=vars)
- # <qt>/bin/*.dll -> <setup>/PySide2
+ # <qt>/bin/*.dll and Qt *.exe -> <setup>/PySide2
+ qt_artifacts_permanent = [
+ "opengl*.dll",
+ "d3d*.dll",
+ "designer.exe",
+ "linguist.exe",
+ "lrelease.exe",
+ "lupdate.exe",
+ "lconvert.exe",
+ "qtdiag.exe"
+ ]
copydir("{qt_bin_dir}", "{pyside_package_dir}/PySide2",
- filter=[
- "*.dll",
- "designer.exe",
- "linguist.exe",
- "lrelease.exe",
- "lupdate.exe",
- "lconvert.exe"],
- ignore=["*d4.dll"],
+ filter=qt_artifacts_permanent,
recursive=False, vars=vars)
- if self.debug:
- # <qt>/bin/*d4.dll -> <setup>/PySide2
- copydir("{qt_bin_dir}", "{pyside_package_dir}/PySide2",
- filter=["*d4.dll"] + pdbs,
- recursive=False, vars=vars)
- if self.debug or self.build_type == 'RelWithDebInfo':
- # <qt>/lib/*.pdb -> <setup>/PySide2
- copydir("{qt_lib_dir}", "{pyside_package_dir}/PySide2",
- filter=["*.pdb"],
- recursive=False, vars=vars)
+ # <qt>/bin/*.dll and Qt *.pdbs -> <setup>/PySide2 part two
+ # File filter to copy only debug or only release files.
+ qt_dll_patterns = ["Qt5*{}.dll", "lib*{}.dll"]
+ if copy_pdbs:
+ qt_dll_patterns += ["Qt5*{}.pdb", "lib*{}.pdb"]
+ def qt_build_config_filter(patterns, file_name, file_full_path):
+ release = [a.format('') for a in patterns]
+ debug = [a.format('d') for a in patterns]
+
+ # If qt is not a debug_and_release build, that means there is only one set of shared
+ # libraries, so we can just copy them.
+ if qtinfo.build_type != 'debug_and_release':
+ if filter_match(file_name, release):
+ return True
+ return False
+
+ # In debug_and_release case, choosing which files to copy is more difficult. We want
+ # to copy only the files that match the PySide2 build type. So if PySide2 is built in
+ # debug mode, we want to copy only Qt debug libraries (ending with "d.dll"). Or vice
+ # versa. The problem is that some libraries have "d" as the last character of the actual
+ # library name (for example Qt5Gamepad.dll and Qt5Gamepadd.dll). So we can't just
+ # match a pattern ending in "d". Instead we check if there exists a file with the same
+ # name plus an additional "d" at the end, and using that information we can judge if
+ # the currently processed file is a debug or release file.
+
+ # e.g. ["Qt5Cored", ".dll"]
+ file_split = os.path.splitext(file_name)
+ file_base_name = file_split[0]
+ file_ext = file_split[1]
+ # e.g. "/home/work/qt/qtbase/bin"
+ file_path_dir_name = os.path.dirname(file_full_path)
+ # e.g. "Qt5Coredd"
+ maybe_debug_name = file_base_name + 'd'
+ if self.debug:
+ filter = debug
+ def predicate(path): return not os.path.exists(path)
+ else:
+ filter = release
+ def predicate(path): return os.path.exists(path)
+ # e.g. "/home/work/qt/qtbase/bin/Qt5Coredd.dll"
+ other_config_path = os.path.join(file_path_dir_name, maybe_debug_name + file_ext)
+
+ if filter_match(file_name, filter) and predicate(other_config_path):
+ return True
+ return False
- # I think these are the qt-mobility DLLs, at least some are,
- # so let's copy them too
- # <qt>/lib/*.dll -> <setup>/PySide2
- copydir("{qt_lib_dir}", "{pyside_package_dir}/PySide2",
- filter=["*.dll"],
- ignore=["*d?.dll"],
+ qt_dll_filter = functools.partial(qt_build_config_filter, qt_dll_patterns)
+ copydir("{qt_bin_dir}", "{pyside_package_dir}/PySide2",
+ file_filter_function=qt_dll_filter,
recursive=False, vars=vars)
- if self.debug:
- # <qt>/lib/*d4.dll -> <setup>/PySide2
- copydir("{qt_lib_dir}", "{pyside_package_dir}/PySide2",
- filter=["*d?.dll"],
- recursive=False, vars=vars)
- if self.debug or self.build_type == 'RelWithDebInfo':
- # <qt>/lib/*pdb -> <setup>/PySide2
- copydir("{qt_lib_dir}", "{pyside_package_dir}/PySide2",
- filter=pdbs,
- recursive=False, vars=vars)
# <qt>/plugins/* -> <setup>/PySide2/plugins
+ plugin_dll_patterns = ["*{}.dll"]
+ if copy_pdbs:
+ plugin_dll_patterns += ["*{}.pdb"]
+ plugin_dll_filter = functools.partial(qt_build_config_filter, plugin_dll_patterns)
copydir("{qt_plugins_dir}", "{pyside_package_dir}/PySide2/plugins",
- filter=["*.dll"] + pdbs,
+ file_filter_function=plugin_dll_filter,
vars=vars)
+
# <qt>/translations/* -> <setup>/PySide2/translations
copydir("{qt_translations_dir}", "{pyside_package_dir}/PySide2/translations",
- filter=["*.qm"],
+ filter=["*.qm", "*.pak"],
force=False,
vars=vars)
# <qt>/qml/* -> <setup>/PySide2/qml
+ qml_dll_patterns = ["*{}.dll"]
+ if copy_pdbs:
+ qml_dll_patterns += ["*{}.pdb"]
+ qml_ignore = [a.format('') for a in qml_dll_patterns]
+ qml_dll_filter = functools.partial(qt_build_config_filter, qml_dll_patterns)
copydir("{qt_qml_dir}", "{pyside_package_dir}/PySide2/qml",
- filter=None,
+ ignore=qml_ignore,
+ force=False,
+ recursive=True,
+ vars=vars)
+ copydir("{qt_qml_dir}", "{pyside_package_dir}/PySide2/qml",
+ file_filter_function=qml_dll_filter,
force=False,
recursive=True,
vars=vars)
@@ -1633,30 +1681,24 @@ class pyside_build(_build):
recursive=False,
vars=vars)
+ filter = 'QtWebEngineProcess{}.exe'.format('d' if self.debug else '')
copydir("{qt_bin_dir}", "{pyside_package_dir}/PySide2",
- filter=["QtWebEngineProcess*.exe"],
+ filter=[filter],
recursive=False, vars=vars)
self.prepare_standalone_clang(is_win=True)
# pdb files for libshiboken and libpyside
- if self.debug or self.build_type == 'RelWithDebInfo':
- # XXX dbgPostfix gives problems - the structure in shiboken2/data should be re-written!
- # Not sure what the above refers to, but because both the extension module
- # (shiboken2.pyd) and the shared library (shiboken2.dll) have the same basename,
- # the pdb file gets overwritten. This doesn't happen on Unix because the shared library
- # has a 'lib' prefix in the basename.
- # @TODO Change the shared library name on Windows.
- copydir(
- "{build_dir}/shiboken2/libshiboken",
- "{pyside_package_dir}/PySide2",
- filter=pdbs,
- recursive=False, vars=vars)
- copydir(
- "{build_dir}/pyside2/libpyside",
- "{pyside_package_dir}/PySide2",
- filter=pdbs,
- recursive=False, vars=vars)
+ copydir(
+ "{build_dir}/shiboken2/libshiboken",
+ "{pyside_package_dir}/PySide2",
+ filter=pdbs,
+ recursive=False, vars=vars)
+ copydir(
+ "{build_dir}/pyside2/libpyside",
+ "{pyside_package_dir}/PySide2",
+ filter=pdbs,
+ recursive=False, vars=vars)
def prepare_standalone_clang(self, is_win = False):
""" Copies the libclang library to the pyside package so that shiboken exceutable works. """
@@ -1786,7 +1828,7 @@ if wheel_module_exists:
setup(
name = "PySide2",
- version = __version__,
+ version = get_package_version(),
description = ("Python bindings for the Qt cross-platform application and UI framework"),
long_description = README + "\n\n" + CHANGES,
classifiers = [
diff --git a/sources/pyside2/CMakeLists.txt b/sources/pyside2/CMakeLists.txt
index b16bcb55e..4db611f0d 100644
--- a/sources/pyside2/CMakeLists.txt
+++ b/sources/pyside2/CMakeLists.txt
@@ -21,6 +21,27 @@ else()
find_package(PythonLibs 2.6)
endif()
+set(PYSIDE_VERSION_FILE_PATH "${CMAKE_SOURCE_DIR}/pyside_version.py")
+set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS
+ ${PYSIDE_VERSION_FILE_PATH}
+)
+execute_process(
+ COMMAND ${PYTHON_EXECUTABLE} "${PYSIDE_VERSION_FILE_PATH}"
+ OUTPUT_VARIABLE PYSIDE_VERSION_OUTPUT
+ ERROR_VARIABLE PYSIDE_VERSION_OUTPUT_ERROR
+ OUTPUT_STRIP_TRAILING_WHITESPACE)
+if (NOT PYSIDE_VERSION_OUTPUT)
+ message(FATAL_ERROR "Could not identify PySide2 version. Error: ${PYSIDE_VERSION_OUTPUT_ERROR}")
+endif()
+
+list(GET PYSIDE_VERSION_OUTPUT 0 BINDING_API_MAJOR_VERSION)
+list(GET PYSIDE_VERSION_OUTPUT 1 BINDING_API_MINOR_VERSION)
+list(GET PYSIDE_VERSION_OUTPUT 2 BINDING_API_MICRO_VERSION)
+# a - alpha, b - beta, rc - rc
+list(GET PYSIDE_VERSION_OUTPUT 3 BINDING_API_PRE_RELEASE_VERSION_TYPE)
+# the number of the pre release (alpha1, beta3, rc7, etc.)
+list(GET PYSIDE_VERSION_OUTPUT 4 BINDING_API_PRE_RELEASE_VERSION)
+
macro(get_python_extension_suffix)
# Result of imp.get_suffixes() depends on the platform, but generally looks something like:
# [('.cpython-34m-x86_64-linux-gnu.so', 'rb', 3), ('.cpython-34m.so', 'rb', 3),
@@ -161,19 +182,15 @@ if(NOT CMAKE_BUILD_TYPE)
endif()
set(BINDING_NAME PySide2)
-set(BINDING_API_MAJOR_VERSION "2")
-set(BINDING_API_MINOR_VERSION "0")
-set(BINDING_API_MICRO_VERSION "0")
-set(BINDING_API_RELEASE_LEVEL "alpha") # alpha, beta, rc, or final
-set(BINDING_API_SERIAL 0) # leave as 0 when release level is final
-set(BINDING_API_VERSION "${BINDING_API_MAJOR_VERSION}.${BINDING_API_MINOR_VERSION}.${BINDING_API_MICRO_VERSION}" CACHE STRING "PySide version" FORCE)
+
+set(BINDING_API_VERSION "${BINDING_API_MAJOR_VERSION}.${BINDING_API_MINOR_VERSION}.${BINDING_API_MICRO_VERSION}" CACHE STRING "PySide2 version" FORCE)
set(PYSIDE_SO_VERSION ${BINDING_API_MAJOR_VERSION}.${BINDING_API_MINOR_VERSION})
-if (BINDING_API_RELEASE_LEVEL STREQUAL "final")
+if (BINDING_API_PRE_RELEASE_VERSION_TYPE STREQUAL "")
set(BINDING_API_VERSION_FULL "${BINDING_API_MAJOR_VERSION}.${BINDING_API_MINOR_VERSION}.${BINDING_API_MICRO_VERSION}"
- CACHE STRING "PySide version [full]" FORCE)
+ CACHE STRING "PySide2 version [full]" FORCE)
else()
- set(BINDING_API_VERSION_FULL "${BINDING_API_MAJOR_VERSION}.${BINDING_API_MINOR_VERSION}.${BINDING_API_MICRO_VERSION}~${BINDING_API_RELEASE_LEVEL}${BINDING_API_SERIAL}"
- CACHE STRING "PySide version [full]" FORCE)
+ set(BINDING_API_VERSION_FULL "${BINDING_API_MAJOR_VERSION}.${BINDING_API_MINOR_VERSION}.${BINDING_API_MICRO_VERSION}~${BINDING_API_PRE_RELEASE_VERSION_TYPE}${BINDING_API_PRE_RELEASE_VERSION}"
+ CACHE STRING "PySide2 version [full]" FORCE)
endif()
string(TIMESTAMP PYSIDE_BUILD_DATE "%Y-%m-%dT%H:%M:%S+00:00" UTC)
@@ -181,6 +198,19 @@ if (PYSIDE_BUILD_DATE)
set(PYSIDE_BUILD_DATE "__build_date__ = '${PYSIDE_BUILD_DATE}'")
endif()
+if (PYSIDE_SETUP_PY_PACKAGE_VERSION)
+ set(PYSIDE_SETUP_PY_PACKAGE_VERSION_ASSIGNMENT "__setup_py_package_version__ = '${PYSIDE_SETUP_PY_PACKAGE_VERSION}'")
+ set(FINAL_PACKAGE_VERSION ${PYSIDE_SETUP_PY_PACKAGE_VERSION})
+else()
+ set(FINAL_PACKAGE_VERSION ${BINDING_API_VERSION_FULL})
+endif()
+
+if (PYSIDE_SETUP_PY_PACKAGE_TIMESTAMP)
+ set(PYSIDE_SETUP_PY_PACKAGE_TIMESTAMP_ASSIGNMENT "__setup_py_package_timestamp__ = '${PYSIDE_SETUP_PY_PACKAGE_TIMESTAMP}'")
+else()
+ set(PYSIDE_SETUP_PY_PACKAGE_TIMESTAMP_ASSIGNMENT "__setup_py_package_timestamp__ = ''")
+endif()
+
find_package(Git)
if(GIT_FOUND)
# Check if current source folder is inside a git repo, so that commit information can be
@@ -222,11 +252,6 @@ if(GIT_FOUND)
endif()
endif()
-# Used by setup.py to know which symlink to resolve and copy in to the final package, in order to
-# avoid resolving all symlinks and thus copying unnecessary duplicate files.
-set(config_py_shiboken_library_version "")
-set(config_py_pyside_library_version "")
-
include(PySideModules)
macro(COLLECT_MODULE_IF_FOUND shortname)
@@ -316,7 +341,7 @@ if(WIN32)
list(APPEND ALL_OPTIONAL_MODULES AxContainer)
endif()
list(APPEND ALL_OPTIONAL_MODULES WebChannel WebEngineWidgets WebKit WebKitWidgets WebSockets)
-if (Qt5Core_VERSION VERSION_GREATER 5.9.3 AND Qt5Core_VERSION VERSION_LESS 5.10.0) # Depending on fixes in Qt3D
+if (Qt5Core_VERSION VERSION_GREATER 5.9.3) # Depending on fixes in Qt3D
list(APPEND ALL_OPTIONAL_MODULES 3DCore 3DRender 3DInput 3DLogic 3DAnimation 3DExtras)
endif()
@@ -372,8 +397,6 @@ endif()
# Define supported Qt Version
set(SUPPORTED_QT_VERSION "${QT_VERSION_MAJOR}.${QT_VERSION_MINOR}")
-set(BINDING_VERSION ${BINDING_API_VERSION}.${QT_VERSION_MAJOR}.${QT_VERSION_MINOR})
-
# uninstall target
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/cmake_uninstall.cmake"
"${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake"
@@ -382,16 +405,6 @@ add_custom_target(uninstall "${CMAKE_COMMAND}"
-P "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake")
-set(ARCHIVE_NAME pyside-qt${QT_VERSION_MAJOR}.${QT_VERSION_MINOR}+${BINDING_API_VERSION_FULL})
-add_custom_target(dist
- COMMAND mkdir -p "${CMAKE_BINARY_DIR}/${ARCHIVE_NAME}" &&
- git log > "${CMAKE_BINARY_DIR}/${ARCHIVE_NAME}/ChangeLog" &&
- git archive --prefix=${ARCHIVE_NAME}/ HEAD --format=tar --output="${CMAKE_BINARY_DIR}/${ARCHIVE_NAME}.tar" &&
- tar -C "${CMAKE_BINARY_DIR}" --owner=root --group=root -r "${ARCHIVE_NAME}/ChangeLog" -f "${CMAKE_BINARY_DIR}/${ARCHIVE_NAME}.tar" &&
- bzip2 -f9 "${CMAKE_BINARY_DIR}/${ARCHIVE_NAME}.tar" &&
- echo "Source package created at ${CMAKE_BINARY_DIR}/${ARCHIVE_NAME}.tar.bz2.\n"
- WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
-
if (NOT PYTHON_SITE_PACKAGES)
execute_process(
COMMAND ${SHIBOKEN_PYTHON_INTERPRETER} -c "if True:
@@ -424,6 +437,22 @@ else()
message(STATUS "PySide will be generated using the protected hack!")
endif()
+# Build with Address sanitizer enabled if requested. This may break things, so use at your own risk.
+if (SANITIZE_ADDRESS AND NOT MSVC)
+ # Currently this does not check that the clang / gcc version used supports Address sanitizer,
+ # so once again, use at your own risk.
+ add_compile_options("-fsanitize=address" "-g" "-fno-omit-frame-pointer")
+ # We need to add the sanitize address option to all linked executables / shared libraries
+ # so that proper sanitizer symbols are linked in.
+ #
+ # Note that when running tests, you may need to set an additional environment variable
+ # in set_tests_properties for shiboken2 / pyside tests, or exported in your shell. Address
+ # sanitizer will tell you what environment variable needs to be exported. For example:
+ # export DYLD_INSERT_LIBRARIES=/Applications/Xcode.app/Contents/Developer/Toolchains/
+ # ./XcodeDefault.xctoolchain/usr/lib/clang/8.1.0/lib/darwin/libclang_rt.asan_osx_dynamic.dylib
+ set(CMAKE_CXX_STANDARD_LIBRARIES "${CMAKE_STANDARD_LIBRARIES} -fsanitize=address")
+endif()
+
add_subdirectory(libpyside)
find_package(Qt5Designer)
if(Qt5UiTools_FOUND AND Qt5Designer_FOUND)
diff --git a/sources/pyside2/PySide2/CMakeLists.txt b/sources/pyside2/PySide2/CMakeLists.txt
index 21db337e9..971d0a9ef 100644
--- a/sources/pyside2/PySide2/CMakeLists.txt
+++ b/sources/pyside2/PySide2/CMakeLists.txt
@@ -10,6 +10,9 @@ configure_file("${CMAKE_CURRENT_SOURCE_DIR}/__init__.py.in"
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/_config.py.in"
"${CMAKE_CURRENT_BINARY_DIR}/_config.py" @ONLY)
+configure_file("${CMAKE_CURRENT_SOURCE_DIR}/../pyside_version.py"
+ "${CMAKE_CURRENT_BINARY_DIR}/_git_pyside_version.py" @ONLY)
+
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/support/__init__.py"
"${CMAKE_CURRENT_BINARY_DIR}/support/__init__.py" COPYONLY)
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/support/signature/__init__.py"
@@ -69,6 +72,8 @@ install(FILES "${CMAKE_CURRENT_BINARY_DIR}/__init__.py"
DESTINATION "${PYTHON_SITE_PACKAGES}/${BINDING_NAME}${pyside2_SUFFIX}")
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/_config.py"
DESTINATION "${PYTHON_SITE_PACKAGES}/${BINDING_NAME}${pyside2_SUFFIX}")
+install(FILES "${CMAKE_CURRENT_BINARY_DIR}/_git_pyside_version.py"
+ DESTINATION "${PYTHON_SITE_PACKAGES}/${BINDING_NAME}${pyside2_SUFFIX}")
install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/typesystem_templates.xml
DESTINATION share/PySide2${pyside_SUFFIX}/typesystems)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/pyside2_global.h
diff --git a/sources/pyside2/PySide2/QtGui/typesystem_gui_common.xml b/sources/pyside2/PySide2/QtGui/typesystem_gui_common.xml
index 12b0f3115..0f6d48968 100644
--- a/sources/pyside2/PySide2/QtGui/typesystem_gui_common.xml
+++ b/sources/pyside2/PySide2/QtGui/typesystem_gui_common.xml
@@ -1731,6 +1731,9 @@
<modify-argument index="return">
<replace-type modified-type="QString"/>
</modify-argument>
+ <inject-code class="native" position="end">
+ <insert-template name="return_QString_native"/>
+ </inject-code>
<inject-code class="target" position="end">
<insert-template name="return_QString"/>
</inject-code>
diff --git a/sources/pyside2/PySide2/__init__.py.in b/sources/pyside2/PySide2/__init__.py.in
index 92e52a81a..4ce266b69 100644
--- a/sources/pyside2/PySide2/__init__.py.in
+++ b/sources/pyside2/PySide2/__init__.py.in
@@ -1,14 +1,17 @@
__all__ = list("Qt" + body for body in
"@all_module_shortnames@"
.split(";"))
-__version__ = "@BINDING_API_VERSION_FULL@"
-__version_info__ = (@BINDING_API_MAJOR_VERSION@, @BINDING_API_MINOR_VERSION@, @BINDING_API_MICRO_VERSION@, "@BINDING_API_RELEASE_LEVEL@", @BINDING_API_SERIAL@)
+__version__ = "@FINAL_PACKAGE_VERSION@"
+__version_info__ = (@BINDING_API_MAJOR_VERSION@, @BINDING_API_MINOR_VERSION@, @BINDING_API_MICRO_VERSION@, "@BINDING_API_PRE_RELEASE_VERSION_TYPE@", @BINDING_API_PRE_RELEASE_VERSION@)
@PYSIDE_BUILD_DATE@
@PYSIDE_BUILD_COMMIT_DATE@
@PYSIDE_BUILD_COMMIT_HASH@
@PYSIDE_BUILD_COMMIT_HASH_DESCRIBED@
+# Timestamp used for snapshot build, which is part of snapshot package version.
+@PYSIDE_SETUP_PY_PACKAGE_TIMESTAMP_ASSIGNMENT@
+
def _setupQtDirectories():
import sys
import os
diff --git a/sources/pyside2/PySide2/_config.py.in b/sources/pyside2/PySide2/_config.py.in
index db8a17210..6f8d022dc 100644
--- a/sources/pyside2/PySide2/_config.py.in
+++ b/sources/pyside2/PySide2/_config.py.in
@@ -4,3 +4,14 @@ built_modules = list(name for name in
shiboken_library_soversion = str(@SHIBOKEN_SO_VERSION@)
pyside_library_soversion = str(@PYSIDE_SO_VERSION@)
+
+version = "@FINAL_PACKAGE_VERSION@"
+version_info = (@BINDING_API_MAJOR_VERSION@, @BINDING_API_MINOR_VERSION@, @BINDING_API_MICRO_VERSION@, "@BINDING_API_PRE_RELEASE_VERSION_TYPE@", @BINDING_API_PRE_RELEASE_VERSION@)
+
+@PYSIDE_BUILD_DATE@
+@PYSIDE_BUILD_COMMIT_DATE@
+@PYSIDE_BUILD_COMMIT_HASH@
+@PYSIDE_BUILD_COMMIT_HASH_DESCRIBED@
+
+# Timestamp used for snapshot build, which is part of snapshot package version.
+@PYSIDE_SETUP_PY_PACKAGE_TIMESTAMP_ASSIGNMENT@
diff --git a/sources/pyside2/PySide2/typesystem_templates.xml b/sources/pyside2/PySide2/typesystem_templates.xml
index 1715c0253..11a384263 100644
--- a/sources/pyside2/PySide2/typesystem_templates.xml
+++ b/sources/pyside2/PySide2/typesystem_templates.xml
@@ -286,6 +286,12 @@
<template name="return_QString">
%PYARG_0 = %CONVERTTOPYTHON[QString](%1);
</template>
+ <template name="return_QString_native">
+ if (%ISCONVERTIBLE[QString](%PYARG_0))
+ %1 = %CONVERTTOCPP[QString](%PYARG_0);
+ else
+ qWarning("%TYPE::%FUNCTION_NAME: Argument is not convertible to unicode.");
+ </template>
<template name="return_tuple_QValidator_QString_int">
%BEGIN_ALLOW_THREADS
diff --git a/sources/pyside2/doc/inheritance_diagram.py b/sources/pyside2/doc/inheritance_diagram.py
index a7f376ccd..038c22f29 100644
--- a/sources/pyside2/doc/inheritance_diagram.py
+++ b/sources/pyside2/doc/inheritance_diagram.py
@@ -52,15 +52,38 @@ from docutils.parsers.rst import directives
from sphinx.ext.graphviz import render_dot_html, render_dot_latex
from sphinx.util.compat import Directive
-
-class_sig_re = re.compile(r'''^([\w.]*\.)? # module names
- (\w+) \s* $ # class/final module name
- ''', re.VERBOSE)
-
-
class InheritanceException(Exception):
pass
+# When passed something like:
+# PySide2.QtCore.QStateMachine.SignalEvent
+# try to import the underlying module and return a
+# handle to the object. In a loop, import
+# PySide2.QtCore.QStateMachine.SignalEvent
+# PySide2.QtCore.QStateMachine
+# PySide2.QtCore
+# until the import succeeds and walk up the attributes
+# to obtain the object
+
+def importClassOrModule(name):
+ components = name.split('.')
+ for i in range(len(components), 0, -1):
+ importPath = '.'.join(components[: i])
+ try:
+ __import__(importPath)
+ except ImportError:
+ continue
+ if i == len(components):
+ return sys.modules[importPath]
+ remaining = components[i :]
+ cls = sys.modules[importPath]
+ for component in remaining:
+ try:
+ cls = getattr(cls, component)
+ except Exception: # No such attribute
+ return None
+ return cls
+ return None
class InheritanceGraph(object):
"""
@@ -86,38 +109,13 @@ class InheritanceGraph(object):
"""
Import a class using its fully-qualified *name*.
"""
- try:
- path, base = class_sig_re.match(name).groups()
- except (AttributeError, ValueError):
- raise InheritanceException('Invalid class or module %r specified '
- 'for inheritance diagram' % name)
-
- fullname = (path or '') + base
- path = (path and path.rstrip('.') or '')
-
- # two possibilities: either it is a module, then import it
- try:
- __import__(fullname)
- todoc = sys.modules[fullname]
- except ImportError:
- # else it is a class, then import the module
- if not path:
- if currmodule:
- # try the current module
- path = currmodule
- else:
- raise InheritanceException(
- 'Could not import class %r specified for '
- 'inheritance diagram' % base)
- try:
- __import__(path)
- todoc = getattr(sys.modules[path], base)
- except (ImportError, AttributeError):
- raise InheritanceException(
- 'Could not import class or module %r specified for '
- 'inheritance diagram' % (path + '.' + base))
-
- # If a class, just return it
+ todoc = importClassOrModule(name)
+ if not todoc and currmodule is not None:
+ todoc = importClassOrModule(currmodule + '.' + name)
+ if not todoc:
+ moduleStr = '(module {})'.format(currmodule) if currmodule else ''
+ raise InheritanceException('Could not import class {} specified for '
+ 'inheritance diagram {}.'.format(name, moduleStr))
if inspect.isclass(todoc):
return [todoc]
elif inspect.ismodule(todoc):
@@ -167,7 +165,7 @@ class InheritanceGraph(object):
for cls in classes:
recurse(cls)
- return all_classes.values()
+ return list(all_classes.values())
def class_name(self, cls, parts=0):
"""Given a class object, return a fully-qualified name.
@@ -200,8 +198,8 @@ class InheritanceGraph(object):
'shape': 'box',
'fontsize': 10,
'height': 0.25,
- 'fontname': 'Vera Sans, DejaVu Sans, Liberation Sans, '
- 'Arial, Helvetica, sans',
+ 'fontname': '"Vera Sans, DejaVu Sans, Liberation Sans, '
+ 'Arial, Helvetica, sans"',
'style': '"setlinewidth(0.5)"',
}
default_edge_attrs = {
@@ -314,7 +312,8 @@ class InheritanceDiagram(Directive):
def get_graph_hash(node):
- return md5(node['content'] + str(node['parts'])).hexdigest()[-10:]
+ hashString = node['content'] + str(node['parts'])
+ return md5(hashString.encode('utf-8')).hexdigest()[-10:]
def html_visit_inheritance_diagram(self, node):
@@ -336,7 +335,7 @@ def html_visit_inheritance_diagram(self, node):
urls[child['reftitle']] = '#' + child.get('refid')
dotcode = graph.generate_dot(name, urls, env=self.builder.env)
- render_dot_html(self, node, dotcode, [], 'inheritance', 'inheritance',
+ render_dot_html(self, node, dotcode, {}, 'inheritance', 'inheritance',
alt='Inheritance diagram of ' + node['content'])
raise nodes.SkipNode
@@ -352,7 +351,7 @@ def latex_visit_inheritance_diagram(self, node):
dotcode = graph.generate_dot(name, env=self.builder.env,
graph_attrs={'size': '"6.0,6.0"'})
- render_dot_latex(self, node, dotcode, [], 'inheritance')
+ render_dot_latex(self, node, dotcode, {}, 'inheritance')
raise nodes.SkipNode
diff --git a/sources/pyside2/doc/pysideversion.rst b/sources/pyside2/doc/pysideversion.rst
index 3bcebe0da..5ad308c1e 100644
--- a/sources/pyside2/doc/pysideversion.rst
+++ b/sources/pyside2/doc/pysideversion.rst
@@ -1,31 +1,31 @@
Getting PySide and Qt version
*****************************
-PySide exports their version numbers among the version of Qt used to compile PySide in a pythonnic way, you can check it using the variables:
+PySide exports their version numbers among the version of Qt used to compile PySide in a pythonic way, you can check it using the variables:
::
- import PySide
+ import PySide2.QtCore
# Prints PySide version
# e.g. 1.0.2
- print PySide.__version__
+ print(PySide2.__version__)
# Gets a tuple with each version component
# e.g. (1, 0, 2, 'final', 1)
- print PySide.__version_info__
+ print(PySide2.__version_info__)
# Prints the Qt version used to compile PySide
- # e.g. "4.7.2"
- print PySide2.QtCore.__version__
+ # e.g. "5.11.0"
+ print(PySide2.QtCore.__version__)
# Gets a tuple with each version components of Qt used to compile PySide
- # e.g. (4, 7, 2)
- print PySide2.QtCore.__version_info__
+ # e.g. (5, 11, 0)
+ print(PySide2.QtCore.__version_info__)
Note that the Qt version used to compile PySide may differ from the version used to run PySide, to get the current running Qt version you can do:
::
- print PySide2.QtCore.qVersion()
+ print(PySide2.QtCore.qVersion())
diff --git a/sources/pyside2/libpyside/pysidesignal.cpp b/sources/pyside2/libpyside/pysidesignal.cpp
index 04b1cf1f4..a901d10af 100644
--- a/sources/pyside2/libpyside/pysidesignal.cpp
+++ b/sources/pyside2/libpyside/pysidesignal.cpp
@@ -413,11 +413,64 @@ PyObject* signalInstanceConnect(PyObject* self, PyObject* args, PyObject* kwds)
sourceWalk = reinterpret_cast<PySideSignalInstance*>(sourceWalk->d->next);
}
} else {
- //try the first signature
+ // Check signature of the slot (method or function) to match signal
+ int slotArgs = -1;
+ bool useSelf = false;
+ bool isMethod = PyMethod_Check(slot);
+ bool isFunction = PyFunction_Check(slot);
+ bool matchedSlot = false;
+
+ QByteArray functionName;
+ PySideSignalInstance *it = source;
+
+ if (isMethod || isFunction) {
+ PyObject *function = isMethod ? PyMethod_GET_FUNCTION(slot) : slot;
+ PyCodeObject *objCode = reinterpret_cast<PyCodeObject *>(PyFunction_GET_CODE(function));
+ PyFunctionObject *function_obj = reinterpret_cast<PyFunctionObject *>(function);
+ functionName = Shiboken::String::toCString(function_obj->func_name);
+ useSelf = isMethod;
+ slotArgs = objCode->co_flags & CO_VARARGS ? -1 : objCode->co_argcount;
+ if (useSelf)
+ slotArgs -= 1;
+
+ // Get signature args
+ bool isShortCircuit = false;
+ int signatureArgs = 0;
+ QStringList argsSignature;
+
+ argsSignature = PySide::Signal::getArgsFromSignature(it->d->signature,
+ &isShortCircuit);
+ signatureArgs = argsSignature.length();
+
+ // Iterate the possible types of connection for this signal and compare
+ // it with slot arguments
+ if (signatureArgs != slotArgs) {
+ while (it->d->next != nullptr) {
+ it = it->d->next;
+ argsSignature = PySide::Signal::getArgsFromSignature(it->d->signature,
+ &isShortCircuit);
+ signatureArgs = argsSignature.length();
+ if (signatureArgs == slotArgs) {
+ matchedSlot = true;
+ break;
+ }
+ }
+ }
+ }
+
+ // Adding references to pyArgs
PyList_Append(pyArgs, source->d->source);
- Shiboken::AutoDecRef signature(PySide::Signal::buildQtCompatible(source->d->signature));
- PyList_Append(pyArgs, signature);
+ if (matchedSlot) {
+ // If a slot matching the same number of arguments was found,
+ // include signature to the pyArgs
+ Shiboken::AutoDecRef signature(PySide::Signal::buildQtCompatible(it->d->signature));
+ PyList_Append(pyArgs, signature);
+ } else {
+ // Try the first by default if the slot was not found
+ Shiboken::AutoDecRef signature(PySide::Signal::buildQtCompatible(source->d->signature));
+ PyList_Append(pyArgs, signature);
+ }
PyList_Append(pyArgs, slot);
match = true;
}
diff --git a/sources/pyside2/pyside_version.py b/sources/pyside2/pyside_version.py
new file mode 100644
index 000000000..3a678cd14
--- /dev/null
+++ b/sources/pyside2/pyside_version.py
@@ -0,0 +1,49 @@
+#############################################################################
+##
+## Copyright (C) 2018 The Qt Company Ltd.
+## Contact: https://www.qt.io/licensing/
+##
+## This file is part of PySide2.
+##
+## $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$
+##
+#############################################################################
+
+major_version = "5"
+minor_version = "11"
+patch_version = "0"
+pre_release_version_type = "a" # e.g. "a", "b", "rc".
+pre_release_version = "1" # e.g "1", "2", (which means "beta1", "beta2", if type is "b")
+
+if __name__ == '__main__':
+ # Used by CMake.
+ print('{0};{1};{2};{3};{4}'.format(major_version, minor_version, patch_version,
+ pre_release_version_type, pre_release_version))
diff --git a/sources/pyside2/tests/QtWidgets/qvalidator_test.py b/sources/pyside2/tests/QtWidgets/qvalidator_test.py
index 951d6b2b0..dd5eaadb3 100644
--- a/sources/pyside2/tests/QtWidgets/qvalidator_test.py
+++ b/sources/pyside2/tests/QtWidgets/qvalidator_test.py
@@ -29,38 +29,49 @@
from PySide2.QtCore import *
from PySide2.QtGui import *
from PySide2.QtWidgets import *
+from PySide2.QtTest import *
import unittest
from helper import UsesQApplication
class MyValidator1(QValidator):
- def fixUp(self, input):
+ def fixup(self, input):
return "fixed"
def validate(self, input, pos):
return (QValidator.Acceptable, "fixed", 1)
class MyValidator2(QValidator):
- def fixUp(self, input):
+ def fixup(self, input):
return "fixed"
def validate(self, input, pos):
return (QValidator.Acceptable, "fixed")
class MyValidator3(QValidator):
- def fixUp(self, input):
+ def fixup(self, input):
return "fixed"
def validate(self, input, pos):
return (QValidator.Acceptable,)
class MyValidator4(QValidator):
- def fixUp(self, input):
+ def fixup(self, input):
return "fixed"
def validate(self, input, pos):
return QValidator.Acceptable
+class MyValidator5(QValidator):
+ def validate(self, input, pos):
+ if input.islower():
+ return (QValidator.Intermediate, input, pos)
+ else:
+ return (QValidator.Acceptable, input, pos)
+
+ def fixup(self, input):
+ return "22"
+
class QValidatorTest(UsesQApplication):
def testValidator1(self):
line = QLineEdit()
@@ -110,5 +121,13 @@ class QValidatorTest(UsesQApplication):
self.assertEqual(line.text(), "foo")
self.assertEqual(line.cursorPosition(), 3)
+ def testValidator5(self):
+ line = QLineEdit()
+ line.show()
+ line.setValidator(MyValidator5())
+ line.setText("foo")
+ QTest.keyClick(line, Qt.Key_Return)
+ self.assertEqual(line.text(), "22")
+
if __name__ == '__main__':
unittest.main()
diff --git a/sources/pyside2/tests/pysidetest/version_test.py b/sources/pyside2/tests/pysidetest/version_test.py
index 5901a56c1..01e88dbc6 100644
--- a/sources/pyside2/tests/pysidetest/version_test.py
+++ b/sources/pyside2/tests/pysidetest/version_test.py
@@ -33,8 +33,10 @@ from PySide2 import __version_info__, __version__, QtCore
class CheckForVariablesTest(unittest.TestCase):
def testVesions(self):
- self.assertTrue(__version_info__ >= (1, 0, 0))
- self.assertTrue(__version_info__ < (99, 99, 99))
+ version_tuple = (__version_info__[0], __version_info__[1], __version_info__[2])
+ self.assertTrue(version_tuple >= (1, 0, 0))
+
+ self.assertTrue(version_tuple < (99, 99, 99))
self.assertTrue(__version__)
self.assertTrue(QtCore.__version_info__ >= (4, 5, 0))
diff --git a/sources/pyside2/tests/signals/signal_signature_test.py b/sources/pyside2/tests/signals/signal_signature_test.py
index 349619aac..e94c1722d 100644
--- a/sources/pyside2/tests/signals/signal_signature_test.py
+++ b/sources/pyside2/tests/signals/signal_signature_test.py
@@ -34,7 +34,11 @@ import unittest
from PySide2.QtCore import *
from helper import UsesQCoreApplication
+called = False
+name = "Old"
class Obj(QObject):
+ dummySignalArgs = Signal(str)
+ numberSignal = Signal(int)
def __init__(self):
QObject.__init__(self)
self.signal = ''
@@ -42,8 +46,20 @@ class Obj(QObject):
def connectNotify(self, signal):
self.signal = signal
+ @staticmethod
+ def static_method():
+ global called
+ called = True
+
+ @staticmethod
+ def static_method_args(arg="default"):
+ global name
+ name = arg
+
def callback(arg=None):
pass
+def callback_empty():
+ pass
class TestConnectNotifyWithNewStyleSignals(UsesQCoreApplication):
'''Test case for signal signature received by QObject::connectNotify().'''
@@ -65,12 +81,34 @@ class TestConnectNotifyWithNewStyleSignals(UsesQCoreApplication):
def testNewStyle(self):
sender = Obj()
- sender.destroyed.connect(callback)
+ sender.destroyed.connect(callback_empty)
self.assertEqual(sender.signal.methodSignature(), 'destroyed()')
sender.destroyed[QObject].connect(callback)
self.assertEqual(sender.signal.methodSignature(), 'destroyed(QObject*)')
+ def testStaticSlot(self):
+ global called
+ sender = Obj()
+ sender.connect(sender, SIGNAL("dummySignal()"), Obj.static_method)
+ sender.emit(SIGNAL("dummySignal()"))
+ self.assertTrue(called)
+
+
+ def testStaticSlotArgs(self):
+ global name
+ sender = Obj()
+ sender.dummySignalArgs.connect(Obj.static_method_args)
+ sender.dummySignalArgs[str].emit("New")
+ self.assertEqual(name, "New")
+
+ def testLambdaSlot(self):
+ sender = Obj()
+ sender.numberSignal[int].connect(lambda x: 42)
+ with self.assertRaises(IndexError):
+ sender.numberSignal[str].emit("test")
+
+
if __name__ == '__main__':
unittest.main()
diff --git a/sources/shiboken2/ApiExtractor/doc/typesystem_specifying_types.rst b/sources/shiboken2/ApiExtractor/doc/typesystem_specifying_types.rst
index 0d24a6d52..646e76043 100644
--- a/sources/shiboken2/ApiExtractor/doc/typesystem_specifying_types.rst
+++ b/sources/shiboken2/ApiExtractor/doc/typesystem_specifying_types.rst
@@ -150,6 +150,7 @@ enum-type
<typesystem>
<enum-type name="..."
identified-by-value="..."
+ class="yes | no"
since="..."
flags="yes | no"
flags-revision="..."
@@ -179,6 +180,10 @@ enum-type
Notice that the **enum-type** tag can either have **name** or **identified-by-value**
but not both.
+ The *optional* boolean attribute **class** specifies whether the underlying
+ enumeration is a C++ 11 enumeration class. In that case, the enumeration values
+ need to be qualified by the enumeration name to match the C++ Syntax.
+
The **revision** attribute can be used to specify a revision for each type, easing the
production of ABI compatible bindings.
diff --git a/sources/shiboken2/ApiExtractor/typesystem.cpp b/sources/shiboken2/ApiExtractor/typesystem.cpp
index 4a8d3063d..69dccbb86 100644
--- a/sources/shiboken2/ApiExtractor/typesystem.cpp
+++ b/sources/shiboken2/ApiExtractor/typesystem.cpp
@@ -58,6 +58,12 @@ static inline QString enumNameAttribute() { return QStringLiteral("enum-name");
static inline QString argumentTypeAttribute() { return QStringLiteral("argument-type"); }
static inline QString returnTypeAttribute() { return QStringLiteral("return-type"); }
static inline QString xPathAttribute() { return QStringLiteral("xpath"); }
+static inline QString enumIdentifiedByValueAttribute() { return QStringLiteral("identified-by-value"); }
+
+static inline QString noAttributeValue() { return QStringLiteral("no"); }
+static inline QString yesAttributeValue() { return QStringLiteral("yes"); }
+static inline QString trueAttributeValue() { return QStringLiteral("true"); }
+static inline QString falseAttributeValue() { return QStringLiteral("false"); }
static QVector<CustomConversion *> customConversionsForReview;
@@ -534,21 +540,22 @@ bool Handler::importFileElement(const QXmlStreamAttributes &atts)
return true;
}
-bool Handler::convertBoolean(const QString &_value, const QString &attributeName, bool defaultValue)
+static bool convertBoolean(const QString &value, const QString &attributeName, bool defaultValue)
{
- QString value = _value.toLower();
- if (value == QLatin1String("true") || value == QLatin1String("yes"))
+ if (value.compare(trueAttributeValue(), Qt::CaseInsensitive) == 0
+ || value.compare(yesAttributeValue(), Qt::CaseInsensitive) == 0) {
return true;
- else if (value == QLatin1String("false") || value == QLatin1String("no"))
+ }
+ if (value.compare(falseAttributeValue(), Qt::CaseInsensitive) == 0
+ || value.compare(noAttributeValue(), Qt::CaseInsensitive) == 0) {
return false;
- else {
- QString warn = QStringLiteral("Boolean value '%1' not supported in attribute '%2'. Use 'yes' or 'no'. Defaulting to '%3'.")
+ }
+ const QString warn = QStringLiteral("Boolean value '%1' not supported in attribute '%2'. Use 'yes' or 'no'. Defaulting to '%3'.")
.arg(value, attributeName,
- defaultValue ? QLatin1String("yes") : QLatin1String("no"));
+ defaultValue ? yesAttributeValue() : noAttributeValue());
- qCWarning(lcShiboken).noquote().nospace() << warn;
- return defaultValue;
- }
+ qCWarning(lcShiboken).noquote().nospace() << warn;
+ return defaultValue;
}
static bool convertRemovalAttribute(const QString& removalAttribute, Modification& mod, QString& errorMsg)
@@ -733,8 +740,8 @@ bool Handler::startElement(const QStringRef &n, const QXmlStreamAttributes &atts
case StackElement::PrimitiveTypeEntry:
attributes.insert(QLatin1String("target-lang-name"), QString());
attributes.insert(QLatin1String("target-lang-api-name"), QString());
- attributes.insert(QLatin1String("preferred-conversion"), QLatin1String("yes"));
- attributes.insert(QLatin1String("preferred-target-lang-type"), QLatin1String("yes"));
+ attributes.insert(QLatin1String("preferred-conversion"), yesAttributeValue());
+ attributes.insert(QLatin1String("preferred-target-lang-type"), yesAttributeValue());
attributes.insert(QLatin1String("default-constructor"), QString());
break;
case StackElement::ContainerTypeEntry:
@@ -750,23 +757,24 @@ bool Handler::startElement(const QStringRef &n, const QXmlStreamAttributes &atts
attributes.insert(QLatin1String("flags-revision"), QString());
attributes.insert(QLatin1String("upper-bound"), QString());
attributes.insert(QLatin1String("lower-bound"), QString());
- attributes.insert(QLatin1String("force-integer"), QLatin1String("no"));
- attributes.insert(QLatin1String("extensible"), QLatin1String("no"));
- attributes.insert(QLatin1String("identified-by-value"), QString());
+ attributes.insert(QLatin1String("force-integer"), noAttributeValue());
+ attributes.insert(QLatin1String("extensible"), noAttributeValue());
+ attributes.insert(enumIdentifiedByValueAttribute(), QString());
+ attributes.insert(classAttribute(), falseAttributeValue());
break;
case StackElement::ValueTypeEntry:
attributes.insert(QLatin1String("default-constructor"), QString());
Q_FALLTHROUGH();
case StackElement::ObjectTypeEntry:
- attributes.insert(QLatin1String("force-abstract"), QLatin1String("no"));
- attributes.insert(QLatin1String("deprecated"), QLatin1String("no"));
+ attributes.insert(QLatin1String("force-abstract"), noAttributeValue());
+ attributes.insert(QLatin1String("deprecated"), noAttributeValue());
attributes.insert(QLatin1String("hash-function"), QString());
- attributes.insert(QLatin1String("stream"), QLatin1String("no"));
+ attributes.insert(QLatin1String("stream"), noAttributeValue());
Q_FALLTHROUGH();
case StackElement::InterfaceTypeEntry:
attributes[QLatin1String("default-superclass")] = m_defaultSuperclass;
attributes.insert(QLatin1String("polymorphic-id-expression"), QString());
- attributes.insert(QLatin1String("delete-in-main-thread"), QLatin1String("no"));
+ attributes.insert(QLatin1String("delete-in-main-thread"), noAttributeValue());
attributes.insert(QLatin1String("held-type"), QString());
attributes.insert(QLatin1String("copyable"), QString());
Q_FALLTHROUGH();
@@ -775,10 +783,10 @@ bool Handler::startElement(const QStringRef &n, const QXmlStreamAttributes &atts
attributes[QLatin1String("package")] = m_defaultPackage;
attributes.insert(QLatin1String("expense-cost"), QLatin1String("1"));
attributes.insert(QLatin1String("expense-limit"), QLatin1String("none"));
- attributes.insert(QLatin1String("polymorphic-base"), QLatin1String("no"));
- attributes.insert(QLatin1String("generate"), QLatin1String("yes"));
+ attributes.insert(QLatin1String("polymorphic-base"), noAttributeValue());
+ attributes.insert(QLatin1String("generate"), yesAttributeValue());
attributes.insert(QLatin1String("target-type"), QString());
- attributes.insert(QLatin1String("generic-class"), QLatin1String("no"));
+ attributes.insert(QLatin1String("generic-class"), noAttributeValue());
break;
case StackElement::FunctionTypeEntry:
attributes.insert(QLatin1String("signature"), QString());
@@ -846,9 +854,10 @@ bool Handler::startElement(const QStringRef &n, const QXmlStreamAttributes &atts
}
if (element->type == StackElement::EnumTypeEntry) {
+ const QString identifiedByValue = attributes.value(enumIdentifiedByValueAttribute());
if (name.isEmpty()) {
- name = attributes[QLatin1String("identified-by-value")];
- } else if (!attributes[QLatin1String("identified-by-value")].isEmpty()) {
+ name = identifiedByValue;
+ } else if (!identifiedByValue.isEmpty()) {
m_error = QLatin1String("can't specify both 'name' and 'identified-by-value' attributes");
return false;
}
@@ -933,7 +942,11 @@ bool Handler::startElement(const QStringRef &n, const QXmlStreamAttributes &atts
m_currentEnum =
new EnumTypeEntry(QStringList(names.mid(0, names.size() - 1)).join(colonColon()),
names.constLast(), since);
- m_currentEnum->setAnonymous(!attributes[QLatin1String("identified-by-value")].isEmpty());
+ if (!attributes.value(enumIdentifiedByValueAttribute()).isEmpty()) {
+ m_currentEnum->setEnumKind(EnumTypeEntry::AnonymousEnum);
+ } else if (convertBoolean(attributes.value(classAttribute()), classAttribute(), false)) {
+ m_currentEnum->setEnumKind(EnumTypeEntry::EnumClass);
+ }
element->entry = m_currentEnum;
m_currentEnum->setCodeGeneration(m_generate);
m_currentEnum->setTargetLangPackage(m_defaultPackage);
@@ -988,7 +1001,7 @@ bool Handler::startElement(const QStringRef &n, const QXmlStreamAttributes &atts
if (!element->entry)
element->entry = new ObjectTypeEntry(name, since);
- element->entry->setStream(attributes[QLatin1String("stream")] == QLatin1String("yes"));
+ element->entry->setStream(attributes[QLatin1String("stream")] == yesAttributeValue());
ComplexTypeEntry *ctype = static_cast<ComplexTypeEntry *>(element->entry);
ctype->setTargetLangPackage(attributes[QLatin1String("package")]);
@@ -1174,7 +1187,7 @@ bool Handler::startElement(const QStringRef &n, const QXmlStreamAttributes &atts
break;
case StackElement::LoadTypesystem:
attributes.insert(nameAttribute(), QString());
- attributes.insert(QLatin1String("generate"), QLatin1String("yes"));
+ attributes.insert(QLatin1String("generate"), yesAttributeValue());
break;
case StackElement::NoNullPointers:
attributes.insert(QLatin1String("default-value"), QString());
@@ -1193,28 +1206,28 @@ bool Handler::startElement(const QStringRef &n, const QXmlStreamAttributes &atts
attributes.insert(QLatin1String("signature"), QString());
attributes.insert(QLatin1String("return-type"), QLatin1String("void"));
attributes.insert(QLatin1String("access"), QLatin1String("public"));
- attributes.insert(QLatin1String("static"), QLatin1String("no"));
+ attributes.insert(QLatin1String("static"), noAttributeValue());
break;
case StackElement::ModifyFunction:
attributes.insert(QLatin1String("signature"), QString());
attributes.insert(QLatin1String("access"), QString());
attributes.insert(QLatin1String("remove"), QString());
attributes.insert(QLatin1String("rename"), QString());
- attributes.insert(QLatin1String("deprecated"), QLatin1String("no"));
+ attributes.insert(QLatin1String("deprecated"), noAttributeValue());
attributes.insert(QLatin1String("associated-to"), QString());
- attributes.insert(QLatin1String("virtual-slot"), QLatin1String("no"));
- attributes.insert(QLatin1String("thread"), QLatin1String("no"));
- attributes.insert(QLatin1String("allow-thread"), QLatin1String("no"));
+ attributes.insert(QLatin1String("virtual-slot"), noAttributeValue());
+ attributes.insert(QLatin1String("thread"), noAttributeValue());
+ attributes.insert(QLatin1String("allow-thread"), noAttributeValue());
break;
case StackElement::ModifyArgument:
attributes.insert(QLatin1String("index"), QString());
attributes.insert(QLatin1String("replace-value"), QString());
- attributes.insert(QLatin1String("invalidate-after-use"), QLatin1String("no"));
+ attributes.insert(QLatin1String("invalidate-after-use"), noAttributeValue());
break;
case StackElement::ModifyField:
attributes.insert(nameAttribute(), QString());
- attributes.insert(QLatin1String("write"), QLatin1String("true"));
- attributes.insert(QLatin1String("read"), QLatin1String("true"));
+ attributes.insert(QLatin1String("write"), trueAttributeValue());
+ attributes.insert(QLatin1String("read"), trueAttributeValue());
attributes.insert(QLatin1String("remove"), QString());
break;
case StackElement::Access:
@@ -1245,7 +1258,7 @@ bool Handler::startElement(const QStringRef &n, const QXmlStreamAttributes &atts
attributes.insert(QLatin1String("file"), QString());
break;
case StackElement::TargetToNative:
- attributes.insert(QLatin1String("replace"), QLatin1String("yes"));
+ attributes.insert(QLatin1String("replace"), yesAttributeValue());
break;
case StackElement::AddConversion:
attributes.insert(QLatin1String("type"), QString());
@@ -1429,7 +1442,7 @@ bool Handler::startElement(const QStringRef &n, const QXmlStreamAttributes &atts
m_error = QLatin1String("Target to Native conversions can only be specified for custom conversion rules.");
return false;
}
- bool replace = attributes[QLatin1String("replace")] == QLatin1String("yes");
+ bool replace = attributes[QLatin1String("replace")] == yesAttributeValue();
static_cast<TypeEntry*>(m_current->entry)->customConversion()->setReplaceOriginalTargetToNativeConversions(replace);
}
break;
@@ -1679,8 +1692,8 @@ bool Handler::startElement(const QStringRef &n, const QXmlStreamAttributes &atts
QString read = attributes[QLatin1String("read")];
QString write = attributes[QLatin1String("write")];
- if (read == QLatin1String("true")) fm.modifiers |= FieldModification::Readable;
- if (write == QLatin1String("true")) fm.modifiers |= FieldModification::Writable;
+ if (read == trueAttributeValue()) fm.modifiers |= FieldModification::Readable;
+ if (write == trueAttributeValue()) fm.modifiers |= FieldModification::Writable;
m_contextStack.top()->fieldMods << fm;
}
@@ -1706,7 +1719,7 @@ bool Handler::startElement(const QStringRef &n, const QXmlStreamAttributes &atts
}
AddedFunction func(signature, attributes[QLatin1String("return-type")], since);
- func.setStatic(attributes[QLatin1String("static")] == QLatin1String("yes"));
+ func.setStatic(attributes[QLatin1String("static")] == yesAttributeValue());
if (!signature.contains(QLatin1Char('(')))
signature += QLatin1String("()");
m_currentSignature = signature;
@@ -2273,19 +2286,6 @@ QString FlagsTypeEntry::targetLangPackage() const
return m_enum->targetLangPackage();
}
-void EnumTypeEntry::addEnumValueRedirection(const QString &rejected, const QString &usedValue)
-{
- m_enumRedirections << EnumValueRedirection(rejected, usedValue);
-}
-
-QString EnumTypeEntry::enumValueRedirection(const QString &value) const
-{
- for (int i = 0; i < m_enumRedirections.size(); ++i)
- if (m_enumRedirections.at(i).rejected == value)
- return m_enumRedirections.at(i).used;
- return QString();
-}
-
QString FlagsTypeEntry::qualifiedTargetLangName() const
{
return targetLangPackage() + QLatin1Char('.') + m_enum->targetLangQualifier()
diff --git a/sources/shiboken2/ApiExtractor/typesystem.h b/sources/shiboken2/ApiExtractor/typesystem.h
index c8a1c88fa..62d348f69 100644
--- a/sources/shiboken2/ApiExtractor/typesystem.h
+++ b/sources/shiboken2/ApiExtractor/typesystem.h
@@ -1042,21 +1042,15 @@ private:
PrimitiveTypeEntry* m_referencedTypeEntry = nullptr;
};
-struct EnumValueRedirection
-{
- EnumValueRedirection() {}
- EnumValueRedirection(const QString &rej, const QString &us)
- : rejected(rej),
- used(us)
- {
- }
- QString rejected;
- QString used;
-};
-
class EnumTypeEntry : public TypeEntry
{
public:
+ enum EnumKind {
+ CEnum, // Standard C: enum Foo { value1, value2 }
+ AnonymousEnum, // enum { value1, value2 }
+ EnumClass // C++ 11 : enum class Foo { value1, value2 }
+ };
+
explicit EnumTypeEntry(const QString &nspace, const QString &enumName, double vr);
QString targetLangPackage() const override;
@@ -1077,6 +1071,9 @@ public:
m_qualifier = q;
}
+ EnumKind enumKind() const { return m_enumKind; }
+ void setEnumKind(EnumKind kind) { m_enumKind = kind; }
+
bool preferredConversion() const override;
bool isBoundsChecked() const
@@ -1120,7 +1117,7 @@ public:
m_extensible = is;
}
- bool isEnumValueRejected(const QString &name)
+ bool isEnumValueRejected(const QString &name) const
{
return m_rejectedEnums.contains(name);
}
@@ -1133,9 +1130,6 @@ public:
return m_rejectedEnums;
}
- void addEnumValueRedirection(const QString &rejected, const QString &usedValue);
- QString enumValueRedirection(const QString &value) const;
-
bool forceInteger() const
{
return m_forceInteger;
@@ -1145,14 +1139,7 @@ public:
m_forceInteger = force;
}
- bool isAnonymous() const
- {
- return m_anonymous;
- }
- void setAnonymous(bool anonymous)
- {
- m_anonymous = anonymous;
- }
+ bool isAnonymous() const { return m_enumKind == AnonymousEnum; }
private:
QString m_packageName;
@@ -1163,15 +1150,17 @@ private:
QString m_upperBound;
QStringList m_rejectedEnums;
- QVector<EnumValueRedirection> m_enumRedirections;
FlagsTypeEntry *m_flags = nullptr;
+ EnumKind m_enumKind = CEnum;
+
bool m_extensible = false;
bool m_forceInteger = false;
- bool m_anonymous = false;
};
+// EnumValueTypeEntry is used for resolving integer type templates
+// like array<EnumValue>.
class EnumValueTypeEntry : public TypeEntry
{
public:
diff --git a/sources/shiboken2/ApiExtractor/typesystem_p.h b/sources/shiboken2/ApiExtractor/typesystem_p.h
index fd67ef49b..d3485726e 100644
--- a/sources/shiboken2/ApiExtractor/typesystem_p.h
+++ b/sources/shiboken2/ApiExtractor/typesystem_p.h
@@ -153,7 +153,6 @@ private:
QHash<QString, QString> *acceptedAttributes);
bool importFileElement(const QXmlStreamAttributes &atts);
- bool convertBoolean(const QString &, const QString &, bool);
void addFlags(const QString &name, QString flagName,
const QHash<QString, QString> &attributes, double since);
diff --git a/sources/shiboken2/CMakeLists.txt b/sources/shiboken2/CMakeLists.txt
index 006924ad9..ccabc72e3 100644
--- a/sources/shiboken2/CMakeLists.txt
+++ b/sources/shiboken2/CMakeLists.txt
@@ -11,12 +11,6 @@ find_package(Qt5 REQUIRED COMPONENTS Core Xml XmlPatterns)
add_definitions(${Qt5Core_DEFINITIONS})
-set(shiboken_MAJOR_VERSION "2")
-set(shiboken_MINOR_VERSION "0")
-set(shiboken_MICRO_VERSION "0")
-set(shiboken2_VERSION "${shiboken_MAJOR_VERSION}.${shiboken_MINOR_VERSION}.${shiboken_MICRO_VERSION}")
-set(shiboken2_library_so_version "${shiboken_MAJOR_VERSION}.${shiboken_MINOR_VERSION}")
-
option(BUILD_TESTS "Build tests." TRUE)
option(USE_PYTHON_VERSION "Use specific python version to build shiboken2." "")
@@ -126,6 +120,31 @@ message(STATUS "CLANG builtins includes directory chosen: ${CLANG_BUILTIN_INCLUD
set(CLANG_EXTRA_INCLUDES ${CLANG_DIR}/include)
set(CLANG_EXTRA_LIBRARIES ${CLANG_LIBRARY})
+set(SHIBOKEN_VERSION_FILE_PATH "${CMAKE_SOURCE_DIR}/shiboken_version.py")
+set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS
+ ${SHIBOKEN_VERSION_FILE_PATH}
+)
+execute_process(
+ COMMAND ${PYTHON_EXECUTABLE} "${SHIBOKEN_VERSION_FILE_PATH}"
+ OUTPUT_VARIABLE SHIBOKEN_VERSION_OUTPUT
+ ERROR_VARIABLE SHIBOKEN_VERSION_OUTPUT_ERROR
+ OUTPUT_STRIP_TRAILING_WHITESPACE)
+if (NOT SHIBOKEN_VERSION_OUTPUT)
+ message(FATAL_ERROR "Could not identify shiboken version. \
+ Error: ${SHIBOKEN_VERSION_OUTPUT_ERROR}")
+endif()
+
+list(GET SHIBOKEN_VERSION_OUTPUT 0 shiboken_MAJOR_VERSION)
+list(GET SHIBOKEN_VERSION_OUTPUT 1 shiboken_MINOR_VERSION)
+list(GET SHIBOKEN_VERSION_OUTPUT 2 shiboken_MICRO_VERSION)
+# a - alpha, b - beta, rc - rc
+list(GET SHIBOKEN_VERSION_OUTPUT 3 shiboken_PRE_RELEASE_VERSION_TYPE)
+# the number of the pre release (alpha1, beta3, rc7, etc.)
+list(GET SHIBOKEN_VERSION_OUTPUT 4 shiboken_PRE_RELEASE_VERSION)
+
+set(shiboken2_VERSION "${shiboken_MAJOR_VERSION}.${shiboken_MINOR_VERSION}.${shiboken_MICRO_VERSION}")
+set(shiboken2_library_so_version "${shiboken_MAJOR_VERSION}.${shiboken_MINOR_VERSION}")
+
## For debugging the PYTHON* variables
message("PYTHONLIBS_FOUND: " ${PYTHONLIBS_FOUND})
message("PYTHON_LIBRARIES: " ${PYTHON_LIBRARIES})
@@ -247,6 +266,22 @@ if(CMAKE_HOST_APPLE)
endif()
endif()
+# Build with Address sanitizer enabled if requested. This may break things, so use at your own risk.
+if (SANITIZE_ADDRESS AND NOT MSVC)
+ # Currently this does not check that the clang / gcc version used supports Address sanitizer,
+ # so once again, use at your own risk.
+ add_compile_options("-fsanitize=address" "-g" "-fno-omit-frame-pointer")
+ # We need to add the sanitize address option to all linked executables / shared libraries
+ # so that proper sanitizer symbols are linked in.
+ #
+ # Note that when running tests, you may need to set an additional environment variable
+ # in set_tests_properties for shiboken2 / pyside tests, or exported in your shell. Address
+ # sanitizer will tell you what environment variable needs to be exported. For example:
+ # export DYLD_INSERT_LIBRARIES=/Applications/Xcode.app/Contents/Developer/Toolchains/
+ # ./XcodeDefault.xctoolchain/usr/lib/clang/8.1.0/lib/darwin/libclang_rt.asan_osx_dynamic.dylib
+ set(CMAKE_CXX_STANDARD_LIBRARIES "${CMAKE_STANDARD_LIBRARIES} -fsanitize=address")
+endif()
+
add_subdirectory(ApiExtractor)
set(generator_plugin_DIR ${LIB_INSTALL_DIR}/generatorrunner${generator_SUFFIX})
@@ -366,16 +401,3 @@ else()
endif()
add_subdirectory(data)
-
-# dist target
-set(ARCHIVE_NAME ${CMAKE_PROJECT_NAME}-${shiboken2_VERSION})
-add_custom_target(dist
- COMMAND mkdir -p "${CMAKE_BINARY_DIR}/${ARCHIVE_NAME}" &&
- git log > "${CMAKE_BINARY_DIR}/${ARCHIVE_NAME}/ChangeLog" &&
- git archive --prefix=${ARCHIVE_NAME}/ HEAD --format=tar --output="${CMAKE_BINARY_DIR}/${ARCHIVE_NAME}.tar" &&
- tar -C "${CMAKE_BINARY_DIR}" --owner=root --group=root -r "${ARCHIVE_NAME}/ChangeLog" -f "${CMAKE_BINARY_DIR}/${ARCHIVE_NAME}.tar" &&
- bzip2 -f9 "${CMAKE_BINARY_DIR}/${ARCHIVE_NAME}.tar" &&
- echo "Source package created at ${CMAKE_BINARY_DIR}/${ARCHIVE_NAME}.tar.bz2."
- WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
-
-
diff --git a/sources/shiboken2/generator/qtdoc/qtdocgenerator.cpp b/sources/shiboken2/generator/qtdoc/qtdocgenerator.cpp
index fb4de46d2..7cce97ae1 100644
--- a/sources/shiboken2/generator/qtdoc/qtdocgenerator.cpp
+++ b/sources/shiboken2/generator/qtdoc/qtdocgenerator.cpp
@@ -111,9 +111,14 @@ static int writeEscapedRstText(QTextStream &str, const String &s)
{
int escaped = 0;
for (const QChar &c : s) {
- if (c == QLatin1Char('*') || c == QLatin1Char('_')) {
+ switch (c.unicode()) {
+ case '*':
+ case '`':
+ case '_':
+ case '\\':
str << '\\';
++escaped;
+ break;
}
str << c;
}
@@ -199,7 +204,7 @@ QtXmlToSphinx::QtXmlToSphinx(QtDocGenerator* generator, const QString& doc, cons
m_handlerMap.insert(QLatin1String("argument"), &QtXmlToSphinx::handleArgumentTag);
m_handlerMap.insert(QLatin1String("teletype"), &QtXmlToSphinx::handleArgumentTag);
m_handlerMap.insert(QLatin1String("link"), &QtXmlToSphinx::handleLinkTag);
- m_handlerMap.insert(QLatin1String("inlineimage"), &QtXmlToSphinx::handleImageTag);
+ m_handlerMap.insert(QLatin1String("inlineimage"), &QtXmlToSphinx::handleInlineImageTag);
m_handlerMap.insert(QLatin1String("image"), &QtXmlToSphinx::handleImageTag);
m_handlerMap.insert(QLatin1String("list"), &QtXmlToSphinx::handleListTag);
m_handlerMap.insert(QLatin1String("term"), &QtXmlToSphinx::handleTermTag);
@@ -365,6 +370,16 @@ QString QtXmlToSphinx::transform(const QString& doc)
m_lastTagName = reader.name().toString();
}
}
+
+ if (!m_inlineImages.isEmpty()) {
+ // Write out inline image definitions stored in handleInlineImageTag().
+ m_output << endl;
+ for (const InlineImage &img : qAsConst(m_inlineImages))
+ m_output << ".. |" << img.tag << "| image:: " << img.href << endl;
+ m_output << endl;
+ m_inlineImages.clear();
+ }
+
m_output.flush();
QString retval = popOutputBuffer();
Q_ASSERT(m_buffers.isEmpty());
@@ -936,22 +951,46 @@ static bool copyImage(const QString &href, const QString &docDataDir,
return true;
}
+bool QtXmlToSphinx::copyImage(const QString &href) const
+{
+ QString errorMessage;
+ const bool result =
+ ::copyImage(href, m_generator->docDataDir(), m_context,
+ m_generator->outputDirectory(), &errorMessage);
+ if (!result)
+ qCWarning(lcShiboken, "%s", qPrintable(errorMessage));
+ return result;
+}
+
void QtXmlToSphinx::handleImageTag(QXmlStreamReader& reader)
{
- QXmlStreamReader::TokenType token = reader.tokenType();
- if (token == QXmlStreamReader::StartElement) {
- QString href = reader.attributes().value(QLatin1String("href")).toString();
- QString errorMessage;
- if (!copyImage(href,m_generator->docDataDir(), m_context,
- m_generator->outputDirectory(), &errorMessage)) {
- qCWarning(lcShiboken, "%s", qPrintable(errorMessage));
- }
+ if (reader.tokenType() != QXmlStreamReader::StartElement)
+ return;
+ const QString href = reader.attributes().value(QLatin1String("href")).toString();
+ if (copyImage(href))
+ m_output << INDENT << ".. image:: " << href << endl << endl;
+}
- if (reader.name() == QLatin1String("image"))
- m_output << INDENT << ".. image:: " << href << endl << endl;
- else
- m_output << ".. image:: " << href << ' ';
- }
+void QtXmlToSphinx::handleInlineImageTag(QXmlStreamReader& reader)
+{
+ if (reader.tokenType() != QXmlStreamReader::StartElement)
+ return;
+ const QString href = reader.attributes().value(QLatin1String("href")).toString();
+ if (!copyImage(href))
+ return;
+ // Handle inline images by substitution references. Insert a unique tag
+ // enclosed by '|' and define it further down. Determine tag from the base
+ //file name with number.
+ QString tag = href;
+ int pos = tag.lastIndexOf(QLatin1Char('/'));
+ if (pos != -1)
+ tag.remove(0, pos + 1);
+ pos = tag.indexOf(QLatin1Char('.'));
+ if (pos != -1)
+ tag.truncate(pos);
+ tag += QString::number(m_inlineImages.size() + 1);
+ m_inlineImages.append(InlineImage{tag, href});
+ m_output << '|' << tag << '|' << ' ';
}
void QtXmlToSphinx::handleRawTag(QXmlStreamReader& reader)
@@ -1323,7 +1362,7 @@ void QtDocGenerator::generateClass(QTextStream &s, GeneratorContext &classContex
s << className << endl;
s << Pad('*', className.count()) << endl << endl;
- s << ".. inheritance-diagram:: " << className << endl
+ s << ".. inheritance-diagram:: " << getClassTargetFullName(metaClass, true) << endl
<< " :parts: 2" << endl << endl; // TODO: This would be a parameter in the future...
diff --git a/sources/shiboken2/generator/qtdoc/qtdocgenerator.h b/sources/shiboken2/generator/qtdoc/qtdocgenerator.h
index 1977f3019..af26b7fab 100644
--- a/sources/shiboken2/generator/qtdoc/qtdocgenerator.h
+++ b/sources/shiboken2/generator/qtdoc/qtdocgenerator.h
@@ -49,6 +49,12 @@ class QtDocGenerator;
class QtXmlToSphinx
{
public:
+ struct InlineImage
+ {
+ QString tag;
+ QString href;
+ };
+
struct TableCell
{
short rowSpan;
@@ -127,6 +133,7 @@ private:
void handleDotsTag(QXmlStreamReader& reader);
void handleLinkTag(QXmlStreamReader& reader);
void handleImageTag(QXmlStreamReader& reader);
+ void handleInlineImageTag(QXmlStreamReader& reader);
void handleListTag(QXmlStreamReader& reader);
void handleTermTag(QXmlStreamReader& reader);
void handleSuperScriptTag(QXmlStreamReader& reader);
@@ -168,6 +175,7 @@ private:
bool m_insideItalic;
QString m_lastTagName;
QString m_opened_anchor;
+ QVector<InlineImage> m_inlineImages;
QString readFromLocations(const QStringList &locations, const QString &path,
const QString &identifier, QString *errorMessage);
@@ -176,6 +184,7 @@ private:
void pushOutputBuffer();
QString popOutputBuffer();
void writeTable(Table& table);
+ bool copyImage(const QString &href) const;
};
inline QTextStream& operator<<(QTextStream& s, const QtXmlToSphinx& xmlToSphinx)
diff --git a/sources/shiboken2/generator/shiboken2/cppgenerator.cpp b/sources/shiboken2/generator/shiboken2/cppgenerator.cpp
index 7a688d1da..be42adb0f 100644
--- a/sources/shiboken2/generator/shiboken2/cppgenerator.cpp
+++ b/sources/shiboken2/generator/shiboken2/cppgenerator.cpp
@@ -4471,6 +4471,7 @@ void CppGenerator::writeEnumInitialization(QTextStream& s, const AbstractMetaEnu
const AbstractMetaClass* enclosingClass = getProperEnclosingClassForEnum(cppEnum);
const AbstractMetaClass* upper = enclosingClass ? enclosingClass->enclosingClass() : 0;
bool hasUpperEnclosingClass = upper && upper->typeEntry()->codeGeneration() != TypeEntry::GenerateForSubclass;
+ const EnumTypeEntry *enumTypeEntry = cppEnum->typeEntry();
QString enclosingObjectVariable;
if (enclosingClass)
enclosingObjectVariable = QLatin1Char('&') + cpythonTypeName(enclosingClass);
@@ -4483,14 +4484,17 @@ void CppGenerator::writeEnumInitialization(QTextStream& s, const AbstractMetaEnu
s << (cppEnum->isAnonymous() ? "anonymous enum identified by enum value" : "enum");
s << " '" << cppEnum->name() << "'." << endl;
+ QString enumVarTypeObj;
if (!cppEnum->isAnonymous()) {
- FlagsTypeEntry* flags = cppEnum->typeEntry()->flags();
+ FlagsTypeEntry* flags = enumTypeEntry->flags();
if (flags) {
s << INDENT << cpythonTypeNameExt(flags) << " = PySide::QFlags::create(\"" << flags->flagsName() << "\", &"
<< cpythonEnumName(cppEnum) << "_as_number);" << endl;
}
- s << INDENT << cpythonTypeNameExt(cppEnum->typeEntry()) << " = Shiboken::Enum::";
+ enumVarTypeObj = cpythonTypeNameExt(enumTypeEntry);
+
+ s << INDENT << enumVarTypeObj << " = Shiboken::Enum::";
s << ((enclosingClass || hasUpperEnclosingClass) ? "createScopedEnum" : "createGlobalEnum");
s << '(' << enclosingObjectVariable << ',' << endl;
{
@@ -4512,7 +4516,7 @@ void CppGenerator::writeEnumInitialization(QTextStream& s, const AbstractMetaEnu
const AbstractMetaEnumValueList &enumValues = cppEnum->values();
for (const AbstractMetaEnumValue *enumValue : enumValues) {
- if (cppEnum->typeEntry()->isEnumValueRejected(enumValue->name()))
+ if (enumTypeEntry->isEnumValueRejected(enumValue->name()))
continue;
QString enumValueText;
@@ -4528,7 +4532,8 @@ void CppGenerator::writeEnumInitialization(QTextStream& s, const AbstractMetaEnu
enumValueText += QString::number(enumValue->value());
}
- if (cppEnum->isAnonymous()) {
+ switch (enumTypeEntry->enumKind()) {
+ case EnumTypeEntry::AnonymousEnum:
if (enclosingClass || hasUpperEnclosingClass) {
s << INDENT << '{' << endl;
{
@@ -4551,15 +4556,27 @@ void CppGenerator::writeEnumInitialization(QTextStream& s, const AbstractMetaEnu
s << INDENT << "return " << m_currentErrorCode << ';' << endl;
}
}
- } else {
+ break;
+ case EnumTypeEntry::CEnum: {
s << INDENT << "if (!Shiboken::Enum::";
s << ((enclosingClass || hasUpperEnclosingClass) ? "createScopedEnumItem" : "createGlobalEnumItem");
- s << '(' << cpythonTypeNameExt(cppEnum->typeEntry()) << ',' << endl;
+ s << '(' << enumVarTypeObj << ',' << endl;
Indentation indent(INDENT);
s << INDENT << enclosingObjectVariable << ", \"" << enumValue->name() << "\", ";
s << enumValueText << "))" << endl;
s << INDENT << "return " << m_currentErrorCode << ';' << endl;
}
+ break;
+ case EnumTypeEntry::EnumClass: {
+ s << INDENT << "if (!Shiboken::Enum::createScopedEnumItem("
+ << enumVarTypeObj << ',' << endl;
+ Indentation indent(INDENT);
+ s << INDENT << enumVarTypeObj<< ", \"" << enumValue->name() << "\", "
+ << enumValueText << "))" << endl
+ << INDENT << "return " << m_currentErrorCode << ';' << endl;
+ }
+ break;
+ }
}
writeEnumConverterInitialization(s, cppEnum);
diff --git a/sources/shiboken2/libshiboken/sbkenum.cpp b/sources/shiboken2/libshiboken/sbkenum.cpp
index a62448aa6..c817a21de 100644
--- a/sources/shiboken2/libshiboken/sbkenum.cpp
+++ b/sources/shiboken2/libshiboken/sbkenum.cpp
@@ -492,11 +492,11 @@ bool createGlobalEnumItem(PyTypeObject* enumType, PyObject* module, const char*
return false;
}
-bool createScopedEnumItem(PyTypeObject* enumType, SbkObjectType* scope, const char* itemName, long itemValue)
+bool createScopedEnumItem(PyTypeObject *enumType, PyTypeObject *scope,
+ const char *itemName, long itemValue)
{
- PyObject* enumItem = createEnumItem(enumType, itemName, itemValue);
- if (enumItem) {
- if (PyDict_SetItemString(scope->super.ht_type.tp_dict, itemName, enumItem) < 0)
+ if (PyObject *enumItem = createEnumItem(enumType, itemName, itemValue)) {
+ if (PyDict_SetItemString(scope->tp_dict, itemName, enumItem) < 0)
return false;
Py_DECREF(enumItem);
return true;
@@ -504,6 +504,11 @@ bool createScopedEnumItem(PyTypeObject* enumType, SbkObjectType* scope, const ch
return false;
}
+bool createScopedEnumItem(PyTypeObject* enumType, SbkObjectType* scope, const char* itemName, long itemValue)
+{
+ return createScopedEnumItem(enumType, &scope->super.ht_type, itemName, itemValue);
+}
+
PyObject* newItem(PyTypeObject* enumType, long itemValue, const char* itemName)
{
bool newValue = true;
diff --git a/sources/shiboken2/libshiboken/sbkenum.h b/sources/shiboken2/libshiboken/sbkenum.h
index 4b572dbcc..b01114ba6 100644
--- a/sources/shiboken2/libshiboken/sbkenum.h
+++ b/sources/shiboken2/libshiboken/sbkenum.h
@@ -95,6 +95,8 @@ namespace Enum
*/
LIBSHIBOKEN_API bool createGlobalEnumItem(PyTypeObject* enumType, PyObject* module, const char* itemName, long itemValue);
/// This function does the same as createGlobalEnumItem, but adds the enum to a Shiboken type or namespace.
+ LIBSHIBOKEN_API bool createScopedEnumItem(PyTypeObject *enumType, PyTypeObject *scope,
+ const char *itemName, long itemValue);
LIBSHIBOKEN_API bool createScopedEnumItem(PyTypeObject* enumType, SbkObjectType* scope, const char* itemName, long itemValue);
LIBSHIBOKEN_API PyObject* newItem(PyTypeObject* enumType, long itemValue, const char* itemName = 0);
diff --git a/sources/shiboken2/shiboken_version.py b/sources/shiboken2/shiboken_version.py
new file mode 100644
index 000000000..3a678cd14
--- /dev/null
+++ b/sources/shiboken2/shiboken_version.py
@@ -0,0 +1,49 @@
+#############################################################################
+##
+## Copyright (C) 2018 The Qt Company Ltd.
+## Contact: https://www.qt.io/licensing/
+##
+## This file is part of PySide2.
+##
+## $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$
+##
+#############################################################################
+
+major_version = "5"
+minor_version = "11"
+patch_version = "0"
+pre_release_version_type = "a" # e.g. "a", "b", "rc".
+pre_release_version = "1" # e.g "1", "2", (which means "beta1", "beta2", if type is "b")
+
+if __name__ == '__main__':
+ # Used by CMake.
+ print('{0};{1};{2};{3};{4}'.format(major_version, minor_version, patch_version,
+ pre_release_version_type, pre_release_version))
diff --git a/sources/shiboken2/tests/libsample/objecttype.h b/sources/shiboken2/tests/libsample/objecttype.h
index 752659488..91fb45515 100644
--- a/sources/shiboken2/tests/libsample/objecttype.h
+++ b/sources/shiboken2/tests/libsample/objecttype.h
@@ -45,6 +45,12 @@ struct Event
SOME_EVENT,
ANY_EVENT
};
+
+ enum class EventTypeClass {
+ Value1,
+ Value2
+ };
+
Event(EventType eventType) : m_eventType(eventType) {}
EventType eventType() { return m_eventType; }
private:
diff --git a/sources/shiboken2/tests/samplebinding/enum_test.py b/sources/shiboken2/tests/samplebinding/enum_test.py
index 6468d3cc4..711215c35 100644
--- a/sources/shiboken2/tests/samplebinding/enum_test.py
+++ b/sources/shiboken2/tests/samplebinding/enum_test.py
@@ -110,6 +110,11 @@ class EnumTest(unittest.TestCase):
self.assertEqual(SampleNamespace.AnonymousClassEnum_Value0, 0)
self.assertEqual(SampleNamespace.AnonymousClassEnum_Value1, 1)
+ def testEnumClasses(self):
+ # C++ 11: values of enum classes need to be fully qualified to match C++
+ sum = Event.EventTypeClass.Value1 + Event.EventTypeClass.Value2
+ self.assertEqual(sum, 1)
+
def testEnumTpPrintImplementation(self):
'''Without SbkEnum.tp_print 'print' returns the enum represented as an int.'''
tmpfile = createTempFile()
diff --git a/sources/shiboken2/tests/samplebinding/typesystem_sample.xml b/sources/shiboken2/tests/samplebinding/typesystem_sample.xml
index ffb5c976f..5a12eeccd 100644
--- a/sources/shiboken2/tests/samplebinding/typesystem_sample.xml
+++ b/sources/shiboken2/tests/samplebinding/typesystem_sample.xml
@@ -798,6 +798,7 @@
<value-type name="Event">
<enum-type name="EventType"/>
+ <enum-type name="EventTypeClass" class="yes"/>
</value-type>
<value-type name="BlackBox">
diff --git a/utils.py b/utils.py
index d82614aad..d1bf2d6b9 100644
--- a/utils.py
+++ b/utils.py
@@ -323,7 +323,7 @@ def makefile(dst, content=None, vars=None):
def copydir(src, dst, filter=None, ignore=None, force=True, recursive=True, vars=None,
- dir_filter_function=None, force_copy_symlinks=False):
+ dir_filter_function=None, file_filter_function=None, force_copy_symlinks=False):
if vars is not None:
src = src.format(**vars)
@@ -357,10 +357,12 @@ def copydir(src, dst, filter=None, ignore=None, force=True, recursive=True, vars
if recursive:
results.extend(
copydir(srcname, dstname, filter, ignore, force, recursive,
- vars, dir_filter_function, force_copy_symlinks))
+ vars, dir_filter_function, file_filter_function,
+ force_copy_symlinks))
else:
- if (filter is not None and not filter_match(name, filter)) or \
- (ignore is not None and filter_match(name, ignore)):
+ if (file_filter_function is not None and not file_filter_function(name, srcname)) \
+ or (filter is not None and not filter_match(name, filter)) \
+ or (ignore is not None and filter_match(name, ignore)):
continue
if not os.path.exists(dst):
os.makedirs(dst)
@@ -983,3 +985,28 @@ def rpathsHasOrigin(rpaths):
if match:
return True
return False
+
+def memoize(function):
+ """ Decorator to wrap a function with a memoizing callable.
+ It returns cached values when the wrapped function is called with the same arguments.
+ """
+ memo = {}
+ def wrapper(*args):
+ if args in memo:
+ return memo[args]
+ else:
+ rv = function(*args)
+ memo[args] = rv
+ return rv
+ return wrapper
+
+def get_python_dict(python_script_path):
+ try:
+ with open(python_script_path) as f:
+ python_dict = {}
+ code = compile(f.read(), python_script_path, 'exec')
+ exec(code, {}, python_dict)
+ return python_dict
+ except IOError as e:
+ print("get_python_dict: Couldn't get dict from python file: {}.".format(python_script_path))
+ raise