From: Vladimír Vondruš Date: Tue, 24 Sep 2024 16:19:46 +0000 (+0200) Subject: documentation/python: expose also plain relative types to the template. X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~cjwatson/git?a=commitdiff_plain;h=595376e6a89f3e3e7c289ea03c59334f391a8d67;p=blog.git documentation/python: expose also plain relative types to the template. Not just type links, but also plain types with redundant name prefixes removed, for use in contexts where a link doesn't make sense, such as tooltips. They're HTML escaped like the other variants, but right now not used in the template at all. The only breaking change is that what was `param.default` / `data.value` is now in `param.default_link` / `data.value_link`. Those are different from `param.default` / `data.value` only if the type is a (known) enum, in which case the link is to the enum value, other than that these two are the same. --- diff --git a/doc/documentation/python.rst b/doc/documentation/python.rst index 40c8ce71..55126328 100644 --- a/doc/documentation/python.rst +++ b/doc/documentation/python.rst @@ -1263,10 +1263,15 @@ Property Description :py:`enum.id` Enum ID [5]_ :py:`enum.summary` Doc summary :py:`enum.content` Detailed documentation, if any -:py:`enum.base` Base class from which the enum is - derived. Set to :py:`None` if no base - class information is available. -:py:`enum.base_link` Like :py:`enum.base`, but with +:py:`enum.base` Fully qualified name of a base class + from which the enum is derived. Set to + :py:`None` if no base class information + is available. +:py:`enum.base_relative` Like :py:`enum.base`, but relative, + i.e. with a common prefix of the base + class and containing module / class + omitted +:py:`enum.base_link` Like :py:`enum.base_relative`, but with cross-linked types :py:`enum.values` List of enum values :py:`enum.has_details` If there is enough content for the full @@ -1301,8 +1306,12 @@ Property Description :py:`function.id` Function ID [5]_ :py:`function.summary` Doc summary :py:`function.content` Detailed documentation, if any -:py:`function.type` Function return type annotation [2]_ -:py:`function.type_link` Like :py:`function.type`, but with +:py:`function.type` Fully qualified function return type + annotation [2]_ +:py:`function.type_relative` Like :py:`function.type`, but relative, + i.e. with a common prefix of the type and + containing module / class omitted +:py:`function.type_link` Like :py:`function.type_relative`, but with cross-linked types :py:`function.params` List of function parameters. See below for details. @@ -1332,18 +1341,30 @@ description. Each item has the following properties: .. class:: m-table m-fullwidth -=========================== =================================================== -Property Description -=========================== =================================================== -:py:`param.name` Parameter name -:py:`param.type` Parameter type annotation [2]_ -:py:`param.type_link` Like :py:`param.type`, but with cross-linked types -:py:`param.default` Default parameter value, if any -:py:`param.kind` Parameter kind, a string equivalent to one of the - `inspect.Parameter.kind `_ - values -:py:`param.content` Detailed documentation, if any -=========================== =================================================== +=============================== =============================================== +Property Description +=============================== =============================================== +:py:`param.name` Parameter name +:py:`param.type` Fully qualified parameter type annotation [2]_ +:py:`param.type_relative` Like :py:`param.type`, but relative, i.e. with + a common prefix of the type and containing + module / class omitted +:py:`param.type_link` Like :py:`param.type_relative`, but with + cross-linked types +:py:`param.default` Default parameter value, if any. If + :py:`param.type` is an enum, is a + fully-qualified enum value. +:py:`param.default_relative` Like :py:`param.default`, but relative, i.e. + with a common prefix of the type and containing + module / class omitted if :py:`param.type` is + an enum +:py:`param.default_link` Like :py:`param.default_relative`, but with + cross-linked types +:py:`param.kind` Parameter kind, a string equivalent to one of + the `inspect.Parameter.kind `_ + values +:py:`param.content` Detailed documentation, if any +=============================== =============================================== In some cases (for example in case of native APIs), the parameters can't be introspected. In that case, the parameter list is a single entry with ``name`` @@ -1354,59 +1375,69 @@ Each item has the following properties: .. class:: m-table m-fullwidth -=========================== =================================================== -Property Description -=========================== =================================================== -:py:`exception.type` Exception type -:py:`exception.type_link` Like :py:`exception`, but with a cross-linked type -:py:`exception.content` Detailed documentation -=========================== =================================================== +=============================== =============================================== +Property Description +=============================== =============================================== +:py:`exception.type` Fully qualified exception type +:py:`exception.type_relative` Like :py:`exception.type`, but relative, i.e. + with a common prefix of the type and containing + module / class omitted +:py:`exception.type_link` Like :py:`exception.type_relative`, but with a + cross-linked type +:py:`exception.content` Detailed documentation +=============================== =============================================== `Property properties`_ `````````````````````` .. class:: m-table m-fullwidth -=================================== =========================================== -Property Description -=================================== =========================================== -:py:`property.name` Property name -:py:`property.id` Property ID [5]_ -:py:`property.type` Property getter return type annotation [2]_ -:py:`property.type_link` Like :py:`property.type`, but with - cross-linked types -:py:`property.summary` Doc summary -:py:`property.content` Detailed documentation, if any -:py:`property.exceptions` List of exceptions raised when accessing - this property. Same as - :py:`function.exceptions` described in - `function properties`_. -:py:`property.is_gettable` If the property is gettable -:py:`property.is_settable` If the property is settable -:py:`property.is_deletable` If the property is deletable with :py:`del` -:py:`property.has_details` If there is enough content for the full - description block [3]_ -=================================== =========================================== +=============================== =============================================== +Property Description +=============================== =============================================== +:py:`property.name` Property name +:py:`property.id` Property ID [5]_ +:py:`property.type` Fully qualified property getter return type + annotation [2]_ +:py:`property.type_relative` Like :py:`property.type`, but relative, i.e. + with a common prefix of the type and containing + module / class omitted +:py:`property.type_link` Like :py:`property.type_relative`, but with + cross-linked types +:py:`property.summary` Doc summary +:py:`property.content` Detailed documentation, if any +:py:`property.exceptions` List of exceptions raised when accessing this + property. Same as :py:`function.exceptions` + described in `function properties`_. +:py:`property.is_gettable` If the property is gettable +:py:`property.is_settable` If the property is settable +:py:`property.is_deletable` If the property is deletable with :py:`del` +:py:`property.has_details` If there is enough content for the full + description block [3]_ +=============================== =============================================== `Data properties`_ `````````````````` .. class:: m-table m-fullwidth -=================================== =========================================== -Property Description -=================================== =========================================== -:py:`data.name` Data name -:py:`data.id` Data ID [5]_ -:py:`data.type` Data type -:py:`data.type_link` Like :py:`data.type_link`, but with - cross-linked types -:py:`data.summary` Doc summary -:py:`data.content` Detailed documentation, if any -:py:`data.value` Data value representation -:py:`data.has_details` If there is enough content for the full - description block [3]_ -=================================== =========================================== +=============================== =============================================== +Property Description +=============================== =============================================== +:py:`data.name` Data name +:py:`data.id` Data ID [5]_ +:py:`data.type` Fully qualified data type annotation [2]_ +:py:`data.type_relative` Like :py:`data.type`, but relative, i.e. with a + common prefix of the type and containing module + / class omitted +:py:`data.type_link` Like :py:`data.type_relative`, but with + cross-linked types +:py:`data.summary` Doc summary +:py:`data.content` Detailed documentation, if any +:py:`data.value` Data value representation +:py:`data.has_details` If there is enough content for the full + description block [3]_ +=============================== =============================================== `Index page templates`_ ----------------------- @@ -1458,8 +1489,9 @@ Module/class list is ordered in a way that all modules are before all classes. ------------------------------- -.. [2] :py:`i.type` is extracted out of function annotation. If the types - aren't annotated, the annotation is empty. +.. [2] :py:`i.type` is extracted out of function parameter type, function + return type, property type and data type annotation. If the types aren't + annotated, the annotation is empty. .. [3] :py:`page.has_*_details` and :py:`i.has_details` are :py:`True` if there is detailed description, function parameter documentation or *documented* enum value listing that makes it worth to render the full diff --git a/documentation/python.py b/documentation/python.py index 088ed138..34302d98 100755 --- a/documentation/python.py +++ b/documentation/python.py @@ -836,7 +836,7 @@ def make_relative_name(state: State, referrer_path: List[str], name): return '.'.join(shortened_path) -def make_name_link(state: State, referrer_path: List[str], name) -> str: +def make_name_relative_link(state: State, referrer_path: List[str], name) -> Tuple[str, str]: assert isinstance(name, str) # Not found, return as-is. However, if the prefix is one of the @@ -851,13 +851,13 @@ def make_name_link(state: State, referrer_path: List[str], name) -> str: if name.startswith(module_name + '.'): logging.warning("could not resolve a link to %s which is among INPUT_MODULES (referred from %s), possibly hidden/undocumented?", name, '.'.join(referrer_path)) break - return name + return name, name # Make a shorter name that's relative to the referrer but still unambiguous relative_name = make_relative_name(state, referrer_path, name) entry = state.name_map[name] - return '{}'.format(entry.url, ' '.join(entry.css_classes), relative_name) + return relative_name, '{}'.format(entry.url, ' '.join(entry.css_classes), relative_name) _pybind_name_rx = re.compile('[a-zA-Z0-9_]*') _pybind_arg_name_rx = re.compile('[/*a-zA-Z0-9_]+') @@ -924,13 +924,13 @@ def _pybind_map_name_prefix_or_add_typing_suffix(state: State, input_type: str): else: return map_name_prefix(state, input_type) -def parse_pybind_type(state: State, referrer_path: List[str], signature: str): +def parse_pybind_type(state: State, referrer_path: List[str], signature: str) -> Tuple[str, str, str, str]: match = _pybind_type_rx.match(signature) if match: input_type = match.group(0) signature = signature[len(input_type):] type = _pybind_map_name_prefix_or_add_typing_suffix(state, input_type) - type_link = make_name_link(state, referrer_path, type) + type_relative, type_link = make_name_relative_link(state, referrer_path, type) else: raise SyntaxError("Cannot match pybind type") @@ -942,6 +942,7 @@ def parse_pybind_type(state: State, referrer_path: List[str], signature: str): i += 1 lvl += 1 type += c + type_relative += c type_link += c continue if lvl == 0: @@ -950,11 +951,13 @@ def parse_pybind_type(state: State, referrer_path: List[str], signature: str): i += 1 lvl -= 1 type += c + type_relative += c type_link += c continue if c in ', ': i += 1 type += c + type_relative += c type_link += c continue match = _pybind_type_rx.match(signature[i:]) @@ -964,16 +967,18 @@ def parse_pybind_type(state: State, referrer_path: List[str], signature: str): i += len(input_type) input_type = _pybind_map_name_prefix_or_add_typing_suffix(state, input_type) type += input_type - type_link += make_name_link(state, referrer_path, input_type) + input_type_relative, input_type_link = make_name_relative_link(state, referrer_path, input_type) + type_relative += input_type_relative + type_link += input_type_link if lvl != 0: raise SyntaxError("Unbalanced [] in python type {}".format(signature)) signature = signature[i:] - return signature, type, type_link + return signature, type, type_relative, type_link # Returns function name, summary, list of arguments (name, type, type with HTML # links, default value) and return type. If argument parsing failed, the # argument list is a single "ellipsis" item. -def parse_pybind_signature(state: State, referrer_path: List[str], signature: str) -> Tuple[str, str, List[Tuple[str, str, str, str]], str]: +def parse_pybind_signature(state: State, referrer_path: List[str], signature: str) -> Tuple[str, str, List[Tuple[str, str, str, str, str]], str, str, str]: original_signature = signature # For error reporting name = _pybind_name_rx.match(signature).group(0) signature = signature[len(name):] @@ -994,10 +999,9 @@ def parse_pybind_signature(state: State, referrer_path: List[str], signature: st # Type (optional) if signature.startswith(': '): signature = signature[2:] - signature, arg_type, arg_type_link = parse_pybind_type(state, referrer_path, signature) + signature, arg_type, arg_type_relative, arg_type_link = parse_pybind_type(state, referrer_path, signature) else: - arg_type = None - arg_type_link = None + arg_type, arg_type_relative, arg_type_link = None, None, None # Default (optional) if signature.startswith(' = '): @@ -1006,7 +1010,7 @@ def parse_pybind_signature(state: State, referrer_path: List[str], signature: st else: default = None - args += [(arg_name, arg_type, arg_type_link, default)] + args += [(arg_name, arg_type, arg_type_relative, arg_type_link, default)] if signature[0] == ')': break @@ -1020,9 +1024,9 @@ def parse_pybind_signature(state: State, referrer_path: List[str], signature: st # Return type (optional) if signature.startswith(' -> '): signature = signature[4:] - signature, return_type, return_type_link = parse_pybind_type(state, referrer_path, signature) + signature, return_type, return_type_relative, return_type_link = parse_pybind_type(state, referrer_path, signature) else: - return_type, return_type_link = None, None + return_type, return_type_relative, return_type_link = None, None, None # Expecting end of the signature line now, if not there, we failed if signature and signature[0] != '\n': raise SyntaxError("Expected end of the signature, got `{}`".format(signature)) @@ -1035,14 +1039,14 @@ def parse_pybind_signature(state: State, referrer_path: List[str], signature: st docstring = inspect.cleandoc(original_signature[end + 1:]) else: docstring = '' - return (name, docstring, [('…', None, None, None)], None, None) + return (name, docstring, [('…', None, None, None, None)], None, None, None) if len(signature) > 1 and signature[1] == '\n': docstring = inspect.cleandoc(signature[2:]) else: docstring = '' - return (name, docstring, args, return_type, return_type_link) + return (name, docstring, args, return_type, return_type_relative, return_type_link) def parse_pybind_docstring(state: State, referrer_path: List[str], doc: str) -> List[Tuple[str, str, List[Tuple[str, str, str]], str]]: name = referrer_path[-1] @@ -1078,21 +1082,23 @@ def parse_pybind_docstring(state: State, referrer_path: List[str], doc: str) -> # Used to format function default arguments and data values. *Not* pybind's # function default arguments, as those are parsed from a string representation. -def format_value(state: State, referrer_path: List[str], value) -> Optional[str]: +def format_value(state: State, referrer_path: List[str], value) -> Optional[Tuple[str, str, str]]: if value is None: - return str(value) + return str(value), str(value), str(value) if isinstance(value, enum.Enum): - return make_name_link(state, referrer_path, '{}.{}.{}'.format(value.__class__.__module__, value.__class__.__qualname__, value.name)) + # TODO Python 3.8+ supports `a, *b`, switch to that once 3.7 is dropped + return (value.name, ) + make_name_relative_link(state, referrer_path, '{}.{}.{}'.format(value.__class__.__module__, value.__class__.__qualname__, value.name)) # pybind enums have the __members__ attribute instead elif state.config['PYBIND11_COMPATIBILITY'] and hasattr(value.__class__, '__members__'): - return make_name_link(state, referrer_path, '{}.{}.{}'.format(value.__class__.__module__, value.__class__.__qualname__, value.name)) + # TODO Python 3.8+ supports `a, *b`, switch to that once 3.7 is dropped + return (value.name, ) + make_name_relative_link(state, referrer_path, '{}.{}.{}'.format(value.__class__.__module__, value.__class__.__qualname__, value.name)) elif inspect.isfunction(value): - return html.escape(''.format(value.__name__)) + return (html.escape(''.format(value.__name__)), )*3 elif '__repr__' in type(value).__dict__: rendered = repr(value) # TODO: tuples of non-representable values will still be ugly # If the value is too large, return just an ellipsis - return html.escape(rendered) if len(rendered) < 128 else '…' + return (html.escape(rendered) if len(rendered) < 128 else '…', )*3 else: return None @@ -1232,31 +1238,31 @@ def get_type_hints_or_nothing(state: State, path: List[str], object) -> Dict: logging.warning("failed to dereference type hints for %s (%s), falling back to non-dereferenced", '.'.join(path), e.__class__.__name__) return {} -def extract_annotation(state: State, referrer_path: List[str], annotation) -> Tuple[str, str]: +def extract_annotation(state: State, referrer_path: List[str], annotation) -> Tuple[str, str, str]: # Empty annotation, as opposed to a None annotation, handled below if annotation is inspect.Signature.empty: - return None, None + return None, None, None # If dereferencing with typing.get_type_hints() failed, we might end up # with forward-referenced types being plain strings. Keep them as is, since # those are most probably an error. if type(annotation) == str: - return annotation, annotation + return (annotation, )*3 # Or the plain strings might be inside (e.g. List['Foo']), which gets # converted by Python to ForwardRef. Hammer out the actual string and again # leave it as-is, since it's most probably an error. elif isinstance(annotation, typing.ForwardRef if sys.version_info >= (3, 7) else typing._ForwardRef): - return annotation.__forward_arg__, annotation.__forward_arg__ + return (annotation.__forward_arg__, )*3 # Generic type names -- use their name directly elif isinstance(annotation, typing.TypeVar): - return annotation.__name__, annotation.__name__ + return annotation.__name__, annotation.__name__, annotation.__name__ # Ellipsis -- print a literal `...` # TODO: any chance to link this to python official docs? elif annotation is ...: - return '...', '...' + return ('...', )*3 # If the annotation is from the typing module, it ... gets complicated. It # could be a "bracketed" type, in which case we want to recurse to its @@ -1301,10 +1307,10 @@ def extract_annotation(state: State, referrer_path: List[str], annotation) -> Tu # representation at least. else: # pragma: no cover logging.warning("can't inspect annotation %s for %s, falling back to a string representation", annotation, '.'.join(referrer_path)) - return str(annotation), str(annotation) + return (str(annotation), )*3 # Add type links to name - name_link = make_name_link(state, referrer_path, name) + name_relative, name_link = make_name_relative_link(state, referrer_path, name) # Arguments of generic types, recurse inside if args: @@ -1314,58 +1320,66 @@ def extract_annotation(state: State, referrer_path: List[str], annotation) -> Tu assert len(args) >= 1 nested_types = [] + nested_types_relative = [] nested_type_links = [] for i in args[:-1]: - nested_type, nested_type_link = extract_annotation(state, referrer_path, i) + nested_type, nested_type_relative, nested_type_link = extract_annotation(state, referrer_path, i) nested_types += [nested_type] + nested_types_relative += [nested_type_relative] nested_type_links += [nested_type_link] - nested_return_type, nested_return_type_link = extract_annotation(state, referrer_path, args[-1]) + nested_return_type, nested_return_type_relative, nested_return_type_link = extract_annotation(state, referrer_path, args[-1]) # If nested parsing failed (the invalid annotation below), # fail the whole thing if None in nested_types or nested_return_type is None: - return None, None + return None, None, None return ( '{}[[{}], {}]'.format(name, ', '.join(nested_types), nested_return_type), + '{}[[{}], {}]'.format(name, ', '.join(nested_types_relative), nested_return_type_relative), '{}[[{}], {}]'.format(name_link, ', '.join(nested_type_links), nested_return_type_link) ) else: nested_types = [] + nested_types_relative = [] nested_type_links = [] for i in args: - nested_type, nested_type_link = extract_annotation(state, referrer_path, i) + nested_type, nested_type_relative, nested_type_link = extract_annotation(state, referrer_path, i) nested_types += [nested_type] + nested_types_relative += [nested_type_relative] nested_type_links += [nested_type_link] # If nested parsing failed (the invalid annotation below), # fail the whole thing if None in nested_types: - return None, None + return None, None, None return ( '{}[{}]'.format(name, ', '.join(nested_types)), + '{}[{}]'.format(name_relative, ', '.join(nested_types_relative)), '{}[{}]'.format(name_link, ', '.join(nested_type_links)), ) - else: return name, name_link + else: return name, name_relative, name_link # Things like (float, int) instead of Tuple[float, int] or using np.array # instead of np.ndarray. Ignore with a warning. elif not isinstance(annotation, type): logging.warning("invalid annotation %s in %s, ignoring", annotation, '.'.join(referrer_path)) - return None, None + return None, None, None # According to https://www.python.org/dev/peps/pep-0484/#using-none, # None and type(None) are equivalent. Calling extract_type() on None would # give us NoneType, which is unnecessarily long. elif annotation is type(None): - return 'None', make_name_link(state, referrer_path, 'None') + # TODO Python 3.8+ supports `a, *b`, switch to that once 3.7 is dropped + return ('None', ) + make_name_relative_link(state, referrer_path, 'None') # Otherwise it's a plain type. Turn it into a link. name = extract_type(annotation) - return name, make_name_link(state, referrer_path, map_name_prefix(state, name)) + # TODO Python 3.8+ supports `a, *b`, switch to that once 3.7 is dropped + return (name, ) + make_name_relative_link(state, referrer_path, map_name_prefix(state, name)) def extract_module_doc(state: State, entry: Empty): assert inspect.ismodule(entry.object) @@ -1420,7 +1434,7 @@ def extract_enum_doc(state: State, entry: Empty): docstring = entry.object.__doc__ out.base = extract_type(entry.object.__base__) - out.base_link = make_name_link(state, entry.path, out.base) + out.base_relative, out.base_link = make_name_relative_link(state, entry.path, out.base) for i in entry.object: value = Empty() @@ -1522,13 +1536,13 @@ def extract_function_doc(state: State, parent, entry: Empty) -> List[Any]: # already, so check that we have that consistent assert (len(funcs) > 1) == (entry.type == EntryType.OVERLOADED_FUNCTION) overloads = [] - for name, summary, args, type, type_link in funcs: + for name, summary, args, type, type_relative, type_link in funcs: out = Empty() out.name = name out.params = [] out.has_complex_params = False out.has_details = False - out.type, out.type_link = type, type_link + out.type, out.type_relative, out.type_link = type, type_relative, type_link # There's no other way to check staticmethods than to check for # self being the name of first parameter :( No support for @@ -1589,7 +1603,7 @@ def extract_function_doc(state: State, parent, entry: Empty) -> List[Any]: param_types = [] signature = [] for i, arg in enumerate(args): - arg_name, arg_type, arg_type_link, arg_default = arg + arg_name, arg_type, arg_type_relative, arg_type_link, arg_default = arg param = Empty() param.name = arg_name param_names += [arg_name] @@ -1606,11 +1620,11 @@ def extract_function_doc(state: State, parent, entry: Empty) -> List[Any]: # Don't include redundant type for the self argument if i == 0 and arg_name == 'self': - param.type, param.type_link = None, None + param.type, param.type_relative, param.type_link = None, None, None param_types += [None] signature += ['self'] else: - param.type, param.type_link = arg_type, arg_type_link + param.type, param.type_relative, param.type_link = arg_type, arg_type_relative, arg_type_link param_types += [arg_type] signature += ['{}: {}'.format(arg_name, arg_type)] @@ -1621,11 +1635,13 @@ def extract_function_doc(state: State, parent, entry: Empty) -> List[Any]: # fully qualified), concatenate it together to have # `module.EnumType.VALUE` if arg_type in state.name_map and state.name_map[arg_type].type == EntryType.ENUM: - param.default = make_name_link(state, entry.path, '.'.join(state.name_map[arg_type].path[:-1] + [arg_default])) + param.default = '.'.join(state.name_map[arg_type].path[:-1] + [arg_default]) + param.default_relative, param.default_link = make_name_relative_link(state, entry.path, param.default) else: param.default = html.escape(arg_default) + param.default_relative, param.default_link = param.default, param.default else: - param.default = None + param.default, param.default_relative, param.default_link = None, None, None if arg_type or arg_default: out.has_complex_params = True # *args / **kwargs can still appear in the parsed signatures if @@ -1687,24 +1703,24 @@ def extract_function_doc(state: State, parent, entry: Empty) -> List[Any]: signature = inspect.signature(entry.object) if 'return' in type_hints: - out.type, out.type_link = extract_annotation(state, entry.path, type_hints['return']) + out.type, out.type_relative, out.type_link = extract_annotation(state, entry.path, type_hints['return']) else: - out.type, out.type_link = extract_annotation(state, entry.path, signature.return_annotation) + out.type, out.type_relative, out.type_link = extract_annotation(state, entry.path, signature.return_annotation) param_names = [] for i in signature.parameters.values(): param = Empty() param.name = i.name param_names += [i.name] if i.name in type_hints: - param.type, param.type_link = extract_annotation(state, entry.path, type_hints[i.name]) + param.type, param.type_relative, param.type_link = extract_annotation(state, entry.path, type_hints[i.name]) else: - param.type, param.type_link = extract_annotation(state, entry.path, i.annotation) + param.type, param.type_relative, param.type_link = extract_annotation(state, entry.path, i.annotation) if param.type: out.has_complex_params = True if i.default is inspect.Signature.empty: - param.default = None + param.default, param.default_relative, param.default_link = None, None, None else: - param.default = format_value(state, entry.path, i.default) or '…' + param.default, param.default_relative, param.default_link = format_value(state, entry.path, i.default) or ('…', )*3 out.has_complex_params = True param.kind = str(i.kind) out.params += [param] @@ -1715,10 +1731,10 @@ def extract_function_doc(state: State, parent, entry: Empty) -> List[Any]: except ValueError: param = Empty() param.name = '...' - param.type, param.type_link = None, None - param.default = None + param.type, param.type_relative, param.type_link = None, None, None + param.default, param.default_relative, param.default_link = None, None, None out.params = [param] - out.type, out.type_link = None, None + out.type, out.type_relative, out.type_link = None, None, None param_names = [] # Call all scope enter hooks @@ -1785,7 +1801,7 @@ def extract_function_doc(state: State, parent, entry: Empty) -> List[Any]: for type_, content in function_docs['raise']: exception = Empty() exception.type = type_ - exception.type_link = make_name_link(state, entry.path, type_) + exception.type_relative, exception.type_link = make_name_relative_link(state, entry.path, type_) exception.content = render_inline_rst(state, content) out.exceptions += [exception] out.has_details = True @@ -1814,7 +1830,7 @@ def extract_function_doc(state: State, parent, entry: Empty) -> List[Any]: if len(overloads) != 1: for i in range(len(out.params)): param = out.params[i] - result.params += ['{}: {}'.format(param.name, make_relative_name(state, entry.path, param.type)) if param.type else param.name] + result.params += ['{}: {}'.format(param.name, param.type_relative) if param.type_relative else param.name] state.search += [result] return overloads @@ -1837,7 +1853,7 @@ def extract_property_doc(state: State, parent, entry: Empty): out.is_gettable = True out.is_settable = True out.is_deletable = True - out.type, out.type_link = extract_annotation(state, entry.path, entry.object.type) + out.type, out.type_relative, out.type_link = extract_annotation(state, entry.path, entry.object.type) # If this is a slot, there won't be any fget / fset / fdel. Assume they're # gettable and settable (couldn't find any way to make them *inspectably* @@ -1860,11 +1876,11 @@ def extract_property_doc(state: State, parent, entry: Empty): type_hints = get_type_hints_or_nothing(state, entry.path, parent) if out.name in type_hints: - out.type, out.type_link = extract_annotation(state, entry.path, type_hints[out.name]) + out.type, out.type_relative, out.type_link = extract_annotation(state, entry.path, type_hints[out.name]) elif hasattr(parent, '__annotations__') and out.name in parent.__annotations__: - out.type, out.type_link = extract_annotation(state, entry.path, parent.__annotations__[out.name]) + out.type, out.type_relative, out.type_link = extract_annotation(state, entry.path, parent.__annotations__[out.name]) else: - out.type, out.type_link = None, None + out.type, out.type_relative, out.type_link = None, None, None # The properties can be defined using the low-level descriptor protocol # instead of the higher-level property() decorator. That means there's no @@ -1880,7 +1896,7 @@ def extract_property_doc(state: State, parent, entry: Empty): out.is_gettable = True out.is_settable = False out.is_deletable = False - out.type, out.type_link = None, None + out.type, out.type_relative, out.type_link = None, None, None # Otherwise it's a classic property else: @@ -1915,9 +1931,9 @@ def extract_property_doc(state: State, parent, entry: Empty): type_hints = get_type_hints_or_nothing(state, entry.path, entry.object.fget) if 'return' in type_hints: - out.type, out.type_link = extract_annotation(state, entry.path, type_hints['return']) + out.type, out.type_relative, out.type_link = extract_annotation(state, entry.path, type_hints['return']) else: - out.type, out.type_link = extract_annotation(state, entry.path, signature.return_annotation) + out.type, out.type_relative, out.type_link = extract_annotation(state, entry.path, signature.return_annotation) else: assert entry.object.fset signature = inspect.signature(entry.object.fset) @@ -1930,25 +1946,25 @@ def extract_property_doc(state: State, parent, entry: Empty): # non-dereferenced version value_parameter = list(signature.parameters.values())[1] if value_parameter.name in type_hints: - out.type, out.type_link = extract_annotation(state, entry.path, type_hints[value_parameter.name]) + out.type, out.type_relative, out.type_link = extract_annotation(state, entry.path, type_hints[value_parameter.name]) else: - out.type, out.type_link = extract_annotation(state, entry.path, value_parameter.annotation) + out.type, out.type_relative, out.type_link = extract_annotation(state, entry.path, value_parameter.annotation) except ValueError: # pybind11 properties have the type in the docstring if state.config['PYBIND11_COMPATIBILITY']: if entry.object.fget: - out.type, out.type_link = parse_pybind_signature(state, entry.path, entry.object.fget.__doc__)[3:] + out.type, out.type_relative, out.type_link = parse_pybind_signature(state, entry.path, entry.object.fget.__doc__)[3:] else: assert entry.object.fset parsed_args = parse_pybind_signature(state, entry.path, entry.object.fset.__doc__)[2] # If argument parsing failed, we're screwed if len(parsed_args) == 1: - out.type, out.type_link = None, None + out.type, out.type_relative, out.type_link = None, None, None else: - out.type, out.type_link = parsed_args[1][1:3] + out.type, out.type_relative, out.type_link = parsed_args[1][1:4] else: - out.type, out.type_link = None, None + out.type, out.type_relative, out.type_link = None, None, None # Call all scope enter hooks before rendering the docs for hook in state.hooks_pre_scope: @@ -1970,7 +1986,7 @@ def extract_property_doc(state: State, parent, entry: Empty): for type_, content in exception_docs: exception = Empty() exception.type = type_ - exception.type_link = make_name_link(state, entry.path, type_) + exception.type_relative, exception.type_link = make_name_relative_link(state, entry.path, type_) exception.content = render_inline_rst(state, content) out.exceptions += [exception] out.has_details = True @@ -2009,13 +2025,13 @@ def extract_data_doc(state: State, parent, entry: Empty): type_hints = get_type_hints_or_nothing(state, entry.path, parent) if out.name in type_hints: - out.type, out.type_link = extract_annotation(state, entry.path, type_hints[out.name]) + out.type, out.type_relative, out.type_link = extract_annotation(state, entry.path, type_hints[out.name]) elif hasattr(parent, '__annotations__') and out.name in parent.__annotations__: - out.type, out.type_link = extract_annotation(state, entry.path, parent.__annotations__[out.name]) + out.type, out.type_relative, out.type_link = extract_annotation(state, entry.path, parent.__annotations__[out.name]) else: - out.type, out.type_link = None, None + out.type, out.type_relative, out.type_link = None, None, None - out.value = format_value(state, entry.path, entry.object) + out.value, out.value_relative, out.value_link = format_value(state, entry.path, entry.object) or (None, None, None) if not state.config['SEARCH_DISABLED']: result = Empty() diff --git a/documentation/templates/python/details-function.html b/documentation/templates/python/details-function.html index 25440508..c4008218 100644 --- a/documentation/templates/python/details-function.html +++ b/documentation/templates/python/details-function.html @@ -1,7 +1,7 @@

