def test_indent_type(self):
        sl = StringList(['', '', "hello", "world", '', '', '', "1234"])

        assert sl.indent_type == '\t'

        with pytest.raises(ValueError, match="'type' cannot an empty string."):
            sl.indent_type = ''

        assert sl.indent_type == '\t'

        sl.indent_type = ' '
        assert sl.indent_type == ' '

        sl.set_indent_type('\t')
        assert sl.indent_type == '\t'

        sl.set_indent_type(' ')
        assert sl.indent_type == ' '

        with pytest.raises(ValueError, match="'type' cannot an empty string."):
            sl.set_indent_type('')

        assert sl.indent_type == ' '

        sl.set_indent_type()
        assert sl.indent_type == '\t'
Exemple #2
0
		def __repr__(self) -> str:
			buf = StringList()
			buf.indent_type = "    "
			buf.append(f"{self.__class__.__module__}.{self.__class__.__qualname__}(")

			with buf.with_indent_size(1):
				for attrib in attr.fields(self.__class__):
					value = getattr(self, attrib.name)

					if isinstance(value, datetime):
						buf.append(f"{attrib.name}={value.isoformat()!r},")

					elif isinstance(value, str):
						lines = textwrap.wrap(value, width=80 - len(attrib.name) - 1)
						buf.append(f"{attrib.name}={lines.pop(0)!r}")

						for line in lines:
							buf.append(' ' * len(attrib.name) + ' ' + repr(line))

						buf[-1] = f"{buf[-1][len(buf.indent_type) * buf.indent_size:]},"
					elif value is None:
						buf.append(f"{attrib.name}=None,")
					else:
						buf.append(f"{attrib.name}={prettyprinter.pformat(value)},")

			buf.append(')')
			return str(buf)
Exemple #3
0
def format_signature(obj: Union[type, FunctionType]) -> StringList:
    """
	Format the signature of the given object, for insertion into the highlight panel.

	:param obj:

	:return: A list of reStructuredText lines.
	"""

    with monkeypatcher():
        obj.__annotations__ = get_type_hints(obj)

    signature: inspect.Signature = inspect.signature(obj)

    buf = StringList(".. parsed-literal::")
    buf.blankline()
    buf.indent_type = "    "
    buf.indent_size = 1

    if signature.return_annotation is not inspect.Signature.empty and not isinstance(
            obj, type):
        return_annotation = f") -> {format_annotation(signature.return_annotation)}"
    else:
        return_annotation = f")"

    total_length = len(obj.__name__) + len(return_annotation)

    arguments_buf: DelimitedList[str] = DelimitedList()

    param: inspect.Parameter
    for param in signature.parameters.values():
        arguments_buf.append(f"{format_parameter(param)}")
        total_length += len(arguments_buf[-1])

    if total_length <= 60:
        signature_buf = StringList(''.join(
            [f"{obj.__name__}(", f"{arguments_buf:, }", return_annotation]))
    else:
        signature_buf = StringList([f"{obj.__name__}("])
        signature_buf.indent_type = "  "
        with signature_buf.with_indent_size(1):
            signature_buf.extend(
                [f"{arguments_buf:,\n}" + ',', return_annotation])

    buf.extend(signature_buf)

    return buf
Exemple #4
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 make_node_content(
    requirements: List[str],
    package_name: str,
    extra: str,
    scope: str = "module",
) -> str:
    """
	Create the content of an extras_require node.

	:param requirements: List of additional :pep:`508` requirements.
	:param package_name: The name of the module/package on PyPI.
	:param extra: The name of the "extra".
	:param scope: The scope of the additional requirements, e.g. ``"module"``, ``"package"``.

	:return: The content of an extras_require node.
	"""

    content = StringList(convert_indents=True)
    content.indent_type = ' ' * 4
    content.append(
        f"This {scope} has the following additional {_requirement(len(requirements))}:"
    )
    content.blankline(ensure_single=True)

    with content.with_indent_size(content.indent_size + 1):
        content.append(".. code-block:: text")
        content.blankline(ensure_single=True)

        with content.with_indent_size(content.indent_size + 1):
            content.extend(requirements)

    content.blankline(ensure_single=True)

    if len(requirements) > 1:
        content.append("These can be installed as follows:")
    else:
        content.append("This can be installed as follows:")

    content.blankline(ensure_single=True)

    with content.with_indent_size(content.indent_size + 1):
        content.append(".. prompt:: bash")
        content.blankline(ensure_single=True)

        with content.with_indent_size(content.indent_size + 1):
            content.append(f"python -m pip install {package_name}[{extra}]")

    content.blankline(ensure_single=True)
    content.blankline()

    return str(content)
