diff options
Diffstat (limited to 'sources/shiboken6/shibokenmodule/files.dir/shibokensupport/signature/lib/enum_sig.py')
-rw-r--r-- | sources/shiboken6/shibokenmodule/files.dir/shibokensupport/signature/lib/enum_sig.py | 228 |
1 files changed, 153 insertions, 75 deletions
diff --git a/sources/shiboken6/shibokenmodule/files.dir/shibokensupport/signature/lib/enum_sig.py b/sources/shiboken6/shibokenmodule/files.dir/shibokensupport/signature/lib/enum_sig.py index 400c36de5..5650e2bc1 100644 --- a/sources/shiboken6/shibokenmodule/files.dir/shibokensupport/signature/lib/enum_sig.py +++ b/sources/shiboken6/shibokenmodule/files.dir/shibokensupport/signature/lib/enum_sig.py @@ -1,41 +1,5 @@ -############################################################################# -## -## Copyright (C) 2020 The Qt Company Ltd. -## Contact: https://www.qt.io/licensing/ -## -## This file is part of Qt for Python. -## -## $QT_BEGIN_LICENSE:LGPL$ -## Commercial License Usage -## Licensees holding valid commercial Qt licenses may use this file in -## accordance with the commercial license agreement provided with the -## Software or, alternatively, in accordance with the terms contained in -## a written agreement between you and The Qt Company. For licensing terms -## and conditions see https://www.qt.io/terms-conditions. For further -## information use the contact form at https://www.qt.io/contact-us. -## -## GNU Lesser General Public License Usage -## Alternatively, this file may be used under the terms of the GNU Lesser -## General Public License version 3 as published by the Free Software -## Foundation and appearing in the file LICENSE.LGPL3 included in the -## packaging of this file. Please review the following information to -## ensure the GNU Lesser General Public License version 3 requirements -## will be met: https://www.gnu.org/licenses/lgpl-3.0.html. -## -## GNU General Public License Usage -## Alternatively, this file may be used under the terms of the GNU -## General Public License version 2.0 or (at your option) the GNU General -## Public license version 3 or any later version approved by the KDE Free -## Qt Foundation. The licenses are as published by the Free Software -## Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 -## included in the packaging of this file. Please review the following -## information to ensure the GNU General Public License requirements will -## be met: https://www.gnu.org/licenses/gpl-2.0.html and -## https://www.gnu.org/licenses/gpl-3.0.html. -## -## $QT_END_LICENSE$ -## -############################################################################# +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only """ enum_sig.py @@ -47,12 +11,35 @@ It is not easy to adhere to this protocol, but in the end, it paid off by producing a lot of clarity. """ +import inspect import sys import types -from shibokensupport.signature import inspect from shibokensupport.signature import get_signature as get_sig +""" +PYSIDE-1599: Making sure that pyi files always are tested. + +A new problem popped up when supporting true properties: +When there exists an item named "property", then we cannot use +the builtin `property` as decorator, but need to prefix it with "builtins". + +We scan for such a name in a class, and if there should a property be +declared in the same class, we use `builtins.property` in the class and +all sub-classes. The same consideration holds for "overload". +""" + +_normal_functions = (types.BuiltinFunctionType, types.FunctionType) +if hasattr(sys, "pypy_version_info"): + # In PyPy, there are more types because our builtin functions + # are created differently in C++ and not in PyPy. + _normal_functions += (type(get_sig),) + + +def signal_check(thing): + return thing and type(thing) in (Signal, SignalInstance) + + class ExactEnumerator(object): """ ExactEnumerator enumerates all signatures in a module as they are. @@ -63,20 +50,19 @@ class ExactEnumerator(object): """ def __init__(self, formatter, result_type=dict): - global EnumMeta + global EnumMeta, Signal, SignalInstance try: # Lazy import - from PySide6.QtCore import Qt + from PySide6.QtCore import Qt, Signal, SignalInstance EnumMeta = type(Qt.Key) except ImportError: - EnumMeta = None + EnumMeta = Signal = SignalInstance = None self.fmt = formatter self.result_type = result_type self.fmt.level = 0 - self.fmt.after_enum = self.after_enum - self._after_enum = False self.fmt.is_method = self.is_method + self.collision_candidates = {"property", "overload"} def is_method(self): """ @@ -84,11 +70,11 @@ class ExactEnumerator(object): We check if it is a simple function. """ tp = type(self.func) - return tp not in (types.BuiltinFunctionType, types.FunctionType) + return tp not in _normal_functions - def after_enum(self): - ret = self._after_enum - self._after_enum = False + def section(self): + if hasattr(self.fmt, "section"): + self.fmt.section() def module(self, mod_name): __import__(mod_name) @@ -100,24 +86,31 @@ class ExactEnumerator(object): ret = self.result_type() self.fmt.class_name = None for class_name, klass in members: + self.collision_track = set() ret.update(self.klass(class_name, klass)) - if isinstance(klass, EnumMeta): - raise SystemError("implement enum instances at module level") + if len(members): + self.section() for func_name, func in functions: ret.update(self.function(func_name, func)) + if len(functions): + self.section() return ret def klass(self, class_name, klass): ret = self.result_type() + if ("._") in class_name: + # This happens when introspecting enum.Enum etc. Python 3.8.8 does not + # like this, but we want to remove that, anyway. + return ret if "<" in class_name: # This is happening in QtQuick for some reason: - ## class QSharedPointer<QQuickItemGrabResult >: + # class std::shared_ptr<QQuickItemGrabResult >: # We simply skip over this class. return ret bases_list = [] for base in klass.__bases__: - name = base.__name__ - if name not in ("object", "type"): + name = base.__qualname__ + if name not in ("object", "property", "type"): name = base.__module__ + "." + name bases_list.append(name) bases_str = ', '.join(bases_list) @@ -128,60 +121,126 @@ class ExactEnumerator(object): subclasses = [] functions = [] enums = [] + properties = [] + signals = [] + attributes = {} for thing_name, thing in class_members: - if inspect.isclass(thing): + if signal_check(thing): + signals.append((thing_name, thing)) + elif inspect.isclass(thing): + # If this is the only member of the class, it causes the stub + # to be printed empty without ..., as self.fmt.have_body will + # then be True. (Example: QtCore.QCborTag). Skip it to avoid + # this problem. + if thing_name == "_member_type_": + continue subclass_name = ".".join((class_name, thing_name)) subclasses.append((subclass_name, thing)) elif inspect.isroutine(thing): func_name = thing_name.split(".")[0] # remove ".overload" - signature = getattr(thing, "__signature__", None) - if signature is not None: - functions.append((func_name, thing)) + functions.append((func_name, thing)) elif type(type(thing)) is EnumMeta: # take the real enum name, not what is in the dict - enums.append((thing_name, type(thing).__qualname__, thing)) + if not thing_name.startswith("_"): + enums.append((thing_name, type(thing).__qualname__, thing)) + elif isinstance(thing, property): + properties.append((thing_name, thing)) + # Support attributes that have PySide types as values, + # but we skip the 'staticMetaObject' that needs + # to be defined at a QObject level. + elif "PySide" in str(type(thing)) and "QMetaObject" not in str(type(thing)): + if class_name not in attributes: + attributes[class_name] = {} + attributes[class_name][thing_name] = thing + + if thing_name in self.collision_candidates: + self.collision_track.add(thing_name) + init_signature = getattr(klass, "__signature__", None) - enums.sort(key=lambda tup: tup[1 : 3]) # sort by class then enum value - self.fmt.have_body = bool(subclasses or functions or enums or init_signature) + # sort by class then enum value + enums.sort(key=lambda tup: (tup[1], tup[2].value)) + + # We want to handle functions and properties together. + func_prop = sorted(functions + properties, key=lambda tup: tup[0]) + + # find out how many functions create a signature + sigs = list(_ for _ in functions if get_sig(_[1])) + self.fmt.have_body = bool(subclasses or sigs or properties or enums or # noqa W:504 + init_signature or signals or attributes) with self.fmt.klass(class_name, class_str): self.fmt.level += 1 self.fmt.class_name = class_name if hasattr(self.fmt, "enum"): # this is an optional feature + if len(enums): + self.section() for enum_name, enum_class_name, value in enums: - with self.fmt.enum(enum_class_name, enum_name, int(value)): + with self.fmt.enum(enum_class_name, enum_name, value.value): + pass + if hasattr(self.fmt, "signal"): + # this is an optional feature + if len(signals): + self.section() + for signal_name, signal in signals: + sig_class = type(signal) + sig_class_name = f"{sig_class.__qualname__}" + sig_str = str(signal) + with self.fmt.signal(sig_class_name, signal_name, sig_str): pass + if hasattr(self.fmt, "attribute"): + if len(attributes): + self.section() + for class_name, attrs in attributes.items(): + for attr_name, attr_value in attrs.items(): + with self.fmt.attribute(attr_name, attr_value): + pass + if len(subclasses): + self.section() for subclass_name, subclass in subclasses: - if klass == subclass: - # this is a side effect of the typing module for Python 2.7 - # via the "._gorg" property, which we can safely ignore. - print(f"Warning: {class_name} points to itself via {subclass_name}, skipped!") - continue + save = self.collision_track.copy() ret.update(self.klass(subclass_name, subclass)) + self.collision_track = save self.fmt.class_name = class_name + if len(subclasses): + self.section() ret.update(self.function("__init__", klass)) - for func_name, func in functions: + for func_name, func in func_prop: if func_name != "__init__": - ret.update(self.function(func_name, func)) + if isinstance(func, property): + ret.update(self.fproperty(func_name, func)) + else: + ret.update(self.function(func_name, func)) self.fmt.level -= 1 + if len(func_prop): + self.section() return ret @staticmethod def get_signature(func): - return func.__signature__ + return get_sig(func) - def function(self, func_name, func): + def function(self, func_name, func, decorator=None): self.func = func # for is_method() ret = self.result_type() - signature = self.get_signature(func) + if decorator in self.collision_track: + decorator = f"builtins.{decorator}" + signature = self.get_signature(func, decorator) if signature is not None: - with self.fmt.function(func_name, signature) as key: + with self.fmt.function(func_name, signature, decorator) as key: ret[key] = signature del self.func return ret + def fproperty(self, prop_name, prop): + ret = self.function(prop_name, prop.fget, type(prop).__qualname__) + if prop.fset: + ret.update(self.function(prop_name, prop.fset, f"{prop_name}.setter")) + if prop.fdel: + ret.update(self.function(prop_name, prop.fdel, f"{prop_name}.deleter")) + return ret + def stringify(signature): if isinstance(signature, list): @@ -221,6 +280,25 @@ class HintingEnumerator(ExactEnumerator): hinting stubs. Only default values are replaced by "...". """ - @staticmethod - def get_signature(func): - return get_sig(func, "hintingstub") + def __init__(self, *args, **kwds): + super().__init__(*args, **kwds) + # We need to provide default signatures for class properties. + cls_param = inspect.Parameter("cls", inspect._POSITIONAL_OR_KEYWORD) + set_param = inspect.Parameter("arg_1", inspect._POSITIONAL_OR_KEYWORD, annotation=object) + self.getter_sig = inspect.Signature([cls_param], return_annotation=object) + self.setter_sig = inspect.Signature([cls_param, set_param]) + self.deleter_sig = inspect.Signature([cls_param]) + + def get_signature(self, func, decorator=None): + # Class properties don't have signature support (yet). + # In that case, produce a fake one. + sig = get_sig(func, "hintingstub") + if not sig: + if decorator: + if decorator.endswith(".setter"): + sig = self.setter_sig + elif decorator.endswith(".deleter"): + sig = self.deleter_sig + else: + sig = self.getter_sig + return sig |