From 01b43dc3d93e632c3c42fb8e1105f200b9cee2ae Mon Sep 17 00:00:00 2001 From: Christian Tismer Date: Tue, 13 Aug 2019 11:15:52 +0200 Subject: Add QtCore.Slot.__signature__ and much more manually The signature of QtCore.Slot and other classes could not automatically be generated because the function is not generated by cppgenerator.cpp . We add it manually in the C++ code into the generation process. The case of QtCore.Slot had diverse follow-up issues to be solved: - Classes which did not inherit from Shiboken were not generated. This is a long-standing omission and creates very many new simple types. - The arity of Slot has default arguments after the varargs parameter "*types". This needed an extended Python parser analysis that fixes the arguments given to the inspect module, accordingly. - The signature generation was completely new implemented and relies no longer on the restricted syntax of a Python (2) function but generates signatures directly as Parameter instances. Implemented classes with hand-made signatures: QtCore.ClassInfo QtCore.MetaFunction, QtCore.MetaSignal QtCore.Property QtCore.Signal QtCore.SignalInstance QtCore.Slot QtQml.ListProperty QtQml.VolatileBool As a side effect, many more subtypes were published. Enums are done, which concludes this work. Fixes: PYSIDE-945 Fixes: PYSIDE-1052 Change-Id: Ic09f02ece3a90325519e42e4e39719beb0c27ae9 Reviewed-by: Christian Tismer --- sources/pyside2/PySide2/support/generate_pyi.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) (limited to 'sources/pyside2/PySide2/support/generate_pyi.py') diff --git a/sources/pyside2/PySide2/support/generate_pyi.py b/sources/pyside2/PySide2/support/generate_pyi.py index c732227f4..8aa69c983 100644 --- a/sources/pyside2/PySide2/support/generate_pyi.py +++ b/sources/pyside2/PySide2/support/generate_pyi.py @@ -141,7 +141,7 @@ class Formatter(Writer): self.outfile.seek(here) self.outfile.truncate() # Note: we cannot use class_str when we have no body. - self.print("{spaces}class {class_name}: ...".format(**locals())) + self.print("{spaces}class {class_str}: ...".format(**locals())) if "<" in class_name: # This is happening in QtQuick for some reason: ## class QSharedPointer: @@ -168,6 +168,13 @@ class Formatter(Writer): self.print('{spaces}@staticmethod'.format(**locals())) self.print('{spaces}def {func_name}{signature}: ...'.format(**locals())) + @contextmanager + def enum(self, class_name, enum_name, value): + spaces = self.spaces + hexval = hex(value) + self.print("{spaces}{enum_name:20}: {class_name} = ... # {hexval}".format(**locals())) + yield + def get_license_text(): with io.open(sourcepath) as f: @@ -247,10 +254,10 @@ def generate_all_pyi(outpath, options): os.environ["PYTHONPATH"] = pypath # now we can import - global PySide2, inspect, HintingEnumerator + global PySide2, inspect, HintingEnumerator, EnumType import PySide2 from PySide2.support.signature import inspect - from PySide2.support.signature.lib.enum_sig import HintingEnumerator + from PySide2.support.signature.lib.enum_sig import HintingEnumerator, EnumType # propagate USE_PEP563 to the mapping module. # Perhaps this can be automated? -- cgit v1.2.3 From d4acbacd7a655e3ff9e17663a23ed0d822b84850 Mon Sep 17 00:00:00 2001 From: Christian Tismer Date: Thu, 22 Aug 2019 18:57:11 +0200 Subject: signature: Support typing.Optional[T] and refine a bit The signature was missing "typing.Optional[T]" which has to be wrapped around any argument with a default value of "None". This is the only case where the repr of a type looks different than it was written, because it renders as "typing.Union[T, NoneType]". Solving that by redefining a few typing structures was way too hard and too error prone. It was finally solved by a regex replacemet that is run as a post process in generate_pyi.py . The enumerations are now even more complete, since toplevel enums are also included. This had the effect that enums with Python keywords were revealed, and so the function "createEnumItem" had to be modified. The order of creation was also changed to avoid name clashes. The overall structure was improved, and instead of parsing the generated signatures to find out if something is a class method, this is now very cleanly implemented as an inquiry to get_signature(). I tried to make sense of the flags structure that comes with many enums. PyQt5 has a standard set of "__...__" methods without useful signature information. I could mimick that as well, but that would create a whole lot of pointless extra information. We should decide later if it makes sense to include that. Right now the flags structures show the class name, only. This patch will be merged with the 5.14 branch. The additions of this patch could fortunately be placed into areas which do almost not overlap with the 5.14 signature additions. Change-Id: Ie513e15917b04d746ab597fb7a9eb1fd766f7c73 Fixes: PYSIDE-1079 Reviewed-by: Cristian Maureira-Fredes --- sources/pyside2/PySide2/support/generate_pyi.py | 73 ++++++++++++++++++------- 1 file changed, 52 insertions(+), 21 deletions(-) (limited to 'sources/pyside2/PySide2/support/generate_pyi.py') diff --git a/sources/pyside2/PySide2/support/generate_pyi.py b/sources/pyside2/PySide2/support/generate_pyi.py index 8aa69c983..d5bbe5d7c 100644 --- a/sources/pyside2/PySide2/support/generate_pyi.py +++ b/sources/pyside2/PySide2/support/generate_pyi.py @@ -100,6 +100,38 @@ class Formatter(Writer): The separation in formatter and enumerator is done to keep the unrelated tasks of enumeration and formatting apart. """ + def __init__(self, *args): + Writer.__init__(self, *args) + # patching __repr__ to disable the __repr__ of typing.TypeVar: + """ + def __repr__(self): + if self.__covariant__: + prefix = '+' + elif self.__contravariant__: + prefix = '-' + else: + prefix = '~' + return prefix + self.__name__ + """ + def _typevar__repr__(self): + return "typing." + self.__name__ + typing.TypeVar.__repr__ = _typevar__repr__ + + # Adding a pattern to substitute "Union[T, NoneType]" by "Optional[T]" + # I tried hard to replace typing.Optional by a simple override, but + # this became _way_ too much. + # See also the comment in layout.py . + brace_pat = build_brace_pattern(3) + pattern = (r"\b Union \s* \[ \s* {brace_pat} \s*, \s* NoneType \s* \]" + .format(**locals())) + replace = r"Optional[\1]" + optional_searcher = re.compile(pattern, flags=re.VERBOSE) + def optional_replacer(source): + return optional_searcher.sub(replace, str(source)) + self.optional_replacer = optional_replacer + # self.level is maintained by enum_sig.py + # self.after_enum() is a one-shot set by enum_sig.py . + @contextmanager def module(self, mod_name): self.mod_name = mod_name @@ -121,26 +153,21 @@ class Formatter(Writer): @contextmanager def klass(self, class_name, class_str): - self.class_name = class_name - spaces = "" + spaces = indent * self.level while "." in class_name: - spaces += indent class_name = class_name.split(".", 1)[-1] class_str = class_str.split(".", 1)[-1] self.print() - if not spaces: + if self.level == 0: self.print() here = self.outfile.tell() self.print("{spaces}class {class_str}:".format(**locals())) - self.print() pos = self.outfile.tell() - self.spaces = spaces yield if pos == self.outfile.tell(): # we have not written any function self.outfile.seek(here) self.outfile.truncate() - # Note: we cannot use class_str when we have no body. self.print("{spaces}class {class_str}: ...".format(**locals())) if "<" in class_name: # This is happening in QtQuick for some reason: @@ -150,29 +177,32 @@ class Formatter(Writer): self.outfile.truncate() @contextmanager - def function(self, func_name, signature): + def function(self, func_name, signature, modifier=None): + if self.after_enum() or func_name == "__init__": + self.print() key = func_name - spaces = indent + self.spaces if self.class_name else "" + spaces = indent * self.level if type(signature) == type([]): for sig in signature: self.print('{spaces}@typing.overload'.format(**locals())) - self._function(func_name, sig, spaces) + self._function(func_name, sig, modifier, spaces) else: - self._function(func_name, signature, spaces) + self._function(func_name, signature, modifier, spaces) + if func_name == "__init__": + self.print() yield key - def _function(self, func_name, signature, spaces): - # this would be nicer to get somehow together with the signature - is_meth = re.match(r"\((\w*)", str(signature)).group(1) == "self" - if self.class_name and not is_meth: - self.print('{spaces}@staticmethod'.format(**locals())) + def _function(self, func_name, signature, modifier, spaces): + if modifier: + self.print('{spaces}@{modifier}'.format(**locals())) + signature = self.optional_replacer(signature) self.print('{spaces}def {func_name}{signature}: ...'.format(**locals())) @contextmanager def enum(self, class_name, enum_name, value): - spaces = self.spaces + spaces = indent * self.level hexval = hex(value) - self.print("{spaces}{enum_name:20}: {class_name} = ... # {hexval}".format(**locals())) + self.print("{spaces}{enum_name:25}: {class_name} = ... # {hexval}".format(**locals())) yield @@ -254,10 +284,11 @@ def generate_all_pyi(outpath, options): os.environ["PYTHONPATH"] = pypath # now we can import - global PySide2, inspect, HintingEnumerator, EnumType + global PySide2, inspect, typing, HintingEnumerator, build_brace_pattern import PySide2 - from PySide2.support.signature import inspect - from PySide2.support.signature.lib.enum_sig import HintingEnumerator, EnumType + from PySide2.support.signature import inspect, typing + from PySide2.support.signature.lib.enum_sig import HintingEnumerator + from PySide2.support.signature.lib.tool import build_brace_pattern # propagate USE_PEP563 to the mapping module. # Perhaps this can be automated? -- cgit v1.2.3