Exemple #6
0
    def run_generic(self) -> List[nodes.Node]:
        """
		Generate generic reStructuredText output.
		"""

        content = StringList()
        content.indent_type = ' '

        for obj_name in get_random_sample(sorted(set(self.content))):
            if self.options.get("module", '') and obj_name.startswith('.'):
                obj_name = obj_name.replace('.', f"{self.options['module']}.",
                                            1)

            name_parts = obj_name.split('.')
            module = import_module('.'.join(name_parts[:-1]))
            obj = getattr(module, name_parts[-1])

            if isinstance(obj, FunctionType):
                content.append(
                    f"* :func:`{'.'.join(name_parts[1:])}() <.{obj_name}>`")
            elif isinstance(obj, type):
                content.append(
                    f"* :class:`{'.'.join(name_parts[1:])} <.{obj_name}>`")
            else:
                content.append(
                    f"* :py:obj:`{'.'.join(name_parts[1:])} <.{obj_name}>`")

            with content.with_indent_size(2):
                content.blankline()
                content.append(format_signature(obj))
                content.blankline()
                content.append(
                    inspect.cleandoc(obj.__doc__ or '').split("\n\n")[0])
                content.blankline()

        targetid = f'sphinx-highlights-{self.env.new_serialno("sphinx-highlights"):d}'
        targetnode = nodes.target('', '', ids=[targetid])

        view = ViewList(content)
        body_node = nodes.container(rawsource=str(content))
        self.state.nested_parse(view, self.content_offset,
                                body_node)  # type: ignore

        sphinx_highlights_purger.add_node(self.env, body_node, targetnode,
                                          self.lineno)

        return [targetnode, body_node]
def make_rest_example(
    options: Dict[str, Any],
    env: sphinx.environment.BuildEnvironment,
    content: Sequence[str],
) -> List[str]:
    """
	Make the content of a reST Example node.

	:param options:
	:param content: The user-provided content of the directive.
	"""

    output = StringList(".. container:: rest-example")
    output.indent_type = ' ' * env.config.docutils_tab_width

    output.blankline()

    with output.with_indent_size(1):

        output.append(".. code-block:: rest")

        with output.with_indent_size(2):
            for option, value in options.items():
                if value is None:
                    output.append(f":{option}:")
                else:
                    output.append(f":{option}: {value}")

            output.blankline()

            for line in content:
                output.append(line)

        output.blankline(ensure_single=True)

        for line in content:
            output.append(line)

        output.blankline(ensure_single=True)

    return list(output)
Exemple #8
0
def add_autosummary(self):
	"""
	Add the :rst:dir:`autosummary` table of this documenter.
	"""

	if not self.options.get("autosummary", False):
		return

	content = StringList()
	content.indent_type = ' ' * 4
	sourcename = self.get_sourcename()
	grouped_documenters = self.get_grouped_documenters()

	for section, documenters in grouped_documenters.items():
		if not self.options.get("autosummary-no-titles", False):
			content.append(f"**{section}:**")

		content.blankline(ensure_single=True)

		content.append(".. autosummary::")
		content.blankline(ensure_single=True)

		member_order = get_first_matching(
				lambda x: x != "groupwise",
				[
						self.options.get("member-order", ''),
						self.env.config.autodocsumm_member_order,
						self.env.config.autodoc_member_order,
						],
				default="alphabetical",
				)

		with content.with_indent_size(content.indent_size + 1):
			for documenter, _ in self.sort_members(documenters, member_order):
				content.append(f"~{documenter.fullname}")

		content.blankline()

	for line in content:
		self.add_line(line, sourcename)
