diff --git a/etgtools/extractors.py b/etgtools/extractors.py index 0d776268..35e12c8f 100644 --- a/etgtools/extractors.py +++ b/etgtools/extractors.py @@ -462,9 +462,6 @@ class FunctionDef(BaseDef, FixWxPrefix): """ Create a pythonized version of the argsString in function and method items that can be used as part of the docstring. - - TODO: Maybe (optionally) use this syntax to document arg types? - http://www.python.org/dev/peps/pep-3107/ """ params = list() returns = list() @@ -477,6 +474,7 @@ class FunctionDef(BaseDef, FixWxPrefix): 'wxString()': '""', 'wxArrayString()' : '[]', 'wxArrayInt()' : '[]', + 'wxEmptyString': "''", # Makes signatures much shorter } if isinstance(self, CppMethodDef): # rip apart the argsString instead of using the (empty) list of parameters @@ -495,7 +493,14 @@ class FunctionDef(BaseDef, FixWxPrefix): else: default = self.fixWxPrefix(default, True) # now grab just the last word, it should be the variable name - arg = arg.split()[-1] + # The rest will be the type information + arg_type, arg = arg.rsplit(None, 1) + arg, arg_type = self.parseNameAndType(arg, arg_type) + if arg_type: + if default == 'None': + arg = f'{arg}: {arg_type} | None' + else: + arg = f'{arg}: {arg_type}' if default: arg += '=' + default params.append(arg) @@ -506,25 +511,33 @@ class FunctionDef(BaseDef, FixWxPrefix): continue if param.arraySize: continue - s = param.pyName or param.name + s, param_type = self.parseNameAndType(param.pyName or param.name, param.type) if param.out: - returns.append(s) + if param_type: + returns.append(param_type) else: if param.inOut: - returns.append(s) + if param_type: + returns.append(param_type) + if param_type: + s = f'{s}: {param_type}' if param.default: default = param.default if default in defValueMap: default = defValueMap.get(default) + if param_type and default == 'None': + s = f'{s} | None' default = '|'.join([self.cleanName(x, True) for x in default.split('|')]) s = f'{s}={default}' params.append(s) - self.pyArgsString = '(' + ', '.join(params) + ')' - if len(returns) == 1: - self.pyArgsString += ' -> ' + returns[0] - if len(returns) > 1: - self.pyArgsString += ' -> (' + ', '.join(returns) + ')' + self.pyArgsString = f"({', '.join(params)})" + if not returns: + self.pyArgsString = f'{self.pyArgsString} -> None' + elif len(returns) == 1: + self.pyArgsString = f'{self.pyArgsString} -> {returns[0]}' + elif len(returns) > 1: + self.pyArgsString = f"{self.pyArgsString} -> tuple[{', '.join(returns)}]" def collectPySignatures(self): diff --git a/etgtools/tweaker_tools.py b/etgtools/tweaker_tools.py index e9448eff..b587a489 100644 --- a/etgtools/tweaker_tools.py +++ b/etgtools/tweaker_tools.py @@ -169,11 +169,12 @@ class FixWxPrefix(object): type_map = { # Some types are guesses, marked with TODO to verify automatic # conversion actually happens. Also, these are the type-names - # after processing by cleanName (so spaces are removed) + # after processing by cleanName (so spaces are removed), or + # after potentially lopping off an 'Array' prefix. # --String types 'String': 'str', 'Char': 'str', - 'char':' str', + 'char': 'str', 'FileName': 'str', # TODO: check conversion # --Int types 'byte': 'int', @@ -196,20 +197,38 @@ class FixWxPrefix(object): # --Others 'PyObject': 'Any', 'WindowID': 'int', # defined in wx/defs.h + # A few instances, for example in LogInfo: } type_name = self.cleanName(type_name) # Special handling of Vector types - if type_name.startswith('Vector<') and type_name.endswith('>'): # Special handling for 'Vector' types - type_name = self.cleanName(type_name[7:-1]) + type_name = self.cleanType(type_name[7:-1]) return f'list[{type_name}]' if type_name.startswith('Array'): - type_name = self.cleanName(type_name[5:]) + type_name = self.cleanType(type_name[5:]) if type_name: return f'list[{type_name}]' else: return 'list' return type_map.get(type_name, type_name) + + def parseNameAndType(self, name_string: str, type_string: str | None) -> tuple[str, str | None]: + """Given an identifier name and an optional type annotation, process + these per cleanName and cleanType. Further performs transforms on the + identifier name that may be required due to the type annotation. + Ex. The transformation "any_identifier : ..." -> "*args" requires + modifying both the identifier name and the annotation. + """ + name_string = self.cleanName(name_string) + if type_string: + type_string = self.cleanType(type_string) + if type_string == '...': + name_string = '*args' + type_string = None + if not type_string: + type_string = None + return name_string, type_string def ignoreAssignmentOperators(node):