From b86d72b9eac320044fde5642e0323be3ef80d62c Mon Sep 17 00:00:00 2001 From: Christian Tismer Date: Sat, 14 Jul 2018 15:10:56 +0200 Subject: Create hinting stubs for Python IDEs This implementation formats all signatures in a way that is known as type hinting files (.pyi). Usage ----- The script is to be called by the same Python interpreter that was used to build PySide. It works with Python 2 and 3. On Python 3, it performs a self-test. python3 sources/pyside2/PySide2/support/generate_pyi.py run will generate .pyi files for all compiled PySide modules and places them into site packages to the binaries. An optional outpath can be specified. It is planned to call this script automatically after install. o Local constants are not included, yet. Maybe they never will, unless requested. o The keyword "from" appears 43 times in argument lists. It is fixed in Python, only which does not matter. o When using Python 3.7 or above, it respects Pep 563 and avoids imports which are deferred to runtime. Task-number: PYSIDE-735 Change-Id: I3bcd5d9284b853fe955376bf35c7897e3698da2b Reviewed-by: Cristian Maureira-Fredes --- .../shibokenmodule/support/signature/__init__.py | 2 +- .../support/signature/backport_inspect.py | 4 +-- .../shibokenmodule/support/signature/layout.py | 3 +- .../support/signature/lib/enum_sig.py | 33 ++++++++++++++++++++-- .../shibokenmodule/support/signature/loader.py | 27 +++++++++++++++++- .../shibokenmodule/support/signature/mapping.py | 5 +++- .../shibokenmodule/support/signature/parser.py | 2 +- 7 files changed, 67 insertions(+), 9 deletions(-) (limited to 'sources/shiboken2/shibokenmodule') diff --git a/sources/shiboken2/shibokenmodule/support/signature/__init__.py b/sources/shiboken2/shibokenmodule/support/signature/__init__.py index f639f1ad0..253ba98dc 100644 --- a/sources/shiboken2/shibokenmodule/support/signature/__init__.py +++ b/sources/shiboken2/shibokenmodule/support/signature/__init__.py @@ -41,4 +41,4 @@ from __future__ import print_function, absolute_import # Trigger initialization phase 2. _ = type.__signature__ -from signature_loader import get_signature, inspect +from signature_loader import get_signature, inspect, typing diff --git a/sources/shiboken2/shibokenmodule/support/signature/backport_inspect.py b/sources/shiboken2/shibokenmodule/support/signature/backport_inspect.py index 0eafe9caa..6b97470e2 100644 --- a/sources/shiboken2/shibokenmodule/support/signature/backport_inspect.py +++ b/sources/shiboken2/shibokenmodule/support/signature/backport_inspect.py @@ -113,8 +113,8 @@ CO_NOFREE = 0x0040 # We use '__builtin__' and '__name__' instead. # It is further changed because we use a local copy of typing def formatannotation(annotation, base_module=None): - if getattr(annotation, '__module__', None) == 'PySide2.support.signature.typing': - return repr(annotation).replace('PySide2.support.signature.typing.', '') + if getattr(annotation, '__module__', None) == 'support.signature.typing': + return repr(annotation).replace('support.signature.typing', 'typing') if isinstance(annotation, type): if annotation.__module__ in ('__builtin__', base_module): return annotation.__name__ diff --git a/sources/shiboken2/shibokenmodule/support/signature/layout.py b/sources/shiboken2/shibokenmodule/support/signature/layout.py index 47f97fc2b..cd3a5dc8f 100644 --- a/sources/shiboken2/shibokenmodule/support/signature/layout.py +++ b/sources/shiboken2/shibokenmodule/support/signature/layout.py @@ -57,6 +57,7 @@ used literally as strings like "signature", "existence", etc. from textwrap import dedent from signature_loader import inspect +from signature_loader.mapping import ellipsis class SimpleNamespace(object): @@ -223,7 +224,7 @@ def create_signature(props, key): if not layout.defaults: defaults = () if layout.ellipsis: - defaults = ("...",) * len(defaults) + defaults = (ellipsis,) * len(defaults) annotations = props["annotations"].copy() if not layout.return_annotation and "return" in annotations: del annotations["return"] diff --git a/sources/shiboken2/shibokenmodule/support/signature/lib/enum_sig.py b/sources/shiboken2/shibokenmodule/support/signature/lib/enum_sig.py index 2ec14d62b..f79f3266a 100644 --- a/sources/shiboken2/shibokenmodule/support/signature/lib/enum_sig.py +++ b/sources/shiboken2/shibokenmodule/support/signature/lib/enum_sig.py @@ -39,6 +39,16 @@ from __future__ import print_function, absolute_import +""" +enum_sig.py + +Enumerate all signatures of a class. + +This module separates the enumeration process from the formatting. +It is not easy to adhere to this protocol, but in the end, it paid off +by producing a lot of clarity. +""" + import sys from signature_loader import get_signature, inspect @@ -51,6 +61,7 @@ class ExactEnumerator(object): 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 @@ -91,7 +102,8 @@ class ExactEnumerator(object): subclass_name = ".".join((class_name, thing_name)) subclasses.append((subclass_name, thing)) else: - ret.update(self.function(thing_name, thing)) + func_name = thing_name.split(".")[0] # remove ".overload" + ret.update(self.function(func_name, thing)) for subclass_name, subclass in subclasses: ret.update(self.klass(subclass_name, subclass)) return ret @@ -101,7 +113,7 @@ class ExactEnumerator(object): signature = getattr(func, '__signature__', None) if signature is not None: with self.fmt.function(func_name, signature) as key: - ret[key] = str(signature) + ret[key] = signature return ret @@ -133,3 +145,20 @@ class SimplifyingEnumerator(ExactEnumerator): with self.fmt.function(func_name, sig) as key: ret[key] = sig return ret + +class HintingEnumerator(ExactEnumerator): + """ + HintingEnumerator enumerates all signatures in a module slightly changed. + + This class is used for generating complete listings of all signatures for + hinting stubs. Only default values are replaced by "...". + """ + + def function(self, func_name, func): + ret = self.result_type() + signature = get_signature(func, 'hintingstub') + if signature is not None: + with self.fmt.function(func_name, signature) as key: + ret[key] = signature + return ret + diff --git a/sources/shiboken2/shibokenmodule/support/signature/loader.py b/sources/shiboken2/shibokenmodule/support/signature/loader.py index 83493e511..170fb0a2a 100644 --- a/sources/shiboken2/shibokenmodule/support/signature/loader.py +++ b/sources/shiboken2/shibokenmodule/support/signature/loader.py @@ -59,7 +59,6 @@ import os import traceback import types from contextlib import contextmanager -from distutils.sysconfig import get_python_lib """ A note on the import problem (solved): @@ -111,6 +110,30 @@ def ensure_import_support(): sys.path.pop(0) +# patching inspect's formatting to keep the word "typing": +def formatannotation(annotation, base_module=None): + # if getattr(annotation, '__module__', None) == 'typing': + # return repr(annotation).replace('typing.', '') + if isinstance(annotation, type): + if annotation.__module__ in ('builtins', base_module): + return annotation.__qualname__ + return annotation.__module__+'.'+annotation.__qualname__ + return repr(annotation) + +# 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__ + with ensure_import_support(): # We store all needed modules in signature_loader. # This way, they are always accessible. @@ -119,6 +142,7 @@ with ensure_import_support(): if sys.version_info >= (3,): import typing import inspect + inspect.formatannotation = formatannotation else: import inspect namespace = inspect.__dict__ @@ -129,6 +153,7 @@ with ensure_import_support(): 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("_")) + typing.TypeVar.__repr__ = _typevar__repr__ def put_into_loader_package(module, loader=signature_loader): # Note: the "with" statement hides that we are no longer in a diff --git a/sources/shiboken2/shibokenmodule/support/signature/mapping.py b/sources/shiboken2/shibokenmodule/support/signature/mapping.py index 724b0c751..bca1ce307 100644 --- a/sources/shiboken2/shibokenmodule/support/signature/mapping.py +++ b/sources/shiboken2/shibokenmodule/support/signature/mapping.py @@ -56,7 +56,10 @@ import pkgutil from signature_loader import typing -ellipsis = "..." +class ellipsis(object): + def __repr__(self): + return "..." +ellipsis = ellipsis() Char = typing.Union[str, int] # how do I model the limitation to 1 char? StringList = typing.List[str] IntList = typing.List[int] diff --git a/sources/shiboken2/shibokenmodule/support/signature/parser.py b/sources/shiboken2/shibokenmodule/support/signature/parser.py index 82cd9aab3..4bb1bf234 100644 --- a/sources/shiboken2/shibokenmodule/support/signature/parser.py +++ b/sources/shiboken2/shibokenmodule/support/signature/parser.py @@ -240,7 +240,7 @@ def fixup_multilines(sig_str): if idx > 0: continue # remove duplicates - multi_lines = list(set(multi_lines)) + multi_lines = sorted(set(multi_lines)) # renumber or return a single line nmulti = len(multi_lines) if nmulti > 1: -- cgit v1.2.3