1############################################################################# 2## 3## Copyright (C) 2019 The Qt Company Ltd. 4## Contact: https://www.qt.io/licensing/ 5## 6## This file is part of Qt for Python. 7## 8## $QT_BEGIN_LICENSE:LGPL$ 9## Commercial License Usage 10## Licensees holding valid commercial Qt licenses may use this file in 11## accordance with the commercial license agreement provided with the 12## Software or, alternatively, in accordance with the terms contained in 13## a written agreement between you and The Qt Company. For licensing terms 14## and conditions see https://www.qt.io/terms-conditions. For further 15## information use the contact form at https://www.qt.io/contact-us. 16## 17## GNU Lesser General Public License Usage 18## Alternatively, this file may be used under the terms of the GNU Lesser 19## General Public License version 3 as published by the Free Software 20## Foundation and appearing in the file LICENSE.LGPL3 included in the 21## packaging of this file. Please review the following information to 22## ensure the GNU Lesser General Public License version 3 requirements 23## will be met: https://www.gnu.org/licenses/lgpl-3.0.html. 24## 25## GNU General Public License Usage 26## Alternatively, this file may be used under the terms of the GNU 27## General Public License version 2.0 or (at your option) the GNU General 28## Public license version 3 or any later version approved by the KDE Free 29## Qt Foundation. The licenses are as published by the Free Software 30## Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 31## included in the packaging of this file. Please review the following 32## information to ensure the GNU General Public License requirements will 33## be met: https://www.gnu.org/licenses/gpl-2.0.html and 34## https://www.gnu.org/licenses/gpl-3.0.html. 35## 36## $QT_END_LICENSE$ 37## 38############################################################################# 39 40from __future__ import print_function, absolute_import 41 42""" 43layout.py 44 45The signature module now has the capability to configure 46differently formatted versions of signatures. The default 47layout is known from the "__signature__" attribute. 48 49The function "get_signature(ob, modifier=None)" produces the same 50signatures by default. By passing different modifiers, you 51can select different layouts. 52 53This module configures the different layouts which can be used. 54It also implements them in this file. The configurations are 55used literally as strings like "signature", "existence", etc. 56""" 57 58from textwrap import dedent 59from shibokensupport.signature import inspect, typing 60from shibokensupport.signature.mapping import ellipsis 61from shibokensupport.signature.lib.tool import SimpleNamespace 62 63 64class SignatureLayout(SimpleNamespace): 65 """ 66 Configure a signature. 67 68 The layout of signatures can have different layouts which are 69 controlled by keyword arguments: 70 71 definition=True Determines if self will generated. 72 defaults=True 73 ellipsis=False Replaces defaults by "...". 74 return_annotation=True 75 parameter_names=True False removes names before ":". 76 """ 77 allowed_keys = SimpleNamespace(definition=True, 78 defaults=True, 79 ellipsis=False, 80 return_annotation=True, 81 parameter_names=True) 82 allowed_values = True, False 83 84 def __init__(self, **kwds): 85 args = SimpleNamespace(**self.allowed_keys.__dict__) 86 args.__dict__.update(kwds) 87 self.__dict__.update(args.__dict__) 88 err_keys = list(set(self.__dict__) - set(self.allowed_keys.__dict__)) 89 if err_keys: 90 self._attributeerror(err_keys) 91 err_values = list(set(self.__dict__.values()) - set(self.allowed_values)) 92 if err_values: 93 self._valueerror(err_values) 94 95 def __setattr__(self, key, value): 96 if key not in self.allowed_keys.__dict__: 97 self._attributeerror([key]) 98 if value not in self.allowed_values: 99 self._valueerror([value]) 100 self.__dict__[key] = value 101 102 def _attributeerror(self, err_keys): 103 err_keys = ", ".join(err_keys) 104 allowed_keys = ", ".join(self.allowed_keys.__dict__.keys()) 105 raise AttributeError(dedent("""\ 106 Not allowed: '{err_keys}'. 107 The only allowed keywords are '{allowed_keys}'. 108 """.format(**locals()))) 109 110 def _valueerror(self, err_values): 111 err_values = ", ".join(map(str, err_values)) 112 allowed_values = ", ".join(map(str, self.allowed_values)) 113 raise ValueError(dedent("""\ 114 Not allowed: '{err_values}'. 115 The only allowed values are '{allowed_values}'. 116 """.format(**locals()))) 117 118# The following names are used literally in this module. 119# This way, we avoid the dict hashing problem. 120signature = SignatureLayout() 121 122existence = SignatureLayout(definition=False, 123 defaults=False, 124 return_annotation=False, 125 parameter_names=False) 126 127hintingstub = SignatureLayout(ellipsis=True) 128 129typeerror = SignatureLayout(definition=False, 130 return_annotation=False, 131 parameter_names=False) 132 133 134def define_nameless_parameter(): 135 """ 136 Create Nameless Parameters 137 138 A nameless parameter has a reduced string representation. 139 This is done by cloning the parameter type and overwriting its 140 __str__ method. The inner structure is still a valid parameter. 141 """ 142 def __str__(self): 143 # for Python 2, we must change self to be an instance of P 144 klass = self.__class__ 145 self.__class__ = P 146 txt = P.__str__(self) 147 self.__class__ = klass 148 txt = txt[txt.index(":") + 1:].strip() if ":" in txt else txt 149 return txt 150 151 P = inspect.Parameter 152 newname = "NamelessParameter" 153 bases = P.__bases__ 154 body = dict(P.__dict__) # get rid of mappingproxy 155 if "__slots__" in body: 156 # __slots__ would create duplicates 157 for name in body["__slots__"]: 158 del body[name] 159 body["__str__"] = __str__ 160 return type(newname, bases, body) 161 162 163NamelessParameter = define_nameless_parameter() 164 165""" 166Note on the "Optional" feature: 167 168When an annotation has a default value that is None, then the 169type has to be wrapped into "typing.Optional". 170 171Note that only the None value creates an Optional expression, 172because the None leaves the domain of the variable. 173Defaults like integer values are ignored: They stay in the domain. 174 175That information would be lost when we use the "..." convention. 176 177Note that the typing module has the remarkable expansion 178 179 Optional[T] is Variant[T, NoneType] 180 181We want to avoid that when generating the .pyi file. 182This is done by a regex in generate_pyi.py . 183The following would work in Python 3, but this is a version-dependent 184hack that also won't work in Python 2 and would be _very_ complex. 185""" 186# import sys 187# if sys.version_info[0] == 3: 188# class hugo(list):pass 189# typing._normalize_alias["hugo"] = "Optional" 190# Optional = typing._alias(hugo, typing.T, inst=False) 191# else: 192# Optional = typing.Optional 193 194 195def make_signature_nameless(signature): 196 """ 197 Make a Signature Nameless 198 199 We use an existing signature and change the type of its parameters. 200 The signature looks different, but is totally intact. 201 """ 202 for key in signature.parameters.keys(): 203 signature.parameters[key].__class__ = NamelessParameter 204 205 206_POSITIONAL_ONLY = inspect._POSITIONAL_ONLY 207_POSITIONAL_OR_KEYWORD = inspect._POSITIONAL_OR_KEYWORD 208_VAR_POSITIONAL = inspect._VAR_POSITIONAL 209_KEYWORD_ONLY = inspect._KEYWORD_ONLY 210_VAR_KEYWORD = inspect._VAR_KEYWORD 211_empty = inspect._empty 212 213def create_signature(props, key): 214 if not props: 215 # empty signatures string 216 return 217 if isinstance(props["multi"], list): 218 # multi sig: call recursively 219 return list(create_signature(elem, key) 220 for elem in props["multi"]) 221 if type(key) is tuple: 222 sig_kind, modifier = key 223 else: 224 sig_kind, modifier = key, "signature" 225 226 layout = globals()[modifier] # lookup of the modifier in this module 227 if not isinstance(layout, SignatureLayout): 228 raise SystemError("Modifiers must be names of a SignatureLayout " 229 "instance") 230 231 # this is the basic layout of a signature 232 varnames = props["varnames"] 233 if layout.definition: 234 # PYSIDE-1328: We no longer use info from the sig_kind which is 235 # more complex for multiple signatures. We now get `self` from the 236 # parser. 237 pass 238 else: 239 if "self" in varnames[:1]: 240 varnames = varnames[1:] 241 242 # calculate the modifications 243 defaults = props["defaults"][:] 244 if not layout.defaults: 245 defaults = () 246 annotations = props["annotations"].copy() 247 if not layout.return_annotation and "return" in annotations: 248 del annotations["return"] 249 250 # Build a signature. 251 kind = inspect._POSITIONAL_OR_KEYWORD 252 params = [] 253 for idx, name in enumerate(varnames): 254 if name.startswith("**"): 255 kind = _VAR_KEYWORD 256 elif name.startswith("*"): 257 kind = _VAR_POSITIONAL 258 ann = annotations.get(name, _empty) 259 if ann == "self": 260 ann = _empty 261 name = name.lstrip("*") 262 defpos = idx - len(varnames) + len(defaults) 263 default = defaults[defpos] if defpos >= 0 else _empty 264 if default is None: 265 ann = typing.Optional[ann] 266 if default is not _empty and layout.ellipsis: 267 default = ellipsis 268 param = inspect.Parameter(name, kind, annotation=ann, default=default) 269 params.append(param) 270 if kind == _VAR_POSITIONAL: 271 kind = _KEYWORD_ONLY 272 sig = inspect.Signature(params, 273 return_annotation=annotations.get('return', _empty), 274 __validate_parameters__=False) 275 276 # the special case of nameless parameters 277 if not layout.parameter_names: 278 make_signature_nameless(sig) 279 return sig 280 281# end of file 282