: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
: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.
.. 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 <https://docs.python.org/3/library/inspect.html#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 <https://docs.python.org/3/library/inspect.html#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``
.. 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`_
-----------------------
-------------------------------
-.. [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
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
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 '<a href="{}" class="{}">{}</a>'.format(entry.url, ' '.join(entry.css_classes), relative_name)
+ return relative_name, '<a href="{}" class="{}">{}</a>'.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_]+')
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")
i += 1
lvl += 1
type += c
+ type_relative += c
type_link += c
continue
if lvl == 0:
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:])
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):]
# 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(' = '):
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
# 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))
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]
# 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('<function {}>'.format(value.__name__))
+ return (html.escape('<function {}>'.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
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
# 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:
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)
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()
# 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
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]
# 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)]
# 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
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]
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
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
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
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*
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
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:
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)
# 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:
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
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()
<section class="m-doc-details" id="{{ function.id }}"><div>
<h3>
{% set j = joiner('\n ' if function.has_complex_params else ' ') %}
- <span class="m-doc-wrap-bumper">def {{ prefix }}</span><span class="m-doc-wrap"><span class="m-doc-wrap-bumper"><a href="#{{ function.id }}" class="m-doc-self">{{ function.name }}</a>(</span><span class="m-doc-wrap">{% for param in function.params %}{% if loop.index0 %}{% if function.params[loop.index0 - 1].kind == 'POSITIONAL_OR_KEYWORD' and param.kind == 'KEYWORD_ONLY' %},<span class="m-text m-dim"> *,</span>{% 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') %}<span class="m-text m-dim">, /</span>{% endif %}{% endfor %}){% if function.type_link %} -> {{ function.type_link }}{% endif %}{% if function.is_classmethod %} <span class="m-label m-success">classmethod</span>{% elif function.is_staticmethod %} <span class="m-label m-info">staticmethod</span>{% endif %}</span></span>
+ <span class="m-doc-wrap-bumper">def {{ prefix }}</span><span class="m-doc-wrap"><span class="m-doc-wrap-bumper"><a href="#{{ function.id }}" class="m-doc-self">{{ function.name }}</a>(</span><span class="m-doc-wrap">{% for param in function.params %}{% if loop.index0 %}{% if function.params[loop.index0 - 1].kind == 'POSITIONAL_OR_KEYWORD' and param.kind == 'KEYWORD_ONLY' %},<span class="m-text m-dim"> *,</span>{% 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') %}<span class="m-text m-dim">, /</span>{% endif %}{% endfor %}){% if function.type_link %} -> {{ function.type_link }}{% endif %}{% if function.is_classmethod %} <span class="m-label m-success">classmethod</span>{% elif function.is_staticmethod %} <span class="m-label m-info">staticmethod</span>{% endif %}</span></span>
</h3>
{% if function.summary %}
<p>{{ function.summary }}</p>
<dt{% if not data.has_details %} id="{{ data.id }}"{% endif %}>
- <a href="#{{ data.id }}" class="m-doc{% if not data.has_details %}-self{% endif %}">{{ data.name }}</a>{% if data.type_link %}: {{ data.type_link }}{% endif %}{% if data.value %} = {{ data.value }}{% endif %}
+ <a href="#{{ data.id }}" class="m-doc{% if not data.has_details %}-self{% endif %}">{{ data.name }}</a>{% 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 #}
</dt>
<dt{% if not function.has_details %} id="{{ function.id }}"{% endif %}>
{% set j = joiner('\n ' if function.has_complex_params else ' ') %}
- <span class="m-doc-wrap-bumper">def <a href="#{{ function.id }}" class="m-doc{% if not function.has_details %}-self{% endif %}">{{ function.name }}</a>(</span><span class="m-doc-wrap">{% for param in function.params %}{% if loop.index0 %}{% if function.params[loop.index0 - 1].kind == 'POSITIONAL_OR_KEYWORD' and param.kind == 'KEYWORD_ONLY' %},<span class="m-text m-dim"> *,</span>{% 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') %}<span class="m-text m-dim">, /</span>{% endif %}{% endfor %}){% if function.type_link %} -> {{ function.type_link }}{% endif %}</span>
+ <span class="m-doc-wrap-bumper">def <a href="#{{ function.id }}" class="m-doc{% if not function.has_details %}-self{% endif %}">{{ function.name }}</a>(</span><span class="m-doc-wrap">{% for param in function.params %}{% if loop.index0 %}{% if function.params[loop.index0 - 1].kind == 'POSITIONAL_OR_KEYWORD' and param.kind == 'KEYWORD_ONLY' %},<span class="m-text m-dim"> *,</span>{% 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') %}<span class="m-text m-dim">, /</span>{% endif %}{% endfor %}){% if function.type_link %} -> {{ function.type_link }}{% endif %}</span>
</dt>
<dd>{{ function.summary }}</dd>
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 [
]:
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 [
]:
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 [
]:
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
]:
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)
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 = <Enum.FOO_BAR: -13376>)'),
('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, [<Enum.FOO_BAR: -13376>], <Enum.FIZZ_PISS: 1>))'),
('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 = <Enum_MISSING_DOT:>)'),
('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 = <FooBar object at 0xabcd>, b: int = 3)'),
('foo', '', [
- ('a', 'FooBar', 'FooBar', '<FooBar object at 0xabcd>'),
- ('b', 'int', 'int', '3')
- ], None, None))
+ ('a', 'FooBar', 'FooBar', 'FooBar', '<FooBar object at 0xabcd>'),
+ ('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 = <Enum_MISSING_GT: -1)'),
('foo', '', [
- ('a', 'Enum', 'Enum', '<Enum_MISSING_GT: -1')
- ], None, None))
+ ('a', 'Enum', 'Enum', 'Enum', '<Enum_MISSING_GT: -1')
+ ], None, None, None))
# This will fail
- 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: Enum = <Enum.CHARACTERS_AFTER: 17>a)'), bad_signature)
self.assertEqual(parse_pybind_signature(self.state, [], 'foo(a: Enum = <Enum.CHARACTERS_AFTER: 89><)'), 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[[]',
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 [
'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 [
'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)
]:
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):