Exemple #9
0
    def make_documentation(cls):
        """
		Returns the reStructuredText documentation for the :class:`~.ConfigVar`.
		"""

        docstring = cls.__doc__ or ''
        docstring = (indent(dedent(docstring), tab))

        if not docstring.startswith('\n'):
            docstring = '\n' + docstring

        buf = StringList()
        buf.indent_type = "    "
        buf.blankline(ensure_single=True)
        buf.append(f".. conf:: {cls.__name__}")
        buf.append(docstring)
        buf.blankline()

        buf.indent_size += 1

        buf.append(f"**Required**: {'yes' if cls.required else 'no'}")
        buf.blankline()
        buf.blankline()

        if not cls.required:
            if cls.default == []:
                buf.append("**Default**: [ ]")
            elif cls.default == {}:
                buf.append("**Default**: { }")
            elif isinstance(cls.default, Callable):  # type: ignore
                buf.append(
                    f"**Default**: The value of :conf:`{cls.default.__name__}`"
                )
            elif isinstance(cls.default, bool):
                buf.append(f"**Default**: :py:obj:`{cls.default}`")
            elif isinstance(cls.default, str):
                if cls.default == '':
                    buf.append("**Default**: <blank>")
                else:
                    buf.append(f"**Default**: ``{cls.default}``")
            else:
                buf.append(f"**Default**: {cls.default}")

            buf.blankline()
            buf.blankline()

        buf.append(f"**Type**: {get_yaml_type(cls.dtype)}")

        if is_literal_type(cls.dtype):
            valid_values = ", ".join(f"``{x}``" for x in cls.dtype.__args__)
            buf.blankline()
            buf.blankline()
            buf.append(f"**Allowed values**: {valid_values}")
        elif hasattr(cls.dtype, "__args__") and is_literal_type(
                cls.dtype.__args__[0]):
            valid_values = ", ".join(f"``{x}``"
                                     for x in cls.dtype.__args__[0].__args__)
            buf.blankline()
            buf.blankline()
            buf.append(f"**Allowed values**: {valid_values}")

        buf.indent_size -= 1

        return str(buf)
Exemple #10
0
def walk_attrs(module: ModuleType, attr_name, converter=Converter()) -> str:
    """
	Create stubs for given class, including all attributes.

	:param module:
	:param attr_name:
	:param converter:

	:return:
	"""

    buf = StringList(convert_indents=True)
    buf.indent_type = "    "

    if not is_dunder(attr_name):
        obj = getattr(module, attr_name)

        # TODO: case where obj is not a class
        if not isinstance(obj, FunctionType):
            bases = []
            for base in obj.__bases__:
                if base not in {System.Object, object}:
                    if base.__name__ in converter.type_mapping:
                        bases.append(converter.type_mapping[base.__name__])
                    else:
                        bases.append(base.__name__)

            bases = list(filter(lambda x: x is Any, bases))

            if bases:
                buf.append(f"class {attr_name}({', '.join(bases)}):\n")
            else:
                buf.append(f"class {attr_name}:\n")

            for child_attr_name in get_child_attrs(obj):
                try:
                    child_obj = getattr(obj, child_attr_name)
                except TypeError as e:
                    if str(e) in {
                            "instance property must be accessed through a class instance",
                            "property cannot be read",
                    }:

                        make_property(buf, child_attr_name)
                        continue

                    elif str(
                            e
                    ) == "instance attribute must be accessed through a class instance":
                        print(
                            f"{e.__class__.__name__}: '{e}' occurred for {attr_name}.{child_attr_name}"
                        )
                        continue

                    else:
                        raise e

                # TODO: if isinstance(child_obj, FunctionType):

                return_type, arguments = get_signature(child_obj,
                                                       child_attr_name,
                                                       converter)

                with buf.with_indent_size(buf.indent_size + 1):

                    if arguments is not None and arguments:
                        signature = []

                        for idx, argument in enumerate(arguments.split(", ")):
                            signature.append(
                                f"{'_' * (idx + 1)}: {converter.convert_type(argument)}"
                            )

                        line = f"def {child_attr_name}(self, {', '.join(signature)}) -> {return_type}: ..."

                        if len(line) > 88:
                            buf.blankline(ensure_single=True)
                            buf.append(f"def {child_attr_name}(")

                            with buf.with_indent_size(buf.indent_size + 2):
                                buf.append("self,")
                                for line in signature:
                                    buf.append(f"{line},")
                                buf.append(f") -> {return_type}: ...\n")
                        else:
                            buf.append(line)

                    elif arguments is None:
                        buf.append(
                            f"def {child_attr_name}(self, *args, **kwargs) -> {return_type}: ..."
                        )

                    elif not arguments:
                        # i.e. takes no arguments
                        buf.append(
                            f"def {child_attr_name}(self) -> {return_type}: ..."
                        )

        buf.blankline(ensure_single=True)
        return str(buf)

    return ''
