# Otherwise it's a plain type. Turn it into a link.
return make_name_link(state, referrer_path, map_name_prefix(state, extract_type(annotation)))
-def extract_module_doc(state: State, path: List[str], module):
- assert inspect.ismodule(module)
+def extract_module_doc(state: State, entry: Empty):
+ assert inspect.ismodule(entry.object)
out = Empty()
- out.url = state.config['URL_FORMATTER'](EntryType.MODULE, path)[1]
- out.name = path[-1]
- out.summary = extract_summary(state, state.class_docs, path, module.__doc__)
+ out.url = entry.url
+ out.name = entry.path[-1]
+ out.summary = extract_summary(state, state.class_docs, entry.path, entry.object.__doc__)
return out
-def extract_class_doc(state: State, path: List[str], class_):
- assert inspect.isclass(class_)
+def extract_class_doc(state: State, entry: Empty):
+ assert inspect.isclass(entry.object)
out = Empty()
- out.url = state.config['URL_FORMATTER'](EntryType.CLASS, path)[1]
- out.name = path[-1]
- out.summary = extract_summary(state, state.class_docs, path, class_.__doc__)
+ out.url = entry.url
+ out.name = entry.path[-1]
+ out.summary = extract_summary(state, state.class_docs, entry.path, entry.object.__doc__)
return out
-def extract_enum_doc(state: State, path: List[str], enum_):
+def extract_enum_doc(state: State, entry: Empty):
out = Empty()
- out.name = path[-1]
- out.id = state.config['ID_FORMATTER'](EntryType.ENUM, path[-1:])
+ out.name = entry.path[-1]
+ out.id = state.config['ID_FORMATTER'](EntryType.ENUM, entry.path[-1:])
out.values = []
out.has_details = False
out.has_value_details = False
# The happy case
- if issubclass(enum_, enum.Enum):
+ if issubclass(entry.object, enum.Enum):
# Enum doc is by default set to a generic value. That's useless as well.
- if enum_.__doc__ == 'An enumeration.':
+ if entry.object.__doc__ == 'An enumeration.':
out.summary = ''
else:
# TODO: external summary for enums
- out.summary = extract_summary(state, {}, [], enum_.__doc__)
+ out.summary = extract_summary(state, {}, [], entry.object.__doc__)
- out.base = extract_type(enum_.__base__)
- if out.base: out.base = make_name_link(state, path, out.base)
+ out.base = extract_type(entry.object.__base__)
+ if out.base: out.base = make_name_link(state, entry.path, out.base)
- for i in enum_:
+ for i in entry.object:
value = Empty()
value.name = i.name
- value.id = state.config['ID_FORMATTER'](EntryType.ENUM_VALUE, path[-1:] + [i.name])
+ value.id = state.config['ID_FORMATTER'](EntryType.ENUM_VALUE, entry.path[-1:] + [i.name])
value.value = html.escape(repr(i.value))
# Value doc gets by default inherited from the enum, that's useless
- if i.__doc__ == enum_.__doc__:
+ if i.__doc__ == entry.object.__doc__:
value.summary = ''
else:
# TODO: external summary for enum values
# Pybind11 enums are ... different
elif state.config['PYBIND11_COMPATIBILITY']:
- assert hasattr(enum_, '__members__')
+ assert hasattr(entry.object, '__members__')
# TODO: external summary for enums
- out.summary = extract_summary(state, {}, [], enum_.__doc__)
+ out.summary = extract_summary(state, {}, [], entry.object.__doc__)
out.base = None
- for name, v in enum_.__members__.items():
+ for name, v in entry.object.__members__.items():
value = Empty()
value. name = name
- value.id = state.config['ID_FORMATTER'](EntryType.ENUM_VALUE, path[-1:] + [name])
+ value.id = state.config['ID_FORMATTER'](EntryType.ENUM_VALUE, entry.path[-1:] + [name])
value.value = int(v)
# TODO: once https://github.com/pybind/pybind11/pull/1160 is
# released, extract from class docs (until then the class
return out
-def extract_function_doc(state: State, parent, path: List[str], function) -> List[Any]:
- assert inspect.isfunction(function) or inspect.ismethod(function) or inspect.isroutine(function)
+def extract_function_doc(state: State, parent, entry: Empty) -> List[Any]:
+ assert inspect.isfunction(entry.object) or inspect.ismethod(entry.object) or inspect.isroutine(entry.object)
# Extract the signature from the docstring for pybind11, since it can't
# expose it to the metadata: https://github.com/pybind/pybind11/issues/990
#
# Some shitty packages might be setting __doc__ to None (attrs is one of
# them), explicitly check for that first.
- if state.config['PYBIND11_COMPATIBILITY'] and function.__doc__ and function.__doc__.startswith(path[-1]):
- funcs = parse_pybind_docstring(state, path, function.__doc__)
+ if state.config['PYBIND11_COMPATIBILITY'] and entry.object.__doc__ and entry.object.__doc__.startswith(entry.path[-1]):
+ funcs = parse_pybind_docstring(state, entry.path, entry.object.__doc__)
overloads = []
for name, summary, args, type in funcs:
out = Empty()
- out.name = path[-1]
+ out.name = entry.path[-1]
out.params = []
out.has_complex_params = False
out.has_details = False
# Don't show None return type for functions w/o a return
out.type = None if type == 'None' else type
- if out.type: out.type = make_name_link(state, path, out.type)
+ if out.type: out.type = make_name_link(state, entry.path, out.type)
# There's no other way to check staticmethods than to check for
# self being the name of first parameter :( No support for
# fully qualified), concatenate it together to have
# `module.EnumType.VALUE`
if type in state.name_map and state.name_map[type].type == EntryType.ENUM:
- param.default = make_name_link(state, path, '.'.join(state.name_map[type].path[:-1] + [default]))
+ param.default = make_name_link(state, entry.path, '.'.join(state.name_map[type].path[:-1] + [default]))
else: param.default = html.escape(default)
else:
param.default = None
# Format the anchor. Pybind11 functions are sometimes overloaded,
# thus name alone is not enough.
- out.id = state.config['ID_FORMATTER'](EntryType.OVERLOADED_FUNCTION, path[-1:] + arg_types)
+ out.id = state.config['ID_FORMATTER'](EntryType.OVERLOADED_FUNCTION, entry.path[-1:] + arg_types)
overloads += [out]
# Sane introspection path for non-pybind11 code
else:
out = Empty()
- out.name = path[-1]
- out.id = state.config['ID_FORMATTER'](EntryType.FUNCTION, path[-1:])
+ out.name = entry.path[-1]
+ out.id = state.config['ID_FORMATTER'](EntryType.FUNCTION, entry.path[-1:])
out.params = []
out.has_complex_params = False
out.has_details = False
# TODO: external summary for functions
- out.summary = extract_summary(state, {}, [], function.__doc__)
+ out.summary = extract_summary(state, {}, [], entry.object.__doc__)
# Decide if classmethod or staticmethod in case this is a method
if inspect.isclass(parent):
- out.is_classmethod = inspect.ismethod(function)
+ out.is_classmethod = inspect.ismethod(entry.object)
out.is_staticmethod = out.name in parent.__dict__ and isinstance(parent.__dict__[out.name], staticmethod)
# First try to get fully dereferenced type hints (with strings
# converted to actual annotations). If that fails (e.g. because a type
# doesn't exist), we'll take the non-dereferenced annotations from
# inspect instead.
- type_hints = get_type_hints_or_nothing(state, path, function)
+ type_hints = get_type_hints_or_nothing(state, entry.path, entry.object)
try:
- signature = inspect.signature(function)
+ signature = inspect.signature(entry.object)
if 'return' in type_hints:
- out.type = extract_annotation(state, path, type_hints['return'])
+ out.type = extract_annotation(state, entry.path, type_hints['return'])
else:
- out.type = extract_annotation(state, path, signature.return_annotation)
+ out.type = extract_annotation(state, entry.path, signature.return_annotation)
for i in signature.parameters.values():
param = Empty()
param.name = i.name
if i.name in type_hints:
- param.type = extract_annotation(state, path, type_hints[i.name])
+ param.type = extract_annotation(state, entry.path, type_hints[i.name])
else:
- param.type = extract_annotation(state, path, i.annotation)
+ param.type = 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
else:
- param.default = format_value(state, path, i.default) or '…'
+ param.default = format_value(state, entry.path, i.default) or '…'
out.has_complex_params = True
param.kind = str(i.kind)
out.params += [param]
return [out]
-def extract_property_doc(state: State, parent, path: List[str], property):
- assert inspect.isdatadescriptor(property)
+def extract_property_doc(state: State, parent, entry: Empty):
+ assert inspect.isdatadescriptor(entry.object)
out = Empty()
- out.name = path[-1]
- out.id = state.config['ID_FORMATTER'](EntryType.PROPERTY, path[-1:])
+ out.name = entry.path[-1]
+ out.id = state.config['ID_FORMATTER'](EntryType.PROPERTY, entry.path[-1:])
# 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*
# previously set value). Unfortunately we can't get any docstring for these
# either.
# TODO: any better way to detect that those are slots?
- if property.__class__.__name__ == 'member_descriptor' and property.__class__.__module__ == 'builtins':
+ if entry.object.__class__.__name__ == 'member_descriptor' and entry.object.__class__.__module__ == 'builtins':
out.is_gettable = True
out.is_settable = True
out.is_deletable = True
# First try to get fully dereferenced type hints (with strings
# converted to actual annotations). If that fails (e.g. because a type
# doesn't exist), we'll take the non-dereferenced annotations instead.
- type_hints = get_type_hints_or_nothing(state, path, parent)
+ type_hints = get_type_hints_or_nothing(state, entry.path, parent)
if out.name in type_hints:
- out.type = extract_annotation(state, path, type_hints[out.name])
+ out.type = extract_annotation(state, entry.path, type_hints[out.name])
elif hasattr(parent, '__annotations__') and out.name in parent.__annotations__:
- out.type = extract_annotation(state, path, parent.__annotations__[out.name])
+ out.type = extract_annotation(state, entry.path, parent.__annotations__[out.name])
else:
out.type = None
# __delete__ directly. This is fairly rare (datetime.date is one and
# BaseException.args is another I could find), so don't bother with it much
# --- assume readonly and no docstrings / annotations whatsoever.
- if property.__class__.__name__ == 'getset_descriptor' and property.__class__.__module__ == 'builtins':
+ if entry.object.__class__.__name__ == 'getset_descriptor' and entry.object.__class__.__module__ == 'builtins':
out.is_gettable = True
out.is_settable = False
out.is_deletable = False
return out
# TODO: external summary for properties
- out.is_gettable = property.fget is not None
- if property.fget or (property.fset and property.__doc__):
- out.summary = extract_summary(state, {}, [], property.__doc__)
+ out.is_gettable = entry.object.fget is not None
+ if entry.object.fget or (entry.object.fset and entry.object.__doc__):
+ out.summary = extract_summary(state, {}, [], entry.object.__doc__)
else:
- assert property.fset
- out.summary = extract_summary(state, {}, [], property.fset.__doc__)
- out.is_settable = property.fset is not None
- out.is_deletable = property.fdel is not None
+ assert entry.object.fset
+ out.summary = extract_summary(state, {}, [], entry.object.fset.__doc__)
+ out.is_settable = entry.object.fset is not None
+ out.is_deletable = entry.object.fdel is not None
out.has_details = False
# For the type, if the property is gettable, get it from getters's return
# annotation.
try:
- if property.fget:
- signature = inspect.signature(property.fget)
+ if entry.object.fget:
+ signature = inspect.signature(entry.object.fget)
# First try to get fully dereferenced type hints (with strings
# converted to actual annotations). If that fails (e.g. because a
# TypeError from typing.get_type_hints(). This way they throw
# ValueError from inspect and we don't need to handle TypeError in
# get_type_hints_or_nothing().
- type_hints = get_type_hints_or_nothing(state, path, property.fget)
+ type_hints = get_type_hints_or_nothing(state, entry.path, entry.object.fget)
if 'return' in type_hints:
- out.type = extract_annotation(state, path, type_hints['return'])
+ out.type = extract_annotation(state, entry.path, type_hints['return'])
else:
- out.type = extract_annotation(state, path, signature.return_annotation)
+ out.type = extract_annotation(state, entry.path, signature.return_annotation)
else:
- assert property.fset
- signature = inspect.signature(property.fset)
+ assert entry.object.fset
+ signature = inspect.signature(entry.object.fset)
# Same as the lengthy comment above
- type_hints = get_type_hints_or_nothing(state, path, property.fset)
+ type_hints = get_type_hints_or_nothing(state, entry.path, entry.object.fset)
# Get second parameter name, then try to fetch it from type_hints
# and if that fails get its annotation from the non-dereferenced
# version
value_parameter = list(signature.parameters.values())[1]
if value_parameter.name in type_hints:
- out.type = extract_annotation(state, path, type_hints[value_parameter.name])
+ out.type = extract_annotation(state, entry.path, type_hints[value_parameter.name])
else:
- out.type = extract_annotation(state, path, value_parameter.annotation)
+ out.type = extract_annotation(state, entry.path, value_parameter.annotation)
except ValueError:
# pybind11 properties have the type in the docstring
if state.config['PYBIND11_COMPATIBILITY']:
- if property.fget:
- out.type = parse_pybind_signature(state, path, property.fget.__doc__)[3]
+ if entry.object.fget:
+ out.type = parse_pybind_signature(state, entry.path, entry.object.fget.__doc__)[3]
else:
- assert property.fset
- parsed_args = parse_pybind_signature(state, path, property.fset.__doc__)[2]
+ 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 = None
else: out.type = parsed_args[1][2]
return out
-def extract_data_doc(state: State, parent, path: List[str], data):
- assert not inspect.ismodule(data) and not inspect.isclass(data) and not inspect.isroutine(data) and not inspect.isframe(data) and not inspect.istraceback(data) and not inspect.iscode(data)
+def extract_data_doc(state: State, parent, entry: Empty):
+ assert not inspect.ismodule(entry.object) and not inspect.isclass(entry.object) and not inspect.isroutine(entry.object) and not inspect.isframe(entry.object) and not inspect.istraceback(entry.object) and not inspect.iscode(entry.object)
out = Empty()
- out.name = path[-1]
- out.id = state.config['ID_FORMATTER'](EntryType.DATA, path[-1:])
+ out.name = entry.path[-1]
+ out.id = state.config['ID_FORMATTER'](EntryType.DATA, entry.path[-1:])
# Welp. https://stackoverflow.com/questions/8820276/docstring-for-variable
out.summary = ''
out.has_details = False
# First try to get fully dereferenced type hints (with strings converted to
# actual annotations). If that fails (e.g. because a type doesn't exist),
# we'll take the non-dereferenced annotations instead.
- type_hints = get_type_hints_or_nothing(state, path, parent)
+ type_hints = get_type_hints_or_nothing(state, entry.path, parent)
if out.name in type_hints:
- out.type = extract_annotation(state, path, type_hints[out.name])
+ out.type = extract_annotation(state, entry.path, type_hints[out.name])
elif hasattr(parent, '__annotations__') and out.name in parent.__annotations__:
- out.type = extract_annotation(state, path, parent.__annotations__[out.name])
+ out.type = extract_annotation(state, entry.path, parent.__annotations__[out.name])
else:
out.type = None
- out.value = format_value(state, path, data)
+ out.value = format_value(state, entry.path, entry.object)
# External data summary, if provided
- path_str = '.'.join(path)
+ path_str = '.'.join(entry.path)
if path_str in state.data_docs:
# TODO: use also the contents
out.summary = render_inline_rst(state, state.data_docs[path_str]['summary'])
logging.warning("%s is undocumented", subpath_str)
if member_entry.type == EntryType.MODULE:
- page.modules += [extract_module_doc(state, subpath, member_entry.object)]
+ page.modules += [extract_module_doc(state, member_entry)]
elif member_entry.type == EntryType.CLASS:
- page.classes += [extract_class_doc(state, subpath, member_entry.object)]
+ page.classes += [extract_class_doc(state, member_entry)]
elif member_entry.type == EntryType.ENUM:
- enum_ = extract_enum_doc(state, subpath, member_entry.object)
+ enum_ = extract_enum_doc(state, member_entry)
page.enums += [enum_]
if enum_.has_details: page.has_enum_details = True
elif member_entry.type == EntryType.FUNCTION:
- page.functions += extract_function_doc(state, module, subpath, member_entry.object)
+ page.functions += extract_function_doc(state, module, member_entry)
elif member_entry.type == EntryType.DATA:
- page.data += [extract_data_doc(state, module, subpath, member_entry.object)]
+ page.data += [extract_data_doc(state, module, member_entry)]
else: # pragma: no cover
assert False
logging.warning("%s is undocumented", subpath_str)
if member_entry.type == EntryType.CLASS:
- page.classes += [extract_class_doc(state, subpath, member_entry.object)]
+ page.classes += [extract_class_doc(state, member_entry)]
elif member_entry.type == EntryType.ENUM:
- enum_ = extract_enum_doc(state, subpath, member_entry.object)
+ enum_ = extract_enum_doc(state, member_entry)
page.enums += [enum_]
if enum_.has_details: page.has_enum_details = True
elif member_entry.type == EntryType.FUNCTION:
- for function in extract_function_doc(state, class_, subpath, member_entry.object):
+ for function in extract_function_doc(state, class_, member_entry):
if name.startswith('__'):
page.dunder_methods += [function]
elif function.is_classmethod:
else:
page.methods += [function]
elif member_entry.type == EntryType.PROPERTY:
- page.properties += [extract_property_doc(state, class_, subpath, member_entry.object)]
+ page.properties += [extract_property_doc(state, class_, member_entry)]
elif member_entry.type == EntryType.DATA:
- page.data += [extract_data_doc(state, class_, subpath, member_entry.object)]
+ page.data += [extract_data_doc(state, class_, member_entry)]
else: # pragma: no cover
assert False