diff options
Diffstat (limited to 'sources/shiboken6/shibokenmodule/files.dir/shibokensupport/signature/lib')
4 files changed, 131 insertions, 199 deletions
diff --git a/sources/shiboken6/shibokenmodule/files.dir/shibokensupport/signature/lib/__init__.py b/sources/shiboken6/shibokenmodule/files.dir/shibokensupport/signature/lib/__init__.py index 2d640cb89..db4f381f5 100644 --- a/sources/shiboken6/shibokenmodule/files.dir/shibokensupport/signature/lib/__init__.py +++ b/sources/shiboken6/shibokenmodule/files.dir/shibokensupport/signature/lib/__init__.py @@ -1,40 +1,5 @@ -############################################################################# -## -## 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$ -## -############################################################################# +# 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 +from __future__ import annotations # this file intentionally left blank 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 149881e01..9d98d7b1b 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,6 @@ -############################################################################# -## -## 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 +from __future__ import annotations """ enum_sig.py @@ -50,9 +15,7 @@ by producing a lot of clarity. import inspect import sys import types -import typing from shibokensupport.signature import get_signature as get_sig -from shibokensupport.signature.layout import create_signature """ @@ -67,6 +30,16 @@ 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): """ @@ -78,13 +51,13 @@ 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 @@ -98,7 +71,7 @@ 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 section(self): if hasattr(self.fmt, "section"): @@ -126,9 +99,13 @@ class ExactEnumerator(object): 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 = [] @@ -146,9 +123,19 @@ class ExactEnumerator(object): 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): @@ -156,22 +143,32 @@ class ExactEnumerator(object): 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 + # 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 hasattr(_[1], "__signature__") and _[1].__signature__) - self.fmt.have_body = bool(subclasses or sigs or properties or enums or init_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 @@ -181,8 +178,25 @@ class ExactEnumerator(object): 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: @@ -206,7 +220,7 @@ class ExactEnumerator(object): @staticmethod def get_signature(func): - return func.__signature__ + return get_sig(func) def function(self, func_name, func, decorator=None): self.func = func # for is_method() diff --git a/sources/shiboken6/shibokenmodule/files.dir/shibokensupport/signature/lib/pyi_generator.py b/sources/shiboken6/shibokenmodule/files.dir/shibokensupport/signature/lib/pyi_generator.py index 8a786b574..641b6693a 100644 --- a/sources/shiboken6/shibokenmodule/files.dir/shibokensupport/signature/lib/pyi_generator.py +++ b/sources/shiboken6/shibokenmodule/files.dir/shibokensupport/signature/lib/pyi_generator.py @@ -1,44 +1,11 @@ LICENSE_TEXT = """ -############################################################################# -## -## Copyright (C) 2021 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 +from __future__ import annotations """ +# flake8: noqa E:402 + """ pyi_generator.py @@ -50,7 +17,6 @@ import io import logging import os import re -import subprocess import sys import typing @@ -58,7 +24,6 @@ from pathlib import Path from contextlib import contextmanager from textwrap import dedent -from shiboken6 import Shiboken from shibokensupport.signature.lib.enum_sig import HintingEnumerator from shibokensupport.signature.lib.tool import build_brace_pattern @@ -96,6 +61,7 @@ class Formatter(Writer): unrelated tasks of enumeration and formatting apart. """ def __init__(self, outfile, options, *args): + # XXX Find out which of these patches is still necessary! self.options = options Writer.__init__(self, outfile, *args) # patching __repr__ to disable the __repr__ of typing.TypeVar: @@ -111,8 +77,12 @@ class Formatter(Writer): """ def _typevar__repr__(self): return f"typing.{self.__name__}" - typing.TypeVar.__repr__ = _typevar__repr__ - + # This is no longer necessary for modern typing versions. + # Ignore therefore if the repr is read-only and cannot be changed. + try: + typing.TypeVar.__repr__ = _typevar__repr__ + except TypeError: + pass # Adding a pattern to substitute "Union[T, NoneType]" by "Optional[T]" # I tried hard to replace typing.Optional by a simple override, but # this became _way_ too much. @@ -121,6 +91,7 @@ class Formatter(Writer): pattern = fr"\b Union \s* \[ \s* {brace_pat} \s*, \s* NoneType \s* \]" replace = r"Optional[\1]" optional_searcher = re.compile(pattern, flags=re.VERBOSE) + def optional_replacer(source): return optional_searcher.sub(replace, str(source)) self.optional_replacer = optional_replacer @@ -138,8 +109,6 @@ class Formatter(Writer): txt = f"""\ # Module `{mod_name}` - from shiboken6 import Shiboken - <<IMPORTS>> """ self.print(dedent(txt)) @@ -190,17 +159,34 @@ class Formatter(Writer): self.print(f"{spaces}{enum_name:25}: {class_name} = ... # {hexval}") yield + @contextmanager + def attribute(self, attr_name, attr_value): + spaces = indent * self.level + self.print(f"{spaces}{attr_name:25} = ... # type: {type(attr_value).__qualname__}") + yield + + @contextmanager + def signal(self, class_name, sig_name, sig_str): + spaces = indent * self.level + self.print(f"{spaces}{sig_name:25}: ClassVar[{class_name}] = ... # {sig_str}") + yield + def find_imports(text): return [imp for imp in PySide6.__all__ if f"PySide6.{imp}." in text] FROM_IMPORTS = [ - ("typing", typing.__all__), - ("PySide6.QtCore", ["PyClassProperty"]), (None, ["builtins"]), + (None, ["os"]), + (None, ["enum"]), + ("collections.abc", ["Iterable"]), + ("typing", sorted(typing.__all__)), + ("PySide6.QtCore", ["PyClassProperty", "Signal", "SignalInstance"]), + ("shiboken6", ["Shiboken"]), ] + def filter_from_imports(from_struct, text): """ Build a reduced new `from` structure (nfs) with found entries, only @@ -210,8 +196,15 @@ def filter_from_imports(from_struct, text): lis = [] nfs.append((mod, lis)) for each in imports: - if re.search(rf"(\b|@){each}\b", text): - lis.append(each) + # PYSIDE-1603: We search text that is a usage of the class `each`, + # but only if the class is not also defined here. + if (f"class {each}(") not in text: + if re.search(rf"(\b|@){each}\b([^\s\(:]|\n)", text): + lis.append(each) + # Search if a type is present in the return statement + # of function declarations: '... -> here:' + if re.search(rf"->.*{each}.*:", text): + lis.append(each) if not lis: nfs.pop() return nfs @@ -250,12 +243,10 @@ def generate_pyi(import_name, outpath, options): obj = getattr(top, plainname) if import_name != plainname else top if not getattr(obj, "__file__", None) or Path(obj.__file__).is_dir(): raise ModuleNotFoundError(f"We do not accept a namespace as module `{plainname}`") - module = sys.modules[import_name] outfile = io.StringIO() fmt = Formatter(outfile, options) fmt.print(LICENSE_TEXT.strip()) - need_imports = options._pyside_call and not USE_PEP563 if USE_PEP563: fmt.print("from __future__ import annotations") fmt.print() @@ -282,35 +273,30 @@ def generate_pyi(import_name, outpath, options): # we remove the "<<IMPORTS>>" marker and insert imports if needed if line == "<<IMPORTS>>": text = outfile.getvalue() + wr.print("import " + import_name) + for mod_name in find_imports(text): + imp = "PySide6." + mod_name + if imp != import_name: + wr.print("import " + imp) + wr.print() for mod, imports in filter_from_imports(FROM_IMPORTS, text): import_args = ', '.join(imports) if mod is None: # special case, a normal import wr.print(f"import {import_args}") - elif mod != import_name: + else: wr.print(f"from {mod} import {import_args}") wr.print() - if need_imports: - for mod_name in find_imports(text): - imp = "PySide6." + mod_name - if imp != import_name: - wr.print("import " + imp) - # Do not import Shiboken which is handled already. - if import_name != "Shiboken": - wr.print("import " + import_name) wr.print() + wr.print("NoneType: TypeAlias = type[None]") wr.print() else: wr.print(line) if not options.quiet: options.logger.info(f"Generated: {outfilepath}") - if options and (options.check or options.is_ci): - # Python 3.7 and up: We can check the file directly if the syntax is ok. - if USE_PEP563: - subprocess.check_output([sys.executable, os.fspath(outfilepath)]) -if __name__ == "__main__": +def main(): parser = argparse.ArgumentParser( formatter_class=argparse.RawDescriptionHelpFormatter, description=dedent("""\ @@ -322,11 +308,10 @@ if __name__ == "__main__": pyi_generator will try to generate an interface "<module>.pyi". """)) parser.add_argument("module", - help="The full path name of an importable module binary (.pyd, .so)") + help="The full path name of an importable module binary (.pyd, .so)") # noqa E:128 parser.add_argument("--quiet", action="store_true", help="Run quietly") - parser.add_argument("--check", action="store_true", help="Test the output") parser.add_argument("--outpath", - help="the output directory (default = location of module binary)") + help="the output directory (default = location of module binary)") # noqa E:128 options = parser.parse_args() module = options.module outpath = options.outpath @@ -344,4 +329,7 @@ if __name__ == "__main__": options.logger = logger generate_pyi(module, outpath, options=options) + +if __name__ == "__main__": + main() # eof diff --git a/sources/shiboken6/shibokenmodule/files.dir/shibokensupport/signature/lib/tool.py b/sources/shiboken6/shibokenmodule/files.dir/shibokensupport/signature/lib/tool.py index 4ded95c36..48546d7cb 100644 --- a/sources/shiboken6/shibokenmodule/files.dir/shibokensupport/signature/lib/tool.py +++ b/sources/shiboken6/shibokenmodule/files.dir/shibokensupport/signature/lib/tool.py @@ -1,41 +1,6 @@ -############################################################################# -## -## Copyright (C) 2019 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 +from __future__ import annotations """ tool.py @@ -80,8 +45,8 @@ def build_brace_pattern(level, separators): ro, rc = round_ = "()" so, sc = square = "[]" - co, cc = curly = "CD" # we insert "{}", later... - ao, ac = angle = "<>" + co, cc = curly = "CD" # noqa E:201 we insert "{}", later... + ao, ac = angle = "<>" # noqa E:201 q2, bs, q1 = '"', "\\", "'" allpat = round_ + square + curly + angle __ = " " @@ -115,8 +80,8 @@ def build_brace_pattern(level, separators): {indent} )* """) for idx in range(level): - pattern = pattern.format(replacer = repeated if idx < level-1 else no_braces_q, - indent = idx * " ", **locals()) + pattern = pattern.format(replacer=repeated if idx < level - 1 else no_braces_q, + indent=idx * " ", **locals()) return pattern.replace("C", "{").replace("D", "}") |