{% set j = joiner('\n ' if function.has_complex_params else ' ') %} - def {{ prefix }}{{ function.name }}({% for param in function.params %}{% if loop.index0 %}{% if function.params[loop.index0 - 1].kind == 'POSITIONAL_OR_KEYWORD' and param.kind == 'KEYWORD_ONLY' %}, *,{% else %},{% endif %}{% endif %}{{ j() }}{% if param.kind == 'VAR_POSITIONAL' %}*{% elif param.kind == 'VAR_KEYWORD' %}**{% endif %}{{ param.name }}{% if param.type_link %}: {{ param.type_link }}{% endif %}{% if param.default %} = {{ param.default }}{% endif %}{% if param.kind == 'POSITIONAL_ONLY' and (loop.last or function.params[loop.index0 + 1].kind != 'POSITIONAL_ONLY') %}, /{% endif %}{% endfor %}){% if function.type_link %} -> {{ function.type_link }}{% endif %}{% if function.is_classmethod %} classmethod{% elif function.is_staticmethod %} staticmethod{% endif %} + def {{ prefix }}{{ function.name }}({% for param in function.params %}{% if loop.index0 %}{% if function.params[loop.index0 - 1].kind == 'POSITIONAL_OR_KEYWORD' and param.kind == 'KEYWORD_ONLY' %}, *,{% else %},{% endif %}{% endif %}{{ j() }}{% if param.kind == 'VAR_POSITIONAL' %}*{% elif param.kind == 'VAR_KEYWORD' %}**{% endif %}{{ param.name }}{% if param.type_link %}: {{ param.type_link }}{% endif %}{% if param.default_link %} = {{ param.default_link }}{% endif %}{% if param.kind == 'POSITIONAL_ONLY' and (loop.last or function.params[loop.index0 + 1].kind != 'POSITIONAL_ONLY') %}, /{% endif %}{% endfor %}){% if function.type_link %} -> {{ function.type_link }}{% endif %}{% if function.is_classmethod %} classmethod{% elif function.is_staticmethod %} staticmethod{% endif %}

