diff options
author | Friedemann Kleint <Friedemann.Kleint@qt.io> | 2019-09-05 08:13:27 +0200 |
---|---|---|
committer | Friedemann Kleint <Friedemann.Kleint@qt.io> | 2019-09-05 23:21:35 +0200 |
commit | aeacefcdccdc3a0cb5e9f405fb3af3276a987c08 (patch) | |
tree | 11d71afb96518f3f108ed8f45afaa321862d2301 | |
parent | 3dcef048966bf0783cec66bf7e63307a1182cadf (diff) | |
parent | 45bdefeb1e2be7d005dfdef5c7f85c3b98ca6123 (diff) |
Merge "Merge remote-tracking branch 'origin/5.13' into 5.14"
32 files changed, 1667 insertions, 778 deletions
diff --git a/README.pyside2.md b/README.pyside2.md index f1bd8aaa4..94cdd16e3 100644 --- a/README.pyside2.md +++ b/README.pyside2.md @@ -69,3 +69,9 @@ our [guidelines](https://wiki.qt.io/Qt_for_Python/Reporting_Bugs). Check *#qt-pyside*, our official IRC channel on FreeNode, or contact us via our [mailing list](http://lists.qt-project.org/mailman/listinfo/pyside). + +### Licensing + +PySide2 is available under both Open Source (LGPLv3/GPLv2) and commercial license. +Using PyPi is the recommended installation source, because the content of the wheels is valid for both cases. +For more information, refer to the [Qt Licensing page](https://www.qt.io/licensing/). diff --git a/build_scripts/config.py b/build_scripts/config.py index cad4e2f57..25036a65f 100644 --- a/build_scripts/config.py +++ b/build_scripts/config.py @@ -174,6 +174,7 @@ class Config(object): 'Environment :: Win32 (MS Windows)', 'Intended Audience :: Developers', 'License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)', + 'License :: Other/Proprietary License', 'Operating System :: MacOS :: MacOS X', 'Operating System :: POSIX', 'Operating System :: POSIX :: Linux', diff --git a/build_scripts/main.py b/build_scripts/main.py index 4d9f95b14..05e9b0f72 100644 --- a/build_scripts/main.py +++ b/build_scripts/main.py @@ -1056,7 +1056,7 @@ class PysideBuild(_build): OPTION_MACOS_SYSROOT)) else: latest_sdk_path = run_process_output(['xcrun', - '--show-sdk-path']) + '--sdk', 'macosx', '--show-sdk-path']) if latest_sdk_path: latest_sdk_path = latest_sdk_path[0] cmake_cmd.append("-DCMAKE_OSX_SYSROOT={}".format( diff --git a/dist/changes-5.13.1 b/dist/changes-5.13.1 new file mode 100644 index 000000000..9b9939092 --- /dev/null +++ b/dist/changes-5.13.1 @@ -0,0 +1,47 @@ + +Qt for Python 5.13.1 is a bug-fix release. + +For more details, refer to the online documentation included in this +distribution. The documentation is also available online: + +https://doc.qt.io/qtforpython/ + +Some of the changes listed in this file include issue tracking numbers +corresponding to tasks in the Qt Bug Tracker: + +https://bugreports.qt.io/ + +Each of these identifiers can be entered in the bug tracker to obtain more +information about a particular change. + +**************************************************************************** +* PySide2 * +**************************************************************************** + + - [Fixes https://github.com/spyder-ide/qtpy/issues/195] Fix RuntimeError: dictionary changed size during iteration + - [PYSIDE-634] Add support for parameterNames in Signals + - [PYSIDE-951] Cleanup signature module before substantial change + - [PYSIDE-951] correct QtPrintSupport dependency + - [PYSIDE-1010] Add optional support for types in QSettings::value + - [PYSIDE-1020] Fix pyside2-uic to generate correct code for QWebview and QQuickWidget + - [PYSIDE-1028] Leave QVariantMap as a name, not a type + - [PYSIDE-1033] CMake modularization: macros creation + - [PYSIDE-1035] Fix pyside2-uic to generate correct code for QTableWidget + - [PYSIDE-1051] Fix heaptype conflict with QtCore.QObject.__new__in Python 2.7 + - [PYSIDE-1052] Add QtCore.Slot.__signature__ and much more manually + - [PYSIDE-1059] Documentation: update QInputDialog snippets + - [PYSIDE-1066] Fix Xcode sdk value embedded into PySide2 binaries + - [PYSIDE-1067] Update docs style + - [PYSIDE-1073] Fix a typing bug in Python 2.7 and update + - [PYSIDE-1077] Fix wrong Python init return codes + - [PYSIDE-1079] signature: Support typing.Optional[T] and refine a bit + + +**************************************************************************** +* Shiboken2 * +**************************************************************************** + + - [PYSIDE-1024] shiboken: Generate code for smart pointers only within declaring package + - [PYSIDE-1037] shiboken: Allow for "auto" as target of type for CONVERTTOCPP in injected code + + diff --git a/sources/cmake_helpers/helpers.cmake b/sources/cmake_helpers/helpers.cmake index e64b8d9d3..81b52920c 100644 --- a/sources/cmake_helpers/helpers.cmake +++ b/sources/cmake_helpers/helpers.cmake @@ -1,3 +1,160 @@ +macro(collect_essential_modules) +# Collect all essential modules. +# note: the order of this list is relevant for dependencies. +# For instance: Qt5Printsupport must come before Qt5WebKitWidgets. +set(ALL_ESSENTIAL_MODULES Core Gui Widgets PrintSupport Sql Network Test Concurrent) +if(UNIX AND NOT APPLE) + list(APPEND ALL_ESSENTIAL_MODULES X11Extras) +endif() +if(WIN32) + list(APPEND ALL_ESSENTIAL_MODULES WinExtras) +endif() +if(APPLE) + list(APPEND ALL_ESSENTIAL_MODULES MacExtras) +endif() +endmacro() + +macro(collect_optional_modules) +# Collect all optional modules. +set(ALL_OPTIONAL_MODULES Xml XmlPatterns Help Multimedia +MultimediaWidgets OpenGL OpenGLFunctions Positioning Location Qml Quick QuickWidgets RemoteObjects Scxml Script ScriptTools Sensors TextToSpeech Charts Svg DataVisualization) +find_package(Qt5UiTools) +if(Qt5UiTools_FOUND) + list(APPEND ALL_OPTIONAL_MODULES UiTools) +else() + set(DISABLE_QtUiTools 1) +endif() +if(WIN32) + list(APPEND ALL_OPTIONAL_MODULES AxContainer) +endif() +# Disabling WebKit by default +# If WebKit support is needed add the following elements +# to the list: WebKit WebKitWidgets +list(APPEND ALL_OPTIONAL_MODULES WebChannel WebEngineCore WebEngine WebEngineWidgets WebSockets) +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() +endmacro() + +macro(check_os) +set(ENABLE_X11 "0") +set(ENABLE_MAC "0") +set(ENABLE_WIN "0") +set(ENABLE_SIMULATOR "0") + +if(CMAKE_HOST_APPLE) + set(ENABLE_MAC "1") + set(AUTO_OS "mac") +elseif(CMAKE_HOST_WIN32) + set(ENABLE_WIN "1") + set(AUTO_OS "win") +elseif(CMAKE_HOST_UNIX) + set(ENABLE_X11 "1") + set(AUTO_OS "x11") +else() + message(FATAL_ERROR "OS not supported") +endif() +endmacro() + +macro(use_protected_as_public_hack) +# 2017-04-24 The protected hack can unfortunately not be disabled, because +# Clang does produce linker errors when we disable the hack. +# But the ugly workaround in Python is replaced by a shiboken change. +if(WIN32 OR DEFINED AVOID_PROTECTED_HACK) + message(STATUS "PySide2 will be generated avoiding the protected hack!") + set(GENERATOR_EXTRA_FLAGS ${GENERATOR_EXTRA_FLAGS} --avoid-protected-hack) + add_definitions(-DAVOID_PROTECTED_HACK) +else() + message(STATUS "PySide will be generated using the protected hack!") +endif() +endmacro() + +macro(remove_skipped_modules) +# Removing from the MODULES list the items that were defined with +# -DSKIP_MODULES on command line +if (SKIP_MODULES) + foreach(s ${SKIP_MODULES}) + list(REMOVE_ITEM MODULES ${s}) + endforeach() +endif() + +foreach(m ${MODULES}) + COLLECT_MODULE_IF_FOUND(${m}) + list(FIND all_module_shortnames ${m} is_module_collected) + # If the module was collected, remove it from disabled modules list. + if (NOT is_module_collected EQUAL -1) + list(REMOVE_ITEM DISABLED_MODULES ${m}) + endif() +endforeach() +endmacro() + +macro(COLLECT_MODULE_IF_FOUND shortname) + set(name "Qt5${shortname}") + set(_qt_module_name "${name}") + if ("${shortname}" STREQUAL "OpenGLFunctions") + set(_qt_module_name "Qt5Gui") + endif() + # Determine essential/optional/missing + set(module_state "missing") + list(FIND ALL_ESSENTIAL_MODULES "${shortname}" essentialIndex) + if(${essentialIndex} EQUAL -1) + list(FIND ALL_OPTIONAL_MODULES "${shortname}" optionalIndex) + if(NOT ${optionalIndex} EQUAL -1) + set(module_state "optional") + endif() + else() + set(module_state "essential") + endif() + + # Silence warnings when optional packages are not found when doing a quiet build. + set(quiet_argument "") + if (QUIET_BUILD AND "${module_state}" STREQUAL "optional") + set(quiet_argument "QUIET") + endif() + + find_package(${_qt_module_name} ${quiet_argument}) + # If package is found, _name_found will be equal to 1 + set(_name_found "${_qt_module_name}_FOUND") + # _name_dir will keep the path to the directory where the CMake rules were found + # e.g: ~/qt5.9-install/qtbase/lib/cmake/Qt5Core or /usr/lib64/cmake/Qt5Core + set(_name_dir "${_qt_module_name}_DIR") + # Qt5Core will set the base path to check if all the modules are on the same + # directory, to avoid CMake looking in another path. + # This will be saved in a global variable at the beginning of the modules + # collection process. + string(FIND "${name}" "Qt5Core" qtcore_found) + if(("${qtcore_found}" GREATER "0") OR ("${qtcore_found}" EQUAL "0")) + get_filename_component(_core_abs_dir "${${_name_dir}}/../" ABSOLUTE) + # Setting the absolute path where the Qt5Core was found + # e.g: ~/qt5.9-install/qtbase/lib/cmake or /usr/lib64/cmake + message(STATUS "CORE_ABS_DIR:" ${_core_abs_dir}) + endif() + + # Getting the absolute path for each module where the CMake was found, to + # compare it with CORE_ABS_DIR and check if they are in the same source directory + # e.g: ~/qt5.9-install/qtbase/lib/cmake/Qt5Script or /usr/lib64/cmake/Qt5Script + get_filename_component(_module_dir "${${_name_dir}}" ABSOLUTE) + string(FIND "${_module_dir}" "${_core_abs_dir}" found_basepath) + + # If the module was found, and also the module path is the same as the + # Qt5Core base path, we will generate the list with the modules to be installed + set(looked_in_message ". Looked in: ${${_name_dir}}") + if("${${_name_found}}" AND (("${found_basepath}" GREATER "0") OR ("${found_basepath}" EQUAL "0"))) + message(STATUS "${module_state} module ${name} found (${ARGN})${looked_in_message}") + # record the shortnames for the tests + list(APPEND all_module_shortnames ${shortname}) + else() + if("${module_state}" STREQUAL "optional") + message(STATUS "optional module ${name} skipped${looked_in_message}") + elseif("${module_state}" STREQUAL "essential") + message(STATUS "skipped module ${name} is essential!\n" + " We do not guarantee that all tests are working.${looked_in_message}") + else() + message(FATAL_ERROR "module ${name} MISSING${looked_in_message}") + endif() + endif() +endmacro() + macro(compute_config_py_values full_version_var_name ) diff --git a/sources/pyside2/CMakeLists.txt b/sources/pyside2/CMakeLists.txt index c5dbc623c..16038594a 100644 --- a/sources/pyside2/CMakeLists.txt +++ b/sources/pyside2/CMakeLists.txt @@ -9,8 +9,10 @@ cmake_policy(SET CMP0046 NEW) project(pysidebindings) set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/../cmake_helpers/ + ${CMAKE_CURRENT_SOURCE_DIR}/../shiboken2/data/ ${CMAKE_CURRENT_SOURCE_DIR}/cmake/Macros/ ${CMAKE_MODULE_PATH}) +include(shiboken_helpers) include(helpers) # Don't display "up-to-date / install" messages when installing, to reduce visual clutter. @@ -128,31 +130,8 @@ if (QtCore_is_framework) get_filename_component(QT_INCLUDE_DIR "${QT_INCLUDE_DIR}/../../include" ABSOLUTE) endif() -if(MSVC) - # Qt5: this flag has changed from /Zc:wchar_t- in Qt4.X - set(CMAKE_CXX_FLAGS "/Zc:wchar_t /GR /EHsc /DNOCOLOR /DWIN32 /D_WINDOWS /D_SCL_SECURE_NO_WARNINGS") # XXX -else() - if(CMAKE_HOST_UNIX AND NOT CYGWIN) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -fvisibility=hidden -Wno-strict-aliasing") - endif() - set(CMAKE_CXX_FLAGS_DEBUG "-g") - option(ENABLE_GCC_OPTIMIZATION "Enable specific GCC flags to optimization library size and performance. Only available on Release Mode" 0) - if(ENABLE_GCC_OPTIMIZATION) - set(CMAKE_CXX_FLAGS_RELEASE "-DNDEBUG -Os -Wl,-O1") - if(NOT CMAKE_HOST_APPLE) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wl,--hash-style=gnu") - endif() - endif() +set_cmake_cxx_flags() - if(CMAKE_HOST_APPLE) - # ALTERNATIVE_QT_INCLUDE_DIR is deprecated, because CMake takes care of finding the proper - # include folders using the qmake found in the environment. Only use it for now in case - # something goes wrong with the cmake process. - if(ALTERNATIVE_QT_INCLUDE_DIR AND NOT QT_INCLUDE_DIR) - set(QT_INCLUDE_DIR ${ALTERNATIVE_QT_INCLUDE_DIR}) - endif() - endif() -endif() message(STATUS "*** computed QT_INCLUDE_DIR as ${QT_INCLUDE_DIR}") set(BINDING_NAME PySide2) @@ -171,111 +150,13 @@ compute_config_py_values(BINDING_API_VERSION) include(PySideModules) -macro(COLLECT_MODULE_IF_FOUND shortname) - set(name "Qt5${shortname}") - set(_qt_module_name "${name}") - if ("${shortname}" STREQUAL "OpenGLFunctions") - set(_qt_module_name "Qt5Gui") - endif() - # Determine essential/optional/missing - set(module_state "missing") - list(FIND ALL_ESSENTIAL_MODULES "${shortname}" essentialIndex) - if(${essentialIndex} EQUAL -1) - list(FIND ALL_OPTIONAL_MODULES "${shortname}" optionalIndex) - if(NOT ${optionalIndex} EQUAL -1) - set(module_state "optional") - endif() - else() - set(module_state "essential") - endif() - - # Silence warnings when optional packages are not found when doing a quiet build. - set(quiet_argument "") - if (QUIET_BUILD AND "${module_state}" STREQUAL "optional") - set(quiet_argument "QUIET") - endif() - - find_package(${_qt_module_name} ${quiet_argument}) - # If package is found, _name_found will be equal to 1 - set(_name_found "${_qt_module_name}_FOUND") - # _name_dir will keep the path to the directory where the CMake rules were found - # e.g: ~/qt5.9-install/qtbase/lib/cmake/Qt5Core or /usr/lib64/cmake/Qt5Core - set(_name_dir "${_qt_module_name}_DIR") - # Qt5Core will set the base path to check if all the modules are on the same - # directory, to avoid CMake looking in another path. - # This will be saved in a global variable at the beginning of the modules - # collection process. - string(FIND "${name}" "Qt5Core" qtcore_found) - if(("${qtcore_found}" GREATER "0") OR ("${qtcore_found}" EQUAL "0")) - get_filename_component(_core_abs_dir "${${_name_dir}}/../" ABSOLUTE) - # Setting the absolute path where the Qt5Core was found - # e.g: ~/qt5.9-install/qtbase/lib/cmake or /usr/lib64/cmake - message(STATUS "CORE_ABS_DIR:" ${_core_abs_dir}) - endif() - - # Getting the absolute path for each module where the CMake was found, to - # compare it with CORE_ABS_DIR and check if they are in the same source directory - # e.g: ~/qt5.9-install/qtbase/lib/cmake/Qt5Script or /usr/lib64/cmake/Qt5Script - get_filename_component(_module_dir "${${_name_dir}}" ABSOLUTE) - string(FIND "${_module_dir}" "${_core_abs_dir}" found_basepath) - - # If the module was found, and also the module path is the same as the - # Qt5Core base path, we will generate the list with the modules to be installed - set(looked_in_message ". Looked in: ${${_name_dir}}") - if("${${_name_found}}" AND (("${found_basepath}" GREATER "0") OR ("${found_basepath}" EQUAL "0"))) - message(STATUS "${module_state} module ${name} found (${ARGN})${looked_in_message}") - # record the shortnames for the tests - list(APPEND all_module_shortnames ${shortname}) - else() - if("${module_state}" STREQUAL "optional") - message(STATUS "optional module ${name} skipped${looked_in_message}") - elseif("${module_state}" STREQUAL "essential") - message(STATUS "skipped module ${name} is essential!\n" - " We do not guarantee that all tests are working.${looked_in_message}") - else() - message(FATAL_ERROR "module ${name} MISSING${looked_in_message}") - endif() - endif() -endmacro() - # Set default values for pyside2_global.h set (Qt5X11Extras_FOUND "0") set (Qt5Test_FOUND "0") set (Qt5Widgets_FOUND "0") -# Collect all essential modules. -# note: the order of this list is relevant for dependencies. -# For instance: Qt5Printsupport must come before Qt5WebKitWidgets. -set(ALL_ESSENTIAL_MODULES Core Gui Widgets PrintSupport Sql Network Test Concurrent) -if(UNIX AND NOT APPLE) - list(APPEND ALL_ESSENTIAL_MODULES X11Extras) -endif() -if(WIN32) - list(APPEND ALL_ESSENTIAL_MODULES WinExtras) -endif() -if(APPLE) - list(APPEND ALL_ESSENTIAL_MODULES MacExtras) -endif() - -# Collect all optional modules. -set(ALL_OPTIONAL_MODULES Xml XmlPatterns Help Multimedia -MultimediaWidgets OpenGL OpenGLFunctions Positioning Location Qml Quick QuickWidgets RemoteObjects Scxml Script ScriptTools Sensors TextToSpeech Charts Svg DataVisualization) -find_package(Qt5UiTools) -if(Qt5UiTools_FOUND) - list(APPEND ALL_OPTIONAL_MODULES UiTools) -else() - set(DISABLE_QtUiTools 1) -endif() -if(WIN32) - list(APPEND ALL_OPTIONAL_MODULES AxContainer) -endif() -# Disabling WebKit by default -# If WebKit support is needed add the following elements -# to the list: WebKit WebKitWidgets -list(APPEND ALL_OPTIONAL_MODULES WebChannel WebEngineCore WebEngine WebEngineWidgets WebSockets) -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() +collect_essential_modules() +collect_optional_modules() # Modules to be built unless specified by -DMODULES on command line if (NOT MODULES) @@ -285,22 +166,7 @@ endif() # This will contain the set of modules for which bindings are not built. set(DISABLED_MODULES "${ALL_ESSENTIAL_MODULES};${ALL_OPTIONAL_MODULES}") -# Removing from the MODULES list the items that were defined with -# -DSKIP_MODULES on command line -if (SKIP_MODULES) - foreach(s ${SKIP_MODULES}) - list(REMOVE_ITEM MODULES ${s}) - endforeach() -endif() - -foreach(m ${MODULES}) - COLLECT_MODULE_IF_FOUND(${m}) - list(FIND all_module_shortnames ${m} is_module_collected) - # If the module was collected, remove it from disabled modules list. - if (NOT is_module_collected EQUAL -1) - list(REMOVE_ITEM DISABLED_MODULES ${m}) - endif() -endforeach() +remove_skipped_modules() # Mark all non-collected modules as disabled. This is used for disabling tests # that depend on the disabled modules. @@ -323,24 +189,7 @@ endif() # no more supported: include(${QT_USE_FILE}) # Configure OS support -set(ENABLE_X11 "0") -set(ENABLE_MAC "0") -set(ENABLE_WIN "0") -set(ENABLE_SIMULATOR "0") - -# no more Maemo, no more simulator -if(CMAKE_HOST_APPLE) - set(ENABLE_MAC "1") - set(AUTO_OS "mac") -elseif(CMAKE_HOST_WIN32) - set(ENABLE_WIN "1") - set(AUTO_OS "win") -elseif(CMAKE_HOST_UNIX) - set(ENABLE_X11 "1") - set(AUTO_OS "x11") -else() - message(FATAL_ERROR "OS not supported") -endif() +check_os() message(STATUS "Detected OS: ${AUTO_OS}") # Define supported Qt Version @@ -379,33 +228,17 @@ set(GENERATOR_EXTRA_FLAGS --generator-set=shiboken --enable-pyside-extensions --enable-return-value-heuristic --use-isnull-as-nb_nonzero) -# 2017-04-24 The protected hack can unfortunately not be disabled, because -# Clang does produce linker errors when we disable the hack. -# But the ugly workaround in Python is replaced by a shiboken change. -if(WIN32 OR DEFINED AVOID_PROTECTED_HACK) - message(STATUS "PySide2 will be generated avoiding the protected hack!") - set(GENERATOR_EXTRA_FLAGS ${GENERATOR_EXTRA_FLAGS} --avoid-protected-hack) - add_definitions(-DAVOID_PROTECTED_HACK) -else() - message(STATUS "PySide will be generated using the protected hack!") -endif() +use_protected_as_public_hack() # 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") + setup_sanitize_address() endif() +##################################################################### +# Adding sub-directories to build +##################################################################### + add_subdirectory(libpyside) find_package(Qt5Designer) if(Qt5UiTools_FOUND AND Qt5Designer_FOUND) diff --git a/sources/pyside2/PySide2/QtQml/pysideqmlregistertype.cpp b/sources/pyside2/PySide2/QtQml/pysideqmlregistertype.cpp index 2386620ca..6427e5198 100644 --- a/sources/pyside2/PySide2/QtQml/pysideqmlregistertype.cpp +++ b/sources/pyside2/PySide2/QtQml/pysideqmlregistertype.cpp @@ -41,6 +41,7 @@ // shiboken #include <shiboken.h> +#include <signature.h> // pyside #include <pyside.h> @@ -215,13 +216,13 @@ static int propListTpInit(PyObject *self, PyObject *args, PyObject *kwds) &data->at, &data->clear, &data->count)) { - return 0; + return -1; } PySide::Property::setMetaCallHandler(pySelf, &propListMetaCall); PySide::Property::setTypeName(pySelf, "QQmlListProperty<QObject>"); PySide::Property::setUserData(pySelf, data); - return 1; + return 0; } void propListTpFree(void *self) @@ -469,12 +470,22 @@ PyTypeObject *QtQml_VolatileBoolTypeF(void) return type; } +static const char *PropertyList_SignatureStrings[] = { + "PySide2.QtQml.ListProperty(type:type,append:typing.Callable," + "at:typing.Callable=None,clear:typing.Callable=None,count:typing.Callable=None)", + nullptr}; // Sentinel + +static const char *VolatileBool_SignatureStrings[] = { + "PySide2.QtQml.VolatileBool.get()->bool", + "PySide2.QtQml.VolatileBool.set(a:object)", + nullptr}; // Sentinel + void PySide::initQmlSupport(PyObject *module) { ElementFactory<PYSIDE_MAX_QML_TYPES - 1>::init(); // Export QmlListProperty type - if (PyType_Ready(PropertyListTypeF()) < 0) { + if (SbkSpecial_Type_Ready(module, PropertyListTypeF(), PropertyList_SignatureStrings) < 0) { PyErr_Print(); qWarning() << "Error initializing PropertyList type."; return; @@ -484,7 +495,7 @@ void PySide::initQmlSupport(PyObject *module) PyModule_AddObject(module, PepType_GetNameStr(PropertyListTypeF()), reinterpret_cast<PyObject *>(PropertyListTypeF())); - if (PyType_Ready(QtQml_VolatileBoolTypeF()) < 0) { + if (SbkSpecial_Type_Ready(module, QtQml_VolatileBoolTypeF(), VolatileBool_SignatureStrings) < 0) { PyErr_Print(); qWarning() << "Error initializing VolatileBool type."; return; diff --git a/sources/pyside2/PySide2/support/generate_pyi.py b/sources/pyside2/PySide2/support/generate_pyi.py index c732227f4..d5bbe5d7c 100644 --- a/sources/pyside2/PySide2/support/generate_pyi.py +++ b/sources/pyside2/PySide2/support/generate_pyi.py @@ -100,6 +100,38 @@ class Formatter(Writer): The separation in formatter and enumerator is done to keep the unrelated tasks of enumeration and formatting apart. """ + def __init__(self, *args): + Writer.__init__(self, *args) + # patching __repr__ to disable the __repr__ of typing.TypeVar: + """ + def __repr__(self): + if self.__covariant__: + prefix = '+' + elif self.__contravariant__: + prefix = '-' + else: + prefix = '~' + return prefix + self.__name__ + """ + def _typevar__repr__(self): + return "typing." + self.__name__ + typing.TypeVar.__repr__ = _typevar__repr__ + + # Adding a pattern to substitute "Union[T, NoneType]" by "Optional[T]" + # I tried hard to replace typing.Optional by a simple override, but + # this became _way_ too much. + # See also the comment in layout.py . + brace_pat = build_brace_pattern(3) + pattern = (r"\b Union \s* \[ \s* {brace_pat} \s*, \s* NoneType \s* \]" + .format(**locals())) + replace = r"Optional[\1]" + optional_searcher = re.compile(pattern, flags=re.VERBOSE) + def optional_replacer(source): + return optional_searcher.sub(replace, str(source)) + self.optional_replacer = optional_replacer + # self.level is maintained by enum_sig.py + # self.after_enum() is a one-shot set by enum_sig.py . + @contextmanager def module(self, mod_name): self.mod_name = mod_name @@ -121,27 +153,22 @@ class Formatter(Writer): @contextmanager def klass(self, class_name, class_str): - self.class_name = class_name - spaces = "" + spaces = indent * self.level while "." in class_name: - spaces += indent class_name = class_name.split(".", 1)[-1] class_str = class_str.split(".", 1)[-1] self.print() - if not spaces: + if self.level == 0: self.print() here = self.outfile.tell() self.print("{spaces}class {class_str}:".format(**locals())) - self.print() pos = self.outfile.tell() - self.spaces = spaces yield if pos == self.outfile.tell(): # we have not written any function self.outfile.seek(here) self.outfile.truncate() - # Note: we cannot use class_str when we have no body. - self.print("{spaces}class {class_name}: ...".format(**locals())) + self.print("{spaces}class {class_str}: ...".format(**locals())) if "<" in class_name: # This is happening in QtQuick for some reason: ## class QSharedPointer<QQuickItemGrabResult >: @@ -150,24 +177,34 @@ class Formatter(Writer): self.outfile.truncate() @contextmanager - def function(self, func_name, signature): + def function(self, func_name, signature, modifier=None): + if self.after_enum() or func_name == "__init__": + self.print() key = func_name - spaces = indent + self.spaces if self.class_name else "" + spaces = indent * self.level if type(signature) == type([]): for sig in signature: self.print('{spaces}@typing.overload'.format(**locals())) - self._function(func_name, sig, spaces) + self._function(func_name, sig, modifier, spaces) else: - self._function(func_name, signature, spaces) + self._function(func_name, signature, modifier, spaces) + if func_name == "__init__": + self.print() yield key - def _function(self, func_name, signature, spaces): - # this would be nicer to get somehow together with the signature - is_meth = re.match(r"\((\w*)", str(signature)).group(1) == "self" - if self.class_name and not is_meth: - self.print('{spaces}@staticmethod'.format(**locals())) + def _function(self, func_name, signature, modifier, spaces): + if modifier: + self.print('{spaces}@{modifier}'.format(**locals())) + signature = self.optional_replacer(signature) self.print('{spaces}def {func_name}{signature}: ...'.format(**locals())) + @contextmanager + def enum(self, class_name, enum_name, value): + spaces = indent * self.level + hexval = hex(value) + self.print("{spaces}{enum_name:25}: {class_name} = ... # {hexval}".format(**locals())) + yield + def get_license_text(): with io.open(sourcepath) as f: @@ -247,10 +284,11 @@ def generate_all_pyi(outpath, options): os.environ["PYTHONPATH"] = pypath # now we can import - global PySide2, inspect, HintingEnumerator + global PySide2, inspect, typing, HintingEnumerator, build_brace_pattern import PySide2 - from PySide2.support.signature import inspect + from PySide2.support.signature import inspect, typing from PySide2.support.signature.lib.enum_sig import HintingEnumerator + from PySide2.support.signature.lib.tool import build_brace_pattern # propagate USE_PEP563 to the mapping module. # Perhaps this can be automated? diff --git a/sources/pyside2/doc/_themes/pysidedocs/static/pyside.css b/sources/pyside2/doc/_themes/pysidedocs/static/pyside.css index 956e3113b..23e460262 100644 --- a/sources/pyside2/doc/_themes/pysidedocs/static/pyside.css +++ b/sources/pyside2/doc/_themes/pysidedocs/static/pyside.css @@ -3,8 +3,8 @@ /* -- admonitions -- */ div.admonition { - margin: 1em 0 1em; - padding: 7px; + margin: 1.5em 0 1.5em; + padding: 0; } div.admonition dt { @@ -19,6 +19,23 @@ p.admonition-title { margin: 0px 10px 5px 0px; font-weight: bold; } + +div.admonition code { + font-family: inherit; +} + +p.admonition-title + p { + padding-left: 1em; +} + +div.admonition a:after { + content: ', '; +} + +div.admonition a:last-child:after { + content: ''; +} + .body { width: 100% } @@ -35,18 +52,21 @@ div.body p.centered { margin-top: 25px; } +div.warning, div.seealso, div.note { + padding: 6px 0px 6px 10px; + border: none; +} + div.warning { background-color: #ffe4e4; - border: 1px solid #f66; } div.seealso { - background-color: #ffffcc; - border: 1px solid #ffff66; + background-color: #fff2d6; } div.note { - border: 1px solid #e3e3e3; + background-color: #f3f3f4; } table.docutils { @@ -91,7 +111,7 @@ h2 em { .body blockquote { border: none; padding-left: 0; - margin-bottom: 2em; + margin-bottom: 1.5em; } .sphinxsidebar { @@ -146,11 +166,8 @@ h2 em { display: block; padding: 5px; margin: 0 10px 10px 0; - border: 1px solid #ddd; - background-color: #f4f4f4; - -moz-border-radius:6px; - -webkit-border-radius:6px; - -khtml-border-radius:6px; + border: none; + background-color: #e2e2e2; } .section .docutils.container td { @@ -290,14 +307,23 @@ tt.descname { } #functions ul, #virtual-functions ul, #slots ul, #signals ul, #static-functions ul { - list-style: none; - margin: 0px; - padding: 10px; + margin: 0; + padding: 6px; border: 1px solid #ddd; - background-color: #f4f4f4; - -moz-border-radius:10px; - -webkit-border-radius:10px; - -khtml-border-radius:10px; + border-radius: 0; + background-color: #e2e2e2; +} + +#functions p, #virtual-functions p, #slots p, #signals p, #static-functions p { + margin: 0; + padding: 0; +} + +#functions li, #virtual-functions li, #slots li, #signals li, #static-functions li { + list-style: none; + margin: 5px; + padding: 0; + font-size: 90%; } #synopsis span.pre { @@ -312,11 +338,94 @@ tt.descname { margin: 0px; margin-bottom: 10px; padding: 10px; - border: 1px solid #ddd; - background-color: #f4f4f4; - -moz-border-radius:10px; - -webkit-border-radius:10px; - -khtml-border-radius:10px; + font-weight: bold; + background-color: #e2e2e2; + border: none; + border-radius: 0; +} + +#detailed-description dd > blockquote, +#detailed-description dd > .field-list { + font-family: 'Droid Sans Mono'; + font-size: small; + border-left: 10px solid #e2e2e2; + padding-left: 10px; + margin-bottom: 1.5em; +} + +#detailed-description dd > blockquote blockquote { + border: none; + padding: 0; +} + +#detailed-description .class .field-odd, +#detailed-description .method .field-odd, +#detailed-description .staticmethod .field-odd, +#detailed-description .attribute .field-odd { + margin: 0; + padding: 1px 0 0 0; + background-color: #ffffff; + +} + +#detailed-description .class .field-even, +#detailed-description .method .field-even, +#detailed-description .staticmethod .field-even, +#detailed-description .attribute .field-even { + margin: 0; + padding: 1px 0 0 0; + background-color: #ffffff; +} + +#detailed-description .class .field-odd li, +#detailed-description .method .field-odd li, +#detailed-description .staticmethod .field-odd li, +#detailed-description .attribute .field-odd li { + list-style: none; + margin: 0; + padding: 0; + +} + +#detailed-description .class .field-even li, +#detailed-description .method .field-even li, +#detailed-description .staticmethod .field-even li, +#detailed-description .attribute .field-even li { + list-style: none; + margin: 0; + padding: 0; +} + +#detailed-description .class .field-odd p, +#detailed-description .method .field-odd p, +#detailed-description .staticmethod .field-odd p, +#detailed-description .attribute .field-odd p{ + margin: 0; + margin-left: 20px; + +} + +#detailed-description .class .field-even p, +#detailed-description .method .field-even p, +#detailed-description .staticmethod .field-even p, +#detailed-description .attribute .field-even p{ + margin: 0; + margin-left: 20px; +} + +#detailed-description .class .field-odd p:last-child, +#detailed-description .method .field-odd p:last-child, +#detailed-description .staticmethod .field-odd p:last-child, +#detailed-description .attribute .field-odd p:last-child { + margin-bottom: 10px; + +} + +#detailed-description .class .field-even p:last-child, +#detailed-description .method .field-even p:last-child, +#detailed-description .staticmethod .field-even p:last-child, +#detailed-description .attribute .field-even p:last-child{ + margin-bottom: 10px; } .document dl.attribute, @@ -333,6 +442,10 @@ tt.descname { padding-left: 1em; } +#detailed-description .attribute td:nth-child(1) { + font-family: 'Droid Sans Mono'; +} + /* Qt theme */ #navbar { position:fixed; @@ -1056,9 +1169,15 @@ div.multi-column div { } .col-2 h2,.toc h3,.sidebar-content h2, .sidebar-content h3,.sectionlist h2, +.sphinxsidebar { + position: fixed; + overflow: scroll; + overflow-x: hidden; + overflow-y: hidden; +} .sphinxsidebar h3 { - font-weight:400; - margin-bottom:1em + font-weight: bold; + margin-bottom:1em; } .toc h3 a { color:#404244 @@ -1450,6 +1569,10 @@ span.wrap:active { code,.codelike { font-family:"Droid Sans Mono" } +#detailed-description .function dt > code, +#detailed-description .function dt > em { + font-weight:bold +} h3.fn code { font-size:0.75em; float:right; diff --git a/sources/pyside2/doc/gettingstarted.rst b/sources/pyside2/doc/gettingstarted.rst index cc915a5cb..0ee6a9173 100644 --- a/sources/pyside2/doc/gettingstarted.rst +++ b/sources/pyside2/doc/gettingstarted.rst @@ -30,11 +30,25 @@ Testing the Installation ======================== Now that you have |project| installed, you can test your setup by running the following Python -constructs to print version information: +constructs to print version information:: -.. include:: pysideversion.rst - :start-line: 5 - :end-line: 32 + import PySide2.QtCore + + # Prints PySide2 version + # e.g. 5.11.1a1 + print(PySide2.__version__) + + # Gets a tuple with each version component + # e.g. (5, 11, 1, 'a', 1) + print(PySide2.__version_info__) + + # Prints the Qt version used to compile PySide2 + # e.g. "5.11.2" + print(PySide2.QtCore.__version__) + + # Gets a tuple with each version components of Qt used to compile PySide2 + # e.g. (5, 11, 2) + print(PySide2.QtCore.__version_info__) Creating a Simple Application ============================= diff --git a/sources/pyside2/libpyside/pysideclassinfo.cpp b/sources/pyside2/libpyside/pysideclassinfo.cpp index bf2a1307f..fe5ca8765 100644 --- a/sources/pyside2/libpyside/pysideclassinfo.cpp +++ b/sources/pyside2/libpyside/pysideclassinfo.cpp @@ -45,8 +45,7 @@ #include "dynamicqmetaobject.h" #include <shiboken.h> - -#define CLASSINFO_CLASS_NAME "ClassInfo" +#include <signature.h> extern "C" { @@ -65,7 +64,7 @@ static PyType_Slot PySideClassInfoType_slots[] = { {0, 0} }; static PyType_Spec PySideClassInfoType_spec = { - "PySide2.QtCore." CLASSINFO_CLASS_NAME, + "PySide2.QtCore.ClassInfo", sizeof(PySideClassInfo), 0, Py_TPFLAGS_DEFAULT, @@ -161,7 +160,7 @@ int classInfoTpInit(PyObject *self, PyObject *args, PyObject *kwds) } } - return PyErr_Occurred() ? -1 : 1; + return PyErr_Occurred() ? -1 : 0; } void classInfoFree(void *self) @@ -179,13 +178,17 @@ void classInfoFree(void *self) namespace PySide { namespace ClassInfo { +static const char *ClassInfo_SignatureStrings[] = { + "PySide2.QtCore.ClassInfo(**info:typing.Dict[str,str])", + nullptr}; // Sentinel + void init(PyObject *module) { - if (PyType_Ready(PySideClassInfoTypeF()) < 0) + if (SbkSpecial_Type_Ready(module, PySideClassInfoTypeF(), ClassInfo_SignatureStrings) < 0) return; Py_INCREF(PySideClassInfoTypeF()); - PyModule_AddObject(module, CLASSINFO_CLASS_NAME, reinterpret_cast<PyObject *>(PySideClassInfoTypeF())); + PyModule_AddObject(module, "ClassInfo", reinterpret_cast<PyObject *>(PySideClassInfoTypeF())); } bool checkType(PyObject *pyObj) diff --git a/sources/pyside2/libpyside/pysideclassinfo_p.h b/sources/pyside2/libpyside/pysideclassinfo_p.h index 426aee133..c0038a71a 100644 --- a/sources/pyside2/libpyside/pysideclassinfo_p.h +++ b/sources/pyside2/libpyside/pysideclassinfo_p.h @@ -44,7 +44,7 @@ #include <QMetaObject> #include "pysideclassinfo.h" -#define __INFO_ATTR_NAME__ "__classInfo__" +#define __INFO_ATTR_NAME__ "__classInfo__" // not used! ??? struct PySideClassInfo; diff --git a/sources/pyside2/libpyside/pysidemetafunction.cpp b/sources/pyside2/libpyside/pysidemetafunction.cpp index 346117201..e0e0c439b 100644 --- a/sources/pyside2/libpyside/pysidemetafunction.cpp +++ b/sources/pyside2/libpyside/pysidemetafunction.cpp @@ -41,6 +41,7 @@ #include "pysidemetafunction_p.h" #include <shiboken.h> +#include <signature.h> #include <QtCore/QMetaMethod> @@ -65,7 +66,7 @@ static PyType_Slot PySideMetaFunctionType_slots[] = { {0, 0} }; static PyType_Spec PySideMetaFunctionType_spec = { - "PySide.MetaFunction", + "PySide2.QtCore.MetaFunction", sizeof(PySideMetaFunction), 0, Py_TPFLAGS_DEFAULT, @@ -101,11 +102,16 @@ PyObject *functionCall(PyObject *self, PyObject *args, PyObject * /* kw */) namespace PySide { namespace MetaFunction { +static const char *MetaFunction_SignatureStrings[] = { + "PySide2.QtCore.MetaFunction.__call__(*args:typing.Any)->typing.Any", + nullptr}; // Sentinel + void init(PyObject *module) { - if (PyType_Ready(PySideMetaFunctionTypeF()) < 0) + if (SbkSpecial_Type_Ready(module, PySideMetaFunctionTypeF(), MetaFunction_SignatureStrings) < 0) return; + Py_INCREF(PySideMetaFunctionTypeF()); PyModule_AddObject(module, "MetaFunction", reinterpret_cast<PyObject *>(PySideMetaFunctionTypeF())); } diff --git a/sources/pyside2/libpyside/pysideproperty.cpp b/sources/pyside2/libpyside/pysideproperty.cpp index a2bf5fd2b..d9d15eb3b 100644 --- a/sources/pyside2/libpyside/pysideproperty.cpp +++ b/sources/pyside2/libpyside/pysideproperty.cpp @@ -45,8 +45,7 @@ #include "pysidesignal_p.h" #include <shiboken.h> - -#define QPROPERTY_CLASS_NAME "Property" +#include <signature.h> extern "C" { @@ -82,7 +81,7 @@ static PyType_Slot PySidePropertyType_slots[] = { }; // Dotted modulename is crucial for PyType_FromSpec to work. Is this name right? static PyType_Spec PySidePropertyType_spec = { - "PySide2.QtCore." QPROPERTY_CLASS_NAME, + "PySide2.QtCore.Property", sizeof(PySideProperty), 0, Py_TPFLAGS_DEFAULT|Py_TPFLAGS_HAVE_GC|Py_TPFLAGS_BASETYPE, @@ -175,7 +174,7 @@ int qpropertyTpInit(PyObject *self, PyObject *args, PyObject *kwds) /*s*/ &doc, /*O*/ &(pData->notify), /*bbbbbb*/ &(pData->designable), &(pData->scriptable), &(pData->stored), &(pData->user), &(pData->constant), &(pData->final))) { - return 0; + return -1; } if (doc) { @@ -198,7 +197,7 @@ int qpropertyTpInit(PyObject *self, PyObject *args, PyObject *kwds) Py_XINCREF(pData->freset); Py_XINCREF(pData->fdel); Py_XINCREF(pData->notify); - return 1; + return 0; } pData->fget = nullptr; pData->fset = nullptr; @@ -321,13 +320,24 @@ static PyObject *getFromType(PyTypeObject *type, PyObject *name) namespace PySide { namespace Property { +static const char *Property_SignatureStrings[] = { + "PySide2.QtCore.Property(type:type,fget:typing.Callable=None,fset:typing.Callable=None," + "freset:typing.Callable=None,fdel:typing.Callable=None,doc:str=None," + "notify:typing.Callable=None,designable:bool=True,scriptable:bool=True," + "stored:bool=True,user:bool=False,constant:bool=False,final:bool=False)", + "PySide2.QtCore.Property.getter(func:typing.Callable)", + "PySide2.QtCore.Property.read(func:typing.Callable)", + "PySide2.QtCore.Property.setter(func:typing.Callable)", + "PySide2.QtCore.Property.write(func:typing.Callable)", + nullptr}; // Sentinel + void init(PyObject *module) { - if (PyType_Ready(PySidePropertyTypeF()) < 0) + if (SbkSpecial_Type_Ready(module, PySidePropertyTypeF(), Property_SignatureStrings) < 0) return; Py_INCREF(PySidePropertyTypeF()); - PyModule_AddObject(module, QPROPERTY_CLASS_NAME, reinterpret_cast<PyObject *>(PySidePropertyTypeF())); + PyModule_AddObject(module, "Property", reinterpret_cast<PyObject *>(PySidePropertyTypeF())); } bool checkType(PyObject *pyObj) diff --git a/sources/pyside2/libpyside/pysidesignal.cpp b/sources/pyside2/libpyside/pysidesignal.cpp index 47a5fff43..a09c17a24 100644 --- a/sources/pyside2/libpyside/pysidesignal.cpp +++ b/sources/pyside2/libpyside/pysidesignal.cpp @@ -47,12 +47,11 @@ #include <QtCore/QObject> #include <QtCore/QMetaMethod> #include <QtCore/QMetaObject> +#include <signature.h> #include <algorithm> #include <utility> -#define SIGNAL_CLASS_NAME "Signal" -#define SIGNAL_INSTANCE_NAME "SignalInstance" #define QT_SIGNAL_SENTINEL '2' namespace PySide { @@ -101,35 +100,35 @@ static PyObject *signalCall(PyObject *, PyObject *, PyObject *); static PyObject *metaSignalCheck(PyObject *, PyObject *); -static PyMethodDef Signal_methods[] = { - {"__instancecheck__", (PyCFunction)metaSignalCheck, METH_O, NULL}, +static PyMethodDef MetaSignal_methods[] = { + {"__instancecheck__", (PyCFunction)metaSignalCheck, METH_O|METH_STATIC, NULL}, {0, 0, 0, 0} }; -static PyType_Slot PySideSignalMetaType_slots[] = { - {Py_tp_methods, (void *)Signal_methods}, +static PyType_Slot PySideMetaSignalType_slots[] = { + {Py_tp_methods, (void *)MetaSignal_methods}, {Py_tp_base, (void *)&PyType_Type}, {Py_tp_free, (void *)PyObject_GC_Del}, {Py_tp_dealloc, (void *)object_dealloc}, {0, 0} }; -static PyType_Spec PySideSignalMetaType_spec = { +static PyType_Spec PySideMetaSignalType_spec = { "PySide2.QtCore.MetaSignal", 0, // sizeof(PyHeapTypeObject) is filled in by PyType_FromSpecWithBases // which calls PyType_Ready which calls inherit_special. 0, Py_TPFLAGS_DEFAULT, - PySideSignalMetaType_slots, + PySideMetaSignalType_slots, }; -PyTypeObject *PySideSignalMetaTypeF(void) +PyTypeObject *PySideMetaSignalTypeF(void) { static PyTypeObject *type = nullptr; if (!type) { PyObject *bases = Py_BuildValue("(O)", &PyType_Type); - type = (PyTypeObject *)PyType_FromSpecWithBases(&PySideSignalMetaType_spec, bases); + type = (PyTypeObject *)PyType_FromSpecWithBases(&PySideMetaSignalType_spec, bases); Py_XDECREF(bases); } return type; @@ -146,7 +145,7 @@ static PyType_Slot PySideSignalType_slots[] = { {0, 0} }; static PyType_Spec PySideSignalType_spec = { - "PySide2.QtCore." SIGNAL_CLASS_NAME, + "PySide2.QtCore.Signal", sizeof(PySideSignal), 0, Py_TPFLAGS_DEFAULT, @@ -160,7 +159,7 @@ PyTypeObject *PySideSignalTypeF(void) if (!type) { type = (PyTypeObject *)PyType_FromSpec(&PySideSignalType_spec); PyTypeObject *hold = Py_TYPE(type); - Py_TYPE(type) = PySideSignalMetaTypeF(); + Py_TYPE(type) = PySideMetaSignalTypeF(); Py_INCREF(Py_TYPE(type)); Py_DECREF(hold); } @@ -185,7 +184,7 @@ static PyType_Slot PySideSignalInstanceType_slots[] = { {0, 0} }; static PyType_Spec PySideSignalInstanceType_spec = { - "PySide2.QtCore." SIGNAL_INSTANCE_NAME, + "PySide2.QtCore.SignalInstance", sizeof(PySideSignalInstance), 0, Py_TPFLAGS_DEFAULT, @@ -211,8 +210,8 @@ int signalTpInit(PyObject *self, PyObject *args, PyObject *kwds) emptyTuple = PyTuple_New(0); if (!PyArg_ParseTupleAndKeywords(emptyTuple, kwds, - "|sO:QtCore." SIGNAL_CLASS_NAME, const_cast<char **>(kwlist), &argName, &argArguments)) - return 0; + "|sO:QtCore.Signal", const_cast<char **>(kwlist), &argName, &argArguments)) + return -1; bool tupledArgs = false; PySideSignal *data = reinterpret_cast<PySideSignal *>(self); @@ -257,7 +256,7 @@ int signalTpInit(PyObject *self, PyObject *args, PyObject *kwds) PySide::Signal::SignalSignature(sig)); } - return 1; + return 0; } void signalFree(void *self) @@ -317,7 +316,7 @@ PyObject *signalInstanceConnect(PyObject *self, PyObject *args, PyObject *kwds) static const char *kwlist[] = {"slot", "type", nullptr}; if (!PyArg_ParseTupleAndKeywords(args, kwds, - "O|O:" SIGNAL_INSTANCE_NAME, const_cast<char **>(kwlist), &slot, &type)) + "O|O:SignalInstance", const_cast<char **>(kwlist), &slot, &type)) return 0; PySideSignalInstance *source = reinterpret_cast<PySideSignalInstance *>(self); @@ -585,9 +584,9 @@ PyObject *signalInstanceCall(PyObject *self, PyObject *args, PyObject *kw) return PyCFunction_Call(homonymousMethod, args, kw); } -static PyObject *metaSignalCheck(PyObject * /* klass */, PyObject *args) +static PyObject *metaSignalCheck(PyObject * /* klass */, PyObject *arg) { - if (PyType_IsSubtype(Py_TYPE(args), PySideSignalInstanceTypeF())) + if (PyType_IsSubtype(Py_TYPE(arg), PySideSignalInstanceTypeF())) Py_RETURN_TRUE; else Py_RETURN_FALSE; @@ -598,21 +597,36 @@ static PyObject *metaSignalCheck(PyObject * /* klass */, PyObject *args) namespace PySide { namespace Signal { +static const char *MetaSignal_SignatureStrings[] = { + "PySide2.QtCore.MetaSignal.__instancecheck__(object:object)->bool", + nullptr}; // Sentinel + +static const char *Signal_SignatureStrings[] = { + "PySide2.QtCore.Signal(*types:type,name:str=nullptr,arguments:str=nullptr)", + nullptr}; // Sentinel + +static const char *SignalInstance_SignatureStrings[] = { + "PySide2.QtCore.SignalInstance.connect(slot:object,type:type=nullptr)", + "PySide2.QtCore.SignalInstance.disconnect(slot:object=nullptr)", + "PySide2.QtCore.SignalInstance.emit(*args:typing.Any)", + nullptr}; // Sentinel + void init(PyObject *module) { - if (PyType_Ready(PySideSignalMetaTypeF()) < 0) + if (SbkSpecial_Type_Ready(module, PySideMetaSignalTypeF(), MetaSignal_SignatureStrings) < 0) return; + Py_INCREF(PySideSignalTypeF()); + PyModule_AddObject(module, "MetaSignal", reinterpret_cast<PyObject *>(PySideMetaSignalTypeF())); - if (PyType_Ready(PySideSignalTypeF()) < 0) + if (SbkSpecial_Type_Ready(module, PySideSignalTypeF(), Signal_SignatureStrings) < 0) return; - Py_INCREF(PySideSignalTypeF()); - PyModule_AddObject(module, SIGNAL_CLASS_NAME, reinterpret_cast<PyObject *>(PySideSignalTypeF())); + PyModule_AddObject(module, "Signal", reinterpret_cast<PyObject *>(PySideSignalTypeF())); - if (PyType_Ready(PySideSignalInstanceTypeF()) < 0) + if (SbkSpecial_Type_Ready(module, PySideSignalInstanceTypeF(), SignalInstance_SignatureStrings) < 0) return; - Py_INCREF(PySideSignalInstanceTypeF()); + PyModule_AddObject(module, "SignalInstance", reinterpret_cast<PyObject *>(PySideSignalInstanceTypeF())); } bool checkType(PyObject *pyObj) diff --git a/sources/pyside2/libpyside/pysideslot.cpp b/sources/pyside2/libpyside/pysideslot.cpp index 2cdf32626..204253aa2 100644 --- a/sources/pyside2/libpyside/pysideslot.cpp +++ b/sources/pyside2/libpyside/pysideslot.cpp @@ -45,8 +45,7 @@ #include <QtCore/QMetaObject> #include <QtCore/QString> - -#define SLOT_DEC_NAME "Slot" +#include <signature.h> struct SlotData { @@ -76,7 +75,7 @@ static PyType_Slot PySideSlotType_slots[] = { {0, 0} }; static PyType_Spec PySideSlotType_spec = { - "PySide2.QtCore." SLOT_DEC_NAME, + "PySide2.QtCore.Slot", sizeof(PySideSlot), 0, Py_TPFLAGS_DEFAULT, @@ -102,9 +101,9 @@ int slotTpInit(PyObject *self, PyObject *args, PyObject *kw) if (emptyTuple == 0) emptyTuple = PyTuple_New(0); - if (!PyArg_ParseTupleAndKeywords(emptyTuple, kw, "|sO:QtCore." SLOT_DEC_NAME, + if (!PyArg_ParseTupleAndKeywords(emptyTuple, kw, "|sO:QtCore.Slot", const_cast<char **>(kwlist), &argName, &argResult)) { - return 0; + return -1; } PySideSlot *data = reinterpret_cast<PySideSlot *>(self); @@ -128,7 +127,7 @@ int slotTpInit(PyObject *self, PyObject *args, PyObject *kw) data->slotData->resultType = argResult ? PySide::Signal::getTypeName(argResult) : PySide::Signal::voidType(); - return 1; + return 0; } PyObject *slotCall(PyObject *self, PyObject *args, PyObject * /* kw */) @@ -177,15 +176,20 @@ PyObject *slotCall(PyObject *self, PyObject *args, PyObject * /* kw */) } // extern "C" -namespace PySide { namespace Slot { +namespace PySide { +namespace Slot { + +static const char *Slot_SignatureStrings[] = { + "PySide2.QtCore.Slot(*types:type,name:str=nullptr,result:str=nullptr)->typing.Callable[...,typing.Optional[str]]", + nullptr}; // Sentinel void init(PyObject *module) { - if (PyType_Ready(PySideSlotTypeF()) < 0) + if (SbkSpecial_Type_Ready(module, PySideSlotTypeF(), Slot_SignatureStrings) < 0) return; Py_INCREF(PySideSlotTypeF()); - PyModule_AddObject(module, SLOT_DEC_NAME, reinterpret_cast<PyObject *>(PySideSlotTypeF())); + PyModule_AddObject(module, "Slot", reinterpret_cast<PyObject *>(PySideSlotTypeF())); } } // namespace Slot diff --git a/sources/shiboken2/ApiExtractor/CMakeLists.txt b/sources/shiboken2/ApiExtractor/CMakeLists.txt index c8a5fb336..c2a4c208e 100644 --- a/sources/shiboken2/ApiExtractor/CMakeLists.txt +++ b/sources/shiboken2/ApiExtractor/CMakeLists.txt @@ -30,6 +30,22 @@ parser/enumvalue.cpp xmlutils.cpp ) +find_package(Qt5XmlPatterns 5.12) +find_package(Qt5Xml 5.12) +find_package(LibXml2 2.6.32) +find_package(LibXslt 1.1.19) + +set(HAS_LIBXSLT 0) +if (LIBXSLT_FOUND AND LIBXML2_FOUND) + set(HAS_LIBXSLT 1) +endif() + +if(NOT Qt5XmlPatterns_FOUND AND NOT HAS_LIBXSLT) + set(DISABLE_DOCSTRINGS TRUE) + message(WARNING + "Documentation will not be built due to missing dependency (no Qt5XmlPatterns found).") +endif() + add_library(apiextractor STATIC ${apiextractor_SRC}) target_include_directories(apiextractor PRIVATE ${CLANG_EXTRA_INCLUDES} ${CMAKE_CURRENT_SOURCE_DIR} @@ -68,6 +84,7 @@ target_compile_definitions(apiextractor PRIVATE CMAKE_CXX_COMPILER="${CMAKE_CXX_ set(LIB_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/lib${LIB_SUFFIX}" CACHE PATH "The subdirectory relative to the install prefix where libraries will be installed (default is /lib${LIB_SUFFIX})" FORCE) if (BUILD_TESTS) + find_package(Qt5Test 5.12 REQUIRED) set(LIBRARY_OUTPUT_PATH ${CMAKE_BINARY_DIR}/tests) enable_testing() add_subdirectory(tests) diff --git a/sources/shiboken2/CMakeLists.txt b/sources/shiboken2/CMakeLists.txt index be1b4cd7f..c1349cae6 100644 --- a/sources/shiboken2/CMakeLists.txt +++ b/sources/shiboken2/CMakeLists.txt @@ -19,42 +19,9 @@ option(USE_PYTHON_VERSION "Use specific python version to build shiboken2." "") option(DISABLE_DOCSTRINGS "Disable documentation extraction." FALSE) find_package(Qt5 5.12 REQUIRED COMPONENTS Core) -find_package(Qt5Xml 5.12) -find_package(Qt5XmlPatterns 5.12) -find_package(LibXml2 2.6.32) -find_package(LibXslt 1.1.19) -if(BUILD_TESTS) - find_package(Qt5Test 5.12 REQUIRED) -endif() - -set(HAS_LIBXSLT 0) -if (LIBXSLT_FOUND AND LIBXML2_FOUND) - set(HAS_LIBXSLT 1) -endif() - -if(NOT Qt5XmlPatterns_FOUND AND NOT HAS_LIBXSLT) - set(DISABLE_DOCSTRINGS TRUE) - message(WARNING - "Documentation will not be built due to missing dependency (no Qt5XmlPatterns found).") -endif() - -# Don't display "up-to-date / install" messages when installing, to reduce visual clutter. -if (QUIET_BUILD) - set(CMAKE_INSTALL_MESSAGE NEVER) -endif() -# Override message not to display info messages when doing a quiet build. if (QUIET_BUILD) - function(message) - list(GET ARGV 0 MessageType) - if (MessageType STREQUAL FATAL_ERROR OR - MessageType STREQUAL SEND_ERROR OR - MessageType STREQUAL WARNING OR - MessageType STREQUAL AUTHOR_WARNING) - list(REMOVE_AT ARGV 0) - _message(${MessageType} "${ARGV}") - endif() - endfunction() + set_quiet_build() endif() if (USE_PYTHON_VERSION) @@ -63,115 +30,11 @@ else() shiboken_find_required_python() endif() -macro(get_python_arch) - execute_process( - COMMAND ${PYTHON_EXECUTABLE} -c "if True: - import sys - print('64' if sys.maxsize > 2**31-1 else '32') - " - OUTPUT_VARIABLE PYTHON_ARCH - OUTPUT_STRIP_TRAILING_WHITESPACE) - message("PYTHON_ARCH: " ${PYTHON_ARCH}) -endmacro() - if (NOT PYTHON_ARCH) get_python_arch() endif() -macro(get_llvm_config) - execute_process( - COMMAND ${PYTHON_EXECUTABLE} -c "if True: - import os - import sys - sys.path.append(os.path.realpath(os.path.join('${CMAKE_CURRENT_LIST_DIR}', '..', '..'))) - from build_scripts.utils import find_llvm_config - llvmConfig = find_llvm_config() - if llvmConfig: - print(llvmConfig) - " - OUTPUT_VARIABLE LLVM_CONFIG - OUTPUT_STRIP_TRAILING_WHITESPACE) - message("LLVM_CONFIG: " ${LLVM_CONFIG}) -endmacro() - -set(CLANG_DIR "") -set(CLANG_DIR_SOURCE "") - -set(clang_not_found_message "Unable to detect CLANG location by checking LLVM_INSTALL_DIR, \ - CLANG_INSTALL_DIR or running llvm-config.") - -if (DEFINED ENV{LLVM_INSTALL_DIR}) - set(CLANG_DIR $ENV{LLVM_INSTALL_DIR}) - string(REPLACE "_ARCH_" "${PYTHON_ARCH}" CLANG_DIR "${CLANG_DIR}") - set(CLANG_DIR_SOURCE "LLVM_INSTALL_DIR") -elseif (DEFINED ENV{CLANG_INSTALL_DIR}) - set(CLANG_DIR $ENV{CLANG_INSTALL_DIR}) - string(REPLACE "_ARCH_" "${PYTHON_ARCH}" CLANG_DIR "${CLANG_DIR}") - set(CLANG_DIR_SOURCE "CLANG_INSTALL_DIR") -else () - if (NOT LLVM_CONFIG) - get_llvm_config() - endif() - set(CLANG_DIR_SOURCE "${LLVM_CONFIG}") - if ("${CLANG_DIR_SOURCE}" STREQUAL "") - message(FATAL_ERROR "${clang_not_found_message}") - endif() - - EXEC_PROGRAM("${LLVM_CONFIG}" ARGS "--prefix" OUTPUT_VARIABLE CLANG_DIR) - if (NOT "${CLANG_DIR}" STREQUAL "") - EXEC_PROGRAM("${LLVM_CONFIG}" ARGS "--version" OUTPUT_VARIABLE CLANG_VERSION) - if (CLANG_VERSION VERSION_LESS 3.9) - message(FATAL_ERROR "libclang version 3.9 or higher is required (${LLVM_CONFIG} detected ${CLANG_VERSION} at ${CLANG_DIR}).") - endif() - endif() -endif() - -if ("${CLANG_DIR}" STREQUAL "") - message(FATAL_ERROR "${clang_not_found_message}") -elseif (NOT IS_DIRECTORY ${CLANG_DIR}) - message(FATAL_ERROR "${CLANG_DIR} detected by ${CLANG_DIR_SOURCE} does not exist.") -endif() - -# The non-development Debian / Ubuntu packages (e.g. libclang1-6.0) do not ship a -# libclang.so symlink, but only libclang-6.0.so.1 and libclang.so.1 (adjusted for version number). -# Thus searching for libclang would not succeed. -# The "libclang.so" symlink is shipped as part of the development package (libclang-6.0-dev) which -# we need anyway because of the headers. Thus we will search for libclang.so.1 also, and complain -# about the headers not being found in a check further down. This is more friendly to the user, -# so they don't scratch their head thinking that they have already installed the necessary package. -set(CLANG_LIB_NAMES clang libclang.so libclang.so.1) -if(MSVC) - set(CLANG_LIB_NAMES libclang) -endif() - -find_library(CLANG_LIBRARY NAMES ${CLANG_LIB_NAMES} HINTS ${CLANG_DIR}/lib) -if (NOT EXISTS ${CLANG_LIBRARY}) - string(REPLACE ";" ", " CLANG_LIB_NAMES_STRING "${CLANG_LIB_NAMES}") - message(FATAL_ERROR "Unable to find the Clang library in ${CLANG_DIR}.\ - Names tried: ${CLANG_LIB_NAMES_STRING}.") -endif() - -message(STATUS "CLANG: ${CLANG_DIR}, ${CLANG_LIBRARY} detected by ${CLANG_DIR_SOURCE}") - -set(CLANG_EXTRA_INCLUDES ${CLANG_DIR}/include) -set(CLANG_EXTRA_LIBRARIES ${CLANG_LIBRARY}) - -# Check if one of the required clang headers is found. Error out early at CMake time instead of -# compile time if not found. -# It can happen that a user uses a distro-provided libclang.so, but no development header package -# was installed (e.g. libclang-6.0-dev on Ubuntu). -set(CMAKE_REQUIRED_INCLUDES ${CLANG_EXTRA_INCLUDES}) -set(CLANG_HEADER_FILE_TO_CHECK "clang-c/Index.h") -check_include_file_cxx(${CLANG_HEADER_FILE_TO_CHECK} CLANG_INCLUDE_FOUND) -unset(CMAKE_REQUIRED_INCLUDES) -if (NOT CLANG_INCLUDE_FOUND) - # Need to unset so that when installing the package, CMake doesn't complain that the header - # still isn't found. - unset(CLANG_INCLUDE_FOUND CACHE) - message(FATAL_ERROR "Unable to find required Clang header file ${CLANG_HEADER_FILE_TO_CHECK} \ - in ${CLANG_DIR}/include. Perhaps you forgot to install the clang development header \ - package? (e.g. libclang-6.0-dev)") -endif() +setup_clang() set(SHIBOKEN_VERSION_FILE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/shiboken_version.py") set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS @@ -209,29 +72,6 @@ message("PYTHONINTERP_FOUND: " ${PYTHONINTERP_FOUND}) message("PYTHON_EXECUTABLE: " ${PYTHON_EXECUTABLE}) message("PYTHON_VERSION: " ${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR}.${PYTHON_VERSION_PATCH}) -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), - # ('.abi3.so', 'rb', 3), ('.so', 'rb', 3), ('.py', 'r', 1), ('.pyc', 'rb', 2)] - # We pick the first most detailed one, strip of the file extension part. - - execute_process( - COMMAND ${PYTHON_EXECUTABLE} -c "if True: - import imp, re - first_suffix = imp.get_suffixes()[0][0] - res = re.search(r'^(.+)\\.', first_suffix) - if res: - first_suffix = res.group(1) - else: - first_suffix = '' - print(first_suffix) - " - OUTPUT_VARIABLE PYTHON_EXTENSION_SUFFIX - OUTPUT_STRIP_TRAILING_WHITESPACE) - message("PYTHON_EXTENSION_SUFFIX: " ${PYTHON_EXTENSION_SUFFIX}) -endmacro() - - if (NOT PYTHON_EXTENSION_SUFFIX) get_python_extension_suffix() endif() @@ -242,42 +82,15 @@ set(PYTHON_LIMITED_API 0) shiboken_check_if_limited_api() if (PYTHON_LIMITED_API) - if (WIN32 AND NOT EXISTS "${PYTHON_LIMITED_LIBRARIES}") - message(FATAL_ERROR "The Limited API was enabled, but ${PYTHON_LIMITED_LIBRARIES} was not found!") - endif() - message(STATUS "******************************************************") - message(STATUS "** Limited API enabled ${PYTHON_LIMITED_LIBRARIES}") - message(STATUS "******************************************************") + set_limited_api() endif() if (NOT PYTHON_CONFIG_SUFFIX) - if (PYTHON_VERSION_MAJOR EQUAL 2) - set(PYTHON_CONFIG_SUFFIX "-python${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR}") - if (PYTHON_EXTENSION_SUFFIX) - set(PYTHON_CONFIG_SUFFIX "${PYTHON_CONFIG_SUFFIX}${PYTHON_EXTENSION_SUFFIX}") - endif() - elseif (PYTHON_VERSION_MAJOR EQUAL 3) - if (PYTHON_LIMITED_API) - if(WIN32) - set(PYTHON_EXTENSION_SUFFIX "") - else() - set(PYTHON_EXTENSION_SUFFIX ".abi3") - endif() - set(PYTHON_CONFIG_SUFFIX ".abi3") - else() - set(PYTHON_CONFIG_SUFFIX "${PYTHON_EXTENSION_SUFFIX}") - endif() - endif() + set_python_config_suffix() endif() if (NOT PYTHON_SHARED_LIBRARY_SUFFIX) - set(PYTHON_SHARED_LIBRARY_SUFFIX "${PYTHON_CONFIG_SUFFIX}") - - # Append a "v" to disambiguate the python version and the shiboken version in the - # shared library file name. - if (APPLE AND PYTHON_VERSION_MAJOR EQUAL 2) - set(PYTHON_SHARED_LIBRARY_SUFFIX "${PYTHON_SHARED_LIBRARY_SUFFIX}v") - endif() + set_python_shared_library_suffix() endif() if (NOT PYTHON_CONFIG_SUFFIX) @@ -291,39 +104,10 @@ message(STATUS "PYTHON_SHARED_LIBRARY_SUFFIX: ${PYTHON_SHARED_LIBRARY_SUFFIX}") if (NOT PYTHON_SITE_PACKAGES) - execute_process( - COMMAND ${PYTHON_EXECUTABLE} -c "if True: - from distutils import sysconfig - from os.path import sep - print(sysconfig.get_python_lib(1, 0, prefix='${CMAKE_INSTALL_PREFIX}').replace(sep, '/')) - " - OUTPUT_VARIABLE PYTHON_SITE_PACKAGES - OUTPUT_STRIP_TRAILING_WHITESPACE) - if (NOT PYTHON_SITE_PACKAGES) - message(FATAL_ERROR "Could not detect Python module installation directory.") - elseif (APPLE) - message(STATUS "!!! The generated bindings will be installed on ${PYTHON_SITE_PACKAGES}, is it right!?") - endif() -endif() - -if(MSVC) - # Qt5: this flag has changed from /Zc:wchar_t- in Qt4.X - set(CMAKE_CXX_FLAGS "/Zc:wchar_t /GR /EHsc /DWIN32 /D_WINDOWS /D_SCL_SECURE_NO_WARNINGS") -else() - if(CMAKE_HOST_UNIX AND NOT CYGWIN) - add_definitions(-fPIC) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -fvisibility=hidden -Wno-strict-aliasing") - endif() - set(CMAKE_CXX_FLAGS_DEBUG "-g") - option(ENABLE_GCC_OPTIMIZATION "Enable specific GCC flags to optimization library size and performance. Only available on Release Mode" 0) - if(ENABLE_GCC_OPTIMIZATION) - set(CMAKE_CXX_FLAGS_RELEASE "-DNDEBUG -Os -Wl,-O1") - if(NOT CMAKE_HOST_APPLE) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wl,--hash-style=gnu") - endif() - endif() + set_python_site_packages() endif() +set_cmake_cxx_flags() set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D QT_NO_CAST_FROM_ASCII -D QT_NO_CAST_TO_ASCII") # Force usage of the C++11 standard, without a silent fallback @@ -332,8 +116,10 @@ set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(LIB_SUFFIX "" CACHE STRING "Define suffix of directory name (32/64)" ) -set(LIB_INSTALL_DIR "lib${LIB_SUFFIX}" CACHE PATH "The subdirectory relative to the install prefix where libraries will be installed (default is /lib${LIB_SUFFIX})" FORCE) -set(BIN_INSTALL_DIR "bin" CACHE PATH "The subdirectory relative to the install prefix where dlls will be installed (default is /bin)" FORCE) +set(LIB_INSTALL_DIR "lib${LIB_SUFFIX}" CACHE PATH "The subdirectory relative to the install \ + prefix where libraries will be installed (default is /lib${LIB_SUFFIX})" FORCE) +set(BIN_INSTALL_DIR "bin" CACHE PATH "The subdirectory relative to the install prefix where \ + dlls will be installed (default is /bin)" FORCE) if (WIN32) set(PATH_SEP "\;") @@ -342,26 +128,17 @@ else() endif() if(CMAKE_HOST_APPLE) - set(OSX_USE_LIBCPP "OFF" CACHE BOOL "Explicitly link the libc++ standard library (useful for osx deployment targets lower than 10.9.") + set(OSX_USE_LIBCPP "OFF" CACHE BOOL "Explicitly link the libc++ standard library \ + (useful for osx deployment targets lower than 10.9.") if(OSX_USE_LIBCPP) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++") endif() endif() -# Build with Address sanitizer enabled if requested. This may break things, so use at your own risk. +# 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") + set_sanitize_address() endif() # Detect if the python libs were compiled in debug mode @@ -410,23 +187,12 @@ execute_process( set(SHIBOKEN_BUILD_TYPE "${CMAKE_BUILD_TYPE}") if(CMAKE_BUILD_TYPE STREQUAL "Debug") - set(SHIBOKEN_BUILD_TYPE "Debug") - - if(NOT PYTHON_DEBUG_LIBRARIES) - message(WARNING "Python debug shared library not found; assuming python was built with shared library support disabled.") - endif() - - if(NOT PYTHON_WITH_DEBUG) - message(WARNING "Compiling shiboken2 with debug enabled, but the python executable was not compiled with debug support.") - else() - set(SBK_PKG_CONFIG_PY_DEBUG_DEFINITION " -DPy_DEBUG") - endif() - - if (PYTHON_WITH_COUNT_ALLOCS) - set(SBK_PKG_CONFIG_PY_DEBUG_DEFINITION "${SBK_PKG_CONFIG_PY_DEBUG_DEFINITION} -DCOUNT_ALLOCS") - endif() + set_debug_build() endif() +###################################################################### +# Adding sub directories to build +###################################################################### add_subdirectory(ApiExtractor) set(generator_plugin_DIR ${LIB_INSTALL_DIR}/generatorrunner${generator_SUFFIX}) @@ -451,7 +217,7 @@ if (Qt5Core_FOUND AND PYTHONINTERP_FOUND) add_subdirectory(tests) endif() else() - message(WARNING "Some dependencies were not found, shiboken2 generator compilation disabled!") + message(WARNING "Some dependencies were not found: shiboken2 generator compilation disabled!") endif() add_subdirectory(data) diff --git a/sources/shiboken2/data/shiboken_helpers.cmake b/sources/shiboken2/data/shiboken_helpers.cmake index f4dd4d5dc..8111fa61f 100644 --- a/sources/shiboken2/data/shiboken_helpers.cmake +++ b/sources/shiboken2/data/shiboken_helpers.cmake @@ -1,5 +1,274 @@ include(CMakeParseArguments) +macro(set_python_shared_library_suffix) + set(PYTHON_SHARED_LIBRARY_SUFFIX "${PYTHON_CONFIG_SUFFIX}") + + # Append a "v" to disambiguate the python version and the shiboken version in the + # shared library file name. + if (APPLE AND PYTHON_VERSION_MAJOR EQUAL 2) + set(PYTHON_SHARED_LIBRARY_SUFFIX "${PYTHON_SHARED_LIBRARY_SUFFIX}v") + endif() +endmacro() + +macro(set_limited_api) + if (WIN32 AND NOT EXISTS "${PYTHON_LIMITED_LIBRARIES}") + message(FATAL_ERROR "The Limited API was enabled, but ${PYTHON_LIMITED_LIBRARIES} was not found!") + endif() + message(STATUS "******************************************************") + message(STATUS "** Limited API enabled ${PYTHON_LIMITED_LIBRARIES}") + message(STATUS "******************************************************") +endmacro() + +macro(set_debug_build) + set(SHIBOKEN_BUILD_TYPE "Debug") + + if(NOT PYTHON_DEBUG_LIBRARIES) + message(WARNING "Python debug shared library not found; \ + assuming python was built with shared library support disabled.") + endif() + + if(NOT PYTHON_WITH_DEBUG) + message(WARNING "Compiling shiboken2 with debug enabled, \ + but the python executable was not compiled with debug support.") + else() + set(SBK_PKG_CONFIG_PY_DEBUG_DEFINITION " -DPy_DEBUG") + endif() + + if (PYTHON_WITH_COUNT_ALLOCS) + set(SBK_PKG_CONFIG_PY_DEBUG_DEFINITION "${SBK_PKG_CONFIG_PY_DEBUG_DEFINITION} -DCOUNT_ALLOCS") + endif() +endmacro() + +macro(setup_sanitize_address) + # 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") +endmacro() + +macro(set_cmake_cxx_flags) +if(MSVC) + # Qt5: this flag has changed from /Zc:wchar_t- in Qt4.X + set(CMAKE_CXX_FLAGS "/Zc:wchar_t /GR /EHsc /DWIN32 /D_WINDOWS /D_SCL_SECURE_NO_WARNINGS") + #set(CMAKE_CXX_FLAGS "/Zc:wchar_t /GR /EHsc /DNOCOLOR /DWIN32 /D_WINDOWS /D_SCL_SECURE_NO_WARNINGS") # XXX +else() + if(CMAKE_HOST_UNIX AND NOT CYGWIN) + add_definitions(-fPIC) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -fvisibility=hidden -Wno-strict-aliasing") + endif() + set(CMAKE_CXX_FLAGS_DEBUG "-g") + option(ENABLE_GCC_OPTIMIZATION "Enable specific GCC flags to optimization library \ + size and performance. Only available on Release Mode" 0) + if(ENABLE_GCC_OPTIMIZATION) + set(CMAKE_CXX_FLAGS_RELEASE "-DNDEBUG -Os -Wl,-O1") + if(NOT CMAKE_HOST_APPLE) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wl,--hash-style=gnu") + endif() + endif() + if(CMAKE_HOST_APPLE) + # ALTERNATIVE_QT_INCLUDE_DIR is deprecated, because CMake takes care of finding the proper + # include folders using the qmake found in the environment. Only use it for now in case + # something goes wrong with the cmake process. + if(ALTERNATIVE_QT_INCLUDE_DIR AND NOT QT_INCLUDE_DIR) + set(QT_INCLUDE_DIR ${ALTERNATIVE_QT_INCLUDE_DIR}) + endif() + endif() +endif() + +endmacro() + +macro(set_python_site_packages) + execute_process( + COMMAND ${PYTHON_EXECUTABLE} -c "if True: + from distutils import sysconfig + from os.path import sep + print(sysconfig.get_python_lib(1, 0, prefix='${CMAKE_INSTALL_PREFIX}').replace(sep, '/')) + " + OUTPUT_VARIABLE PYTHON_SITE_PACKAGES + OUTPUT_STRIP_TRAILING_WHITESPACE) + if (NOT PYTHON_SITE_PACKAGES) + message(FATAL_ERROR "Could not detect Python module installation directory.") + elseif (APPLE) + message(STATUS "!!! The generated bindings will be installed on ${PYTHON_SITE_PACKAGES}, \ + is it right!?") + endif() +endmacro() + +macro(set_python_config_suffix) + if (PYTHON_VERSION_MAJOR EQUAL 2) + set(PYTHON_CONFIG_SUFFIX "-python${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR}") + if (PYTHON_EXTENSION_SUFFIX) + set(PYTHON_CONFIG_SUFFIX "${PYTHON_CONFIG_SUFFIX}${PYTHON_EXTENSION_SUFFIX}") + endif() + elseif (PYTHON_VERSION_MAJOR EQUAL 3) + if (PYTHON_LIMITED_API) + if(WIN32) + set(PYTHON_EXTENSION_SUFFIX "") + else() + set(PYTHON_EXTENSION_SUFFIX ".abi3") + endif() + set(PYTHON_CONFIG_SUFFIX ".abi3") + else() + set(PYTHON_CONFIG_SUFFIX "${PYTHON_EXTENSION_SUFFIX}") + endif() + endif() +endmacro() + +macro(setup_clang) + set(CLANG_DIR "") + set(CLANG_DIR_SOURCE "") + + set(clang_not_found_message "Unable to detect CLANG location by checking LLVM_INSTALL_DIR, \ + CLANG_INSTALL_DIR or running llvm-config.") + + if (DEFINED ENV{LLVM_INSTALL_DIR}) + set(CLANG_DIR $ENV{LLVM_INSTALL_DIR}) + string(REPLACE "_ARCH_" "${PYTHON_ARCH}" CLANG_DIR "${CLANG_DIR}") + set(CLANG_DIR_SOURCE "LLVM_INSTALL_DIR") + elseif (DEFINED ENV{CLANG_INSTALL_DIR}) + set(CLANG_DIR $ENV{CLANG_INSTALL_DIR}) + string(REPLACE "_ARCH_" "${PYTHON_ARCH}" CLANG_DIR "${CLANG_DIR}") + set(CLANG_DIR_SOURCE "CLANG_INSTALL_DIR") + else () + if (NOT LLVM_CONFIG) + get_llvm_config() + endif() + set(CLANG_DIR_SOURCE "${LLVM_CONFIG}") + if ("${CLANG_DIR_SOURCE}" STREQUAL "") + message(FATAL_ERROR "${clang_not_found_message}") + endif() + + EXEC_PROGRAM("${LLVM_CONFIG}" ARGS "--prefix" OUTPUT_VARIABLE CLANG_DIR) + if (NOT "${CLANG_DIR}" STREQUAL "") + EXEC_PROGRAM("${LLVM_CONFIG}" ARGS "--version" OUTPUT_VARIABLE CLANG_VERSION) + if (CLANG_VERSION VERSION_LESS 3.9) + message(FATAL_ERROR "libclang version 3.9 or higher is required \ + (${LLVM_CONFIG} detected ${CLANG_VERSION} at ${CLANG_DIR}).") + endif() + endif() + endif() + + if ("${CLANG_DIR}" STREQUAL "") + message(FATAL_ERROR "${clang_not_found_message}") + elseif (NOT IS_DIRECTORY ${CLANG_DIR}) + message(FATAL_ERROR "${CLANG_DIR} detected by ${CLANG_DIR_SOURCE} does not exist.") + endif() + + # The non-development Debian / Ubuntu packages (e.g. libclang1-6.0) do not ship a + # libclang.so symlink, but only libclang-6.0.so.1 and libclang.so.1 (adjusted for version number). + # Thus searching for libclang would not succeed. + # The "libclang.so" symlink is shipped as part of the development package (libclang-6.0-dev) which + # we need anyway because of the headers. Thus we will search for libclang.so.1 also, and complain + # about the headers not being found in a check further down. This is more friendly to the user, + # so they don't scratch their head thinking that they have already installed the necessary package. + set(CLANG_LIB_NAMES clang libclang.so libclang.so.1) + if(MSVC) + set(CLANG_LIB_NAMES libclang) + endif() + + find_library(CLANG_LIBRARY NAMES ${CLANG_LIB_NAMES} HINTS ${CLANG_DIR}/lib) + if (NOT EXISTS ${CLANG_LIBRARY}) + string(REPLACE ";" ", " CLANG_LIB_NAMES_STRING "${CLANG_LIB_NAMES}") + message(FATAL_ERROR "Unable to find the Clang library in ${CLANG_DIR}.\ + Names tried: ${CLANG_LIB_NAMES_STRING}.") + endif() + + message(STATUS "CLANG: ${CLANG_DIR}, ${CLANG_LIBRARY} detected by ${CLANG_DIR_SOURCE}") + + set(CLANG_EXTRA_INCLUDES ${CLANG_DIR}/include) + set(CLANG_EXTRA_LIBRARIES ${CLANG_LIBRARY}) + + # Check if one of the required clang headers is found. Error out early at CMake time instead of + # compile time if not found. + # It can happen that a user uses a distro-provided libclang.so, but no development header package + # was installed (e.g. libclang-6.0-dev on Ubuntu). + set(CMAKE_REQUIRED_INCLUDES ${CLANG_EXTRA_INCLUDES}) + set(CLANG_HEADER_FILE_TO_CHECK "clang-c/Index.h") + check_include_file_cxx(${CLANG_HEADER_FILE_TO_CHECK} CLANG_INCLUDE_FOUND) + unset(CMAKE_REQUIRED_INCLUDES) + if (NOT CLANG_INCLUDE_FOUND) + # Need to unset so that when installing the package, CMake doesn't complain that the header + # still isn't found. + unset(CLANG_INCLUDE_FOUND CACHE) + message(FATAL_ERROR "Unable to find required Clang header file ${CLANG_HEADER_FILE_TO_CHECK} \ + in ${CLANG_DIR}/include. Perhaps you forgot to install the clang development header \ + package? (e.g. libclang-6.0-dev)") + endif() +endmacro() + +macro(set_quiet_build) + # Don't display "up-to-date / install" messages when installing, to reduce visual clutter. + set(CMAKE_INSTALL_MESSAGE NEVER PARENT_SCOPE) + # Override message not to display info messages when doing a quiet build. + function(message) + list(GET ARGV 0 MessageType) + if (MessageType STREQUAL FATAL_ERROR OR + MessageType STREQUAL SEND_ERROR OR + MessageType STREQUAL WARNING OR + MessageType STREQUAL AUTHOR_WARNING) + list(REMOVE_AT ARGV 0) + _message(${MessageType} "${ARGV}") + endif() + endfunction() +endmacro() + +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), + # ('.abi3.so', 'rb', 3), ('.so', 'rb', 3), ('.py', 'r', 1), ('.pyc', 'rb', 2)] + # We pick the first most detailed one, strip of the file extension part. + + execute_process( + COMMAND ${PYTHON_EXECUTABLE} -c "if True: + import imp, re + first_suffix = imp.get_suffixes()[0][0] + res = re.search(r'^(.+)\\.', first_suffix) + if res: + first_suffix = res.group(1) + else: + first_suffix = '' + print(first_suffix) + " + OUTPUT_VARIABLE PYTHON_EXTENSION_SUFFIX + OUTPUT_STRIP_TRAILING_WHITESPACE) + message("PYTHON_EXTENSION_SUFFIX: " ${PYTHON_EXTENSION_SUFFIX}) +endmacro() + +macro(get_llvm_config) + execute_process( + COMMAND ${PYTHON_EXECUTABLE} -c "if True: + import os + import sys + sys.path.append(os.path.realpath(os.path.join('${CMAKE_CURRENT_LIST_DIR}', '..', '..'))) + from build_scripts.utils import find_llvm_config + llvmConfig = find_llvm_config() + if llvmConfig: + print(llvmConfig) + " + OUTPUT_VARIABLE LLVM_CONFIG + OUTPUT_STRIP_TRAILING_WHITESPACE) + message("LLVM_CONFIG: " ${LLVM_CONFIG}) +endmacro() + +macro(get_python_arch) + execute_process( + COMMAND ${PYTHON_EXECUTABLE} -c "if True: + import sys + print('64' if sys.maxsize > 2**31-1 else '32') + " + OUTPUT_VARIABLE PYTHON_ARCH + OUTPUT_STRIP_TRAILING_WHITESPACE) + message("PYTHON_ARCH: " ${PYTHON_ARCH}) +endmacro() + macro(shiboken_parse_all_arguments prefix type flags options multiopts) cmake_parse_arguments(${prefix} "${flags}" "${options}" "${multiopts}" ${ARGN}) if(DEFINED ${prefix}_UNPARSED_ARGUMENTS) diff --git a/sources/shiboken2/doc/_themes/pysidedocs/static/pyside.css b/sources/shiboken2/doc/_themes/pysidedocs/static/pyside.css index 956e3113b..23e460262 100644 --- a/sources/shiboken2/doc/_themes/pysidedocs/static/pyside.css +++ b/sources/shiboken2/doc/_themes/pysidedocs/static/pyside.css @@ -3,8 +3,8 @@ /* -- admonitions -- */ div.admonition { - margin: 1em 0 1em; - padding: 7px; + margin: 1.5em 0 1.5em; + padding: 0; } div.admonition dt { @@ -19,6 +19,23 @@ p.admonition-title { margin: 0px 10px 5px 0px; font-weight: bold; } + +div.admonition code { + font-family: inherit; +} + +p.admonition-title + p { + padding-left: 1em; +} + +div.admonition a:after { + content: ', '; +} + +div.admonition a:last-child:after { + content: ''; +} + .body { width: 100% } @@ -35,18 +52,21 @@ div.body p.centered { margin-top: 25px; } +div.warning, div.seealso, div.note { + padding: 6px 0px 6px 10px; + border: none; +} + div.warning { background-color: #ffe4e4; - border: 1px solid #f66; } div.seealso { - background-color: #ffffcc; - border: 1px solid #ffff66; + background-color: #fff2d6; } div.note { - border: 1px solid #e3e3e3; + background-color: #f3f3f4; } table.docutils { @@ -91,7 +111,7 @@ h2 em { .body blockquote { border: none; padding-left: 0; - margin-bottom: 2em; + margin-bottom: 1.5em; } .sphinxsidebar { @@ -146,11 +166,8 @@ h2 em { display: block; padding: 5px; margin: 0 10px 10px 0; - border: 1px solid #ddd; - background-color: #f4f4f4; - -moz-border-radius:6px; - -webkit-border-radius:6px; - -khtml-border-radius:6px; + border: none; + background-color: #e2e2e2; } .section .docutils.container td { @@ -290,14 +307,23 @@ tt.descname { } #functions ul, #virtual-functions ul, #slots ul, #signals ul, #static-functions ul { - list-style: none; - margin: 0px; - padding: 10px; + margin: 0; + padding: 6px; border: 1px solid #ddd; - background-color: #f4f4f4; - -moz-border-radius:10px; - -webkit-border-radius:10px; - -khtml-border-radius:10px; + border-radius: 0; + background-color: #e2e2e2; +} + +#functions p, #virtual-functions p, #slots p, #signals p, #static-functions p { + margin: 0; + padding: 0; +} + +#functions li, #virtual-functions li, #slots li, #signals li, #static-functions li { + list-style: none; + margin: 5px; + padding: 0; + font-size: 90%; } #synopsis span.pre { @@ -312,11 +338,94 @@ tt.descname { margin: 0px; margin-bottom: 10px; padding: 10px; - border: 1px solid #ddd; - background-color: #f4f4f4; - -moz-border-radius:10px; - -webkit-border-radius:10px; - -khtml-border-radius:10px; + font-weight: bold; + background-color: #e2e2e2; + border: none; + border-radius: 0; +} + +#detailed-description dd > blockquote, +#detailed-description dd > .field-list { + font-family: 'Droid Sans Mono'; + font-size: small; + border-left: 10px solid #e2e2e2; + padding-left: 10px; + margin-bottom: 1.5em; +} + +#detailed-description dd > blockquote blockquote { + border: none; + padding: 0; +} + +#detailed-description .class .field-odd, +#detailed-description .method .field-odd, +#detailed-description .staticmethod .field-odd, +#detailed-description .attribute .field-odd { + margin: 0; + padding: 1px 0 0 0; + background-color: #ffffff; + +} + +#detailed-description .class .field-even, +#detailed-description .method .field-even, +#detailed-description .staticmethod .field-even, +#detailed-description .attribute .field-even { + margin: 0; + padding: 1px 0 0 0; + background-color: #ffffff; +} + +#detailed-description .class .field-odd li, +#detailed-description .method .field-odd li, +#detailed-description .staticmethod .field-odd li, +#detailed-description .attribute .field-odd li { + list-style: none; + margin: 0; + padding: 0; + +} + +#detailed-description .class .field-even li, +#detailed-description .method .field-even li, +#detailed-description .staticmethod .field-even li, +#detailed-description .attribute .field-even li { + list-style: none; + margin: 0; + padding: 0; +} + +#detailed-description .class .field-odd p, +#detailed-description .method .field-odd p, +#detailed-description .staticmethod .field-odd p, +#detailed-description .attribute .field-odd p{ + margin: 0; + margin-left: 20px; + +} + +#detailed-description .class .field-even p, +#detailed-description .method .field-even p, +#detailed-description .staticmethod .field-even p, +#detailed-description .attribute .field-even p{ + margin: 0; + margin-left: 20px; +} + +#detailed-description .class .field-odd p:last-child, +#detailed-description .method .field-odd p:last-child, +#detailed-description .staticmethod .field-odd p:last-child, +#detailed-description .attribute .field-odd p:last-child { + margin-bottom: 10px; + +} + +#detailed-description .class .field-even p:last-child, +#detailed-description .method .field-even p:last-child, +#detailed-description .staticmethod .field-even p:last-child, +#detailed-description .attribute .field-even p:last-child{ + margin-bottom: 10px; } .document dl.attribute, @@ -333,6 +442,10 @@ tt.descname { padding-left: 1em; } +#detailed-description .attribute td:nth-child(1) { + font-family: 'Droid Sans Mono'; +} + /* Qt theme */ #navbar { position:fixed; @@ -1056,9 +1169,15 @@ div.multi-column div { } .col-2 h2,.toc h3,.sidebar-content h2, .sidebar-content h3,.sectionlist h2, +.sphinxsidebar { + position: fixed; + overflow: scroll; + overflow-x: hidden; + overflow-y: hidden; +} .sphinxsidebar h3 { - font-weight:400; - margin-bottom:1em + font-weight: bold; + margin-bottom:1em; } .toc h3 a { color:#404244 @@ -1450,6 +1569,10 @@ span.wrap:active { code,.codelike { font-family:"Droid Sans Mono" } +#detailed-description .function dt > code, +#detailed-description .function dt > em { + font-weight:bold +} h3.fn code { font-size:0.75em; float:right; diff --git a/sources/shiboken2/libshiboken/basewrapper.cpp b/sources/shiboken2/libshiboken/basewrapper.cpp index b9f6735d8..9d233b847 100644 --- a/sources/shiboken2/libshiboken/basewrapper.cpp +++ b/sources/shiboken2/libshiboken/basewrapper.cpp @@ -92,6 +92,7 @@ static PyType_Slot SbkObjectType_Type_slots[] = { {Py_tp_alloc, reinterpret_cast<void *>(PyType_GenericAlloc)}, {Py_tp_new, reinterpret_cast<void *>(SbkObjectTypeTpNew)}, {Py_tp_free, reinterpret_cast<void *>(PyObject_GC_Del)}, + {Py_tp_getset, reinterpret_cast<void *>(SbkObjectType_Type_getsetlist)}, {0, nullptr} }; static PyType_Spec SbkObjectType_Type_spec = { @@ -106,7 +107,7 @@ static PyType_Spec SbkObjectType_Type_spec = { #if PY_VERSION_HEX < 0x03000000 /***************************************************************************** * - * PYSIDE-816: Workaround for Python 2.7 + * PYSIDE-816: Workaround for Python 2.7 for SbkObjectType_TypeF(). * * This is an add-on for function typeobject.c:tp_new_wrapper from Python 2.7 . * Problem: @@ -121,9 +122,16 @@ static PyType_Spec SbkObjectType_Type_spec = { * The problem is that heap types have this unwanted dependency. * But we cannot get at static slot_tp_new, and so we have to use * the original function and patch Py_TPFLAGS_HEAPTYPE away during the call. + * + * PYSIDE-1051: The same problem holds for all dynamic metatypes generated by + * SbkObjectTypeTpNew() and all types generated by + * introduceWrapperType() . + * + * This led to a drastic overhaul of patch_tp_new_wrapper() which now adds + * the new wrapper to exactly those types which have the old wrapper. */ -static PyCFunction old_tp_new_wrapper = nullptr; +ternaryfunc old_tp_new_wrapper = nullptr; static PyObject * tp_new_wrapper(PyObject *self, PyObject *args, PyObject *kwds) @@ -136,9 +144,9 @@ tp_new_wrapper(PyObject *self, PyObject *args, PyObject *kwds) return ret; } -// This is intentionally the new docstring of Python 3.7 . +// This is intentionally the __new__ docstring of Python 3.7 . static struct PyMethodDef tp_new_methoddef[] = { - {"__new__", (PyCFunction)tp_new_wrapper, METH_VARARGS|METH_KEYWORDS, + {"__new__", reinterpret_cast<PyCFunction>(tp_new_wrapper), METH_VARARGS|METH_KEYWORDS, PyDoc_STR("__new__($type, *args, **kwargs)\n--\n\n" "Create and return a new object. " "See help(type) for accurate signature.")}, @@ -146,43 +154,39 @@ static struct PyMethodDef tp_new_methoddef[] = { }; static int -get_old_tp_new_wrapper(void) -{ - // We get the old tp_new_wrapper from any initialized type. - PyTypeObject *type = &PyType_Type; - PyObject *dict = type->tp_dict; - PyObject *key, *func = nullptr; - Py_ssize_t pos = 0; - while (PyDict_Next(dict, &pos, &key, &func)) { - char *name = PyString_AsString(key); - if (strcmp(name, "__new__") == 0) { - break; +patch_tp_new_wrapper(PyTypeObject *type) +{ + /* + * The old tp_new_wrapper is added to all types that have tp_new. + * We patch that with a version that ignores the heaptype flag. + */ + static PyObject *__new__ = nullptr; + if (old_tp_new_wrapper == nullptr) { + if ((__new__ = Py_BuildValue("s", "__new__")) == nullptr) + return -1; + PyObject *func = PyDict_GetItem(PyType_Type.tp_dict, __new__); + PyCFunctionObject *pycf_ob = reinterpret_cast<PyCFunctionObject *>(func); + old_tp_new_wrapper = reinterpret_cast<ternaryfunc>(pycf_ob->m_ml->ml_meth); + } + PyObject *mro = type->tp_mro; + Py_ssize_t i, n = PyTuple_GET_SIZE(mro); + for (i = 0; i < n; i++) { + type = reinterpret_cast<PyTypeObject *>(PyTuple_GET_ITEM(mro, i)); + PyObject *existing = PyDict_GetItem(type->tp_dict, __new__); + if (existing && PyCFunction_Check(existing) + && type->tp_flags & Py_TPFLAGS_HEAPTYPE) { + auto *pycf_ob = reinterpret_cast<PyCFunctionObject *>(existing); + auto existing_wrapper = reinterpret_cast<ternaryfunc>(pycf_ob->m_ml->ml_meth); + if (existing_wrapper == tp_new_wrapper) + break; + if (existing_wrapper == old_tp_new_wrapper) { + PyObject *ob_type = reinterpret_cast<PyObject *>(type); + Shiboken::AutoDecRef func(PyCFunction_New(tp_new_methoddef, ob_type)); + if (func.isNull() || PyDict_SetItem(type->tp_dict, __new__, func)) + return -1; + } } } - if (func == nullptr) - return -1; - PyCFunctionObject *pycf_ob = reinterpret_cast<PyCFunctionObject *>(func); - old_tp_new_wrapper = pycf_ob->m_ml->ml_meth; - return 0; -} - -static int -add_tp_new_wrapper(PyTypeObject *type) -{ - // get the original tp_new_wrapper - if (old_tp_new_wrapper == nullptr && get_old_tp_new_wrapper() < 0) - return -1; - // initialize tp_dict - if (type->tp_dict == nullptr) - type->tp_dict = PyDict_New(); - if (type->tp_dict == nullptr) - return -1; - PyObject *ob_type = reinterpret_cast<PyObject *>(type); - Shiboken::AutoDecRef func(PyCFunction_New(tp_new_methoddef, ob_type)); - if (func.isNull()) - return -1; - if (PyDict_SetItemString(type->tp_dict, "__new__", func)) - return -1; return 0; } /*****************************************************************************/ @@ -197,7 +201,7 @@ PyTypeObject *SbkObjectType_TypeF(void) PepHeapType_SIZE + sizeof(SbkObjectTypePrivate); type = reinterpret_cast<PyTypeObject *>(PyType_FromSpec(&SbkObjectType_Type_spec)); #if PY_VERSION_HEX < 0x03000000 - if (add_tp_new_wrapper(type) < 0) + if (patch_tp_new_wrapper(type) < 0) return nullptr; #endif } @@ -452,6 +456,11 @@ PyObject *SbkObjectTypeTpNew(PyTypeObject *metatype, PyObject *args, PyObject *k auto *newType = reinterpret_cast<SbkObjectType *>(type_new(metatype, args, kwds)); if (!newType) return nullptr; +#if PY_VERSION_HEX < 0x03000000 + // PYSIDE-1051: The newly created metatype needs the PYSIDE-816 wrapper, too. + if (patch_tp_new_wrapper(&newType->type) < 0) + return nullptr; +#endif Shiboken::ObjectType::initPrivateData(newType); SbkObjectTypePrivate *sotp = PepType_SOTP(newType); @@ -842,6 +851,11 @@ introduceWrapperType(PyObject *enclosingObject, Py_TYPE(heaptype) = SbkObjectType_TypeF(); Py_INCREF(Py_TYPE(heaptype)); auto *type = reinterpret_cast<SbkObjectType *>(heaptype); +#if PY_VERSION_HEX < 0x03000000 + // PYSIDE-1051: The newly created type needs the PYSIDE-816 wrapper, too. + if (patch_tp_new_wrapper(&type->type) < 0) + return nullptr; +#endif if (baseType) { if (baseTypes) { for (int i = 0; i < PySequence_Fast_GET_SIZE(baseTypes); ++i) diff --git a/sources/shiboken2/libshiboken/sbkenum.cpp b/sources/shiboken2/libshiboken/sbkenum.cpp index 71fcf5f64..2dc785884 100644 --- a/sources/shiboken2/libshiboken/sbkenum.cpp +++ b/sources/shiboken2/libshiboken/sbkenum.cpp @@ -412,6 +412,13 @@ PyTypeObject *createScopedEnum(SbkObjectType *scope, const char *name, const cha static PyObject *createEnumItem(PyTypeObject *enumType, const char *itemName, long itemValue) { + char mangled[20]; + if (strcmp(itemName, "None") == 0 + || strcmp(itemName, "False") == 0 || strcmp(itemName, "True") == 0) { + strcpy(mangled, itemName); + strcat(mangled, "_"); + itemName = mangled; + } PyObject *enumItem = newItem(enumType, itemValue, itemName); if (PyDict_SetItemString(enumType->tp_dict, itemName, enumItem) < 0) return nullptr; diff --git a/sources/shiboken2/libshiboken/signature.cpp b/sources/shiboken2/libshiboken/signature.cpp index 07ef366ba..543ccac5f 100644 --- a/sources/shiboken2/libshiboken/signature.cpp +++ b/sources/shiboken2/libshiboken/signature.cpp @@ -100,7 +100,7 @@ CreateSignature(PyObject *props, PyObject *key) { /* * Here is the new function to create all signatures. It simply calls - * into Python and creates a signature object for a dummy-function. + * into Python and creates a signature object directly. * This is so much simpler than using all the attributes explicitly * to support '_signature_is_functionlike()'. */ @@ -314,7 +314,7 @@ pyside_tp_get___signature__(PyObject *obtype_mod, const char *modifier) // forward static PyObject * -GetSignature_Cached(PyObject *props, const char *sig_kind, const char *modifier); +GetSignature_Cached(PyObject *props, const char *func_kind, const char *modifier); static PyObject * GetTypeKey(PyObject *ob) @@ -382,16 +382,16 @@ GetSignature_Function(PyObject *obfunc, const char *modifier) Py_RETURN_NONE; int flags = PyCFunction_GET_FLAGS(obfunc); - const char *sig_kind; + const char *func_kind; if (PyModule_Check(obtype_mod)) - sig_kind = "function"; + func_kind = "function"; else if (flags & METH_CLASS) - sig_kind = "classmethod"; + func_kind = "classmethod"; else if (flags & METH_STATIC) - sig_kind = "staticmethod"; + func_kind = "staticmethod"; else - sig_kind = "method"; - return GetSignature_Cached(props, sig_kind, modifier); + func_kind = "method"; + return GetSignature_Cached(props, func_kind, modifier); } static PyObject * @@ -428,11 +428,15 @@ GetSignature_TypeMod(PyObject *ob, const char *modifier) } static PyObject * -GetSignature_Cached(PyObject *props, const char *sig_kind, const char *modifier) +GetSignature_Cached(PyObject *props, const char *func_kind, const char *modifier) { + // Special case: We want to know the func_kind. + if (modifier && strcmp(modifier, "__func_kind__") == 0) + return Py_BuildValue("s", func_kind); + Shiboken::AutoDecRef key(modifier == nullptr - ? Py_BuildValue("s", sig_kind) - : Py_BuildValue("(ss)", sig_kind, modifier)); + ? Py_BuildValue("s", func_kind) + : Py_BuildValue("(ss)", func_kind, modifier)); PyObject *value = PyDict_GetItem(props, key); if (value == nullptr) { // we need to compute a signature object diff --git a/sources/shiboken2/shibokenmodule/CMakeLists.txt b/sources/shiboken2/shibokenmodule/CMakeLists.txt index 5e5cf21d7..e1eafa12f 100644 --- a/sources/shiboken2/shibokenmodule/CMakeLists.txt +++ b/sources/shiboken2/shibokenmodule/CMakeLists.txt @@ -30,7 +30,6 @@ if(WIN32) endif() target_link_libraries(shibokenmodule PUBLIC libshiboken) -add_dependencies(shibokenmodule shiboken2) create_generator_target(shibokenmodule) install(TARGETS shibokenmodule DESTINATION ${PYTHON_SITE_PACKAGES}/shiboken2) diff --git a/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/layout.py b/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/layout.py index bd827f1ee..384273d92 100644 --- a/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/layout.py +++ b/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/layout.py @@ -56,7 +56,7 @@ used literally as strings like "signature", "existence", etc. """ from textwrap import dedent -from shibokensupport.signature import inspect +from shibokensupport.signature import inspect, typing from shibokensupport.signature.mapping import ellipsis from shibokensupport.signature.lib.tool import SimpleNamespace @@ -162,6 +162,35 @@ def define_nameless_parameter(): NamelessParameter = define_nameless_parameter() +""" +Note on the "Optional" feature: + +When an annotation has a default value that is None, then the +type has to be wrapped into "typing.Optional". + +Note that only the None value creates an Optional expression, +because the None leaves the domain of the variable. +Defaults like integer values are ignored: They stay in the domain. + +That information would be lost when we use the "..." convention. + +Note that the typing module has the remarkable expansion + + Optional[T] is Variant[T, NoneType] + +We want to avoid that when generating the .pyi file. +This is done by a regex in generate_pyi.py . +The following would work in Python 3, but this is a version-dependent +hack that also won't work in Python 2 and would be _very_ complex. +""" +# import sys +# if sys.version_info[0] == 3: +# class hugo(list):pass +# typing._normalize_alias["hugo"] = "Optional" +# Optional = typing._alias(hugo, typing.T, inst=False) +# else: +# Optional = typing.Optional + def make_signature_nameless(signature): """ @@ -174,6 +203,13 @@ def make_signature_nameless(signature): signature.parameters[key].__class__ = NamelessParameter +_POSITIONAL_ONLY = inspect._POSITIONAL_ONLY +_POSITIONAL_OR_KEYWORD = inspect._POSITIONAL_OR_KEYWORD +_VAR_POSITIONAL = inspect._VAR_POSITIONAL +_KEYWORD_ONLY = inspect._KEYWORD_ONLY +_VAR_KEYWORD = inspect._VAR_KEYWORD +_empty = inspect._empty + def create_signature(props, key): if not props: # empty signatures string @@ -204,26 +240,39 @@ def create_signature(props, key): elif sig_kind == "classmethod": varnames = ("klass",) + varnames else: - raise SystemError("Methods must be function, method, staticmethod or " - "classmethod") + raise SystemError("Methods must be function, method, staticmethod" + " or classmethod") # calculate the modifications defaults = props["defaults"][:] if not layout.defaults: defaults = () - if layout.ellipsis: - defaults = (ellipsis,) * len(defaults) annotations = props["annotations"].copy() if not layout.return_annotation and "return" in annotations: del annotations["return"] - # attach parameters to a fake function and build a signature - argstr = ", ".join(varnames) - fakefunc = eval("lambda {}: None".format(argstr)) - fakefunc.__name__ = props["name"] - fakefunc.__defaults__ = defaults - fakefunc.__kwdefaults__ = props["kwdefaults"] - fakefunc.__annotations__ = annotations - sig = inspect._signature_from_function(inspect.Signature, fakefunc) + # Build a signature. + kind = inspect._POSITIONAL_OR_KEYWORD + params = [] + for idx, name in enumerate(varnames): + if name.startswith("**"): + kind = _VAR_KEYWORD + elif name.startswith("*"): + kind = _VAR_POSITIONAL + ann = annotations.get(name, _empty) + name = name.lstrip("*") + defpos = idx - len(varnames) + len(defaults) + default = defaults[defpos] if defpos >= 0 else _empty + if default is None: + ann = typing.Optional[ann] + if default is not _empty and layout.ellipsis: + default = ellipsis + param = inspect.Parameter(name, kind, annotation=ann, default=default) + params.append(param) + if kind == _VAR_POSITIONAL: + kind = _KEYWORD_ONLY + sig = inspect.Signature(params, + return_annotation=annotations.get('return', _empty), + __validate_parameters__=False) # the special case of nameless parameters if not layout.parameter_names: diff --git a/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/lib/enum_sig.py b/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/lib/enum_sig.py index e6f6dc379..b026a5d20 100644 --- a/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/lib/enum_sig.py +++ b/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/lib/enum_sig.py @@ -52,6 +52,11 @@ by producing a lot of clarity. import sys from shibokensupport.signature import inspect from shibokensupport.signature import get_signature +try: + from PySide2.QtCore import Qt + EnumType = type(Qt.Key) +except ImportError: + EnumType = None class ExactEnumerator(object): @@ -66,6 +71,13 @@ class ExactEnumerator(object): def __init__(self, formatter, result_type=dict): self.fmt = formatter self.result_type = result_type + self.fmt.level = 0 + self.fmt.after_enum = self.after_enum + self._after_enum = False + + def after_enum(self): + ret = self._after_enum + self._after_enum = False def module(self, mod_name): __import__(mod_name) @@ -75,21 +87,24 @@ class ExactEnumerator(object): functions = inspect.getmembers(module, inspect.isroutine) ret = self.result_type() self.fmt.class_name = None - for func_name, func in functions: - ret.update(self.function(func_name, func)) for class_name, klass in members: ret.update(self.klass(class_name, klass)) + if isinstance(klass, EnumType): + self.enum(klass) + for func_name, func in functions: + ret.update(self.function(func_name, func)) return ret def klass(self, class_name, klass): - if not "Shiboken" in repr(klass.mro()): + modname = klass.__module__ + if not (modname.startswith("PySide2") or modname.startswith("shiboken2")): # don't look into any foreign classes! ret = self.result_type() return ret bases_list = [] for base in klass.__bases__: name = base.__name__ - if name == "object": + if name in ("object", "type"): pass else: modname = base.__module__ @@ -97,30 +112,54 @@ class ExactEnumerator(object): bases_list.append(name) class_str = "{}({})".format(class_name, ", ".join(bases_list)) with self.fmt.klass(class_name, class_str): - ret = self.function("__init__", klass) + ret = self.result_type() # class_members = inspect.getmembers(klass) # gives us also the inherited things. class_members = sorted(list(klass.__dict__.items())) subclasses = [] + functions = [] for thing_name, thing in class_members: if inspect.isclass(thing): subclass_name = ".".join((class_name, thing_name)) subclasses.append((subclass_name, thing)) - else: + elif inspect.isroutine(thing): func_name = thing_name.split(".")[0] # remove ".overload" - ret.update(self.function(func_name, thing)) + functions.append((func_name, thing)) + self.fmt.level += 1 for subclass_name, subclass in subclasses: ret.update(self.klass(subclass_name, subclass)) - return ret + if isinstance(subclass, EnumType): + self.enum(subclass) + ret = self.function("__init__", klass) + for func_name, func in functions: + func_kind = get_signature(func, "__func_kind__") + modifier = func_kind if func_kind in ( + "staticmethod", "classmethod") else None + ret.update(self.function(func_name, func, modifier)) + self.fmt.level -= 1 + return ret - def function(self, func_name, func): + def function(self, func_name, func, modifier=None): + self.fmt.level += 1 ret = self.result_type() signature = getattr(func, '__signature__', None) if signature is not None: - with self.fmt.function(func_name, signature) as key: + with self.fmt.function(func_name, signature, modifier) as key: ret[key] = signature + self.fmt.level -= 1 return ret + def enum(self, subclass): + if not hasattr(self.fmt, "enum"): + # this is an optional feature + return + class_name = subclass.__name__ + for enum_name, value in subclass.__dict__.items(): + if type(type(value)) is EnumType: + with self.fmt.enum(class_name, enum_name, int(value)): + pass + self._after_enum = True + def stringify(signature): if isinstance(signature, list): @@ -142,7 +181,7 @@ class SimplifyingEnumerator(ExactEnumerator): is desired. """ - def function(self, func_name, func): + def function(self, func_name, func, modifier=None): ret = self.result_type() signature = get_signature(func, 'existence') sig = stringify(signature) if signature is not None else None @@ -159,11 +198,11 @@ class HintingEnumerator(ExactEnumerator): hinting stubs. Only default values are replaced by "...". """ - def function(self, func_name, func): + def function(self, func_name, func, modifier=None): ret = self.result_type() signature = get_signature(func, 'hintingstub') if signature is not None: - with self.fmt.function(func_name, signature) as key: + with self.fmt.function(func_name, signature, modifier) as key: ret[key] = signature return ret diff --git a/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/lib/tool.py b/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/lib/tool.py index b34bfb404..3b0825049 100644 --- a/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/lib/tool.py +++ b/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/lib/tool.py @@ -43,6 +43,8 @@ from __future__ import print_function, absolute_import tool.py Some useful stuff, see below. +On the function with_metaclass see the answer from Martijn Pieters on +https://stackoverflow.com/questions/18513821/python-metaclass-understanding-the-with-metaclass """ from textwrap import dedent @@ -132,4 +134,21 @@ def build_brace_pattern(level, separators=""): indent = idx * " ", **locals()) return pattern.replace("C", "{").replace("D", "}") + +# Copied from the six module: +def with_metaclass(meta, *bases): + """Create a base class with a metaclass.""" + # This requires a bit of explanation: the basic idea is to make a dummy + # metaclass for one level of class instantiation that replaces itself with + # the actual metaclass. + class metaclass(type): + + def __new__(cls, name, this_bases, d): + return meta(name, bases, d) + + @classmethod + def __prepare__(cls, name, this_bases): + return meta.__prepare__(name, bases) + return type.__new__(metaclass, 'temporary_class', (), {}) + # eof diff --git a/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/loader.py b/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/loader.py index 3d25c5690..a0367883a 100644 --- a/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/loader.py +++ b/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/loader.py @@ -85,20 +85,6 @@ def formatannotation(annotation, base_module=None): return annotation.__module__ + '.' + annotation.__qualname__ return repr(annotation) -# patching __repr__ to disable the __repr__ of typing.TypeVar: -""" - def __repr__(self): - if self.__covariant__: - prefix = '+' - elif self.__contravariant__: - prefix = '-' - else: - prefix = '~' - return prefix + self.__name__ -""" -def _typevar__repr__(self): - return "typing." + self.__name__ - # Note also that during the tests we have a different encoding that would # break the Python license decorated files without an encoding line. @@ -160,12 +146,18 @@ def list_modules(message): print(" {:23}".format(name), repr(module)[:70]) +orig_typing = True if sys.version_info >= (3,): import typing import inspect inspect.formatannotation = formatannotation else: - from shibokensupport import typing27 as typing + if "typing" not in sys.modules: + orig_typing = False + from shibokensupport import typing27 as typing + sys.modules["typing"] = typing + else: + import typing import inspect namespace = inspect.__dict__ from shibokensupport import backport_inspect as inspect @@ -174,7 +166,6 @@ else: inspect.__doc__ += _doc # force inspect to find all attributes. See "heuristic" in pydoc.py! inspect.__all__ = list(x for x in dir(inspect) if not x.startswith("_")) -typing.TypeVar.__repr__ = _typevar__repr__ # Fix the module names in typing if possible. This is important since # the typing names should be I/O compatible, so that typing.Dict @@ -206,7 +197,7 @@ def move_into_pyside_package(): put_into_package(PySide2.support.signature, importhandler) put_into_package(PySide2.support.signature.lib, enum_sig) - put_into_package(PySide2.support.signature, typing) + put_into_package(None if orig_typing else PySide2.support.signature, typing) put_into_package(PySide2.support.signature, inspect) from shibokensupport.signature import mapping diff --git a/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/mapping.py b/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/mapping.py index 5d6a24016..d77d34f97 100644 --- a/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/mapping.py +++ b/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/mapping.py @@ -55,6 +55,7 @@ import os from shibokensupport.signature import typing from shibokensupport.signature.typing import TypeVar, Generic +from shibokensupport.signature.lib.tool import with_metaclass class ellipsis(object): def __repr__(self): @@ -66,27 +67,15 @@ Variant = typing.Any ModelIndexList = typing.List[int] QImageCleanupFunction = typing.Callable +# unfortunately, typing.Optional[t] expands to typing.Union[t, NoneType] +# Until we can force it to create Optional[t] again, we use this. +NoneType = type(None) + _S = TypeVar("_S") # Building our own Char type, which is much nicer than # Char = typing.Union[str, int] # how do I model the limitation to 1 char? -# Copied from the six module: -def with_metaclass(meta, *bases): - """Create a base class with a metaclass.""" - # This requires a bit of explanation: the basic idea is to make a dummy - # metaclass for one level of class instantiation that replaces itself with - # the actual metaclass. - class metaclass(type): - - def __new__(cls, name, this_bases, d): - return meta(name, bases, d) - - @classmethod - def __prepare__(cls, name, this_bases): - return meta.__prepare__(name, bases) - return type.__new__(metaclass, 'temporary_class', (), {}) - class _CharMeta(type): def __repr__(self): return '%s.%s' % (self.__module__, self.__name__) @@ -420,6 +409,7 @@ def init_sample(): "Foo.HANDLE": int, "HANDLE": int, "Null": None, + "nullptr": None, "ObjectType.Identifier": Missing("sample.ObjectType.Identifier"), "OddBool": bool, "PStr": str, diff --git a/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/parser.py b/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/parser.py index 204f37384..df2b9fa92 100644 --- a/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/parser.py +++ b/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/signature/parser.py @@ -116,7 +116,7 @@ def _parse_line(line): print("KEYWORD", ret) name = name + "_" if "=" in ann: - ann, default = ann.split("=") + ann, default = ann.split("=", 1) tup = name, ann, default else: tup = name, ann @@ -167,7 +167,7 @@ def try_to_guess(thing, valtype): def _resolve_value(thing, valtype, line): if thing in ("0", "None") and valtype: - if valtype.startswith("PySide2."): + if valtype.startswith("PySide2.") or valtype.startswith("typing."): return None map = type_map[valtype] # typing.Any: '_SpecialForm' object has no attribute '__name__' @@ -315,9 +315,9 @@ def calculate_props(line): for idx, tup in enumerate(arglist): name, ann = tup[:2] if ann == "...": - name = "*args" - # copy the fields back :() - ann = 'NULL' # maps to None + name = "*args" if name.startswith("arg_") else "*" + name + # copy the pathed fields back + ann = 'nullptr' # maps to None tup = name, ann arglist[idx] = tup annotations[name] = _resolve_type(ann, line, 0, handle_argvar) diff --git a/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/typing27.py b/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/typing27.py index 786a84ecb..44d78c433 100644 --- a/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/typing27.py +++ b/sources/shiboken2/shibokenmodule/files.dir/shibokensupport/typing27.py @@ -86,6 +86,10 @@ PSF LICENSE AGREEMENT FOR PYTHON 3.7.0 to be bound by the terms and conditions of this License Agreement. """ +# This is the typing module for Python 2.7 +# https://github.com/python/typing +# 2019-08-22 + from __future__ import absolute_import, unicode_literals import abc @@ -108,8 +112,11 @@ __all__ = [ 'Any', 'Callable', 'ClassVar', + 'Final', 'Generic', + 'Literal', 'Optional', + 'Protocol', 'Tuple', 'Type', 'TypeVar', @@ -141,6 +148,7 @@ __all__ = [ 'SupportsAbs', 'SupportsComplex', 'SupportsFloat', + 'SupportsIndex', 'SupportsInt', # Concrete collection types. @@ -152,17 +160,20 @@ __all__ = [ 'Set', 'FrozenSet', 'NamedTuple', # Not really a type. + 'TypedDict', # Not really a type. 'Generator', # One-off things. 'AnyStr', 'cast', + 'final', 'get_type_hints', 'NewType', 'no_type_check', 'no_type_check_decorator', 'NoReturn', 'overload', + 'runtime_checkable', 'Text', 'TYPE_CHECKING', ] @@ -447,7 +458,7 @@ def _type_check(arg, msg): if ( type(arg).__name__ in ('_Union', '_Optional') and not getattr(arg, '__origin__', None) or - isinstance(arg, TypingMeta) and arg._gorg in (Generic, _Protocol) + isinstance(arg, TypingMeta) and arg._gorg in (Generic, Protocol) ): raise TypeError("Plain %s is not valid as type argument" % arg) return arg @@ -466,7 +477,7 @@ def _type_repr(obj): return _qualname(obj) return '%s.%s' % (obj.__module__, _qualname(obj)) if obj is Ellipsis: - return('...') + return '...' if isinstance(obj, types.FunctionType): return obj.__name__ return repr(obj) @@ -537,6 +548,157 @@ class _ClassVar(_FinalTypingBase): ClassVar = _ClassVar(_root=True) +class _FinalMeta(TypingMeta): + """Metaclass for _Final""" + + def __new__(cls, name, bases, namespace): + cls.assert_no_subclassing(bases) + self = super(_FinalMeta, cls).__new__(cls, name, bases, namespace) + return self + + +class _Final(_FinalTypingBase): + """A special typing construct to indicate that a name + cannot be re-assigned or overridden in a subclass. + For example: + + MAX_SIZE: Final = 9000 + MAX_SIZE += 1 # Error reported by type checker + + class Connection: + TIMEOUT: Final[int] = 10 + class FastConnector(Connection): + TIMEOUT = 1 # Error reported by type checker + + There is no runtime checking of these properties. + """ + + __metaclass__ = _FinalMeta + __slots__ = ('__type__',) + + def __init__(self, tp=None, **kwds): + self.__type__ = tp + + def __getitem__(self, item): + cls = type(self) + if self.__type__ is None: + return cls(_type_check(item, + '{} accepts only single type.'.format(cls.__name__[1:])), + _root=True) + raise TypeError('{} cannot be further subscripted' + .format(cls.__name__[1:])) + + def _eval_type(self, globalns, localns): + new_tp = _eval_type(self.__type__, globalns, localns) + if new_tp == self.__type__: + return self + return type(self)(new_tp, _root=True) + + def __repr__(self): + r = super(_Final, self).__repr__() + if self.__type__ is not None: + r += '[{}]'.format(_type_repr(self.__type__)) + return r + + def __hash__(self): + return hash((type(self).__name__, self.__type__)) + + def __eq__(self, other): + if not isinstance(other, _Final): + return NotImplemented + if self.__type__ is not None: + return self.__type__ == other.__type__ + return self is other + + +Final = _Final(_root=True) + + +def final(f): + """This decorator can be used to indicate to type checkers that + the decorated method cannot be overridden, and decorated class + cannot be subclassed. For example: + + class Base: + @final + def done(self) -> None: + ... + class Sub(Base): + def done(self) -> None: # Error reported by type checker + ... + @final + class Leaf: + ... + class Other(Leaf): # Error reported by type checker + ... + + There is no runtime checking of these properties. + """ + return f + + +class _LiteralMeta(TypingMeta): + """Metaclass for _Literal""" + + def __new__(cls, name, bases, namespace): + cls.assert_no_subclassing(bases) + self = super(_LiteralMeta, cls).__new__(cls, name, bases, namespace) + return self + + +class _Literal(_FinalTypingBase): + """A type that can be used to indicate to type checkers that the + corresponding value has a value literally equivalent to the + provided parameter. For example: + + var: Literal[4] = 4 + + The type checker understands that 'var' is literally equal to the + value 4 and no other value. + + Literal[...] cannot be subclassed. There is no runtime checking + verifying that the parameter is actually a value instead of a type. + """ + + __metaclass__ = _LiteralMeta + __slots__ = ('__values__',) + + def __init__(self, values=None, **kwds): + self.__values__ = values + + def __getitem__(self, item): + cls = type(self) + if self.__values__ is None: + if not isinstance(item, tuple): + item = (item,) + return cls(values=item, + _root=True) + raise TypeError('{} cannot be further subscripted' + .format(cls.__name__[1:])) + + def _eval_type(self, globalns, localns): + return self + + def __repr__(self): + r = super(_Literal, self).__repr__() + if self.__values__ is not None: + r += '[{}]'.format(', '.join(map(_type_repr, self.__values__))) + return r + + def __hash__(self): + return hash((type(self).__name__, self.__values__)) + + def __eq__(self, other): + if not isinstance(other, _Literal): + return NotImplemented + if self.__values__ is not None: + return self.__values__ == other.__values__ + return self is other + + +Literal = _Literal(_root=True) + + class AnyMeta(TypingMeta): """Metaclass for Any.""" @@ -1122,10 +1284,11 @@ class GenericMeta(TypingMeta, abc.ABCMeta): if base is Generic: raise TypeError("Cannot inherit from plain Generic") if (isinstance(base, GenericMeta) and - base.__origin__ is Generic): + base.__origin__ in (Generic, Protocol)): if gvars is not None: raise TypeError( - "Cannot inherit from Generic[...] multiple types.") + "Cannot inherit from Generic[...] or" + " Protocol[...] multiple times.") gvars = base.__parameters__ if gvars is None: gvars = tvars @@ -1135,8 +1298,10 @@ class GenericMeta(TypingMeta, abc.ABCMeta): if not tvarset <= gvarset: raise TypeError( "Some type variables (%s) " - "are not listed in Generic[%s]" % + "are not listed in %s[%s]" % (", ".join(str(t) for t in tvars if t not in gvarset), + "Generic" if any(b.__origin__ is Generic + for b in bases) else "Protocol", ", ".join(str(g) for g in gvars))) tvars = gvars @@ -1285,25 +1450,21 @@ class GenericMeta(TypingMeta, abc.ABCMeta): "Parameter list to %s[...] cannot be empty" % _qualname(self)) msg = "Parameters to generic types must be types." params = tuple(_type_check(p, msg) for p in params) - if self is Generic: + if self in (Generic, Protocol): # Generic can only be subscripted with unique type variables. if not all(isinstance(p, TypeVar) for p in params): raise TypeError( - "Parameters to Generic[...] must all be type variables") + "Parameters to %s[...] must all be type variables" % self.__name__) if len(set(params)) != len(params): raise TypeError( - "Parameters to Generic[...] must all be unique") + "Parameters to %s[...] must all be unique" % self.__name__) tvars = params args = params elif self in (Tuple, Callable): tvars = _type_vars(params) args = params - elif self is _Protocol: - # _Protocol is internal, don't check anything. - tvars = params - args = params - elif self.__origin__ in (Generic, _Protocol): - # Can't subscript Generic[...] or _Protocol[...]. + elif self.__origin__ in (Generic, Protocol): + # Can't subscript Generic[...] or Protocol[...]. raise TypeError("Cannot subscript already-subscripted %s" % repr(self)) else: @@ -1343,7 +1504,7 @@ class GenericMeta(TypingMeta, abc.ABCMeta): # latter, we must extend __instancecheck__ too. For simplicity # we just skip the cache check -- instance checks for generic # classes are supposed to be rare anyways. - if not isinstance(instance, type): + if hasattr(instance, "__class__"): return issubclass(instance.__class__, self) return False @@ -1690,85 +1851,175 @@ def overload(func): return _overload_dummy +_PROTO_WHITELIST = ['Callable', 'Iterable', 'Iterator', + 'Hashable', 'Sized', 'Container', 'Collection', + 'Reversible', 'ContextManager'] + + class _ProtocolMeta(GenericMeta): - """Internal metaclass for _Protocol. + """Internal metaclass for Protocol. - This exists so _Protocol classes can be generic without deriving + This exists so Protocol classes can be generic without deriving from Generic. """ + def __init__(cls, *args, **kwargs): + super(_ProtocolMeta, cls).__init__(*args, **kwargs) + if not cls.__dict__.get('_is_protocol', None): + cls._is_protocol = any(b is Protocol or + isinstance(b, _ProtocolMeta) and + b.__origin__ is Protocol + for b in cls.__bases__) + if cls._is_protocol: + for base in cls.__mro__[1:]: + if not (base in (object, Generic) or + base.__module__ == '_abcoll' and + base.__name__ in _PROTO_WHITELIST or + isinstance(base, TypingMeta) and base._is_protocol or + isinstance(base, GenericMeta) and base.__origin__ is Generic): + raise TypeError('Protocols can only inherit from other protocols,' + ' got %r' % base) + cls._callable_members_only = all(callable(getattr(cls, attr)) + for attr in cls._get_protocol_attrs()) + + def _no_init(self, *args, **kwargs): + if type(self)._is_protocol: + raise TypeError('Protocols cannot be instantiated') + cls.__init__ = _no_init + + def _proto_hook(cls, other): + if not cls.__dict__.get('_is_protocol', None): + return NotImplemented + if not isinstance(other, type): + # Similar error as for issubclass(1, int) + # (also not a chance for old-style classes) + raise TypeError('issubclass() arg 1 must be a new-style class') + for attr in cls._get_protocol_attrs(): + for base in other.__mro__: + if attr in base.__dict__: + if base.__dict__[attr] is None: + return NotImplemented + break + else: + return NotImplemented + return True + if '__subclasshook__' not in cls.__dict__: + cls.__subclasshook__ = classmethod(_proto_hook) - def __instancecheck__(self, obj): - if _Protocol not in self.__bases__: - return super(_ProtocolMeta, self).__instancecheck__(obj) - raise TypeError("Protocols cannot be used with isinstance().") + def __instancecheck__(self, instance): + # We need this method for situations where attributes are assigned in __init__ + if isinstance(instance, type): + # This looks like a fundamental limitation of Python 2. + # It cannot support runtime protocol metaclasses, On Python 2 classes + # cannot be correctly inspected as instances of protocols. + return False + if ((not getattr(self, '_is_protocol', False) or + self._callable_members_only) and + issubclass(instance.__class__, self)): + return True + if self._is_protocol: + if all(hasattr(instance, attr) and + (not callable(getattr(self, attr)) or + getattr(instance, attr) is not None) + for attr in self._get_protocol_attrs()): + return True + return super(GenericMeta, self).__instancecheck__(instance) def __subclasscheck__(self, cls): - if not self._is_protocol: - # No structural checks since this isn't a protocol. - return NotImplemented + if (self.__dict__.get('_is_protocol', None) and + not self.__dict__.get('_is_runtime_protocol', None)): + if (sys._getframe(1).f_globals['__name__'] in ['abc', 'functools'] or + # This is needed because we remove subclasses from unions on Python 2. + sys._getframe(2).f_globals['__name__'] == 'typing'): + return False + raise TypeError("Instance and class checks can only be used with" + " @runtime_checkable protocols") + if (self.__dict__.get('_is_runtime_protocol', None) and + not self._callable_members_only): + if sys._getframe(1).f_globals['__name__'] in ['abc', 'functools']: + return super(GenericMeta, self).__subclasscheck__(cls) + raise TypeError("Protocols with non-method members" + " don't support issubclass()") + return super(_ProtocolMeta, self).__subclasscheck__(cls) - if self is _Protocol: - # Every class is a subclass of the empty protocol. - return True + def _get_protocol_attrs(self): + attrs = set() + for base in self.__mro__[:-1]: # without object + if base.__name__ in ('Protocol', 'Generic'): + continue + annotations = getattr(base, '__annotations__', {}) + for attr in list(base.__dict__.keys()) + list(annotations.keys()): + if (not attr.startswith('_abc_') and attr not in ( + '__abstractmethods__', '__annotations__', '__weakref__', + '_is_protocol', '_is_runtime_protocol', '__dict__', + '__args__', '__slots__', '_get_protocol_attrs', + '__next_in_mro__', '__parameters__', '__origin__', + '__orig_bases__', '__extra__', '__tree_hash__', + '__doc__', '__subclasshook__', '__init__', '__new__', + '__module__', '_MutableMapping__marker', + '__metaclass__', '_gorg', '_callable_members_only')): + attrs.add(attr) + return attrs - # Find all attributes defined in the protocol. - attrs = self._get_protocol_attrs() - for attr in attrs: - if not any(attr in d.__dict__ for d in cls.__mro__): - return False - return True +class Protocol(object): + """Base class for protocol classes. Protocol classes are defined as:: - def _get_protocol_attrs(self): - # Get all Protocol base classes. - protocol_bases = [] - for c in self.__mro__: - if getattr(c, '_is_protocol', False) and c.__name__ != '_Protocol': - protocol_bases.append(c) + class Proto(Protocol): + def meth(self): + # type: () -> int + pass - # Get attributes included in protocol. - attrs = set() - for base in protocol_bases: - for attr in base.__dict__.keys(): - # Include attributes not defined in any non-protocol bases. - for c in self.__mro__: - if (c is not base and attr in c.__dict__ and - not getattr(c, '_is_protocol', False)): - break - else: - if (not attr.startswith('_abc_') and - attr != '__abstractmethods__' and - attr != '_is_protocol' and - attr != '_gorg' and - attr != '__dict__' and - attr != '__args__' and - attr != '__slots__' and - attr != '_get_protocol_attrs' and - attr != '__next_in_mro__' and - attr != '__parameters__' and - attr != '__origin__' and - attr != '__orig_bases__' and - attr != '__extra__' and - attr != '__tree_hash__' and - attr != '__module__'): - attrs.add(attr) + Such classes are primarily used with static type checkers that recognize + structural subtyping (static duck-typing), for example:: - return attrs + class C: + def meth(self): + # type: () -> int + return 0 + + def func(x): + # type: (Proto) -> int + return x.meth() + + func(C()) # Passes static type check + See PEP 544 for details. Protocol classes decorated with @typing.runtime_checkable + act as simple-minded runtime protocols that checks only the presence of + given attributes, ignoring their type signatures. -class _Protocol(object): - """Internal base class for protocol classes. + Protocol classes can be generic, they are defined as:: - This implements a simple-minded structural issubclass check - (similar but more general than the one-offs in collections.abc - such as Hashable). + class GenProto(Protocol[T]): + def meth(self): + # type: () -> T + pass """ __metaclass__ = _ProtocolMeta __slots__ = () - _is_protocol = True + def __new__(cls, *args, **kwds): + if cls._gorg is Protocol: + raise TypeError("Type Protocol cannot be instantiated; " + "it can be used only as a base class") + return _generic_new(cls.__next_in_mro__, cls, *args, **kwds) + + +def runtime_checkable(cls): + """Mark a protocol class as a runtime protocol, so that it + can be used with isinstance() and issubclass(). Raise TypeError + if applied to a non-protocol class. + + This allows a simple-minded structural check very similar to the + one-offs in collections.abc such as Hashable. + """ + if not isinstance(cls, _ProtocolMeta) or not cls._is_protocol: + raise TypeError('@runtime_checkable can be only applied to protocol classes,' + ' got %r' % cls) + cls._is_runtime_protocol = True + return cls + # Various ABCs mimicking those in collections.abc. # A few are simply re-exported for completeness. @@ -1786,7 +2037,8 @@ class Iterator(Iterable[T_co]): __extra__ = collections_abc.Iterator -class SupportsInt(_Protocol): +@runtime_checkable +class SupportsInt(Protocol): __slots__ = () @abstractmethod @@ -1794,7 +2046,8 @@ class SupportsInt(_Protocol): pass -class SupportsFloat(_Protocol): +@runtime_checkable +class SupportsFloat(Protocol): __slots__ = () @abstractmethod @@ -1802,7 +2055,8 @@ class SupportsFloat(_Protocol): pass -class SupportsComplex(_Protocol): +@runtime_checkable +class SupportsComplex(Protocol): __slots__ = () @abstractmethod @@ -1810,7 +2064,17 @@ class SupportsComplex(_Protocol): pass -class SupportsAbs(_Protocol[T_co]): +@runtime_checkable +class SupportsIndex(Protocol): + __slots__ = () + + @abstractmethod + def __index__(self): + pass + + +@runtime_checkable +class SupportsAbs(Protocol[T_co]): __slots__ = () @abstractmethod @@ -1823,7 +2087,8 @@ if hasattr(collections_abc, 'Reversible'): __slots__ = () __extra__ = collections_abc.Reversible else: - class Reversible(_Protocol[T_co]): + @runtime_checkable + class Reversible(Protocol[T_co]): __slots__ = () @abstractmethod @@ -1996,21 +2261,6 @@ class DefaultDict(collections.defaultdict, MutableMapping[KT, VT]): return _generic_new(collections.defaultdict, cls, *args, **kwds) -############################ -# Insertion by CT 2019-02-21 -# -class OrderedDict(collections.OrderedDict, MutableMapping[KT, VT]): - __slots__ = () - __extra__ = collections.OrderedDict - - def __new__(cls, *args, **kwds): - if cls._gorg is OrderedDict: - return collections.OrderedDict(*args, **kwds) - return _generic_new(collections.OrderedDict, cls, *args, **kwds) -# -############################ - - class Counter(collections.Counter, Dict[T, int]): __slots__ = () __extra__ = collections.Counter @@ -2100,6 +2350,87 @@ def NamedTuple(typename, fields): return cls +def _check_fails(cls, other): + try: + if sys._getframe(1).f_globals['__name__'] not in ['abc', 'functools', 'typing']: + # Typed dicts are only for static structural subtyping. + raise TypeError('TypedDict does not support instance and class checks') + except (AttributeError, ValueError): + pass + return False + + +def _dict_new(cls, *args, **kwargs): + return dict(*args, **kwargs) + + +def _typeddict_new(cls, _typename, _fields=None, **kwargs): + total = kwargs.pop('total', True) + if _fields is None: + _fields = kwargs + elif kwargs: + raise TypeError("TypedDict takes either a dict or keyword arguments," + " but not both") + + ns = {'__annotations__': dict(_fields), '__total__': total} + try: + # Setting correct module is necessary to make typed dict classes pickleable. + ns['__module__'] = sys._getframe(1).f_globals.get('__name__', '__main__') + except (AttributeError, ValueError): + pass + + return _TypedDictMeta(_typename, (), ns) + + +class _TypedDictMeta(type): + def __new__(cls, name, bases, ns, total=True): + # Create new typed dict class object. + # This method is called directly when TypedDict is subclassed, + # or via _typeddict_new when TypedDict is instantiated. This way + # TypedDict supports all three syntaxes described in its docstring. + # Subclasses and instances of TypedDict return actual dictionaries + # via _dict_new. + ns['__new__'] = _typeddict_new if name == b'TypedDict' else _dict_new + tp_dict = super(_TypedDictMeta, cls).__new__(cls, name, (dict,), ns) + + anns = ns.get('__annotations__', {}) + msg = "TypedDict('Name', {f0: t0, f1: t1, ...}); each t must be a type" + anns = {n: _type_check(tp, msg) for n, tp in anns.items()} + for base in bases: + anns.update(base.__dict__.get('__annotations__', {})) + tp_dict.__annotations__ = anns + if not hasattr(tp_dict, '__total__'): + tp_dict.__total__ = total + return tp_dict + + __instancecheck__ = __subclasscheck__ = _check_fails + + +TypedDict = _TypedDictMeta(b'TypedDict', (dict,), {}) +TypedDict.__module__ = __name__ +TypedDict.__doc__ = \ + """A simple typed name space. At runtime it is equivalent to a plain dict. + + TypedDict creates a dictionary type that expects all of its + instances to have a certain set of keys, with each key + associated with a value of a consistent type. This expectation + is not checked at runtime but is only enforced by type checkers. + Usage:: + + Point2D = TypedDict('Point2D', {'x': int, 'y': int, 'label': str}) + + a: Point2D = {'x': 1, 'y': 2, 'label': 'good'} # OK + b: Point2D = {'z': 3, 'label': 'bad'} # Fails type check + + assert Point2D(x=1, y=2, label='first') == dict(x=1, y=2, label='first') + + The type info could be accessed via Point2D.__annotations__. TypedDict + supports an additional equivalent form:: + + Point2D = TypedDict('Point2D', x=int, y=int, label=str) + """ + + def NewType(name, tp): """NewType creates simple unique types with almost zero runtime overhead. NewType(name, tp) is considered a subtype of tp diff --git a/sources/shiboken2/tests/CMakeLists.txt b/sources/shiboken2/tests/CMakeLists.txt index 085a4344c..464707a9a 100644 --- a/sources/shiboken2/tests/CMakeLists.txt +++ b/sources/shiboken2/tests/CMakeLists.txt @@ -1,3 +1,7 @@ +if(BUILD_TESTS) + find_package(Qt5Test 5.12 REQUIRED) +endif() + add_subdirectory(libminimal) if(NOT DEFINED MINIMAL_TESTS) add_subdirectory(libsample) @@ -74,24 +78,24 @@ add_subdirectory(dumpcodemodel) if (NOT APIEXTRACTOR_DOCSTRINGS_DISABLED) # project(sphinxtabletest) -# +# # # TODO # set(sphinxtabletest_SRC sphinxtabletest.cpp) # qt4_automoc(${sphinxtabletest_SRC}) -# +# # include_directories(${QT_INCLUDE_DIR} # ${QT_QTCORE_INCLUDE_DIR} # ${CMAKE_CURRENT_BINARY_DIR} # ${qtdoc_generator_SOURCE_DIR}) -# +# # add_executable(sphinxtabletest ${sphinxtabletest_SRC}) -# +# # target_link_libraries(sphinxtabletest # ${QT_QTTEST_LIBRARY} # ${APIEXTRACTOR_LIBRARY} # qtdoc_generator # genrunner) -# +# # add_test("sphinxtable" sphinxtabletest) # if (INSTALL_TESTS) # install(PROGRAMS ${CMAKE_CURRENT_BINARY_DIR}/sphinxtabletest DESTINATION ${TEST_INSTALL_DIR}) |