Ejemplo n.º 1
0
    def add_content(self,
                    more_content: Any,
                    no_docstring: bool = False) -> None:
        """
		Add content from docstrings, attribute documentation and user.

		:param more_content:
		:param no_docstring:
		"""

        obj: _TypeVar = self.object
        sourcename = self.get_sourcename()
        constraints = [
            self.resolve_type(c) if isinstance(c, ForwardRef) else c
            for c in obj.__constraints__
        ]
        description = []

        bound_to: Optional[Type]

        if isinstance(obj.__bound__, ForwardRef):
            bound_to = self.resolve_type(obj.__bound__)
        else:
            bound_to = obj.__bound__

        if obj.__covariant__:
            description.append("Covariant")
        elif obj.__contravariant__:
            description.append("Contravariant")
        else:
            description.append("Invariant")

        description.append(":class:`~typing.TypeVar`")

        if constraints:
            description.append("constrained to")
            description.append(
                word_join(
                    format_annotation(c, fully_qualified=True)
                    for c in constraints))
        elif bound_to:
            description.append("bound to")
            description.append(
                format_annotation(bound_to, fully_qualified=True))

        # if self.analyzer:
        # 	attr_docs = self.analyzer.find_attr_docs()
        # 	if self.objpath:
        # 		key = ('.'.join(self.objpath[:-1]), self.objpath[-1])
        # 		if key in attr_docs:
        # 			return

        self.add_line('', sourcename)
        self.add_line(' '.join(description).rstrip() + '.',
                      sourcename)  # "   " +
        self.add_line('', sourcename)

        super().add_content(more_content, no_docstring)
Ejemplo n.º 2
0
def get_variable_type(documenter: Documenter) -> str:
    """
	Returns the formatted type annotation for a variable.

	:param documenter:
	"""

    try:
        annotations = get_type_hints(documenter.parent)
    except NameError:
        # Failed to evaluate ForwardRef (maybe TYPE_CHECKING)
        annotations = safe_getattr(documenter.parent, "__annotations__", {})
    except TypeError:
        annotations = {}
    except KeyError:
        # a broken class found (refs: https://github.com/sphinx-doc/sphinx/issues/8084)
        annotations = {}
    except AttributeError:
        # AttributeError is raised on 3.5.2 (fixed by 3.5.3)
        annotations = {}

    if documenter.objpath[-1] in annotations:
        ann = annotations.get(documenter.objpath[-1])

        if isinstance(ann, str):
            return format_annotation(ann.strip("'\""))

        return format_annotation(ann)
    else:
        # Instance attribute
        key = ('.'.join(documenter.objpath[:-1]), documenter.objpath[-1])

        if documenter.analyzer and key in documenter.analyzer.annotations:
            # Forward references will have quotes
            annotation: str = documenter.analyzer.annotations[key].strip("'\"")

            try:
                module_dict = sys.modules[
                    documenter.parent.__module__].__dict__

                if annotation.isidentifier() and annotation in module_dict:
                    return format_annotation(module_dict[annotation])
                else:
                    if sys.version_info < (3, 9):
                        return format_annotation(
                            ForwardRef(annotation)._evaluate(
                                module_dict, module_dict))
                    else:
                        return format_annotation(
                            ForwardRef(annotation)._evaluate(
                                module_dict, module_dict, set()))

            except (NameError, TypeError, ValueError, AttributeError):
                return annotation
        else:
            return ''
Ejemplo n.º 3
0
	def create_body_overloads(self) -> StringList:
		"""
		Create the overloaded implementations for insertion into to the body of the documenter's output.
		"""

		output = StringList()
		formatted_overloads = []

		output.blankline()
		# output.append(":Overloaded Implementations:")
		output.append(":Overloads:")
		output.blankline()

		# Size varies depending on docutils config
		output.indent_type = ' '
		output.indent_size = self.env.app.config.docutils_tab_width  # type: ignore

		if self.analyzer and '.'.join(self.objpath) in self.analyzer.overloads:

			for overload in self.analyzer.overloads.get('.'.join(self.objpath)):  # type: ignore
				overload = self.process_overload_signature(overload)

				buf = [format_annotation(self.object), r"\("]

				for name, param in overload.parameters.items():
					buf.append(f"**{name}**")
					if param.annotation is not Parameter.empty:
						buf.append(r"\: ")
						buf.append(format_annotation(param.annotation))
					if param.default is not Parameter.empty:
						buf.append(" = ")
						buf.append(param.default)
					buf.append(r"\, ")

				if buf[-2][-1] != '`':
					buf[-1] = r" )"
				else:
					buf[-1] = r")"

				if overload.return_annotation is not Parameter.empty:
					buf.append(" -> ")
					buf.append(format_annotation(overload.return_annotation))

				formatted_overloads.append(''.join(buf))

			if len(formatted_overloads) == 1:
				output.append(formatted_overloads[0])
			else:
				for line in formatted_overloads:
					output.append(f"* {line}")
					output.blankline(ensure_single=True)

			return output

		return StringList()
    def add_directive_header(self, sig: str) -> None:
        sourcename = self.get_sourcename()

        if self.doc_as_attr:
            self.directivetype = "attribute"

        Documenter.add_directive_header(self, sig)

        if self.analyzer and '.'.join(self.objpath) in self.analyzer.finals:
            self.add_line("   :final:", sourcename)

        # add inheritance info, if wanted
        if not self.doc_as_attr and self.options.show_inheritance:
            sourcename = self.get_sourcename()
            self.add_line('', sourcename)
            if hasattr(self.object, "__bases__") and len(
                    self.object.__bases__):
                bases = []

                for b in self.object.__bases__:
                    if b is TemporaryDirectory:
                        bases.append(":py:obj:`~tempfile.TemporaryDirectory`")
                    elif b.__module__ in ("__builtin__", "builtins"):
                        bases.append(f':class:`{b.__name__}`')
                    else:
                        bases.append(format_annotation(b))

                self.add_line("   " + _("Bases: %s") % ", ".join(bases),
                              sourcename)
