Example #1
0
 def test_annotations_present(self):
     """Tests to ensure annotations-present is functioning."""
     docstring_no_annotations = self.sample_api_docstring
     self.assertFalse(APIDocstringParser.is_annotated_docstring(
         docstring_no_annotations))
     docstring_annotations = self.sample_api_annotated_docstring
     self.assertTrue(APIDocstringParser.is_annotated_docstring(
         docstring_annotations))
Example #2
0
def describe_actions(handler):
    """Describe the actions that `handler` supports.

    For each action, which could be a CRUD operation or a custom (piggybacked)
    operation, a dict with the following entries is generated:

      method: string, HTTP method.
      name: string, a human-friendly name for the action.
      doc: string, documentation about the action.
      op: string or None, the op parameter to pass in requests for
          non-CRUD/ReSTful requests.
      restful: Indicates if this is a CRUD/ReSTful action.

    """
    from maasserver.api import support  # Circular import.
    getname = support.OperationsResource.crudmap.get
    for signature, function in handler.exports.items():
        http_method, operation = signature
        name = getname(http_method) if operation is None else operation

        ap = APIDocstringParser()
        doc = getdoc(function)

        if doc is not None:
            if APIDocstringParser.is_annotated_docstring(doc):
                # Because the docstring contains annotations, we
                # need to construct a string suitable for output
                # to stdout that matches the style used for
                # non-annotated docstrings in the CLI.
                ap.parse(doc)
                d = ap.get_dict()
                if d['description_title'] != "":
                    doc = d['description_title'] + "\n\n"
                    doc += d['description'] + "\n\n"
                else:
                    doc = d['description'] + "\n\n"

                # Here, we add the params, but we skip params
                # surrounded by curly brackets (e.g. {foo})
                # because these indicate params that appear in
                # the URI (e.g. /zone/{name}/). I.e. positional
                # arguments. These already appear in the CLI
                # help command output so we don't want duplicates.
                for p in d['params']:
                    if p['name'].find("{") == -1 and p['name'].find("}") == -1:
                        required = "Required. "
                        if p['options']['required'] == "false":
                            required = "Optional. "

                        doc += (":param %s: %s%s" %
                                (p['name'], required, p['description']))
                        doc += (":type %s: %s\n " % (p['name'], p['type']))

        yield dict(
            method=http_method, name=name, doc=doc,
            op=operation, restful=(operation is None))
Example #3
0
def render_api_docs():
    """Render ReST documentation for the REST API.


    This module's docstring forms the head of the documentation; details of
    the API methods follow.

    :return: Documentation, in ReST, for the API.
    :rtype: :class:`unicode`
    """
    from maasserver import urls_api as urlconf

    module = sys.modules[__name__]
    output = StringIO()
    line = partial(print, file=output)

    line(getdoc(module))
    line()
    line()
    line("Operations")
    line("``````````")
    line()

    def export_key(export):
        """Return a sortable key for an export.

        `op` is often `None`, which cannot be compared to non-`None`
        operations.
        """
        (http_method, op), function = export
        if op is None:
            return http_method, "", function
        else:
            return http_method, op, function

    annotation_parser = APIDocstringParser()
    templates = APITemplateRenderer()
    resources = find_api_resources(urlconf)
    for doc in generate_api_docs(resources):
        uri_template = doc.resource_uri_template
        exports = doc.handler.exports.items()
        # Derive a section title from the name of the handler class.
        section_name = doc.handler.api_doc_section_name
        line(section_name)
        line("=" * len(section_name))
        # Note:
        # The following dedent is useless in the following situation:
        #
        # def somefunc(foo)
        #     """No indent here
        #
        #     Here, there is an indent, so dedent doesn't do
        #     anything.
        #    """
        #
        # This fixes the problem:
        #
        # def somefunc(foo)
        #     """
        #     Indent here
        #
        #     Now dedent works because the entire docstring appears
        #     to be indented.
        #    """
        #
        # This also works because the dedent version is the same
        # as the non-dented version:
        #
        # def somefunc(foo)
        #     """No indent here"""
        #
        line(dedent(doc.handler.__doc__).strip())
        line()
        line()
        for (http_method, op), function in sorted(exports, key=export_key):
            operation = " op=%s" % op if op is not None else ""
            subsection = "``%s %s%s``" % (http_method, uri_template, operation)
            docstring = getdoc(function)
            if docstring is not None:
                if APIDocstringParser.is_annotated_docstring(docstring):
                    operation = "op=%s" % op if op is not None else ""
                    annotation_parser.parse(
                        docstring, http_method, uri_template, operation
                    )
                    line(
                        templates.apply_template(
                            os.path.dirname(__file__) + "/tmpl-apidoc.rst",
                            annotation_parser,
                        )
                    )
                else:
                    line("%s\n%s\n" % (subsection, "#" * len(subsection)))
                    line()
                    for docline in dedent(docstring).splitlines():
                        if docline.strip() == "":
                            # Blank line.  Don't indent.
                            line()
                        else:
                            # Print documentation line, indented.
                            line(docline)
                line()
            else:
                line("%s\n%s\n" % (subsection, "#" * len(subsection)))
                line()

    line()
    line()
    line(generate_power_types_doc())
    line()
    line()
    line(generate_pod_types_doc())

    return output.getvalue()