summaryrefslogtreecommitdiff
path: root/sphinx/ext/autodoc/inspector.py
diff options
context:
space:
mode:
Diffstat (limited to 'sphinx/ext/autodoc/inspector.py')
-rw-r--r--sphinx/ext/autodoc/inspector.py176
1 files changed, 176 insertions, 0 deletions
diff --git a/sphinx/ext/autodoc/inspector.py b/sphinx/ext/autodoc/inspector.py
new file mode 100644
index 000000000..da779d531
--- /dev/null
+++ b/sphinx/ext/autodoc/inspector.py
@@ -0,0 +1,176 @@
+# -*- coding: utf-8 -*-
+"""
+ sphinx.ext.autodoc.inspector
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+ Inspect utilities for autodoc
+
+ :copyright: Copyright 2007-2017 by the Sphinx team, see AUTHORS.
+ :license: BSD, see LICENSE for details.
+"""
+
+import typing
+
+from six import StringIO, string_types
+
+from sphinx.util.inspect import object_description
+
+if False:
+ # For type annotation
+ from typing import Any, Callable, Dict, Tuple # NOQA
+
+
+def format_annotation(annotation):
+ # type: (Any) -> str
+ """Return formatted representation of a type annotation.
+
+ Show qualified names for types and additional details for types from
+ the ``typing`` module.
+
+ Displaying complex types from ``typing`` relies on its private API.
+ """
+ if isinstance(annotation, typing.TypeVar): # type: ignore
+ return annotation.__name__
+ if annotation == Ellipsis:
+ return '...'
+ if not isinstance(annotation, type):
+ return repr(annotation)
+
+ qualified_name = (annotation.__module__ + '.' + annotation.__qualname__ # type: ignore
+ if annotation else repr(annotation))
+
+ if annotation.__module__ == 'builtins':
+ return annotation.__qualname__ # type: ignore
+ else:
+ if hasattr(typing, 'GenericMeta') and \
+ isinstance(annotation, typing.GenericMeta):
+ # In Python 3.5.2+, all arguments are stored in __args__,
+ # whereas __parameters__ only contains generic parameters.
+ #
+ # Prior to Python 3.5.2, __args__ is not available, and all
+ # arguments are in __parameters__.
+ params = None
+ if hasattr(annotation, '__args__'):
+ if annotation.__args__ is None or len(annotation.__args__) <= 2: # type: ignore # NOQA
+ params = annotation.__args__ # type: ignore
+ else: # typing.Callable
+ args = ', '.join(format_annotation(a) for a in annotation.__args__[:-1]) # type: ignore # NOQA
+ result = format_annotation(annotation.__args__[-1]) # type: ignore
+ return '%s[[%s], %s]' % (qualified_name, args, result)
+ elif hasattr(annotation, '__parameters__'):
+ params = annotation.__parameters__ # type: ignore
+ if params is not None:
+ param_str = ', '.join(format_annotation(p) for p in params)
+ return '%s[%s]' % (qualified_name, param_str)
+ elif (hasattr(typing, 'UnionMeta') and
+ isinstance(annotation, typing.UnionMeta) and # type: ignore
+ hasattr(annotation, '__union_params__')):
+ params = annotation.__union_params__ # type: ignore
+ if params is not None:
+ param_str = ', '.join(format_annotation(p) for p in params)
+ return '%s[%s]' % (qualified_name, param_str)
+ elif (hasattr(typing, 'CallableMeta') and
+ isinstance(annotation, typing.CallableMeta) and # type: ignore
+ getattr(annotation, '__args__', None) is not None and
+ hasattr(annotation, '__result__')):
+ # Skipped in the case of plain typing.Callable
+ args = annotation.__args__ # type: ignore
+ if args is None:
+ return qualified_name
+ elif args is Ellipsis:
+ args_str = '...'
+ else:
+ formatted_args = (format_annotation(a) for a in args)
+ args_str = '[%s]' % ', '.join(formatted_args)
+ return '%s[%s, %s]' % (qualified_name,
+ args_str,
+ format_annotation(annotation.__result__)) # type: ignore
+ elif (hasattr(typing, 'TupleMeta') and
+ isinstance(annotation, typing.TupleMeta) and # type: ignore
+ hasattr(annotation, '__tuple_params__') and
+ hasattr(annotation, '__tuple_use_ellipsis__')):
+ params = annotation.__tuple_params__ # type: ignore
+ if params is not None:
+ param_strings = [format_annotation(p) for p in params]
+ if annotation.__tuple_use_ellipsis__: # type: ignore
+ param_strings.append('...')
+ return '%s[%s]' % (qualified_name,
+ ', '.join(param_strings))
+ return qualified_name
+
+
+def formatargspec(function, args, varargs=None, varkw=None, defaults=None,
+ kwonlyargs=(), kwonlydefaults={}, annotations={}):
+ # type: (Callable, Tuple[str, ...], str, str, Any, Tuple, Dict, Dict[str, Any]) -> str
+ """Return a string representation of an ``inspect.FullArgSpec`` tuple.
+
+ An enhanced version of ``inspect.formatargspec()`` that handles typing
+ annotations better.
+ """
+
+ def format_arg_with_annotation(name):
+ # type: (str) -> str
+ if name in annotations:
+ return '%s: %s' % (name, format_annotation(get_annotation(name)))
+ return name
+
+ def get_annotation(name):
+ # type: (str) -> str
+ value = annotations[name]
+ if isinstance(value, string_types):
+ return introspected_hints.get(name, value)
+ else:
+ return value
+
+ introspected_hints = (typing.get_type_hints(function)
+ if typing and hasattr(function, '__code__') else {})
+
+ fd = StringIO()
+ fd.write('(')
+
+ formatted = []
+ defaults_start = len(args) - len(defaults) if defaults else len(args)
+
+ for i, arg in enumerate(args):
+ arg_fd = StringIO()
+ if isinstance(arg, list):
+ # support tupled arguments list (only for py2): def foo((x, y))
+ arg_fd.write('(')
+ arg_fd.write(format_arg_with_annotation(arg[0]))
+ for param in arg[1:]:
+ arg_fd.write(', ')
+ arg_fd.write(format_arg_with_annotation(param))
+ arg_fd.write(')')
+ else:
+ arg_fd.write(format_arg_with_annotation(arg))
+ if defaults and i >= defaults_start:
+ arg_fd.write(' = ' if arg in annotations else '=')
+ arg_fd.write(object_description(defaults[i - defaults_start])) # type: ignore
+ formatted.append(arg_fd.getvalue())
+
+ if varargs:
+ formatted.append('*' + format_arg_with_annotation(varargs))
+
+ if kwonlyargs:
+ if not varargs:
+ formatted.append('*')
+
+ for kwarg in kwonlyargs:
+ arg_fd = StringIO()
+ arg_fd.write(format_arg_with_annotation(kwarg))
+ if kwonlydefaults and kwarg in kwonlydefaults:
+ arg_fd.write(' = ' if kwarg in annotations else '=')
+ arg_fd.write(object_description(kwonlydefaults[kwarg])) # type: ignore
+ formatted.append(arg_fd.getvalue())
+
+ if varkw:
+ formatted.append('**' + format_arg_with_annotation(varkw))
+
+ fd.write(', '.join(formatted))
+ fd.write(')')
+
+ if 'return' in annotations:
+ fd.write(' -> ')
+ fd.write(format_annotation(get_annotation('return')))
+
+ return fd.getvalue()