Ejemplo n.º 5
0
    def add_content(self, more_content: Any, no_docstring: bool = False):
        """
		Add the autodocumenter content.

		:param more_content:
		:param no_docstring:
		"""

        name = format_annotation(self.object)
        content = StringList([_("Alias of %s") % name], source='')
        DataDocumenter.add_content(self, content)
Ejemplo n.º 6
0
def _add_generic_bases(documenter: Documenter) -> None:
    """
	Add the generic bases to the output of the given Documenter.

	.. versionadded:: 2.13.0  (undocumented)

	:param documenter:
	"""

    sourcename = documenter.get_sourcename()

    # add inheritance info, if wanted
    fully_qualified = getattr(documenter.env.config,
                              "generic_bases_fully_qualified", False)

    documenter.add_line('', sourcename)
    bases = []

    if (hasattr(documenter.object, "__orig_bases__")
            and len(documenter.object.__orig_bases__)
            and get_origin(documenter.object.__orig_bases__[0]) is
            documenter.object.__bases__[0]):
        # Last condition guards against classes that don't directly subclass a Generic.
        bases = [
            format_annotation(b, fully_qualified)
            for b in documenter.object.__orig_bases__
        ]

    elif hasattr(documenter.object, "__bases__") and len(
            documenter.object.__bases__):
        bases = [
            format_annotation(b, fully_qualified)
            for b in documenter.object.__bases__
        ]

    if bases:
        bases_string = ", ".join(bases).replace("typing_extensions.",
                                                "typing.")
        documenter.add_line("   " + _("Bases: %s") % bases_string, sourcename)
Ejemplo n.º 7
0
    def add_directive_header(self, sig: str):
        """
		Add the directive's header.

		:param sig:
		"""

        sourcename = self.get_sourcename()

        no_value = self.options.get("no-value", False)
        no_type = self.options.get("no-type", False)

        if not self.options.get("annotation", ''):
            ClassLevelDocumenter.add_directive_header(self, sig)

            # data descriptors do not have useful values
            if not no_value and not self._datadescriptor:
                if "value" in self.options:
                    self.add_line("   :value: " + self.options["value"],
                                  sourcename)
                else:
                    with suppress(ValueError):
                        if self.object is not INSTANCEATTR:
                            objrepr = object_description(self.object)
                            self.add_line("   :value: " + objrepr, sourcename)

            self.add_line('', sourcename)

            if not no_type:
                if "type" in self.options:
                    self.add_line(type_template % self.options["type"],
                                  sourcename)
                else:
                    # obtain type annotation for this attribute
                    the_type = get_variable_type(self)
                    if not the_type.strip():
                        obj_type = type(self.object)

                        if obj_type is object:
                            return

                        try:
                            the_type = format_annotation(obj_type)
                        except Exception:
                            return

                    line = type_template % the_type
                    self.add_line(line, sourcename)

        else:
            super().add_directive_header(sig)
Ejemplo n.º 8
0
    def add_directive_header(self, sig: str) -> None:
        """
		Add the directive header.

		:param sig:
		"""

        sourcename = self.get_sourcename()

        if self.doc_as_attr:
            self.directivetype = "attribute"
        Documenter.add_directive_header(self, sig)

        if self.analyzer and '.'.join(self.objpath) in self.analyzer.finals:
            self.add_line("   :final:", sourcename)

        # add inheritance info, if wanted
        if not self.doc_as_attr and self.options.show_inheritance:
            self.add_line('', sourcename)
            bases = []

            if (hasattr(self.object, "__orig_bases__")
                    and len(self.object.__orig_bases__)
                    and get_origin(self.object.__orig_bases__[0]) is
                    self.object.__bases__[0]):
                # Last condition guards against classes that don't directly subclass a Generic.
                bases = [
                    format_annotation(b) for b in self.object.__orig_bases__
                ]

            elif hasattr(self.object, "__bases__") and len(
                    self.object.__bases__):
                bases = [format_annotation(b) for b in self.object.__bases__]

            if bases:
                self.add_line("   " + _("Bases: %s") % ", ".join(bases),
                              sourcename)
