diff options
Diffstat (limited to 'sources')
30 files changed, 1063 insertions, 643 deletions
diff --git a/sources/pyside2/PySide2/CMakeLists.txt b/sources/pyside2/PySide2/CMakeLists.txt index 4709dd073..a101538e3 100644 --- a/sources/pyside2/PySide2/CMakeLists.txt +++ b/sources/pyside2/PySide2/CMakeLists.txt @@ -6,9 +6,21 @@ configure_file("${CMAKE_CURRENT_SOURCE_DIR}/global.h.in" configure_file("${CMAKE_CURRENT_SOURCE_DIR}/__init__.py.in" "${CMAKE_CURRENT_BINARY_DIR}/__init__.py" @ONLY) +configure_file("${CMAKE_CURRENT_SOURCE_DIR}/support/__init__.py" + "${CMAKE_CURRENT_BINARY_DIR}/support/__init__.py" COPYONLY) +configure_file("${CMAKE_CURRENT_SOURCE_DIR}/support/signature/__init__.py" + "${CMAKE_CURRENT_BINARY_DIR}/support/signature/__init__.py" COPYONLY) +configure_file("${CMAKE_CURRENT_SOURCE_DIR}/support/signature/layout.py" + "${CMAKE_CURRENT_BINARY_DIR}/support/signature/layout.py" COPYONLY) +configure_file("${CMAKE_CURRENT_SOURCE_DIR}/support/signature/mapping.py" + "${CMAKE_CURRENT_BINARY_DIR}/support/signature/mapping.py" COPYONLY) +configure_file("${CMAKE_CURRENT_SOURCE_DIR}/support/signature/lib/__init__.py" + "${CMAKE_CURRENT_BINARY_DIR}/support/signature/lib/__init__.py" COPYONLY) +configure_file("${CMAKE_CURRENT_SOURCE_DIR}/support/signature/lib/enum_sig.py" + "${CMAKE_CURRENT_BINARY_DIR}/support/signature/lib/enum_sig.py" COPYONLY) configure_file("${CMAKE_CURRENT_SOURCE_DIR}/_config.py.in" - "${CMAKE_CURRENT_BINARY_DIR}/_config.py" @ONLY) + "${CMAKE_CURRENT_BINARY_DIR}/_config.py" @ONLY) # Use absolute path instead of relative path, to avoid ninja build errors due to # duplicate file dependency inconsistency. @@ -38,27 +50,6 @@ endif() configure_file("${CMAKE_CURRENT_SOURCE_DIR}/support/__init__.py" "${CMAKE_CURRENT_BINARY_DIR}/support/__init__.py" COPYONLY) -configure_file("${CMAKE_CURRENT_SOURCE_DIR}/support/signature/__init__.py" - "${CMAKE_CURRENT_BINARY_DIR}/support/signature/__init__.py" COPYONLY) -configure_file("${CMAKE_CURRENT_SOURCE_DIR}/support/signature/layout.py" - "${CMAKE_CURRENT_BINARY_DIR}/support/signature/layout.py" COPYONLY) -configure_file("${CMAKE_CURRENT_SOURCE_DIR}/support/signature/loader.py" - "${CMAKE_CURRENT_BINARY_DIR}/support/signature/loader.py" COPYONLY) -configure_file("${CMAKE_CURRENT_SOURCE_DIR}/support/signature/mapping.py" - "${CMAKE_CURRENT_BINARY_DIR}/support/signature/mapping.py" COPYONLY) -configure_file("${CMAKE_CURRENT_SOURCE_DIR}/support/signature/parser.py" - "${CMAKE_CURRENT_BINARY_DIR}/support/signature/parser.py" COPYONLY) -configure_file("${CMAKE_CURRENT_SOURCE_DIR}/support/signature/lib/__init__.py" - "${CMAKE_CURRENT_BINARY_DIR}/support/signature/lib/__init__.py" COPYONLY) -configure_file("${CMAKE_CURRENT_SOURCE_DIR}/support/signature/lib/enum_sig.py" - "${CMAKE_CURRENT_BINARY_DIR}/support/signature/lib/enum_sig.py" COPYONLY) -if (PYTHON_VERSION_MAJOR EQUAL 3) -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) -endif() # now compile all modules. file(READ "${CMAKE_CURRENT_BINARY_DIR}/pyside2_global.h" pyside2_global_contents) diff --git a/sources/pyside2/PySide2/support/__init__.py b/sources/pyside2/PySide2/support/__init__.py index 18abd9d0e..dda01474d 100644 --- a/sources/pyside2/PySide2/support/__init__.py +++ b/sources/pyside2/PySide2/support/__init__.py @@ -37,12 +37,4 @@ ## ############################################################################# -# Import VoidPtr type to expose it under PySide2.support.VoidPtr -try: - # The normal import statement when PySide2 is installed. - from PySide2.shiboken2 import VoidPtr -except ImportError: - # When running make test in shiboken build dir, or when running testrunner.py, - # shiboken2 is not part of the PySide2 module, so it needs to be imported as a standalone - # module. - from shiboken2 import VoidPtr +from shiboken2 import VoidPtr diff --git a/sources/pyside2/PySide2/support/signature/__init__.py b/sources/pyside2/PySide2/support/signature/__init__.py index 49224bf92..d587a4d62 100644 --- a/sources/pyside2/PySide2/support/signature/__init__.py +++ b/sources/pyside2/PySide2/support/signature/__init__.py @@ -39,8 +39,8 @@ from __future__ import print_function, absolute_import -from .loader import inspect -from PySide2 import QtCore -if QtCore.QProcess.__signature__: - pass # trigger initialization phase 2, so we can import: -from signature_loader import get_signature +# Trigger initialization phase 2. +_ = type.__signature__ +from signature_loader import get_signature, inspect + +__all__ = "get_signature inspect layout mapping lib".split() diff --git a/sources/pyside2/PySide2/support/signature/layout.py b/sources/pyside2/PySide2/support/signature/layout.py index e18cb2172..c8a4062cb 100644 --- a/sources/pyside2/PySide2/support/signature/layout.py +++ b/sources/pyside2/PySide2/support/signature/layout.py @@ -39,207 +39,4 @@ from __future__ import print_function, absolute_import -""" -layout.py - -The signature module now has the capability to configure -differently formatted versions of signatures. The default -layout is known from the "__signature__" attribute. - -The function "get_signature(ob, modifier=None)" produces the same -signatures by default. By passing different modifiers, you -can select different layouts. - -This module configures the different layouts which can be used. -It also implements them in this file. The configurations are -used literally as strings like "signature", "existence", etc. -""" - -from textwrap import dedent -from .loader import inspect - - -class SimpleNamespace(object): - # From types.rst, because the builtin is implemented in Python 3, only. - def __init__(self, **kwargs): - self.__dict__.update(kwargs) - - def __repr__(self): - keys = sorted(self.__dict__) - items = ("{}={!r}".format(k, self.__dict__[k]) for k in keys) - return "{}({})".format(type(self).__name__, ", ".join(items)) - - def __eq__(self, other): - return self.__dict__ == other.__dict__ - - -class SignatureLayout(SimpleNamespace): - """ - Configure a signature. - - The layout of signatures can have different layouts which are - controlled by keyword arguments: - - definition=True Determines if self will generated. - defaults=True - ellipsis=False Replaces defaults by "...". - return_annotation=True - parameter_names=True False removes names before ":". - """ - allowed_keys = SimpleNamespace(definition=True, - defaults=True, - ellipsis=False, - return_annotation=True, - parameter_names=True) - allowed_values = True, False - - def __init__(self, **kwds): - args = SimpleNamespace(**self.allowed_keys.__dict__) - args.__dict__.update(kwds) - self.__dict__.update(args.__dict__) - err_keys = list(set(self.__dict__) - set(self.allowed_keys.__dict__)) - if err_keys: - self._attributeerror(err_keys) - err_values = list(set(self.__dict__.values()) - set(self.allowed_values)) - if err_values: - self._valueerror(err_values) - - def __setattr__(self, key, value): - if key not in self.allowed_keys.__dict__: - self._attributeerror([key]) - if value not in self.allowed_values: - self._valueerror([value]) - self.__dict__[key] = value - - def _attributeerror(self, err_keys): - err_keys = ", ".join(err_keys) - allowed_keys = ", ".join(self.allowed_keys.__dict__.keys()) - raise AttributeError(dedent("""\ - Not allowed: '{err_keys}'. - The only allowed keywords are '{allowed_keys}'. - """.format(**locals()))) - - def _valueerror(self, err_values): - err_values = ", ".join(map(str, err_values)) - allowed_values = ", ".join(map(str, self.allowed_values)) - raise ValueError(dedent("""\ - Not allowed: '{err_values}'. - The only allowed values are '{allowed_values}'. - """.format(**locals()))) - -# The following names are used literally in this module. -# This way, we avoid the dict hashing problem. -signature = SignatureLayout() - -existence = SignatureLayout(definition=False, - defaults=False, - return_annotation=False, - parameter_names=False) - -hintingstub = SignatureLayout(ellipsis=True) - -typeerror = SignatureLayout(definition=False, - return_annotation=False, - parameter_names=False) - - -def define_nameless_parameter(): - """ - Create Nameless Parameters - - A nameless parameter has a reduced string representation. - This is done by cloning the parameter type and overwriting its - __str__ method. The inner structure is still a valid parameter. - """ - def __str__(self): - # for Python 2, we must change self to be an instance of P - klass = self.__class__ - self.__class__ = P - txt = P.__str__(self) - self.__class__ = klass - txt = txt[txt.index(":") + 1:].strip() if ":" in txt else txt - return txt - - P = inspect.Parameter - newname = "NamelessParameter" - bases = P.__bases__ - body = dict(P.__dict__) # get rid of mappingproxy - if "__slots__" in body: - # __slots__ would create duplicates - for name in body["__slots__"]: - del body[name] - body["__str__"] = __str__ - return type(newname, bases, body) - - -NamelessParameter = define_nameless_parameter() - - -def make_signature_nameless(signature): - """ - Make a Signature Nameless - - We use an existing signature and change the type of its parameters. - The signature looks different, but is totally intact. - """ - for key in signature.parameters.keys(): - signature.parameters[key].__class__ = NamelessParameter - - -def create_signature(props, key): - if not props: - # empty signatures string - return - if isinstance(props["multi"], list): - # multi sig: call recursively - return list(create_signature(elem, key) - for elem in props["multi"]) - if type(key) is tuple: - sig_kind, modifier = key - else: - sig_kind, modifier = key, "signature" - - layout = globals()[modifier] # lookup of the modifier in this module - if not isinstance(layout, SignatureLayout): - raise SystemError("Modifiers must be names of a SignatureLayout " - "instance") - - # this is the basic layout of a signature - varnames = props["varnames"] - if layout.definition: - if sig_kind == "function": - pass - elif sig_kind == "method": - varnames = ("self",) + varnames - elif sig_kind == "staticmethod": - pass - elif sig_kind == "classmethod": - varnames = ("klass",) + varnames - else: - 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 = ("...",) * 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) - - # the special case of nameless parameters - if not layout.parameter_names: - make_signature_nameless(sig) - return sig - -# end of file +from signature_loader.layout import * diff --git a/sources/pyside2/PySide2/support/signature/lib/enum_sig.py b/sources/pyside2/PySide2/support/signature/lib/enum_sig.py index c043f04f8..1d19ad5e4 100644 --- a/sources/pyside2/PySide2/support/signature/lib/enum_sig.py +++ b/sources/pyside2/PySide2/support/signature/lib/enum_sig.py @@ -39,131 +39,4 @@ from __future__ import print_function, absolute_import -import sys -from PySide2.support.signature import inspect, get_signature - - -class ExactEnumerator(object): - """ - ExactEnumerator enumerates all signatures in a module as they are. - - This class is used for generating complete listings of all signatures. - An appropriate formatter should be supplied, if printable output - is desired. - """ - def __init__(self, formatter, result_type=dict): - self.fmt = formatter - self.result_type = result_type - - def module(self, mod_name): - __import__(mod_name) - with self.fmt.module(mod_name): - module = sys.modules[mod_name] - members = inspect.getmembers(module, inspect.isclass) - 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)) - return ret - - def klass(self, class_name, klass): - bases_list = [] - for base in klass.__bases__: - name = base.__name__ - if name == "object": - pass - else: - modname = base.__module__ - name = modname + "." + base.__name__ - 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) - # class_members = inspect.getmembers(klass) - # gives us also the inherited things. - class_members = sorted(list(klass.__dict__.items())) - subclasses = [] - for thing_name, thing in class_members: - if inspect.isclass(thing): - subclass_name = ".".join((class_name, thing_name)) - subclasses.append((subclass_name, thing)) - else: - ret.update(self.function(thing_name, thing)) - for subclass_name, subclass in subclasses: - ret.update(self.klass(subclass_name, subclass)) - return ret - - def function(self, func_name, func): - ret = self.result_type() - signature = getattr(func, '__signature__', None) - if signature is not None: - with self.fmt.function(func_name, signature) as key: - ret[key] = signature - return ret - - -def simplify(signature): - if isinstance(signature, list): - # remove duplicates which still sometimes occour: - ret = set(simplify(sig) for sig in signature) - return sorted(ret) if len(ret) > 1 else list(ret)[0] - ret = [] - for pv in signature.parameters.values(): - txt = str(pv) - if ":" not in txt: # 'self' or '*args' - continue - txt = txt[txt.index(":") + 1:] - if "=" in txt: - txt = txt[:txt.index("=")] - quote = txt[0] - if quote in ("'", '"') and txt[-1] == quote: - txt = txt[1:-1] - ret.append(txt.strip()) - return tuple(ret) - - -### disabled for now: -class SimplifyingEnumerator(ExactEnumerator): - """ - SimplifyingEnumerator enumerates all signatures in a module filtered. - - There are no default values, no variable - names and no self parameter. Only types are present after simplification. - The functions 'next' resp. '__next__' are removed - to make the output identical for Python 2 and 3. - An appropriate formatter should be supplied, if printable output - is desired. - """ - - def function(self, func_name, func): - ret = self.result_type() - signature = get_signature(func, 'existence') - if signature is not None and func_name not in ("next", "__next__"): - with self.fmt.function(func_name, signature) as key: - ret[key] = str(signature) - return ret - - -class SimplifyingEnumerator(ExactEnumerator): - """ - SimplifyingEnumerator enumerates all signatures in a module filtered. - - There are no default values, no variable - names and no self parameter. Only types are present after simplification. - The functions 'next' resp. '__next__' are removed - to make the output identical for Python 2 and 3. - An appropriate formatter should be supplied, if printable output - is desired. - """ - - def function(self, func_name, func): - ret = self.result_type() - signature = getattr(func, '__signature__', None) - sig = simplify(signature) if signature is not None else None - if sig is not None and func_name not in ("next", "__next__", "__div__"): - with self.fmt.function(func_name, sig) as key: - ret[key] = sig - return ret +from signature_loader.enum_sig import * diff --git a/sources/pyside2/PySide2/support/signature/mapping.py b/sources/pyside2/PySide2/support/signature/mapping.py index 23ba6a7f1..15727cb6a 100644 --- a/sources/pyside2/PySide2/support/signature/mapping.py +++ b/sources/pyside2/PySide2/support/signature/mapping.py @@ -47,115 +47,23 @@ to the Python representation. The PySide modules are not loaded in advance, but only after they appear in sys.modules. This minimizes the loading overhead. -In principle, we need to re-load the module, when the imports change. -But it is much easier to do it on demand, when we get an exception. -See _resolve_value() in singature.py """ -import sys -import struct import PySide2 -try: - import sample -except ImportError: - pass - -try: - from . import typing -except ImportError: - import typing - -ellipsis = "..." -Char = typing.Union[str, int] # how do I model the limitation to 1 char? -StringList = typing.List[str] -IntList = typing.List[int] -IntMatrix = typing.List[IntList] -Variant = typing.Any -ModelIndexList = typing.List[int] -QImageCleanupFunction = typing.Callable -FloatList = typing.List[float] -FloatMatrix = typing.List[FloatList] -# Pair could be more specific, but we loose the info in the generator. -Pair = typing.Tuple[typing.Any, typing.Any] -MultiMap = typing.DefaultDict[str, typing.List[str]] - -# ulong_max is only 32 bit on windows. -ulong_max = 2*sys.maxsize+1 if len(struct.pack("L", 1)) != 4 else 0xffffffff -ushort_max = 0xffff - -GL_COLOR_BUFFER_BIT = 0x00004000 -GL_NEAREST = 0x2600 - -WId = int -# from 5.9 -GL_TEXTURE_2D = 0x0DE1 -GL_RGBA = 0x1908 - -class _NotCalled(str): - """ - Wrap some text with semantics - - This class is wrapped around text in order to avoid calling it. - There are three reasons for this: - - - some instances cannot be created since they are abstract, - - some can only be created after qApp was created, - - some have an ugly __repr__ with angle brackets in it. - - By using derived classes, good looking instances can be created - which can be used to generate source code or .pyi files. When the - real object is needed, the wrapper can simply be called. - """ - def __repr__(self): - suppress = "PySide2.support.signature.typing." - text = self[len(suppress):] if self.startswith(suppress) else self - return "{}({})".format(type(self).__name__, text) - - def __call__(self): - from .mapping import __dict__ as namespace - text = self if self.endswith(")") else self + "()" - return eval(text, namespace) - -# Some types are abstract. They just show their name. -class Virtual(_NotCalled): - pass - -# Other types I simply could not find. -class Missing(_NotCalled): - pass - -class Invalid(_NotCalled): - pass +from signature_loader.sbk_mapping import * -# Helper types -class Default(_NotCalled): - pass +Sbk_Reloader = Reloader -class Instance(_NotCalled): - pass - - -class Reloader(object): - def __init__(self): - self.sys_module_count = 0 - self.uninitialized = PySide2.__all__[:] + ["sample"] +class Reloader(Sbk_Reloader): + _uninitialized = Sbk_Reloader._uninitialized + PySide2.__all__ + _prefixes = Sbk_Reloader._prefixes + ["PySide2."] def update(self): - if self.sys_module_count == len(sys.modules): - return - self.sys_module_count = len(sys.modules) - g = globals() - for mod_name in self.uninitialized[:]: - if "PySide2." + mod_name in sys.modules or mod_name == "sample": - self.uninitialized.remove(mod_name) - proc_name = "init_" + mod_name - if proc_name in g: - g.update(g[proc_name]()) - + Sbk_Reloader.update(self, globals()) update_mapping = Reloader().update -type_map = {} + def init_QtCore(): import PySide2.QtCore @@ -490,23 +398,4 @@ def init_QtWinExtras(): }) return locals() -def init_sample(): - type_map.update({ - "sample.int": int, - "Complex": complex, - "sample.OddBool": bool, - "sample.bool": bool, - "sample.PStr": str, - "double[]": FloatList, - "OddBool": bool, - "PStr": str, - "sample.char": Char, - "double[][]": FloatMatrix, - "int[]": IntList, - "int[][]": IntMatrix, - }) - return locals() - -# Here was testbinding, actually the source of all evil. - # end of file diff --git a/sources/pyside2/tests/CMakeLists.txt b/sources/pyside2/tests/CMakeLists.txt index 2386950e9..4289012a8 100644 --- a/sources/pyside2/tests/CMakeLists.txt +++ b/sources/pyside2/tests/CMakeLists.txt @@ -54,6 +54,7 @@ else() endif() add_subdirectory(registry) add_subdirectory(signals) + add_subdirectory(support) foreach(shortname IN LISTS all_module_shortnames) message(STATUS "preparing tests for module 'Qt${shortname}'") diff --git a/sources/pyside2/tests/QtWidgets/qlabel_test.py b/sources/pyside2/tests/QtWidgets/qlabel_test.py index 2c5616001..9d84b5c17 100644 --- a/sources/pyside2/tests/QtWidgets/qlabel_test.py +++ b/sources/pyside2/tests/QtWidgets/qlabel_test.py @@ -32,14 +32,7 @@ import unittest from PySide2.QtGui import QPixmap from PySide2.QtWidgets import QLabel -try: - # The normal import statement when PySide2 is installed. - from PySide2 import shiboken2 as shiboken -except ImportError: - # When running make test in shiboken build dir, or when running - # testrunner.py, shiboken2 is not part of the PySide2 module, - # so it needs to be imported as a standalone module. - import shiboken2 as shiboken +import shiboken2 as shiboken from helper import UsesQApplication diff --git a/sources/pyside2/tests/QtWidgets/qstandarditemmodel_test.py b/sources/pyside2/tests/QtWidgets/qstandarditemmodel_test.py index 7a34cf238..c07bd8705 100644 --- a/sources/pyside2/tests/QtWidgets/qstandarditemmodel_test.py +++ b/sources/pyside2/tests/QtWidgets/qstandarditemmodel_test.py @@ -31,14 +31,7 @@ import sys from PySide2.QtGui import QStandardItemModel, QStandardItem from PySide2.QtWidgets import QWidget -try: - # The normal import statement when PySide2 is installed. - from PySide2 import shiboken2 as shiboken -except ImportError: - # When running make test in shiboken build dir, or when running testrunner.py, - # shiboken2 is not part of the PySide2 module, so it needs to be imported as a standalone - # module. - import shiboken2 as shiboken +import shiboken2 as shiboken from helper import UsesQApplication diff --git a/sources/pyside2/tests/registry/existence_test.py b/sources/pyside2/tests/registry/existence_test.py index 6e870385d..0d8014ad8 100644 --- a/sources/pyside2/tests/registry/existence_test.py +++ b/sources/pyside2/tests/registry/existence_test.py @@ -130,7 +130,7 @@ class TestSignaturesExists(unittest.TestCase): warn(msgMultiSignatureCount(key, found_sigs[key], value)) self.assertTrue(check_warnings()) -tested_versions = (5, 6), (5, 9), (5, 11) +tested_versions = (5, 6), (5, 9), (5, 11) #, (5, 12) # activate this, soon! if not have_refmodule and is_ci and qtVersion()[:2] in tested_versions: class TestFor_CI_Init(unittest.TestCase): diff --git a/sources/pyside2/tests/registry/init_platform.py b/sources/pyside2/tests/registry/init_platform.py index 66ec6f566..ded8ba81c 100644 --- a/sources/pyside2/tests/registry/init_platform.py +++ b/sources/pyside2/tests/registry/init_platform.py @@ -56,7 +56,6 @@ from textwrap import dedent all_modules = list("PySide2." + x for x in PySide2.__all__) from PySide2.QtCore import __version__ - from PySide2.support.signature.lib.enum_sig import SimplifyingEnumerator is_py3 = sys.version_info[0] == 3 diff --git a/sources/pyside2/tests/support/voidptr_test.py b/sources/pyside2/tests/support/voidptr_test.py index 8179407e5..330788c63 100644 --- a/sources/pyside2/tests/support/voidptr_test.py +++ b/sources/pyside2/tests/support/voidptr_test.py @@ -27,7 +27,7 @@ ############################################################################# import unittest -from PySide2 import shiboken2 +import shiboken2 as shiboken from PySide2.support import VoidPtr from PySide2.QtCore import QByteArray @@ -40,7 +40,7 @@ class PySide2Support(unittest.TestCase): # or another VoidPtr object. ba = QByteArray(b"Hello world") voidptr = VoidPtr(ba) - self.assertIsInstance(voidptr, shiboken2.VoidPtr) + self.assertIsInstance(voidptr, shiboken.VoidPtr) if __name__ == '__main__': unittest.main() diff --git a/sources/shiboken2/libshiboken/pep384impl.cpp b/sources/shiboken2/libshiboken/pep384impl.cpp index 869d09529..7cca03c84 100644 --- a/sources/shiboken2/libshiboken/pep384impl.cpp +++ b/sources/shiboken2/libshiboken/pep384impl.cpp @@ -38,7 +38,7 @@ ****************************************************************************/ #include "pep384impl.h" -#include <autodecref.h> +#include "autodecref.h" extern "C" { @@ -502,7 +502,8 @@ static PyTypeObject *getFunctionType(void) PyTypeObject *PepStaticMethod_TypePtr = NULL; -static PyTypeObject *getStaticMethodType(void) +static PyTypeObject * +getStaticMethodType(void) { // this works for Python 3, only // "StaticMethodType = type(str.__dict__['maketrans'])\n"; @@ -511,12 +512,31 @@ static PyTypeObject *getStaticMethodType(void) "StaticMethod_Type = type(spamlist.__dict__['staticmeth'])\n"; return (PyTypeObject *) PepRun_GetResult(prog, "StaticMethod_Type"); } + +typedef struct { + PyObject_HEAD + PyObject *sm_callable; + PyObject *sm_dict; +} staticmethod; + +PyObject * +PyStaticMethod_New(PyObject *callable) +{ + staticmethod *sm = (staticmethod *) + PyType_GenericAlloc(PepStaticMethod_TypePtr, 0); + if (sm != NULL) { + Py_INCREF(callable); + sm->sm_callable = callable; + } + return (PyObject *)sm; +} #endif // Py_LIMITED_API #if PY_VERSION_HEX < 0x03000000 PyTypeObject *PepMethodDescr_TypePtr = NULL; -static PyTypeObject *getMethodDescrType(void) +static PyTypeObject * +getMethodDescrType(void) { static const char prog[] = "MethodDescr_Type = type(str.split)\n"; diff --git a/sources/shiboken2/libshiboken/pep384impl.h b/sources/shiboken2/libshiboken/pep384impl.h index 6649fa95e..78b9defb5 100644 --- a/sources/shiboken2/libshiboken/pep384impl.h +++ b/sources/shiboken2/libshiboken/pep384impl.h @@ -63,6 +63,7 @@ extern "C" */ #ifdef Py_LIMITED_API // Why the hell is this useful debugging function not allowed? +// BTW: When used, it breaks on Windows, intentionally! LIBSHIBOKEN_API void _PyObject_Dump(PyObject *); #endif @@ -467,6 +468,7 @@ LIBSHIBOKEN_API PyObject *_Pep_PrivateMangle(PyObject *self, PyObject *name); #ifdef Py_LIMITED_API extern LIBSHIBOKEN_API PyTypeObject *PepStaticMethod_TypePtr; +LIBSHIBOKEN_API PyObject *PyStaticMethod_New(PyObject *callable); #else #define PepStaticMethod_TypePtr &PyStaticMethod_Type #endif diff --git a/sources/shiboken2/libshiboken/pep384impl_doc.rst b/sources/shiboken2/libshiboken/pep384impl_doc.rst index 2844249ad..ab286dd3e 100644 --- a/sources/shiboken2/libshiboken/pep384impl_doc.rst +++ b/sources/shiboken2/libshiboken/pep384impl_doc.rst @@ -283,7 +283,9 @@ written that skips over dotted name parts. Finally, the function ``_PyObject_Dump`` was excluded from the limited API. This is a useful debugging aid that we always want to have available, -so it is added back, again. +so it is added back, again. Anyway, we did not reimplement it, and so +Windows is not supported. +Therefore, a forgotten debugging call of this functions will break COIN. :-) Using The New Type API diff --git a/sources/shiboken2/libshiboken/signature.cpp b/sources/shiboken2/libshiboken/signature.cpp index 962e50d46..92ce3e50a 100644 --- a/sources/shiboken2/libshiboken/signature.cpp +++ b/sources/shiboken2/libshiboken/signature.cpp @@ -111,12 +111,14 @@ CreateSignature(PyObject *props, PyObject *key) static PyObject * pyside_cf_get___signature__(PyObject *func, const char *modifier) { + init_module_2(); return GetSignature_Function(func, modifier); } static PyObject * pyside_sm_get___signature__(PyObject *sm, const char *modifier) { + init_module_2(); Shiboken::AutoDecRef func(PyObject_GetAttrString(sm, "__func__")); return GetSignature_Function(func, modifier); } @@ -239,6 +241,7 @@ name_key_to_func(PyObject *ob) static PyObject * pyside_md_get___signature__(PyObject *ob_md, const char *modifier) { + init_module_2(); Shiboken::AutoDecRef func(name_key_to_func(ob_md)); if (func.object() == Py_None) return Py_None; @@ -250,12 +253,14 @@ pyside_md_get___signature__(PyObject *ob_md, const char *modifier) static PyObject * pyside_wd_get___signature__(PyObject *ob, const char *modifier) { + init_module_2(); return GetSignature_Wrapper(ob, modifier); } static PyObject * pyside_tp_get___signature__(PyObject *typemod, const char *modifier) { + init_module_2(); return GetSignature_TypeMod(typemod, modifier); } @@ -407,14 +412,23 @@ GetSignature_Cached(PyObject *props, const char *sig_kind, const char *modifier) static const char PySide_PythonCode[] = "from __future__ import print_function, absolute_import\n" R"~(if True: + # This is becoming the 'signature_loader' module. + import sys, os, traceback # We avoid imports in phase 1 that could fail. "import shiboken" of the # binary would even crash in FinishSignatureInitialization. def bootstrap(): global __file__ - import PySide2 as root + try: + import shiboken2 as root + except ImportError: + # uninstalled case without ctest, try only this one which has __init__: + from shibokenmodule import shiboken2 as root rp = os.path.realpath(os.path.dirname(root.__file__)) + # This can be the shiboken2 directory or the binary module, so search. + while len(rp) > 3 and not os.path.exists(os.path.join(rp, 'support')): + rp = os.path.abspath(os.path.join(rp, '..')) __file__ = os.path.join(rp, 'support', 'signature', 'loader.py') try: with open(__file__) as _f: @@ -493,6 +507,7 @@ init_phase_2(safe_globals_struc *p, PyMethodDef *methods) error: Py_XDECREF(v); + PyErr_Print(); PyErr_SetString(PyExc_SystemError, "could not initialize part 2"); return -1; } @@ -569,7 +584,6 @@ get_signature(PyObject *self, PyObject *args) const char *modifier = nullptr; init_module_1(); - init_module_2(); if (!PyArg_ParseTuple(args, "O|s", &ob, &modifier)) return NULL; @@ -618,7 +632,7 @@ void handler(int sig) { #endif // _WIN32 static int -PySideType_Ready(PyTypeObject *type) +PySide_PatchTypes(void) { static int init_done = 0; @@ -642,7 +656,7 @@ PySideType_Ready(PyTypeObject *type) #endif // _WIN32 init_done = 1; } - return PyType_Ready(type); + return 0; } static void @@ -672,14 +686,7 @@ PySide_BuildSignatureArgs(PyObject *module, PyObject *type, * We can ignore the EnclosingObject since we get full name info * from the type. */ - if (PyModule_Check(module)) { - const char *name = PyModule_GetName(module); - if (name == NULL) - return -1; - if (strcmp(name, "testbinding") == 0) - return 0; - } - else + if (!PyModule_Check(module)) assert(PyDict_Check(module)); /* * Normally, we would now just call the Python function with the @@ -758,7 +765,7 @@ SbkSpecial_Type_Ready(PyObject *module, PyTypeObject *type, const char *signatures) { int ret; - if (PySideType_Ready(type) < 0) + if (PyType_Ready(type) < 0) return -1; ret = PySide_BuildSignatureArgs(module, (PyObject *)type, signatures); if (ret < 0) { @@ -778,13 +785,9 @@ PySide_FinishSignatures(PyObject *module, const char *signatures) * Initialization of module functions and resolving of static methods. */ - // CRUCIAL: Do not call this on "testbinding": - // The module is different and should not get signatures, anyway. const char *name = PyModule_GetName(module); if (name == NULL) return -1; - if (strcmp(name, "testbinding") == 0) - return 0; // we abuse the call for types, since they both have a __name__ attribute. if (PySide_BuildSignatureArgs(module, module, signatures) < 0) @@ -846,7 +849,8 @@ _build_func_to_type(PyObject *obtype) * mapping from function to type. * * We walk through the method list of the type - * and record the mapping from function to this type in a dict. + * and record the mapping from static method to this type in a dict. + * We also check for hidden methods, see below. */ PyTypeObject *type = reinterpret_cast<PyTypeObject *>(obtype); PyObject *dict = type->tp_dict; @@ -856,13 +860,51 @@ _build_func_to_type(PyObject *obtype) return 0; for (; meth->ml_name != NULL; meth++) { - if (meth->ml_flags & METH_STATIC) { - PyObject *descr = PyDict_GetItemString(dict, meth->ml_name); - if (descr == NULL) + /* + * It is possible that a method is overwritten by another + * attribute with the same name. This case was obviously provoked + * explicitly in "testbinding.TestObject.staticMethodDouble", + * where instead of the method a "PySide2.QtCore.Signal" object + * was in the dict. + * This overlap is also found in regular PySide under + * "PySide2.QtCore.QProcess.error" where again a signal object is + * returned. These hidden methods will be opened for the + * signature module by adding them under the name + * "{name}.overload". + */ + PyObject *descr = PyDict_GetItemString(dict, meth->ml_name); + const char *look_attr = meth->ml_flags & METH_STATIC ? "__func__" : "__name__"; + int check_name = meth->ml_flags & METH_STATIC ? 0 : 1; + if (descr == NULL) + return -1; + + // We first check all methods if one is hidden by something else. + Shiboken::AutoDecRef look(PyObject_GetAttrString(descr, look_attr)); + Shiboken::AutoDecRef given(Py_BuildValue("s", meth->ml_name)); + if (look.isNull() + || (check_name && PyObject_RichCompareBool(look, given, Py_EQ) != 1)) { + PyErr_Clear(); + Shiboken::AutoDecRef cfunc(PyCFunction_NewEx(meth, (PyObject*)type, NULL)); + if (cfunc.isNull()) + return -1; + if (meth->ml_flags & METH_STATIC) + descr = PyStaticMethod_New(cfunc); + else + descr = PyDescr_NewMethod(type, meth); + if (descr == nullptr) return -1; - Shiboken::AutoDecRef func(PyObject_GetAttrString(descr, "__func__")); - if (func.isNull() || - PyDict_SetItem(pyside_globals->map_dict, func, obtype) < 0) + char mangled_name[200]; + strcpy(mangled_name, meth->ml_name); + strcat(mangled_name, ".overload"); + if (PyDict_SetItemString(dict, mangled_name, descr) < 0) + return -1; + if (PyDict_SetItemString(pyside_globals->map_dict, mangled_name, obtype) < 0) + return -1; + continue; + } + // Then we insert the mapping for static methods. + if (meth->ml_flags & METH_STATIC) { + if (PyDict_SetItem(pyside_globals->map_dict, look, obtype) < 0) return -1; } } @@ -873,11 +915,14 @@ void FinishSignatureInitialization(PyObject *module, const char *signatures) { /* - * This function is called at the very end of a module - * initialization. SbkSpecial_Type_Ready has already been run - * with all the types. - * We now initialize module functions and resolve static methods. + * This function is called at the very end of a module initialization. + * We now patch certain types to support the __signature__ attribute, + * initialize module functions and resolve static methods. + * + * Still, it is not possible to call init phase 2 from here, + * because the import is still running. Do it from Python! */ + PySide_PatchTypes(); if (PySide_FinishSignatures(module, signatures) < 0) { PyErr_Print(); PyErr_SetNone(PyExc_ImportError); diff --git a/sources/shiboken2/shibokenmodule/CMakeLists.txt b/sources/shiboken2/shibokenmodule/CMakeLists.txt index 20baf9f7e..342b6d908 100644 --- a/sources/shiboken2/shibokenmodule/CMakeLists.txt +++ b/sources/shiboken2/shibokenmodule/CMakeLists.txt @@ -52,6 +52,33 @@ install(FILES "${CMAKE_CURRENT_BINARY_DIR}/_config.py" configure_file("${CMAKE_CURRENT_SOURCE_DIR}/__init__.py.in" "${CMAKE_CURRENT_BINARY_DIR}/__init__.py" @ONLY) + +configure_file("${CMAKE_CURRENT_SOURCE_DIR}/support/__init__.py" + "${CMAKE_CURRENT_BINARY_DIR}/support/__init__.py" COPYONLY) +configure_file("${CMAKE_CURRENT_SOURCE_DIR}/support/signature/__init__.py" + "${CMAKE_CURRENT_BINARY_DIR}/support/signature/__init__.py" COPYONLY) +configure_file("${CMAKE_CURRENT_SOURCE_DIR}/support/signature/layout.py" + "${CMAKE_CURRENT_BINARY_DIR}/support/signature/layout.py" COPYONLY) +configure_file("${CMAKE_CURRENT_SOURCE_DIR}/support/signature/loader.py" + "${CMAKE_CURRENT_BINARY_DIR}/support/signature/loader.py" COPYONLY) +configure_file("${CMAKE_CURRENT_SOURCE_DIR}/support/signature/mapping.py" + "${CMAKE_CURRENT_BINARY_DIR}/support/signature/mapping.py" COPYONLY) +configure_file("${CMAKE_CURRENT_SOURCE_DIR}/support/signature/parser.py" + "${CMAKE_CURRENT_BINARY_DIR}/support/signature/parser.py" COPYONLY) +configure_file("${CMAKE_CURRENT_SOURCE_DIR}/support/signature/lib/__init__.py" + "${CMAKE_CURRENT_BINARY_DIR}/support/signature/lib/__init__.py" COPYONLY) +configure_file("${CMAKE_CURRENT_SOURCE_DIR}/support/signature/lib/enum_sig.py" + "${CMAKE_CURRENT_BINARY_DIR}/support/signature/lib/enum_sig.py" COPYONLY) +if (PYTHON_VERSION_MAJOR EQUAL 3) +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) +endif() +install(DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/support" + DESTINATION "${PYTHON_SITE_PACKAGES}/shiboken2") + install(FILES "${CMAKE_CURRENT_BINARY_DIR}/__init__.py" DESTINATION "${PYTHON_SITE_PACKAGES}/shiboken2") diff --git a/sources/shiboken2/shibokenmodule/support/__init__.py b/sources/shiboken2/shibokenmodule/support/__init__.py new file mode 100644 index 000000000..760d89571 --- /dev/null +++ b/sources/shiboken2/shibokenmodule/support/__init__.py @@ -0,0 +1,40 @@ +############################################################################# +## +## Copyright (C) 2017 The Qt Company Ltd. +## Contact: https://www.qt.io/licensing/ +## +## This file is part of Qt for Python. +## +## $QT_BEGIN_LICENSE:LGPL$ +## Commercial License Usage +## Licensees holding valid commercial Qt licenses may use this file in +## accordance with the commercial license agreement provided with the +## Software or, alternatively, in accordance with the terms contained in +## a written agreement between you and The Qt Company. For licensing terms +## and conditions see https://www.qt.io/terms-conditions. For further +## information use the contact form at https://www.qt.io/contact-us. +## +## GNU Lesser General Public License Usage +## Alternatively, this file may be used under the terms of the GNU Lesser +## General Public License version 3 as published by the Free Software +## Foundation and appearing in the file LICENSE.LGPL3 included in the +## packaging of this file. Please review the following information to +## ensure the GNU Lesser General Public License version 3 requirements +## will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +## +## GNU General Public License Usage +## Alternatively, this file may be used under the terms of the GNU +## General Public License version 2.0 or (at your option) the GNU General +## Public license version 3 or any later version approved by the KDE Free +## Qt Foundation. The licenses are as published by the Free Software +## Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +## included in the packaging of this file. Please review the following +## information to ensure the GNU General Public License requirements will +## be met: https://www.gnu.org/licenses/gpl-2.0.html and +## https://www.gnu.org/licenses/gpl-3.0.html. +## +## $QT_END_LICENSE$ +## +############################################################################# + +# this file intentionally left blank diff --git a/sources/pyside2/PySide2/support/signature/PSF-3.7.0.txt b/sources/shiboken2/shibokenmodule/support/signature/PSF-3.7.0.txt index be42010dd..be42010dd 100644 --- a/sources/pyside2/PySide2/support/signature/PSF-3.7.0.txt +++ b/sources/shiboken2/shibokenmodule/support/signature/PSF-3.7.0.txt diff --git a/sources/pyside2/PySide2/support/signature/loader.py b/sources/shiboken2/shibokenmodule/support/signature/__init__.py index 21ecebcc8..f639f1ad0 100644 --- a/sources/pyside2/PySide2/support/signature/loader.py +++ b/sources/shiboken2/shibokenmodule/support/signature/__init__.py @@ -39,51 +39,6 @@ from __future__ import print_function, absolute_import -""" -loader.py - -The loader has to lazy-load the signature module and also provides a few -Python modules to support Python 2.7 . - -This file was originally directly embedded into the C source. -After it grew more and more, I now prefer to have it as Python file. -The remaining stub loader in the C source is now only a short string. - -This version does no longer use an embedded .zip file but is a package. -The old code without a package but with zip compression can still be found -at https://codereview.qt-project.org/#/c/203533/ for reference. -""" - -import sys -import os - -# Make sure that we always have the PySide containing package first. -# This is crucial for the mapping during reload in the tests. -package_dir = __file__ -for _ in "four": - package_dir = os.path.dirname(package_dir) -sys.path.insert(0, package_dir) -if sys.version_info >= (3,): - import inspect -else: - import inspect - namespace = inspect.__dict__ - from PySide2.support.signature import backport_inspect as inspect - _doc = inspect.__doc__ - inspect.__dict__.update(namespace) - 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("_")) - -# name used in signature.cpp -from PySide2.support.signature.parser import pyside_type_init -sys.path.pop(0) -# Note also that during the tests we have a different encoding that would -# break the Python license decorated files without an encoding line. -from PySide2.support.signature import layout - -# name used in signature.cpp -def create_signature(props, key): - return layout.create_signature(props, key) - -# end of file +# Trigger initialization phase 2. +_ = type.__signature__ +from signature_loader import get_signature, inspect diff --git a/sources/pyside2/PySide2/support/signature/backport_inspect.py b/sources/shiboken2/shibokenmodule/support/signature/backport_inspect.py index 0eafe9caa..0eafe9caa 100644 --- a/sources/pyside2/PySide2/support/signature/backport_inspect.py +++ b/sources/shiboken2/shibokenmodule/support/signature/backport_inspect.py diff --git a/sources/pyside2/PySide2/support/signature/fix-complaints.py b/sources/shiboken2/shibokenmodule/support/signature/fix-complaints.py index e078ef1ab..e078ef1ab 100644 --- a/sources/pyside2/PySide2/support/signature/fix-complaints.py +++ b/sources/shiboken2/shibokenmodule/support/signature/fix-complaints.py diff --git a/sources/shiboken2/shibokenmodule/support/signature/layout.py b/sources/shiboken2/shibokenmodule/support/signature/layout.py new file mode 100644 index 000000000..47f97fc2b --- /dev/null +++ b/sources/shiboken2/shibokenmodule/support/signature/layout.py @@ -0,0 +1,245 @@ +############################################################################# +## +## Copyright (C) 2018 The Qt Company Ltd. +## Contact: https://www.qt.io/licensing/ +## +## This file is part of Qt for Python. +## +## $QT_BEGIN_LICENSE:LGPL$ +## Commercial License Usage +## Licensees holding valid commercial Qt licenses may use this file in +## accordance with the commercial license agreement provided with the +## Software or, alternatively, in accordance with the terms contained in +## a written agreement between you and The Qt Company. For licensing terms +## and conditions see https://www.qt.io/terms-conditions. For further +## information use the contact form at https://www.qt.io/contact-us. +## +## GNU Lesser General Public License Usage +## Alternatively, this file may be used under the terms of the GNU Lesser +## General Public License version 3 as published by the Free Software +## Foundation and appearing in the file LICENSE.LGPL3 included in the +## packaging of this file. Please review the following information to +## ensure the GNU Lesser General Public License version 3 requirements +## will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +## +## GNU General Public License Usage +## Alternatively, this file may be used under the terms of the GNU +## General Public License version 2.0 or (at your option) the GNU General +## Public license version 3 or any later version approved by the KDE Free +## Qt Foundation. The licenses are as published by the Free Software +## Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +## included in the packaging of this file. Please review the following +## information to ensure the GNU General Public License requirements will +## be met: https://www.gnu.org/licenses/gpl-2.0.html and +## https://www.gnu.org/licenses/gpl-3.0.html. +## +## $QT_END_LICENSE$ +## +############################################################################# + +from __future__ import print_function, absolute_import + +""" +layout.py + +The signature module now has the capability to configure +differently formatted versions of signatures. The default +layout is known from the "__signature__" attribute. + +The function "get_signature(ob, modifier=None)" produces the same +signatures by default. By passing different modifiers, you +can select different layouts. + +This module configures the different layouts which can be used. +It also implements them in this file. The configurations are +used literally as strings like "signature", "existence", etc. +""" + +from textwrap import dedent +from signature_loader import inspect + + +class SimpleNamespace(object): + # From types.rst, because the builtin is implemented in Python 3, only. + def __init__(self, **kwargs): + self.__dict__.update(kwargs) + + def __repr__(self): + keys = sorted(self.__dict__) + items = ("{}={!r}".format(k, self.__dict__[k]) for k in keys) + return "{}({})".format(type(self).__name__, ", ".join(items)) + + def __eq__(self, other): + return self.__dict__ == other.__dict__ + + +class SignatureLayout(SimpleNamespace): + """ + Configure a signature. + + The layout of signatures can have different layouts which are + controlled by keyword arguments: + + definition=True Determines if self will generated. + defaults=True + ellipsis=False Replaces defaults by "...". + return_annotation=True + parameter_names=True False removes names before ":". + """ + allowed_keys = SimpleNamespace(definition=True, + defaults=True, + ellipsis=False, + return_annotation=True, + parameter_names=True) + allowed_values = True, False + + def __init__(self, **kwds): + args = SimpleNamespace(**self.allowed_keys.__dict__) + args.__dict__.update(kwds) + self.__dict__.update(args.__dict__) + err_keys = list(set(self.__dict__) - set(self.allowed_keys.__dict__)) + if err_keys: + self._attributeerror(err_keys) + err_values = list(set(self.__dict__.values()) - set(self.allowed_values)) + if err_values: + self._valueerror(err_values) + + def __setattr__(self, key, value): + if key not in self.allowed_keys.__dict__: + self._attributeerror([key]) + if value not in self.allowed_values: + self._valueerror([value]) + self.__dict__[key] = value + + def _attributeerror(self, err_keys): + err_keys = ", ".join(err_keys) + allowed_keys = ", ".join(self.allowed_keys.__dict__.keys()) + raise AttributeError(dedent("""\ + Not allowed: '{err_keys}'. + The only allowed keywords are '{allowed_keys}'. + """.format(**locals()))) + + def _valueerror(self, err_values): + err_values = ", ".join(map(str, err_values)) + allowed_values = ", ".join(map(str, self.allowed_values)) + raise ValueError(dedent("""\ + Not allowed: '{err_values}'. + The only allowed values are '{allowed_values}'. + """.format(**locals()))) + +# The following names are used literally in this module. +# This way, we avoid the dict hashing problem. +signature = SignatureLayout() + +existence = SignatureLayout(definition=False, + defaults=False, + return_annotation=False, + parameter_names=False) + +hintingstub = SignatureLayout(ellipsis=True) + +typeerror = SignatureLayout(definition=False, + return_annotation=False, + parameter_names=False) + + +def define_nameless_parameter(): + """ + Create Nameless Parameters + + A nameless parameter has a reduced string representation. + This is done by cloning the parameter type and overwriting its + __str__ method. The inner structure is still a valid parameter. + """ + def __str__(self): + # for Python 2, we must change self to be an instance of P + klass = self.__class__ + self.__class__ = P + txt = P.__str__(self) + self.__class__ = klass + txt = txt[txt.index(":") + 1:].strip() if ":" in txt else txt + return txt + + P = inspect.Parameter + newname = "NamelessParameter" + bases = P.__bases__ + body = dict(P.__dict__) # get rid of mappingproxy + if "__slots__" in body: + # __slots__ would create duplicates + for name in body["__slots__"]: + del body[name] + body["__str__"] = __str__ + return type(newname, bases, body) + + +NamelessParameter = define_nameless_parameter() + + +def make_signature_nameless(signature): + """ + Make a Signature Nameless + + We use an existing signature and change the type of its parameters. + The signature looks different, but is totally intact. + """ + for key in signature.parameters.keys(): + signature.parameters[key].__class__ = NamelessParameter + + +def create_signature(props, key): + if not props: + # empty signatures string + return + if isinstance(props["multi"], list): + # multi sig: call recursively + return list(create_signature(elem, key) + for elem in props["multi"]) + if type(key) is tuple: + sig_kind, modifier = key + else: + sig_kind, modifier = key, "signature" + + layout = globals()[modifier] # lookup of the modifier in this module + if not isinstance(layout, SignatureLayout): + raise SystemError("Modifiers must be names of a SignatureLayout " + "instance") + + # this is the basic layout of a signature + varnames = props["varnames"] + if layout.definition: + if sig_kind == "function": + pass + elif sig_kind == "method": + varnames = ("self",) + varnames + elif sig_kind == "staticmethod": + pass + elif sig_kind == "classmethod": + varnames = ("klass",) + varnames + else: + 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 = ("...",) * 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) + + # the special case of nameless parameters + if not layout.parameter_names: + make_signature_nameless(sig) + return sig + +# end of file diff --git a/sources/shiboken2/shibokenmodule/support/signature/lib/__init__.py b/sources/shiboken2/shibokenmodule/support/signature/lib/__init__.py new file mode 100644 index 000000000..2d640cb89 --- /dev/null +++ b/sources/shiboken2/shibokenmodule/support/signature/lib/__init__.py @@ -0,0 +1,40 @@ +############################################################################# +## +## Copyright (C) 2018 The Qt Company Ltd. +## Contact: https://www.qt.io/licensing/ +## +## This file is part of Qt for Python. +## +## $QT_BEGIN_LICENSE:LGPL$ +## Commercial License Usage +## Licensees holding valid commercial Qt licenses may use this file in +## accordance with the commercial license agreement provided with the +## Software or, alternatively, in accordance with the terms contained in +## a written agreement between you and The Qt Company. For licensing terms +## and conditions see https://www.qt.io/terms-conditions. For further +## information use the contact form at https://www.qt.io/contact-us. +## +## GNU Lesser General Public License Usage +## Alternatively, this file may be used under the terms of the GNU Lesser +## General Public License version 3 as published by the Free Software +## Foundation and appearing in the file LICENSE.LGPL3 included in the +## packaging of this file. Please review the following information to +## ensure the GNU Lesser General Public License version 3 requirements +## will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +## +## GNU General Public License Usage +## Alternatively, this file may be used under the terms of the GNU +## General Public License version 2.0 or (at your option) the GNU General +## Public license version 3 or any later version approved by the KDE Free +## Qt Foundation. The licenses are as published by the Free Software +## Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +## included in the packaging of this file. Please review the following +## information to ensure the GNU General Public License requirements will +## be met: https://www.gnu.org/licenses/gpl-2.0.html and +## https://www.gnu.org/licenses/gpl-3.0.html. +## +## $QT_END_LICENSE$ +## +############################################################################# + +# this file intentionally left blank diff --git a/sources/shiboken2/shibokenmodule/support/signature/lib/enum_sig.py b/sources/shiboken2/shibokenmodule/support/signature/lib/enum_sig.py new file mode 100644 index 000000000..2ec14d62b --- /dev/null +++ b/sources/shiboken2/shibokenmodule/support/signature/lib/enum_sig.py @@ -0,0 +1,135 @@ +############################################################################# +## +## Copyright (C) 2018 The Qt Company Ltd. +## Contact: https://www.qt.io/licensing/ +## +## This file is part of Qt for Python. +## +## $QT_BEGIN_LICENSE:LGPL$ +## Commercial License Usage +## Licensees holding valid commercial Qt licenses may use this file in +## accordance with the commercial license agreement provided with the +## Software or, alternatively, in accordance with the terms contained in +## a written agreement between you and The Qt Company. For licensing terms +## and conditions see https://www.qt.io/terms-conditions. For further +## information use the contact form at https://www.qt.io/contact-us. +## +## GNU Lesser General Public License Usage +## Alternatively, this file may be used under the terms of the GNU Lesser +## General Public License version 3 as published by the Free Software +## Foundation and appearing in the file LICENSE.LGPL3 included in the +## packaging of this file. Please review the following information to +## ensure the GNU Lesser General Public License version 3 requirements +## will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +## +## GNU General Public License Usage +## Alternatively, this file may be used under the terms of the GNU +## General Public License version 2.0 or (at your option) the GNU General +## Public license version 3 or any later version approved by the KDE Free +## Qt Foundation. The licenses are as published by the Free Software +## Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +## included in the packaging of this file. Please review the following +## information to ensure the GNU General Public License requirements will +## be met: https://www.gnu.org/licenses/gpl-2.0.html and +## https://www.gnu.org/licenses/gpl-3.0.html. +## +## $QT_END_LICENSE$ +## +############################################################################# + +from __future__ import print_function, absolute_import + +import sys +from signature_loader import get_signature, inspect + + +class ExactEnumerator(object): + """ + ExactEnumerator enumerates all signatures in a module as they are. + + This class is used for generating complete listings of all signatures. + An appropriate formatter should be supplied, if printable output + is desired. + """ + def __init__(self, formatter, result_type=dict): + self.fmt = formatter + self.result_type = result_type + + def module(self, mod_name): + __import__(mod_name) + with self.fmt.module(mod_name): + module = sys.modules[mod_name] + members = inspect.getmembers(module, inspect.isclass) + 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)) + return ret + + def klass(self, class_name, klass): + bases_list = [] + for base in klass.__bases__: + name = base.__name__ + if name == "object": + pass + else: + modname = base.__module__ + name = modname + "." + base.__name__ + 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) + # class_members = inspect.getmembers(klass) + # gives us also the inherited things. + class_members = sorted(list(klass.__dict__.items())) + subclasses = [] + for thing_name, thing in class_members: + if inspect.isclass(thing): + subclass_name = ".".join((class_name, thing_name)) + subclasses.append((subclass_name, thing)) + else: + ret.update(self.function(thing_name, thing)) + for subclass_name, subclass in subclasses: + ret.update(self.klass(subclass_name, subclass)) + return ret + + def function(self, func_name, func): + ret = self.result_type() + signature = getattr(func, '__signature__', None) + if signature is not None: + with self.fmt.function(func_name, signature) as key: + ret[key] = str(signature) + return ret + + +def stringify(signature): + if isinstance(signature, list): + # remove duplicates which still sometimes occour: + ret = set(stringify(sig) for sig in signature) + return sorted(ret) if len(ret) > 1 else list(ret)[0] + return tuple(str(pv) for pv in signature.parameters.values()) + + +class SimplifyingEnumerator(ExactEnumerator): + """ + SimplifyingEnumerator enumerates all signatures in a module filtered. + + There are no default values, no variable + names and no self parameter. Only types are present after simplification. + The functions 'next' resp. '__next__' are removed + to make the output identical for Python 2 and 3. + An appropriate formatter should be supplied, if printable output + is desired. + """ + + def function(self, func_name, func): + ret = self.result_type() + signature = get_signature(func, 'existence') + sig = stringify(signature) if signature is not None else None + if sig is not None and func_name not in ("next", "__next__", "__div__"): + with self.fmt.function(func_name, sig) as key: + ret[key] = sig + return ret diff --git a/sources/shiboken2/shibokenmodule/support/signature/loader.py b/sources/shiboken2/shibokenmodule/support/signature/loader.py new file mode 100644 index 000000000..83493e511 --- /dev/null +++ b/sources/shiboken2/shibokenmodule/support/signature/loader.py @@ -0,0 +1,176 @@ +############################################################################# +## +## Copyright (C) 2018 The Qt Company Ltd. +## Contact: https://www.qt.io/licensing/ +## +## This file is part of Qt for Python. +## +## $QT_BEGIN_LICENSE:LGPL$ +## Commercial License Usage +## Licensees holding valid commercial Qt licenses may use this file in +## accordance with the commercial license agreement provided with the +## Software or, alternatively, in accordance with the terms contained in +## a written agreement between you and The Qt Company. For licensing terms +## and conditions see https://www.qt.io/terms-conditions. For further +## information use the contact form at https://www.qt.io/contact-us. +## +## GNU Lesser General Public License Usage +## Alternatively, this file may be used under the terms of the GNU Lesser +## General Public License version 3 as published by the Free Software +## Foundation and appearing in the file LICENSE.LGPL3 included in the +## packaging of this file. Please review the following information to +## ensure the GNU Lesser General Public License version 3 requirements +## will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +## +## GNU General Public License Usage +## Alternatively, this file may be used under the terms of the GNU +## General Public License version 2.0 or (at your option) the GNU General +## Public license version 3 or any later version approved by the KDE Free +## Qt Foundation. The licenses are as published by the Free Software +## Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +## included in the packaging of this file. Please review the following +## information to ensure the GNU General Public License requirements will +## be met: https://www.gnu.org/licenses/gpl-2.0.html and +## https://www.gnu.org/licenses/gpl-3.0.html. +## +## $QT_END_LICENSE$ +## +############################################################################# + +from __future__ import print_function, absolute_import + +""" +loader.py + +The loader has to lazy-load the signature module and also provides a few +Python modules to support Python 2.7 . + +This file was originally directly embedded into the C source. +After it grew more and more, I now prefer to have it as Python file. +The remaining stub loader in the C source is now only a short string. + +This version does no longer use an embedded .zip file but is a package. +The old code without a package but with zip compression can still be found +at https://codereview.qt-project.org/#/c/203533/ for reference. +""" + +import sys +import os +import traceback +import types +from contextlib import contextmanager +from distutils.sysconfig import get_python_lib + +""" +A note on the import problem (solved): + +During the tests, the shiboken build structure has the layout + + shiboken2/shibokenmodule/shiboken2.abi3.so + +and the name "shiboken2" in sys.modules points directly to the binary +file, hiding the outer shiboken2 module. + +To fix that, we temporarily remove the binary from sys.path, +do the needed imports and then restore the binary. +This action was put into a context manager for readability. +""" + +# On Python 2, we only have ImportError, which is way too coarse. +# When problems occour, please use Python 3, because it has the finer +# ModuleNotFoundError. + +try: + ModuleNotFoundError +except NameError: + ModuleNotFoundError = ImportError + +@contextmanager +def ensure_import_support(): + # Make sure that we always have the shiboken containing package first. + # This is sometimes hidden by the ctest paths. + # We adjust the path in a way that the support folder comes first. + # This can be in "shiboken2/support" or in "shibokenmodule/support", + # so we use the "support" folder as toplevel. + sbk_support_dir = os.path.abspath(os.path.join(__file__, "..", "..", "..")) + sys.path.insert(0, sbk_support_dir) + sbk = "shiboken2" + save_sbk = sys.modules.pop(sbk) if sbk in sys.modules else None + # make sure that we get at the support folder + try: + import support + yield + except Exception as e: + print("Problem importing support:") + print(e) + traceback.print_exc() + sys.stdout.flush() + sys.exit(-1) + if save_sbk: + sys.modules[sbk] = save_sbk + sys.path.pop(0) + + +with ensure_import_support(): + # We store all needed modules in signature_loader. + # This way, they are always accessible. + import signature_loader + + if sys.version_info >= (3,): + import typing + import inspect + else: + import inspect + namespace = inspect.__dict__ + from support.signature import typing + from support.signature import backport_inspect as inspect + _doc = inspect.__doc__ + inspect.__dict__.update(namespace) + 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("_")) + + def put_into_loader_package(module, loader=signature_loader): + # Note: the "with" statement hides that we are no longer in a + # global context, but inside ensure_import_support. Therefore, + # we need to explicitly pass the signature_loader in. + + # take the last component of the module name + name = module.__name__.rsplit(".", 1)[-1] + # allow access as signature_loader.typing + setattr(loader, name, module) + # put into sys.modules as a package to allow all import options + fullname = "{}.{}".format(loader.__name__, name) + sys.modules[fullname] = module + + put_into_loader_package(typing) + put_into_loader_package(inspect) + from support.signature import mapping as sbk_mapping + sbk_mapping.__name__ = "sbk_mapping" + put_into_loader_package(sbk_mapping) + # We may or may not use PySide. + try: + from PySide2.support.signature import mapping + except ModuleNotFoundError: + mapping = sbk_mapping + mapping.__name__ = "mapping" + put_into_loader_package(mapping) + from support.signature import layout + put_into_loader_package(layout) + from support.signature.lib import enum_sig + put_into_loader_package(enum_sig) + from support.signature.parser import pyside_type_init + + +# Note also that during the tests we have a different encoding that would +# break the Python license decorated files without an encoding line. + +# name used in signature.cpp +def create_signature(props, key): + return layout.create_signature(props, key) + +# name used in signature.cpp +def seterror_argument(args, func_name): + return errorhandler.seterror_argument(args, func_name) + +# end of file diff --git a/sources/shiboken2/shibokenmodule/support/signature/mapping.py b/sources/shiboken2/shibokenmodule/support/signature/mapping.py new file mode 100644 index 000000000..724b0c751 --- /dev/null +++ b/sources/shiboken2/shibokenmodule/support/signature/mapping.py @@ -0,0 +1,199 @@ +############################################################################# +## +## Copyright (C) 2018 The Qt Company Ltd. +## Contact: https://www.qt.io/licensing/ +## +## This file is part of Qt for Python. +## +## $QT_BEGIN_LICENSE:LGPL$ +## Commercial License Usage +## Licensees holding valid commercial Qt licenses may use this file in +## accordance with the commercial license agreement provided with the +## Software or, alternatively, in accordance with the terms contained in +## a written agreement between you and The Qt Company. For licensing terms +## and conditions see https://www.qt.io/terms-conditions. For further +## information use the contact form at https://www.qt.io/contact-us. +## +## GNU Lesser General Public License Usage +## Alternatively, this file may be used under the terms of the GNU Lesser +## General Public License version 3 as published by the Free Software +## Foundation and appearing in the file LICENSE.LGPL3 included in the +## packaging of this file. Please review the following information to +## ensure the GNU Lesser General Public License version 3 requirements +## will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +## +## GNU General Public License Usage +## Alternatively, this file may be used under the terms of the GNU +## General Public License version 2.0 or (at your option) the GNU General +## Public license version 3 or any later version approved by the KDE Free +## Qt Foundation. The licenses are as published by the Free Software +## Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +## included in the packaging of this file. Please review the following +## information to ensure the GNU General Public License requirements will +## be met: https://www.gnu.org/licenses/gpl-2.0.html and +## https://www.gnu.org/licenses/gpl-3.0.html. +## +## $QT_END_LICENSE$ +## +############################################################################# + +from __future__ import print_function, absolute_import + +""" +mapping.py + +This module has the mapping from the pyside C-modules view of signatures +to the Python representation. + +The PySide modules are not loaded in advance, but only after they appear +in sys.modules. This minimizes the loading overhead. +""" + +import sys +import struct +import os +import pkgutil + +from signature_loader import typing + +ellipsis = "..." +Char = typing.Union[str, int] # how do I model the limitation to 1 char? +StringList = typing.List[str] +IntList = typing.List[int] +Point = typing.Tuple[float, float] +PointList = typing.List[Point] +IntMatrix = typing.List[IntList] +Variant = typing.Any +ModelIndexList = typing.List[int] +QImageCleanupFunction = typing.Callable +FloatList = typing.List[float] +FloatMatrix = typing.List[FloatList] +# Pair could be more specific, but we loose the info in the generator. +Pair = typing.Tuple[typing.Any, typing.Any] +MultiMap = typing.DefaultDict[str, typing.List[str]] + +# ulong_max is only 32 bit on windows. +ulong_max = 2*sys.maxsize+1 if len(struct.pack("L", 1)) != 4 else 0xffffffff +ushort_max = 0xffff + +GL_COLOR_BUFFER_BIT = 0x00004000 +GL_NEAREST = 0x2600 + +WId = int + +# from 5.9 +GL_TEXTURE_2D = 0x0DE1 +GL_RGBA = 0x1908 + +class _NotCalled(str): + """ + Wrap some text with semantics + + This class is wrapped around text in order to avoid calling it. + There are three reasons for this: + + - some instances cannot be created since they are abstract, + - some can only be created after qApp was created, + - some have an ugly __repr__ with angle brackets in it. + + By using derived classes, good looking instances can be created + which can be used to generate source code or .pyi files. When the + real object is needed, the wrapper can simply be called. + """ + def __repr__(self): + suppress = "PySide2.support.signature.typing." + text = self[len(suppress):] if self.startswith(suppress) else self + return "{}({})".format(type(self).__name__, text) + + def __call__(self): + from signature_loader.mapping import __dict__ as namespace + text = self if self.endswith(")") else self + "()" + return eval(text, namespace) + +# Some types are abstract. They just show their name. +class Virtual(_NotCalled): + pass + +# Other types I simply could not find. +class Missing(_NotCalled): + pass + +class Invalid(_NotCalled): + pass + +# Helper types +class Default(_NotCalled): + pass + +class Instance(_NotCalled): + pass + + +class Reloader(object): + _uninitialized = ["sample"] + _prefixes = [""] + + def __init__(self): + self.sys_module_count = 0 + self.uninitialized = self._uninitialized + + def update(self, g=None): + if self.sys_module_count == len(sys.modules): + return + self.sys_module_count = len(sys.modules) + if g is None: + g = globals() + for mod_name in self.uninitialized[:]: + for prefix in self._prefixes: + if prefix + mod_name in sys.modules: + self.uninitialized.remove(mod_name) + proc_name = "init_" + mod_name + if proc_name in g: + g.update(g[proc_name]()) + + +update_mapping = Reloader().update +type_map = {} + + +def init_sample(): + import sample + import datetime + type_map.update({ + "sample.int": int, + "Complex": complex, + "sample.OddBool": bool, + "sample.bool": bool, + "sample.PStr": str, + "double[]": FloatList, + "OddBool": bool, + "PStr": str, + "sample.char": Char, + "double[][]": FloatMatrix, + "int[]": IntList, + "int[][]": IntMatrix, + "sample.Point": Point, + "sample.ObjectType": object, + "std.string": str, + "HANDLE": int, + "Foo.HANDLE": int, + "sample.Photon.TemplateBase": Missing("sample.Photon.TemplateBase"), + "ObjectType.Identifier": Missing("sample.ObjectType.Identifier"), + "zero(HANDLE)": 0, + "Null": None, + "zero(sample.ObjectType)": None, + "std.size_t": int, + 'Str("<unknown>")': "<unknown>", + 'Str("<unk")': "<unk", + 'Str("nown>")': "nown>", + "zero(sample.ObjectModel)": None, + "sample.unsigned char": Char, + "sample.double": float, + "zero(sample.bool)": False, + "PyDate": datetime.date, + "ZeroIn": 0, + "Point[]": PointList, + }) + return locals() + +# end of file diff --git a/sources/pyside2/PySide2/support/signature/parser.py b/sources/shiboken2/shibokenmodule/support/signature/parser.py index dd6640fde..82cd9aab3 100644 --- a/sources/pyside2/PySide2/support/signature/parser.py +++ b/sources/shiboken2/shibokenmodule/support/signature/parser.py @@ -45,7 +45,8 @@ import warnings import types import keyword import functools -from .mapping import type_map, update_mapping, __dict__ as namespace +from signature_loader.mapping import ( + type_map, update_mapping, __dict__ as namespace) _DEBUG = False LIST_KEYWORDS = False diff --git a/sources/pyside2/PySide2/support/signature/qt_attribution.json b/sources/shiboken2/shibokenmodule/support/signature/qt_attribution.json index 491ae8054..491ae8054 100644 --- a/sources/pyside2/PySide2/support/signature/qt_attribution.json +++ b/sources/shiboken2/shibokenmodule/support/signature/qt_attribution.json diff --git a/sources/pyside2/PySide2/support/signature/typing27.py b/sources/shiboken2/shibokenmodule/support/signature/typing27.py index ae1d6ba27..5d1c6058b 100644 --- a/sources/pyside2/PySide2/support/signature/typing27.py +++ b/sources/shiboken2/shibokenmodule/support/signature/typing27.py @@ -95,6 +95,7 @@ import functools import re as stdlib_re # Avoid confusion with the re we export. import sys import types +import copy try: import collections.abc as collections_abc except ImportError: @@ -160,6 +161,7 @@ __all__ = [ 'NewType', 'no_type_check', 'no_type_check_decorator', + 'NoReturn', 'overload', 'Text', 'TYPE_CHECKING', @@ -445,7 +447,7 @@ def _type_check(arg, msg): if ( type(arg).__name__ in ('_Union', '_Optional') and not getattr(arg, '__origin__', None) or - isinstance(arg, TypingMeta) and _gorg(arg) 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 @@ -1033,29 +1035,6 @@ class _Optional(_FinalTypingBase): Optional = _Optional(_root=True) -def _gorg(a): - """Return the farthest origin of a generic class (internal helper).""" - assert isinstance(a, GenericMeta) - while a.__origin__ is not None: - a = a.__origin__ - return a - - -def _geqv(a, b): - """Return whether two generic classes are equivalent (internal helper). - - The intention is to consider generic class X and any of its - parameterized forms (X[T], X[int], etc.) as equivalent. - - However, X is not equivalent to a subclass of X. - - The relation is reflexive, symmetric and transitive. - """ - assert isinstance(a, GenericMeta) and isinstance(b, GenericMeta) - # Reduce each to its origin. - return _gorg(a) is _gorg(b) - - def _next_in_mro(cls): """Helper for Generic.__new__. @@ -1065,7 +1044,7 @@ def _next_in_mro(cls): next_in_mro = object # Look for the last occurrence of Generic or Generic[...]. for i, c in enumerate(cls.__mro__[:-1]): - if isinstance(c, GenericMeta) and _gorg(c) is Generic: + if isinstance(c, GenericMeta) and c._gorg is Generic: next_in_mro = cls.__mro__[i + 1] return next_in_mro @@ -1166,13 +1145,15 @@ class GenericMeta(TypingMeta, abc.ABCMeta): extra = namespace.get('__extra__') if extra is not None and type(extra) is abc.ABCMeta and extra not in bases: bases = (extra,) + bases - bases = tuple(_gorg(b) if isinstance(b, GenericMeta) else b for b in bases) + bases = tuple(b._gorg if isinstance(b, GenericMeta) else b for b in bases) # remove bare Generic from bases if there are other generic bases if any(isinstance(b, GenericMeta) and b is not Generic for b in bases): bases = tuple(b for b in bases if b is not Generic) namespace.update({'__origin__': origin, '__extra__': extra}) self = super(GenericMeta, cls).__new__(cls, name, bases, namespace) + super(GenericMeta, self).__setattr__('_gorg', + self if not origin else origin._gorg) self.__parameters__ = tvars # Be prepared that GenericMeta will be subclassed by TupleMeta @@ -1219,7 +1200,7 @@ class GenericMeta(TypingMeta, abc.ABCMeta): def _abc_negative_cache(self): if isinstance(self.__extra__, abc.ABCMeta): return self.__extra__._abc_negative_cache - return _gorg(self)._abc_generic_negative_cache + return self._gorg._abc_generic_negative_cache @_abc_negative_cache.setter def _abc_negative_cache(self, value): @@ -1233,7 +1214,7 @@ class GenericMeta(TypingMeta, abc.ABCMeta): def _abc_negative_cache_version(self): if isinstance(self.__extra__, abc.ABCMeta): return self.__extra__._abc_negative_cache_version - return _gorg(self)._abc_generic_negative_cache_version + return self._gorg._abc_generic_negative_cache_version @_abc_negative_cache_version.setter def _abc_negative_cache_version(self, value): @@ -1283,7 +1264,7 @@ class GenericMeta(TypingMeta, abc.ABCMeta): if self.__origin__ is None: return self tree_args = _subs_tree(self, tvars, args) - return (_gorg(self),) + tuple(tree_args) + return (self._gorg,) + tuple(tree_args) def __eq__(self, other): if not isinstance(other, GenericMeta): @@ -1299,7 +1280,7 @@ class GenericMeta(TypingMeta, abc.ABCMeta): def __getitem__(self, params): if not isinstance(params, tuple): params = (params,) - if not params and not _gorg(self) is Tuple: + if not params and self._gorg is not Tuple: raise TypeError( "Parameter list to %s[...] cannot be empty" % _qualname(self)) msg = "Parameters to generic types must be types." @@ -1343,7 +1324,11 @@ class GenericMeta(TypingMeta, abc.ABCMeta): def __subclasscheck__(self, cls): if self.__origin__ is not None: - if sys._getframe(1).f_globals['__name__'] not in ['abc', 'functools']: + # This should only be modules within the standard + # library. singledispatch is the only exception, because + # it's a Python 2 backport of functools.singledispatch. + if sys._getframe(1).f_globals['__name__'] not in ['abc', 'functools', + 'singledispatch']: raise TypeError("Parameterized generics cannot be used with class " "or instance checks") return False @@ -1362,11 +1347,6 @@ class GenericMeta(TypingMeta, abc.ABCMeta): return issubclass(instance.__class__, self) return False - def __copy__(self): - return self.__class__(self.__name__, self.__bases__, dict(self.__dict__), - self.__parameters__, self.__args__, self.__origin__, - self.__extra__, self.__orig_bases__) - def __setattr__(self, attr, value): # We consider all the subscripted genrics as proxies for original class if ( @@ -1375,7 +1355,17 @@ class GenericMeta(TypingMeta, abc.ABCMeta): ): super(GenericMeta, self).__setattr__(attr, value) else: - super(GenericMeta, _gorg(self)).__setattr__(attr, value) + super(GenericMeta, self._gorg).__setattr__(attr, value) + + +def _copy_generic(self): + """Hack to work around https://bugs.python.org/issue11480 on Python 2""" + return self.__class__(self.__name__, self.__bases__, dict(self.__dict__), + self.__parameters__, self.__args__, self.__origin__, + self.__extra__, self.__orig_bases__) + + +copy._copy_dispatch[GenericMeta] = _copy_generic # Prevent checks for Generic to crash when defining Generic. @@ -1386,10 +1376,18 @@ def _generic_new(base_cls, cls, *args, **kwds): # Assure type is erased on instantiation, # but attempt to store it in __orig_class__ if cls.__origin__ is None: - return base_cls.__new__(cls) + if (base_cls.__new__ is object.__new__ and + cls.__init__ is not object.__init__): + return base_cls.__new__(cls) + else: + return base_cls.__new__(cls, *args, **kwds) else: - origin = _gorg(cls) - obj = base_cls.__new__(origin) + origin = cls._gorg + if (base_cls.__new__ is object.__new__ and + cls.__init__ is not object.__init__): + obj = base_cls.__new__(origin) + else: + obj = base_cls.__new__(origin, *args, **kwds) try: obj.__orig_class__ = cls except AttributeError: @@ -1423,7 +1421,7 @@ class Generic(object): __slots__ = () def __new__(cls, *args, **kwds): - if _geqv(cls, Generic): + if cls._gorg is Generic: raise TypeError("Type Generic cannot be instantiated; " "it can be used only as a base class") return _generic_new(cls.__next_in_mro__, cls, *args, **kwds) @@ -1445,7 +1443,7 @@ class TupleMeta(GenericMeta): @_tp_cache def __getitem__(self, parameters): - if self.__origin__ is not None or not _geqv(self, Tuple): + if self.__origin__ is not None or self._gorg is not Tuple: # Normal generic rules apply if this is not the first subscription # or a subscription of a subclass. return super(TupleMeta, self).__getitem__(parameters) @@ -1474,6 +1472,9 @@ class TupleMeta(GenericMeta): "with issubclass().") +copy._copy_dispatch[TupleMeta] = _copy_generic + + class Tuple(tuple): """Tuple type; Tuple[X, Y] is the cross-product type of X and Y. @@ -1489,7 +1490,7 @@ class Tuple(tuple): __slots__ = () def __new__(cls, *args, **kwds): - if _geqv(cls, Tuple): + if cls._gorg is Tuple: raise TypeError("Type Tuple cannot be instantiated; " "use tuple() instead") return _generic_new(tuple, cls, *args, **kwds) @@ -1504,7 +1505,7 @@ class CallableMeta(GenericMeta): return self._tree_repr(self._subs_tree()) def _tree_repr(self, tree): - if _gorg(self) is not Callable: + if self._gorg is not Callable: return super(CallableMeta, self)._tree_repr(tree) # For actual Callable (not its subclass) we override # super(CallableMeta, self)._tree_repr() for nice formatting. @@ -1524,7 +1525,7 @@ class CallableMeta(GenericMeta): with hashable arguments to improve speed. """ - if self.__origin__ is not None or not _geqv(self, Callable): + if self.__origin__ is not None or self._gorg is not Callable: return super(CallableMeta, self).__getitem__(parameters) if not isinstance(parameters, tuple) or len(parameters) != 2: raise TypeError("Callable must be used as " @@ -1552,6 +1553,9 @@ class CallableMeta(GenericMeta): return super(CallableMeta, self).__getitem__(parameters) +copy._copy_dispatch[CallableMeta] = _copy_generic + + class Callable(object): """Callable type; Callable[[int], str] is a function of (int) -> str. @@ -1568,7 +1572,7 @@ class Callable(object): __slots__ = () def __new__(cls, *args, **kwds): - if _geqv(cls, Callable): + if cls._gorg is Callable: raise TypeError("Type Callable cannot be instantiated; " "use a non-abstract subclass instead") return _generic_new(cls.__next_in_mro__, cls, *args, **kwds) @@ -1618,7 +1622,7 @@ def no_type_check(arg): if isinstance(arg, type): arg_attrs = arg.__dict__.copy() for attr, val in arg.__dict__.items(): - if val in arg.__bases__: + if val in arg.__bases__ + (arg,): arg_attrs.pop(attr) for obj in arg_attrs.values(): if isinstance(obj, types.FunctionType): @@ -1735,6 +1739,7 @@ class _ProtocolMeta(GenericMeta): 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 @@ -1886,7 +1891,7 @@ class List(list, MutableSequence[T]): __extra__ = list def __new__(cls, *args, **kwds): - if _geqv(cls, List): + if cls._gorg is List: raise TypeError("Type List cannot be instantiated; " "use list() instead") return _generic_new(list, cls, *args, **kwds) @@ -1897,7 +1902,7 @@ class Deque(collections.deque, MutableSequence[T]): __extra__ = collections.deque def __new__(cls, *args, **kwds): - if _geqv(cls, Deque): + if cls._gorg is Deque: return collections.deque(*args, **kwds) return _generic_new(collections.deque, cls, *args, **kwds) @@ -1907,7 +1912,7 @@ class Set(set, MutableSet[T]): __extra__ = set def __new__(cls, *args, **kwds): - if _geqv(cls, Set): + if cls._gorg is Set: raise TypeError("Type Set cannot be instantiated; " "use set() instead") return _generic_new(set, cls, *args, **kwds) @@ -1918,7 +1923,7 @@ class FrozenSet(frozenset, AbstractSet[T_co]): __extra__ = frozenset def __new__(cls, *args, **kwds): - if _geqv(cls, FrozenSet): + if cls._gorg is FrozenSet: raise TypeError("Type FrozenSet cannot be instantiated; " "use frozenset() instead") return _generic_new(frozenset, cls, *args, **kwds) @@ -1975,7 +1980,7 @@ class Dict(dict, MutableMapping[KT, VT]): __extra__ = dict def __new__(cls, *args, **kwds): - if _geqv(cls, Dict): + if cls._gorg is Dict: raise TypeError("Type Dict cannot be instantiated; " "use dict() instead") return _generic_new(dict, cls, *args, **kwds) @@ -1986,7 +1991,7 @@ class DefaultDict(collections.defaultdict, MutableMapping[KT, VT]): __extra__ = collections.defaultdict def __new__(cls, *args, **kwds): - if _geqv(cls, DefaultDict): + if cls._gorg is DefaultDict: return collections.defaultdict(*args, **kwds) return _generic_new(collections.defaultdict, cls, *args, **kwds) @@ -1996,7 +2001,7 @@ class Counter(collections.Counter, Dict[T, int]): __extra__ = collections.Counter def __new__(cls, *args, **kwds): - if _geqv(cls, Counter): + if cls._gorg is Counter: return collections.Counter(*args, **kwds) return _generic_new(collections.Counter, cls, *args, **kwds) @@ -2015,7 +2020,7 @@ class Generator(Iterator[T_co], Generic[T_co, T_contra, V_co]): __extra__ = _G_base def __new__(cls, *args, **kwds): - if _geqv(cls, Generator): + if cls._gorg is Generator: raise TypeError("Type Generator cannot be instantiated; " "create a subclass instead") return _generic_new(_G_base, cls, *args, **kwds) @@ -2144,7 +2149,7 @@ class IO(Generic[AnyStr]): def close(self): pass - @abstractmethod + @abstractproperty def closed(self): pass |