{% if function.summary %}

{{ function.summary }}

diff --git a/documentation/templates/python/entry-data.html b/documentation/templates/python/entry-data.html index 49227c7a..26587829 100644 --- a/documentation/templates/python/entry-data.html +++ b/documentation/templates/python/entry-data.html @@ -1,5 +1,5 @@ - {{ data.name }}{% if data.type_link %}: {{ data.type_link }}{% endif %}{% if data.value %} = {{ data.value }}{% endif %} + {{ data.name }}{% if data.type_link %}: {{ data.type_link }}{% endif %}{% if data.value_link %} = {{ data.value_link }}{% endif %} {# This has to be here to avoid the newline being eaten #} diff --git a/documentation/templates/python/entry-function.html b/documentation/templates/python/entry-function.html index 96493ecd..5e771413 100644 --- a/documentation/templates/python/entry-function.html +++ b/documentation/templates/python/entry-function.html @@ -1,5 +1,5 @@ {% set j = joiner('\n ' if function.has_complex_params else ' ') %} - def {{ function.name }}({% for param in function.params %}{% if loop.index0 %}{% if function.params[loop.index0 - 1].kind == 'POSITIONAL_OR_KEYWORD' and param.kind == 'KEYWORD_ONLY' %}, *,{% else %},{% endif %}{% endif %}{{ j() }}{% if param.kind == 'VAR_POSITIONAL' %}*{% elif param.kind == 'VAR_KEYWORD' %}**{% endif %}{{ param.name }}{% if param.type_link %}: {{ param.type_link }}{% endif %}{% if param.default %} = {{ param.default }}{% endif %}{% if param.kind == 'POSITIONAL_ONLY' and (loop.last or function.params[loop.index0 + 1].kind != 'POSITIONAL_ONLY') %}, /{% endif %}{% endfor %}){% if function.type_link %} -> {{ function.type_link }}{% endif %} + def {{ function.name }}({% for param in function.params %}{% if loop.index0 %}{% if function.params[loop.index0 - 1].kind == 'POSITIONAL_OR_KEYWORD' and param.kind == 'KEYWORD_ONLY' %}, *,{% else %},{% endif %}{% endif %}{{ j() }}{% if param.kind == 'VAR_POSITIONAL' %}*{% elif param.kind == 'VAR_KEYWORD' %}**{% endif %}{{ param.name }}{% if param.type_link %}: {{ param.type_link }}{% endif %}{% if param.default_link %} = {{ param.default_link }}{% endif %}{% if param.kind == 'POSITIONAL_ONLY' and (loop.last or function.params[loop.index0 + 1].kind != 'POSITIONAL_ONLY') %}, /{% endif %}{% endfor %}){% if function.type_link %} -> {{ function.type_link }}{% endif %}
{{ function.summary }}
diff --git a/documentation/test_python/test_pybind.py b/documentation/test_python/test_pybind.py index 73db6017..3b581731 100644 --- a/documentation/test_python/test_pybind.py +++ b/documentation/test_python/test_pybind.py @@ -41,60 +41,60 @@ class Signature(unittest.TestCase): self.assertEqual(parse_pybind_signature(self.state, [], 'foo(a: int, a2: module.Thing) -> module.Thing3'), ('foo', '', [ - ('a', 'int', 'int', None), - ('a2', 'module.Thing', 'module.Thing', None), - ], 'module.Thing3', 'module.Thing3')) + ('a', 'int', 'int', 'int', None), + ('a2', 'module.Thing', 'module.Thing', 'module.Thing', None), + ], 'module.Thing3', 'module.Thing3', 'module.Thing3')) def test_newline(self): self.assertEqual(parse_pybind_signature(self.state, [], 'foo(a: int, a2: module.Thing) -> module.Thing3\n'), ('foo', '', [ - ('a', 'int', 'int', None), - ('a2', 'module.Thing', 'module.Thing', None), - ], 'module.Thing3', 'module.Thing3')) + ('a', 'int', 'int', 'int', None), + ('a2', 'module.Thing', 'module.Thing', 'module.Thing', None), + ], 'module.Thing3', 'module.Thing3', 'module.Thing3')) def test_docs(self): self.assertEqual(parse_pybind_signature(self.state, [], 'foo(a: int, a2: module.Thing) -> module.Thing3\n\nDocs here!!'), ('foo', 'Docs here!!', [ - ('a', 'int', 'int', None), - ('a2', 'module.Thing', 'module.Thing', None), - ], 'module.Thing3', 'module.Thing3')) + ('a', 'int', 'int', 'int', None), + ('a2', 'module.Thing', 'module.Thing', 'module.Thing', None), + ], 'module.Thing3', 'module.Thing3', 'module.Thing3')) def test_no_args(self): self.assertEqual(parse_pybind_signature(self.state, [], 'thingy() -> str'), - ('thingy', '', [], 'str', 'str')) + ('thingy', '', [], 'str', 'str', 'str')) def test_no_return(self): self.assertEqual(parse_pybind_signature(self.state, [], '__init__(self: module.Thing)'), ('__init__', '', [ - ('self', 'module.Thing', 'module.Thing', None), - ], None, None)) + ('self', 'module.Thing', 'module.Thing', 'module.Thing', None), + ], None, None, None)) def test_none_return(self): self.assertEqual(parse_pybind_signature(self.state, [], '__init__(self: module.Thing) -> None'), ('__init__', '', [ - ('self', 'module.Thing', 'module.Thing', None), - ], 'None', 'None')) + ('self', 'module.Thing', 'module.Thing', 'module.Thing', None), + ], 'None', 'None', 'None')) def test_no_arg_types(self): self.assertEqual(parse_pybind_signature(self.state, [], 'thingy(self, the_other_thing)'), ('thingy', '', [ - ('self', None, None, None), - ('the_other_thing', None, None, None), - ], None, None)) + ('self', None, None, None, None), + ('the_other_thing', None, None, None, None), + ], None, None, None)) def test_none_arg_types(self): self.assertEqual(parse_pybind_signature(self.state, [], 'thingy(self, the_other_thing: Callable[[], None])'), ('thingy', '', [ - ('self', None, None, None), - ('the_other_thing', 'typing.Callable[[], None]', 'typing.Callable[[], None]', None), - ], None, None)) + ('self', None, None, None, None), + ('the_other_thing', 'typing.Callable[[], None]', 'typing.Callable[[], None]', 'typing.Callable[[], None]', None), + ], None, None, None)) def test_square_brackets(self): for i in [ @@ -105,9 +105,9 @@ class Signature(unittest.TestCase): ]: self.assertEqual(parse_pybind_signature(self.state, [], i), ('foo', '', [ - ('a', 'tuple[int, str]', 'tuple[int, str]', None), - ('no_really', 'str', 'str', None), - ], 'list[str]', 'list[str]')) + ('a', 'tuple[int, str]', 'tuple[int, str]', 'tuple[int, str]', None), + ('no_really', 'str', 'str', 'str', None), + ], 'list[str]', 'list[str]', 'list[str]')) def test_nested_square_brackets(self): for i in [ @@ -118,9 +118,9 @@ class Signature(unittest.TestCase): ]: self.assertEqual(parse_pybind_signature(self.state, [], i), ('foo', '', [ - ('a', 'tuple[int, set[tuple[int, int]]]', 'tuple[int, set[tuple[int, int]]]', None), - ('another', 'float', 'float', None), - ], 'typing.Union[str, None]', 'typing.Union[str, None]')) + ('a', 'tuple[int, set[tuple[int, int]]]', 'tuple[int, set[tuple[int, int]]]', 'tuple[int, set[tuple[int, int]]]', None), + ('another', 'float', 'float', 'float', None), + ], 'typing.Union[str, None]', 'typing.Union[str, None]', 'typing.Union[str, None]')) def test_callable(self): for i in [ @@ -131,43 +131,43 @@ class Signature(unittest.TestCase): ]: self.assertEqual(parse_pybind_signature(self.state, [], i), ('foo', '', [ - ('a', 'typing.Callable[[int, dict[int, int]], float]', 'typing.Callable[[int, dict[int, int]], float]', None), - ('another', 'float', 'float', None), - ], None, None)) + ('a', 'typing.Callable[[int, dict[int, int]], float]', 'typing.Callable[[int, dict[int, int]], float]', 'typing.Callable[[int, dict[int, int]], float]', None), + ('another', 'float', 'float', 'float', None), + ], None, None, None)) def test_kwargs(self): self.assertEqual(parse_pybind_signature(self.state, [], 'foo(*args, **kwargs)'), ('foo', '', [ - ('*args', None, None, None), - ('**kwargs', None, None, None), - ], None, None)) + ('*args', None, None, None, None), + ('**kwargs', None, None, None, None), + ], None, None, None)) def test_keyword_positional_only(self): self.assertEqual(parse_pybind_signature(self.state, [], 'foo(a: int, /, b: float, *, keyword: str)'), ('foo', '', [ - ('a', 'int', 'int', None), - ('/', None, None, None), - ('b', 'float', 'float', None), - ('*', None, None, None), - ('keyword', 'str', 'str', None), - ], None, None)) + ('a', 'int', 'int', 'int', None), + ('/', None, None, None, None), + ('b', 'float', 'float', 'float', None), + ('*', None, None, None, None), + ('keyword', 'str', 'str', 'str', None), + ], None, None, None)) def test_default_values(self): self.assertEqual(parse_pybind_signature(self.state, [], 'foo(a: float = 1.0, b: str = \'hello\')'), ('foo', '', [ - ('a', 'float', 'float', '1.0'), - ('b', 'str', 'str', '\'hello\''), - ], None, None)) + ('a', 'float', 'float', 'float', '1.0'), + ('b', 'str', 'str', 'str', '\'hello\''), + ], None, None, None)) self.assertEqual(parse_pybind_signature(self.state, [], 'foo(a: float = libA.foo(libB.goo(123), libB.bar + 13) + 2, b = 3)'), ('foo', '', [ - ('a', 'float', 'float', 'libA.foo(libB.goo(123), libB.bar + 13) + 2'), - ('b', None, None, '3'), - ], None, None)) + ('a', 'float', 'float', 'float', 'libA.foo(libB.goo(123), libB.bar + 13) + 2'), + ('b', None, None, None, '3'), + ], None, None, None)) for i in [ # pybind11 2.11 and older @@ -177,17 +177,17 @@ class Signature(unittest.TestCase): ]: self.assertEqual(parse_pybind_signature(self.state, [], i), ('foo', '', [ - ('a', 'tuple[int, ...]', 'tuple[int, ...]', + ('a', 'tuple[int, ...]', 'tuple[int, ...]', 'tuple[int, ...]', '(1,("hello", \'world\'),3,4)') - ], None, None)) + ], None, None, None)) self.assertEqual(parse_pybind_signature(self.state, [], 'foo(a: str = [dict(key="A", value=\'B\')["key"][0], None][0])'), ('foo', '', [ - ('a', 'str', 'str', '[dict(key="A", value=\'B\')["key"][0], None][0]') - ], None, None)) + ('a', 'str', 'str', 'str', '[dict(key="A", value=\'B\')["key"][0], None][0]') + ], None, None, None)) - bad_signature = ('foo', '', [('…', None, None, None)], None, None) + bad_signature = ('foo', '', [('…', None, None, None, None)], None, None, None) self.assertEqual(parse_pybind_signature(self.state, [], 'foo(a: float = [0][)'), bad_signature) self.assertEqual(parse_pybind_signature(self.state, [], 'foo(a: float = ()'), bad_signature) @@ -204,54 +204,54 @@ class Signature(unittest.TestCase): self.assertEqual(parse_pybind_signature(self.state, [], 'foo(a: bar.Enum = Enum.FOO_BAR)'), ('foo', '', [ - ('a', 'bar.Enum', 'bar.Enum', 'Enum.FOO_BAR') - ], None, None)) + ('a', 'bar.Enum', 'bar.Enum', 'bar.Enum', 'Enum.FOO_BAR') + ], None, None, None)) # After the insane change self.assertEqual(parse_pybind_signature(self.state, [], 'foo(a: bar.Enum = )'), ('foo', '', [ - ('a', 'bar.Enum', 'bar.Enum', 'Enum.FOO_BAR') - ], None, None)) + ('a', 'bar.Enum', 'bar.Enum', 'bar.Enum', 'Enum.FOO_BAR') + ], None, None, None)) # Nested self.assertEqual(parse_pybind_signature(self.state, [], 'foo(a: bar.Enum = (4, [], ))'), ('foo', '', [ - ('a', 'bar.Enum', 'bar.Enum', '(4, [Enum.FOO_BAR], Enum.FIZZ_PISS)') - ], None, None)) + ('a', 'bar.Enum', 'bar.Enum', 'bar.Enum', '(4, [Enum.FOO_BAR], Enum.FIZZ_PISS)') + ], None, None, None)) # This isn't really expected to happen but yeah it still treats it as # an enum self.assertEqual(parse_pybind_signature(self.state, [], 'foo(a: Enum = )'), ('foo', '', [ - ('a', 'Enum', 'Enum', 'Enum_MISSING_DOT') - ], None, None)) + ('a', 'Enum', 'Enum', 'Enum', 'Enum_MISSING_DOT') + ], None, None, None)) # This is how pybind prints various objects, should be passed as-is. # It should not corrupt any parameters after. self.assertEqual(parse_pybind_signature(self.state, [], 'foo(a: FooBar = , b: int = 3)'), ('foo', '', [ - ('a', 'FooBar', 'FooBar', ''), - ('b', 'int', 'int', '3') - ], None, None)) + ('a', 'FooBar', 'FooBar', 'FooBar', ''), + ('b', 'int', 'int', 'int', '3') + ], None, None, None)) # This is weird and so will be passed as-is. self.assertEqual(parse_pybind_signature(self.state, [], 'foo(a: Enum = a)'), bad_signature) self.assertEqual(parse_pybind_signature(self.state, [], 'foo(a: Enum = <)'), bad_signature) def test_bad_return_type(self): - bad_signature = ('foo', '', [('…', None, None, None)], None, None) + bad_signature = ('foo', '', [('…', None, None, None, None)], None, None, None) for i in [ # pybind11 2.11 and older 'foo() -> List[[]', @@ -268,7 +268,7 @@ class Signature(unittest.TestCase): def test_crazy_stuff(self): self.assertEqual(parse_pybind_signature(self.state, [], 'foo(a: int, b: Math::Vector<4, UnsignedInt>)'), - ('foo', '', [('…', None, None, None)], None, None)) + ('foo', '', [('…', None, None, None, None)], None, None, None)) def test_crazy_stuff_nested(self): for i in [ @@ -278,17 +278,17 @@ class Signature(unittest.TestCase): 'foo(a: int, b: list[Math::Vector<4, UnsignedInt>])' ]: self.assertEqual(parse_pybind_signature(self.state, [], i), - ('foo', '', [('…', None, None, None)], None, None)) + ('foo', '', [('…', None, None, None, None)], None, None, None)) def test_crazy_stuff_docs(self): self.assertEqual(parse_pybind_signature(self.state, [], 'foo(a: int, b: Math::Vector<4, UnsignedInt>)\n\nThis is text!!'), - ('foo', 'This is text!!', [('…', None, None, None)], None, None)) + ('foo', 'This is text!!', [('…', None, None, None, None)], None, None, None)) def test_crazy_return(self): self.assertEqual(parse_pybind_signature(self.state, [], 'foo(a: int) -> Math::Vector<4, UnsignedInt>'), - ('foo', '', [('…', None, None, None)], None, None)) + ('foo', '', [('…', None, None, None, None)], None, None, None)) def test_crazy_return_nested(self): for i in [ @@ -298,17 +298,17 @@ class Signature(unittest.TestCase): 'foo(a: int) -> list[Math::Vector<4, UnsignedInt>]' ]: self.assertEqual(parse_pybind_signature(self.state, [], i), - ('foo', '', [('…', None, None, None)], None, None)) + ('foo', '', [('…', None, None, None, None)], None, None, None)) def test_crazy_return_docs(self): self.assertEqual(parse_pybind_signature(self.state, [], 'foo(a: int) -> Math::Vector<4, UnsignedInt>\n\nThis returns!'), - ('foo', 'This returns!', [('…', None, None, None)], None, None)) + ('foo', 'This returns!', [('…', None, None, None, None)], None, None, None)) def test_no_name(self): self.assertEqual(parse_pybind_signature(self.state, [], '(arg0: MyClass) -> float'), - ('', '', [('arg0', 'MyClass', 'MyClass', None)], 'float', 'float')) + ('', '', [('arg0', 'MyClass', 'MyClass', 'MyClass', None)], 'float', 'float', 'float')) def test_name_mapping(self): state = copy.deepcopy(self.state) @@ -322,9 +322,9 @@ class Signature(unittest.TestCase): ]: self.assertEqual(parse_pybind_signature(state, [], i), ('foo', '', [ - ('a', 'module.Foo', 'module.Foo', None), - ('b', 'tuple[int, module.Bar]', 'tuple[int, module.Bar]', None) - ], 'module.Baz', 'module.Baz')) + ('a', 'module.Foo', 'module.Foo', 'module.Foo', None), + ('b', 'tuple[int, module.Bar]', 'tuple[int, module.Bar]', 'tuple[int, module.Bar]', None) + ], 'module.Baz', 'module.Baz', 'module.Baz')) class Signatures(BaseInspectTestCase): def test_positional_args(self):