Ejemplo n.º 9
0
    def add_directive_header(self, sig: str):
        """
		Add the directive's header.

		:param sig:
		"""

        sourcename = self.get_sourcename()

        no_value = self.options.get("no-value", False)
        no_type = self.options.get("no-type", False)

        if not self.options.get("annotation", ''):
            ModuleLevelDocumenter.add_directive_header(self, sig)

            if not no_value:
                if "value" in self.options:
                    self.add_line(f"   :value: {self.options['value']}",
                                  sourcename)
                else:
                    with suppress(ValueError):
                        if self.object is not UNINITIALIZED_ATTR:
                            objrepr = object_description(self.object)
                            self.add_line(f"   :value: {objrepr}", sourcename)

            self.add_line('', sourcename)

            if not no_type:
                if "type" in self.options:
                    the_type = self.options["type"]
                else:
                    # obtain type annotation for this data
                    the_type = get_variable_type(self)
                    if not the_type.strip():
                        obj_type = type(self.object)

                        if obj_type is object:
                            return

                        try:
                            the_type = format_annotation(obj_type)
                        except Exception:
                            return

                line = type_template % the_type
                self.add_line(line, sourcename)

        else:
            super().add_directive_header(sig)
Ejemplo n.º 10
0
    def sort_members(
        self,
        documenters: List[Tuple[Documenter, bool]],
        order: str,
    ) -> List[Tuple[Documenter, bool]]:
        r"""
		Sort the :class:`typing.NamedTuple`\'s members.

		:param documenters:
		:param order:
		"""

        # The documenters for the fields and methods, in the desired order
        # The fields will be in bysource order regardless of the order option
        documenters = super().sort_members(documenters, order)

        # Size varies depending on docutils config
        a_tab = ' ' * self.env.app.config.docutils_tab_width

        # Mapping of member names to docstrings (as list of strings)
        member_docstrings: Dict[str, List[str]]

        try:
            namedtuple_source = textwrap.dedent(inspect.getsource(self.object))

            # Mapping of member names to docstrings (as list of strings)
            member_docstrings = {
                k[1]: v
                for k, v in ModuleAnalyzer.for_string(
                    namedtuple_source,
                    self.object.__module__).find_attr_docs().items()
            }

        except (TypeError, OSError):
            member_docstrings = {}

        # set sourcename and add content from attribute documentation
        sourcename = self.get_sourcename()

        params, pre_output, post_output = self._get_docstring()

        self.add_line('', sourcename)

        self.add_line(":Fields:", sourcename)
        # TODO: Add xref targets for each field as an attribute
        # TODO: support for default_values
        self.add_line('', sourcename)

        fields = self.object._fields

        for pos, field in enumerate(fields):
            doc: List[str] = ['']
            arg_type: str = ''

            # Prefer doc from class docstring
            if field in params:
                doc, arg_type = params.pop(field).values()  # type: ignore

            # Otherwise use attribute docstring
            if not ''.join(doc).strip() and field in member_docstrings:
                doc = member_docstrings[field]

            # Fallback to namedtuple's default docstring
            if not ''.join(doc).strip():
                doc = [getattr(self.object, field).__doc__]

            # Prefer annotations over docstring types
            type_hints = get_type_hints(self.object)
            if type_hints:
                if field in type_hints:
                    arg_type = format_annotation(type_hints[field])

            field_entry = [f"{a_tab}{pos})", "|nbsp|", f"**{field}**"]
            if arg_type:
                field_entry.append(f"({arg_type}\\)")
            field_entry.append("--")
            field_entry.extend(doc)

            if field_alias_re.match(getattr(self.object, field).__doc__ or ''):
                getattr(self.object, field).__doc__ = ' '.join(doc)

            self.add_line(' '.join(field_entry), sourcename)

        self.add_line('', sourcename)

        for line in post_output:
            self.add_line(line, sourcename)

        self.add_line('', sourcename)

        # Remove documenters corresponding to fields and return the rest
        return [
            d for d in documenters if d[0].name.split('.')[-1] not in fields
        ]
Ejemplo n.º 11
0
def test_format_annotation(annotation: Any, expected: str):
	assert typehints.format_annotation(annotation, True) == expected