Exemple #11
0
    def dump_sections(self, o, sup) -> Tuple[str, Mapping]:
        """
		Serialise a dictionary into TOML sections.

		:param o:
		:param sup:
		"""

        retstr = ''

        if sup != '' and sup[-1] != '.':
            sup += '.'

        arraystr = StringList()
        arraystr.indent_type = ' ' * 4

        retdict = self._dict()  # type: ignore[attr-defined]

        for section in o:
            section = str(section)
            qsection = section

            if not _section_disallowed_re.match(section):
                qsection = _dump_str(section)

            if not isinstance(o[section], Mapping):
                arrayoftables = False

                if isinstance(o[section], Sequence):
                    for a in o[section]:
                        if isinstance(a, Mapping):
                            arrayoftables = True

                if arrayoftables:
                    for a in o[section]:
                        arraytabstr = '\n'

                        # if arraystr:
                        arraystr.blankline(ensure_single=True)

                        arraystr.append(f"[[{sup}{qsection}]]")

                        s, d = self.dump_sections(a, sup + qsection)

                        if s:
                            if s[0] == '[':
                                arraytabstr += s
                            else:
                                arraystr.append(s)

                        while d:  # pragma: no cover
                            newd = self._dict()  # type: ignore[attr-defined]

                            for dsec in d:
                                s1, d1 = self.dump_sections(
                                    d[dsec], f"{sup}{qsection}.{dsec}")

                                if s1:
                                    arraytabstr += f'[{sup}{qsection}.{dsec}]\n{s1}'

                                for s1 in d1:
                                    newd[f"{dsec}.{s1}"] = d1[s1]

                            d = newd

                        arraystr.append(arraytabstr)
                else:
                    if o[section] is not None:
                        retstr += f"{qsection} = {self.dump_value(o[section])}\n"

            elif self.preserve and isinstance(o[section], InlineTableDict):
                retstr += f"{qsection} = {self.dump_inline_table(o[section])}"

            else:
                retdict[qsection] = o[section]

        retstr += str(arraystr)

        return retstr.lstrip(), retdict
Exemple #12
0
    def run_html(self) -> List[nodes.Node]:
        """
		Generate output for ``HTML`` builders.
		"""

        # colours = itertools.cycle(self.delimited_get("colours", "#6ab0de"))
        colours = itertools.cycle(
            get_random_sample(self.delimited_get("colours", "blue")))
        classes = list(
            self.delimited_get(
                "class",
                "col-xl-6 col-lg-6 col-md-12 col-sm-12 col-xs-12 p-2"))

        content = StringList()
        content.append(".. panels::")
        content.indent_type = "    "
        content.indent_size = 1
        content.append(":container: container-xl pb-4 sphinx-highlights")
        content.blankline()

        for obj_name in get_random_sample(sorted(set(self.content))):
            if self.options.get("module", '') and obj_name.startswith('.'):
                obj_name = obj_name.replace('.', f"{self.options['module']}.",
                                            1)

            name_parts = obj_name.split('.')
            module = import_module('.'.join(name_parts[:-1]))
            obj = getattr(module, name_parts[-1])

            colour_class = f"highlight-{next(colours)}"
            content.append(
                f":column: {DelimitedList((*classes, colour_class)): }")

            if isinstance(obj, FunctionType):
                content.append(
                    f":func:`{'.'.join(name_parts[1:])}() <.{obj_name}>`")
            elif isinstance(obj, type):
                content.append(
                    f":class:`{'.'.join(name_parts[1:])} <.{obj_name}>`")
            else:
                content.append(
                    f":py:obj:`{'.'.join(name_parts[1:])} <.{obj_name}>`")

            content.append('^' * len(content[-1]))
            content.blankline()
            # content.append(f".. function:: {name_parts[-1]} {stringify_signature(inspect.signature(obj))}")
            content.append(format_signature(obj))
            content.blankline()
            content.append(
                inspect.cleandoc(obj.__doc__ or '').split("\n\n")[0])
            content.blankline()
            content.append(f"See more in :mod:`{module.__name__}`.")
            content.append("---")

        content.pop(-1)

        targetid = f'sphinx-highlights-{self.env.new_serialno("sphinx-highlights"):d}'
        targetnode = nodes.target('', '', ids=[targetid])

        view = ViewList(content)
        body_node = nodes.paragraph(rawsource=str(content))
        self.state.nested_parse(view, self.content_offset,
                                body_node)  # type: ignore

        sphinx_highlights_purger.add_node(self.env, body_node, targetnode,
                                          self.lineno)

        return [targetnode, body_node]