diff options
Diffstat (limited to 'sources')
11 files changed, 236 insertions, 112 deletions
diff --git a/sources/pyside2/PySide2/support/generate_pyi.py b/sources/pyside2/PySide2/support/generate_pyi.py index adece0776..7a2c79ba2 100644 --- a/sources/pyside2/PySide2/support/generate_pyi.py +++ b/sources/pyside2/PySide2/support/generate_pyi.py @@ -1,3 +1,4 @@ +# This Python file uses the following encoding: utf-8 ############################################################################# ## ## Copyright (C) 2018 The Qt Company Ltd. @@ -49,14 +50,15 @@ import sys import os import io import re -import PySide2 import subprocess import argparse from contextlib import contextmanager from textwrap import dedent -from PySide2.support.signature import inspect -from PySide2.support.signature.lib.enum_sig import HintingEnumerator + +import logging +logging.basicConfig(level=logging.INFO) +logger = logging.getLogger("generate_pyi") # Make sure not to get .pyc in Python2. @@ -101,8 +103,9 @@ class Formatter(Writer): self.print("# Module", mod_name) self.print("import shiboken2 as Shiboken") from PySide2.support.signature import typing - typing_str = "from PySide2.support.signature import typing" - self.print(typing_str) + self.print("from PySide2.support.signature import typing") + self.print("from PySide2.support.signature.mapping import (") + self.print(" Virtual, Missing, Invalid, Default, Instance)") self.print() self.print("class Object(object): pass") self.print() @@ -174,68 +177,151 @@ def find_imports(text): return [imp for imp in PySide2.__all__ if imp + "." in text] +def safe_create(filename): + pid = os.getpid() + locname = "{filename}.{pid}".format(**locals()) + f = io.open(locname, "w") # do not close for atomic rename on Linux + if sys.platform == "win32": + f.close() + try: + os.rename(locname, filename) + logger.debug("{pid}:File {filename} created".format(**locals())) + if sys.platform == "win32": + f = io.open(filename, "w") + return f + except OSError: + logger.debug("{pid}:Could not rename {locname} to {filename}" + .format(**locals())) + try: + os.remove(locname) + except OSError as e: + logger.warning("{pid}: unexpected os.remove error in safe_create: {e}" + .format(**locals())) + return None + + def generate_pyi(import_name, outpath, options): + """ + Generates a .pyi file. + + Returns 1 If the result is valid, else 0. + """ + pid = os.getpid() plainname = import_name.split(".")[-1] if not outpath: outpath = os.path.dirname(PySide2.__file__) outfilepath = os.path.join(outpath, plainname + ".pyi") if options.skip and os.path.exists(outfilepath): - return + logger.debug("{pid}:Skipped existing: {outfilepath}".format(**locals())) + return 1 + workpath = outfilepath + ".working" + if os.path.exists(workpath): + return 0 + realfile = safe_create(workpath) + if not realfile: + return 0 + try: - __import__(import_name) - except ImportError: - return - - module = sys.modules[import_name] - mod_fullname = module.__file__ - outfile = io.StringIO() - fmt = Formatter(outfile) - enu = HintingEnumerator(fmt) - fmt.print(get_license_text()) - need_imports = not USE_PEP563 - if USE_PEP563: - fmt.print("from __future__ import annotations") + top = __import__(import_name) + obj = getattr(top, plainname) + if not getattr(obj, "__file__", None) or os.path.isdir(obj.__file__): + raise ImportError("We do not accept a namespace as module {plainname}" + .format(**locals())) + module = sys.modules[import_name] + + outfile = io.StringIO() + fmt = Formatter(outfile) + enu = HintingEnumerator(fmt) + fmt.print(get_license_text()) # which has encoding, already + need_imports = not USE_PEP563 + if USE_PEP563: + fmt.print("from __future__ import annotations") + fmt.print() + fmt.print(dedent('''\ + """ + This file contains the exact signatures for all functions in module + {import_name}, except for defaults which are replaced by "...". + """ + '''.format(**locals()))) + enu.module(import_name) fmt.print() - fmt.print(dedent('''\ - """ - This file contains the exact signatures for all functions in PySide - for module '{mod_fullname}', - except for defaults which are replaced by "...". - """ - '''.format(**locals()))) - enu.module(import_name) - fmt.print("# eof") - with io.open(outfilepath, "w") as realfile: - wr = Writer(realfile) - outfile.seek(0) - while True: - line = outfile.readline() - if not line: - break - line = line.rstrip() - # we remove the IMPORTS marker and insert imports if needed - if line == "IMPORTS": - if need_imports: - for mod_name in find_imports(outfile.getvalue()): - imp = "PySide2." + mod_name - if imp != import_name: - wr.print("import " + imp) - wr.print("import " + import_name) - wr.print() - wr.print() - else: - wr.print(line) + fmt.print("# eof") + + except ImportError as e: + logger.debug("{pid}:Import problem with module {plainname}: {e}".format(**locals())) + try: + os.remove(workpath) + except OSError as e: + logger.warning("{pid}: unexpected os.remove error in generate_pyi: {e}" + .format(**locals())) + return 0 + + wr = Writer(realfile) + outfile.seek(0) + while True: + line = outfile.readline() + if not line: + break + line = line.rstrip() + # we remove the IMPORTS marker and insert imports if needed + if line == "IMPORTS": + if need_imports: + for mod_name in find_imports(outfile.getvalue()): + imp = "PySide2." + mod_name + if imp != import_name: + wr.print("import " + imp) + wr.print("import " + import_name) + wr.print() + wr.print() + else: + wr.print(line) + realfile.close() - print(outfilepath, file=sys.stderr) + if os.path.exists(outfilepath): + os.remove(outfilepath) + try: + os.rename(workpath, outfilepath) + except OSError: + logger.warning("{pid}: probable duplicate generated: {outfilepath}"# + .format(**locals())) + return 0 + logger.info("Generated: {outfilepath}".format(**locals())) if sys.version_info[0] == 3: # Python 3: We can check the file directly if the syntax is ok. subprocess.check_output([sys.executable, outfilepath]) + return 1 def generate_all_pyi(outpath, options): + ps = os.pathsep + if options.sys_path: + # make sure to propagate the paths from sys_path to subprocesses + sys_path = [os.path.normpath(_) for _ in options.sys_path] + sys.path[0:0] = sys_path + pypath = ps.join(sys_path) + os.environ["PYTHONPATH"] = pypath + if options.lib_path: + # the path changes are automatically propagated to subprocesses + ospath_var = "PATH" if sys.platform == "win32" else "LD_LIBRARY_PATH" + old_val = os.environ.get(ospath_var, "") + lib_path = [os.path.normpath(_) for _ in options.lib_path] + ospath = ps.join(lib_path + old_val.split(ps)) + os.environ[ospath_var] = ospath + + # now we can import + global PySide2, inspect, HintingEnumerator + import PySide2 + from PySide2.support.signature import inspect + from PySide2.support.signature.lib.enum_sig import HintingEnumerator + + valid = 0 for mod_name in PySide2.__all__: import_name = "PySide2." + mod_name - generate_pyi(import_name, outpath, options) + valid += generate_pyi(import_name, outpath, options) + + npyi = len(PySide2.__all__) + if valid == npyi: + logger.info("+++ All {npyi} .pyi files have been created.".format(**locals())) if __name__ == "__main__": @@ -245,14 +331,20 @@ if __name__ == "__main__": parser_run = subparsers.add_parser("run", help="run the generation", description="This script generates the .pyi file for all PySide modules.") - parser_run.add_argument("--skip", action="store_true", help="skip already generated files") - parser_run.add_argument("--outpath", help="the outout folder. Default = location of binaries.") + parser_run.add_argument("--skip", action="store_true", + help="skip existing files") + parser_run.add_argument("--outpath", + help="the output directory (default = binary location)") + parser_run.add_argument("--sys-path", nargs="+", + help="a list of strings prepended to sys.path") + parser_run.add_argument("--lib-path", nargs="+", + help="a list of strings prepended to LD_LIBRARY_PATH (unix) or PATH (windows)") options = parser.parse_args() if options.command == "run": outpath = options.outpath if outpath and not os.path.exists(outpath): os.makedirs(outpath) - print("+++ Created path {outpath}".format(**locals())) + logger.info("+++ Created path {outpath}".format(**locals())) generate_all_pyi(outpath, options=options) else: parser_run.print_help() diff --git a/sources/pyside2/PySide2/support/signature/mapping.py b/sources/pyside2/PySide2/support/signature/mapping.py index cf5857c65..1a769484d 100644 --- a/sources/pyside2/PySide2/support/signature/mapping.py +++ b/sources/pyside2/PySide2/support/signature/mapping.py @@ -315,8 +315,7 @@ def init_QtXmlPatterns(): return locals() -def init_QtMultimedia(): - import PySide2.QtMultimedia +def init_QtMultimediaWidgets(): import PySide2.QtMultimediaWidgets type_map.update({ "QGraphicsVideoItem": PySide2.QtMultimediaWidgets.QGraphicsVideoItem, @@ -386,7 +385,6 @@ def init_QtTest(): def init_QtWebEngineWidgets(): import PySide2.QtWebEngineWidgets type_map.update({ - "PySide2.QtTest.QTouchEventSequence": PySide2.QtTest.QTest.QTouchEventSequence, "zero(PySide2.QtWebEngineWidgets.QWebEnginePage.FindFlags)": 0, }) return locals() diff --git a/sources/pyside2/cmake/Macros/PySideModules.cmake b/sources/pyside2/cmake/Macros/PySideModules.cmake index 88c7a5044..dd4845033 100644 --- a/sources/pyside2/cmake/Macros/PySideModules.cmake +++ b/sources/pyside2/cmake/Macros/PySideModules.cmake @@ -125,8 +125,21 @@ macro(create_pyside_module endif() create_generator_target(${module_name}) + # build type hinting stubs + add_custom_command( TARGET ${module_name} POST_BUILD + COMMAND "${SHIBOKEN_PYTHON_INTERPRETER}" + "${CMAKE_CURRENT_SOURCE_DIR}/../support/generate_pyi.py" run --skip + --sys-path "${CMAKE_BINARY_DIR}" "${CMAKE_BINARY_DIR}/../shiboken2/shibokenmodule" + --lib-path "${CMAKE_BINARY_DIR}/libpyside" "${CMAKE_BINARY_DIR}/../shiboken2/libshiboken" + ) # install - install(TARGETS ${module_name} LIBRARY DESTINATION ${PYTHON_SITE_PACKAGES}/PySide2) + install(TARGETS ${module_name} LIBRARY DESTINATION "${PYTHON_SITE_PACKAGES}/PySide2") + + install(DIRECTORY "${CMAKE_BINARY_DIR}/" DESTINATION "${PYTHON_SITE_PACKAGES}" + OPTIONAL + FILES_MATCHING PATTERN "*.pyi") + + string(TOLOWER ${module_name} lower_module_name) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/PySide2/${module_name}/pyside2_${lower_module_name}_python.h DESTINATION include/PySide2${pyside2_SUFFIX}/${module_name}/) file(GLOB typesystem_files ${CMAKE_CURRENT_SOURCE_DIR}/typesystem_*.xml ${typesystem_path}) diff --git a/sources/shiboken2/libshiboken/pep384impl.h b/sources/shiboken2/libshiboken/pep384impl.h index 78b9defb5..bfa8f38a7 100644 --- a/sources/shiboken2/libshiboken/pep384impl.h +++ b/sources/shiboken2/libshiboken/pep384impl.h @@ -333,10 +333,11 @@ LIBSHIBOKEN_API PyObject *PepFunction_Get(PyObject *, const char *); #define PyFunction_Check(op) (Py_TYPE(op) == PepFunction_TypePtr) #define PyFunction_GET_CODE(func) PyFunction_GetCode(func) -#define PyFunction_GetCode(func) PepFunction_Get((PyObject *)func, "__code__") -#define PepFunction_GetName(func) PepFunction_Get((PyObject *)func, "__name__") +#define PyFunction_GetCode(func) PepFunction_Get((PyObject *)func, "__code__") +#define PepFunction_GetName(func) PepFunction_Get((PyObject *)func, "__name__") #else -#define PepFunction_GetName(func) (((PyFunctionObject *)func)->func_name) +#define PepFunction_TypePtr (&PyFunction_Type) +#define PepFunction_GetName(func) (((PyFunctionObject *)func)->func_name) #endif /***************************************************************************** diff --git a/sources/shiboken2/libshiboken/signature.cpp b/sources/shiboken2/libshiboken/signature.cpp index 92ce3e50a..922f85906 100644 --- a/sources/shiboken2/libshiboken/signature.cpp +++ b/sources/shiboken2/libshiboken/signature.cpp @@ -120,6 +120,8 @@ pyside_sm_get___signature__(PyObject *sm, const char *modifier) { init_module_2(); Shiboken::AutoDecRef func(PyObject_GetAttrString(sm, "__func__")); + if (Py_TYPE(func) == PepFunction_TypePtr) + Py_RETURN_NONE; return GetSignature_Function(func, modifier); } @@ -299,28 +301,38 @@ GetClassKey(PyObject *ob) return Py_BuildValue("O", class_name.object()); } +static PyObject *empty_dict = nullptr; + +static PyObject * +TypeKey_to_PropsDict(PyObject *type_key, PyObject *obtype) +{ + PyObject *dict = PyDict_GetItem(pyside_globals->arg_dict, type_key); + if (dict == nullptr) { + if (empty_dict == nullptr) + empty_dict = PyDict_New(); + dict = empty_dict; + } + if (PyTuple_Check(dict)) + dict = PySide_BuildSignatureProps(obtype); + return dict; +} + static PyObject * GetSignature_Function(PyObject *ob_func, const char *modifier) { + // make sure that we look into PyCFunction, only... + if (Py_TYPE(ob_func) == PepFunction_TypePtr) + Py_RETURN_NONE; Shiboken::AutoDecRef typemod(GetClassOfFunc(ob_func)); Shiboken::AutoDecRef type_key(GetClassKey(typemod)); if (type_key.isNull()) Py_RETURN_NONE; - PyObject *dict = PyDict_GetItem(pyside_globals->arg_dict, type_key); - if (dict == NULL) - Py_RETURN_NONE; - if (PyTuple_Check(dict)) { - /* - * We do the initialization lazily. - * This has also the advantage that we can freely import PySide. - */ - dict = PySide_BuildSignatureProps(typemod); - if (dict == NULL) - Py_RETURN_NONE; - } + PyObject *dict = TypeKey_to_PropsDict(type_key, typemod); + if (dict == nullptr) + return nullptr; Shiboken::AutoDecRef func_name(PyObject_GetAttrString(ob_func, "__name__")); PyObject *props = !func_name.isNull() ? PyDict_GetItem(dict, func_name) : nullptr; - if (props == NULL) + if (props == nullptr) Py_RETURN_NONE; int flags = PyCFunction_GET_FLAGS(ob_func); @@ -333,8 +345,7 @@ GetSignature_Function(PyObject *ob_func, const char *modifier) sig_kind = "staticmethod"; else sig_kind = "method"; - PyObject *ret = GetSignature_Cached(props, sig_kind, modifier); - return ret; + return GetSignature_Cached(props, sig_kind, modifier); } static PyObject * @@ -346,20 +357,11 @@ GetSignature_Wrapper(PyObject *ob, const char *modifier) if (func_name.isNull() || objclass.isNull() || class_key.isNull()) return nullptr; - PyObject *dict = PyDict_GetItem(pyside_globals->arg_dict, class_key); - if (dict == NULL) - Py_RETURN_NONE; - if (PyTuple_Check(dict)) { - /* - * We do the initialization lazily. - * This has also the advantage that we can freely import PySide. - */ - dict = PySide_BuildSignatureProps(objclass); - if (dict == NULL) - Py_RETURN_NONE; - } + PyObject *dict = TypeKey_to_PropsDict(class_key, objclass); + if (dict == nullptr) + return nullptr; PyObject *props = PyDict_GetItem(dict, func_name); - if (props == NULL) + if (props == nullptr) Py_RETURN_NONE; return GetSignature_Cached(props, "method", modifier); } @@ -370,18 +372,11 @@ GetSignature_TypeMod(PyObject *ob, const char *modifier) Shiboken::AutoDecRef ob_name(PyObject_GetAttrString(ob, "__name__")); Shiboken::AutoDecRef ob_key(GetClassKey(ob)); - PyObject *dict = PyDict_GetItem(pyside_globals->arg_dict, ob_key); - if (dict == NULL) - Py_RETURN_NONE; - - if (PyTuple_Check(dict)) { - dict = PySide_BuildSignatureProps(ob); - if (dict == NULL) { - Py_RETURN_NONE; - } - } + PyObject *dict = TypeKey_to_PropsDict(ob_key, ob); + if (dict == nullptr) + return nullptr; PyObject *props = PyDict_GetItem(dict, ob_name); - if (props == NULL) + if (props == nullptr) Py_RETURN_NONE; return GetSignature_Cached(props, "method", modifier); } @@ -587,6 +582,9 @@ get_signature(PyObject *self, PyObject *args) if (!PyArg_ParseTuple(args, "O|s", &ob, &modifier)) return NULL; + if (Py_TYPE(ob) == PepFunction_TypePtr) + Py_RETURN_NONE; + if (Py_TYPE(ob) == &PyCFunction_Type) return pyside_cf_get___signature__(ob, modifier); if (Py_TYPE(ob) == PepStaticMethod_TypePtr) @@ -723,14 +721,13 @@ static PyMethodDef signature_methods[] = { static void init_module_2(void) { - static int init_done = 0, initializing = 0; + static int init_done = 0; if (!init_done) { - if (initializing) - Py_FatalError("Init 2 called recursively!"); - init_phase_2(pyside_globals, signature_methods); + // Phase 2 will call __init__.py which touches a signature, itself. + // Therefore we set init_done prior to init_phase_2(). init_done = 1; - initializing = 0; + init_phase_2(pyside_globals, signature_methods); } } @@ -751,8 +748,14 @@ PySide_BuildSignatureProps(PyObject *classmod) if (arg_tup == nullptr) return nullptr; PyObject *dict = PyObject_CallObject(pyside_globals->sigparse_func, arg_tup); - if (dict == nullptr) - return nullptr; + if (dict == nullptr) { + if (PyErr_Occurred()) + return nullptr; + // No error: return an empty dict. + if (empty_dict == nullptr) + empty_dict = PyDict_New(); + return empty_dict; + } // We replace the arguments by the result dict. if (PyDict_SetItem(pyside_globals->arg_dict, type_key, dict) < 0) diff --git a/sources/shiboken2/shibokenmodule/CMakeLists.txt b/sources/shiboken2/shibokenmodule/CMakeLists.txt index 342b6d908..0eba3eaff 100644 --- a/sources/shiboken2/shibokenmodule/CMakeLists.txt +++ b/sources/shiboken2/shibokenmodule/CMakeLists.txt @@ -74,7 +74,7 @@ else() configure_file("${CMAKE_CURRENT_SOURCE_DIR}/support/signature/backport_inspect.py" "${CMAKE_CURRENT_BINARY_DIR}/support/signature/backport_inspect.py" COPYONLY) configure_file("${CMAKE_CURRENT_SOURCE_DIR}/support/signature/typing27.py" - "${CMAKE_CURRENT_BINARY_DIR}/support/signature/typing.py" COPYONLY) + "${CMAKE_CURRENT_BINARY_DIR}/support/signature/typing27.py" COPYONLY) endif() install(DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/support" DESTINATION "${PYTHON_SITE_PACKAGES}/shiboken2") diff --git a/sources/shiboken2/shibokenmodule/support/signature/__init__.py b/sources/shiboken2/shibokenmodule/support/signature/__init__.py index 253ba98dc..d0791df04 100644 --- a/sources/shiboken2/shibokenmodule/support/signature/__init__.py +++ b/sources/shiboken2/shibokenmodule/support/signature/__init__.py @@ -41,4 +41,7 @@ from __future__ import print_function, absolute_import # Trigger initialization phase 2. _ = type.__signature__ -from signature_loader import get_signature, inspect, typing + +## from signature_loader import get_signature, inspect, typing +# This causes a recursion in Python 2! +# We do everything from signature_loader, instead. diff --git a/sources/shiboken2/shibokenmodule/support/signature/lib/enum_sig.py b/sources/shiboken2/shibokenmodule/support/signature/lib/enum_sig.py index f79f3266a..013ec36cc 100644 --- a/sources/shiboken2/shibokenmodule/support/signature/lib/enum_sig.py +++ b/sources/shiboken2/shibokenmodule/support/signature/lib/enum_sig.py @@ -81,6 +81,10 @@ class ExactEnumerator(object): return ret def klass(self, class_name, klass): + if not "Shiboken" in repr(klass.mro()): + # don't look into any foreign classes! + ret = self.result_type() + return ret bases_list = [] for base in klass.__bases__: name = base.__name__ diff --git a/sources/shiboken2/shibokenmodule/support/signature/loader.py b/sources/shiboken2/shibokenmodule/support/signature/loader.py index 170fb0a2a..de27d441c 100644 --- a/sources/shiboken2/shibokenmodule/support/signature/loader.py +++ b/sources/shiboken2/shibokenmodule/support/signature/loader.py @@ -146,7 +146,8 @@ with ensure_import_support(): else: import inspect namespace = inspect.__dict__ - from support.signature import typing + from support.signature import typing27 as typing + typing.__name__ = "typing" from support.signature import backport_inspect as inspect _doc = inspect.__doc__ inspect.__dict__.update(namespace) diff --git a/sources/shiboken2/shibokenmodule/support/signature/mapping.py b/sources/shiboken2/shibokenmodule/support/signature/mapping.py index bca1ce307..3e76cd94a 100644 --- a/sources/shiboken2/shibokenmodule/support/signature/mapping.py +++ b/sources/shiboken2/shibokenmodule/support/signature/mapping.py @@ -104,7 +104,7 @@ class _NotCalled(str): real object is needed, the wrapper can simply be called. """ def __repr__(self): - suppress = "PySide2.support.signature.typing." + suppress = "support.signature.typing." text = self[len(suppress):] if self.startswith(suppress) else self return "{}({})".format(type(self).__name__, text) @@ -119,7 +119,8 @@ class Virtual(_NotCalled): # Other types I simply could not find. class Missing(_NotCalled): - pass + def __repr__(self): + return '{}("{}")'.format(type(self).__name__, self) class Invalid(_NotCalled): pass @@ -148,7 +149,14 @@ class Reloader(object): g = globals() for mod_name in self.uninitialized[:]: for prefix in self._prefixes: - if prefix + mod_name in sys.modules: + import_name = prefix + mod_name + if import_name in sys.modules: + # check if this is a real module + obj = sys.modules[import_name] + if not getattr(obj, "__file__", None) or os.path.isdir(obj.__file__): + raise ImportError("Module '{mod_name}' is at most a " + "namespace!".format(**locals())) + # module is real self.uninitialized.remove(mod_name) proc_name = "init_" + mod_name if proc_name in g: diff --git a/sources/shiboken2/shibokenmodule/support/signature/parser.py b/sources/shiboken2/shibokenmodule/support/signature/parser.py index 4bb1bf234..5178d9ef9 100644 --- a/sources/shiboken2/shibokenmodule/support/signature/parser.py +++ b/sources/shiboken2/shibokenmodule/support/signature/parser.py @@ -76,6 +76,7 @@ def dprint(*args, **kw): import pprint for arg in args: pprint.pprint(arg) + sys.stdout.flush() def _parse_line(line): line_re = r""" |