diff --git a/debian/control b/debian/control --- a/debian/control +++ b/debian/control @@ -8,7 +8,7 @@ libjs-jquery-flot, libjs-jquery-flot-tooltip, python3-all, - python3-flask-api, + python3-docutils, python3-flask-testing, python3-nose, python3-setuptools, diff --git a/swh/web/ui/apidoc.py b/swh/web/ui/apidoc.py --- a/swh/web/ui/apidoc.py +++ b/swh/web/ui/apidoc.py @@ -143,15 +143,6 @@ 'doc': argdoc, 'default': default } - self.req_args = ['call_args', 'doc_route'] - - def check_args(self, kwargs): - missing = [arg for arg in self.req_args if arg not in kwargs] - if len(missing) > 0: - message = 'Expected keyword args %s, missing %s.' % ( - ', '.join(self.req_args), - ', '.join(missing)) - raise SWHAPIDocException(message) def __call__(self, f): @wraps(f) diff --git a/swh/web/ui/renderers.py b/swh/web/ui/renderers.py --- a/swh/web/ui/renderers.py +++ b/swh/web/ui/renderers.py @@ -6,6 +6,7 @@ import re import yaml import json +import sys from docutils.core import publish_parts from docutils.writers.html4css1 import Writer, HTMLTranslator @@ -119,13 +120,6 @@ self.body_prefix = [] self.body_suffix = [] - # disable blockquotes to ignore indentation issue with docstrings - def visit_block_quote(self, node): - pass - - def depart_block_quote(self, node): - pass - def visit_bullet_list(self, node): self.context.append((self.compact_simple, self.compact_p)) self.compact_p = None @@ -141,6 +135,31 @@ Utility function to htmlize reST-formatted documentation in browsable api. """ + + def trim(docstring): + """Correctly trim triple-quoted docstrings, taking into account + first-line indentation inconsistency. + Source: https://www.python.org/dev/peps/pep-0257/#handling-docstring-indentation # noqa + """ + if not docstring: + return '' + lines = docstring.expandtabs().splitlines() + indent = sys.maxsize + for line in lines[1:]: + stripped = line.lstrip() + if stripped: + indent = min(indent, len(line) - len(stripped)) + trimmed = [lines[0].strip()] + if indent < sys.maxsize: + for line in lines[1:]: + trimmed.append(line[indent:].rstrip()) + while trimmed and not trimmed[-1]: + trimmed.pop() + while trimmed and not trimmed[0]: + trimmed.pop(0) + return '\n'.join(trimmed) + + docstring = trim(docstring) return publish_parts(docstring, writer=DOCSTRING_WRITER)['html_body'] diff --git a/swh/web/ui/templates/api.html b/swh/web/ui/templates/api.html --- a/swh/web/ui/templates/api.html +++ b/swh/web/ui/templates/api.html @@ -5,7 +5,7 @@ {% for route, doc in doc_routes %}

{{ route }}

- {{ doc }} + {% autoescape off %}{{ doc | safe_docstring_display }}{% endautoescape %}

{% endfor %} diff --git a/swh/web/ui/templates/apidoc.html b/swh/web/ui/templates/apidoc.html --- a/swh/web/ui/templates/apidoc.html +++ b/swh/web/ui/templates/apidoc.html @@ -13,7 +13,7 @@

Request

{{ request.method }} {{ request.url }}

Result

-
 {% autoescape off %} {{ response_data | urlize_api_links }} {% endautoescape %} 
+
{% autoescape off %}{{ response_data | urlize_api_links }}{% endautoescape %}
{% endif %}
diff --git a/swh/web/ui/tests/test_renderers.py b/swh/web/ui/tests/test_renderers.py --- a/swh/web/ui/tests/test_renderers.py +++ b/swh/web/ui/tests/test_renderers.py @@ -214,9 +214,9 @@ # update api link with html links content with links docstring = """This is my list header: - - Here is item 1, with a continuation - line right here - - Here is item 2 + - Here is item 1, with a continuation + line right here + - Here is item 2 Here is something that is not part of the list""" diff --git a/swh/web/ui/views/browse.py b/swh/web/ui/views/browse.py --- a/swh/web/ui/views/browse.py +++ b/swh/web/ui/views/browse.py @@ -6,9 +6,6 @@ from encodings.aliases import aliases from flask import render_template, request, url_for, redirect -from flask.ext.api.decorators import set_renderers -from flask.ext.api.renderers import HTMLRenderer - from swh.core.hashutil import ALGORITHMS from .. import service, utils, apidoc from ..exc import BadInputExc, NotFoundExc @@ -19,7 +16,6 @@ @app.route('/api/1/doc/') -@set_renderers(HTMLRenderer) def api_doc(): """Render the API's documentation. """ @@ -32,7 +28,6 @@ @app.route('/search/', methods=['GET', 'POST']) -@set_renderers(HTMLRenderer) def search(): """Search for hashes in swh-storage. @@ -87,7 +82,6 @@ @app.route('/browse/content//') -@set_renderers(HTMLRenderer) def browse_content(q): """Given a hash and a checksum, display the content's meta-data. @@ -175,7 +169,6 @@ # @app.route('/browse/content//origin/') -@set_renderers(HTMLRenderer) def browse_content_with_origin(q): """Show content information. @@ -205,7 +198,6 @@ @app.route('/browse/directory//') @app.route('/browse/directory///') -@set_renderers(HTMLRenderer) def browse_directory(sha1_git, path=None): """Show directory information. @@ -250,7 +242,6 @@ @app.route('/browse/origin//') -@set_renderers(HTMLRenderer) def browse_origin(origin_id): """Browse origin with id id. @@ -273,7 +264,6 @@ @app.route('/browse/person//') -@set_renderers(HTMLRenderer) def browse_person(person_id): """Browse person with id id. @@ -291,7 +281,6 @@ @app.route('/browse/release//') -@set_renderers(HTMLRenderer) def browse_release(sha1_git): """Browse release with sha1_git. @@ -311,7 +300,6 @@ @app.route('/browse/revision//') @app.route('/browse/revision//prev//') -@set_renderers(HTMLRenderer) def browse_revision(sha1_git, prev_sha1s=None): """Browse the revision with git SHA1 sha1_git_cur, while optionally keeping the context from which we came as a list of previous (i.e. later) @@ -351,7 +339,6 @@ @app.route('/browse/revision//log/') @app.route('/browse/revision//prev//log/') -@set_renderers(HTMLRenderer) def browse_revision_log(sha1_git, prev_sha1s=None): """Browse revision with sha1_git's log. If the navigation path through the commit tree is specified, we intersect the earliest revision's log with the @@ -392,7 +379,6 @@ @app.route('/browse/revision' '/origin/' '/ts//log/') -@set_renderers(HTMLRenderer) def browse_revision_log_by(origin_id, branch_name='refs/heads/master', timestamp=None): @@ -427,7 +413,6 @@ @app.route('/browse/revision//prev//') -@set_renderers(HTMLRenderer) def browse_with_rev_context(sha1_git_cur, sha1s): """Browse the revision with git SHA1 sha1_git_cur, while keeping the context from which we came as a list of previous (i.e. later) revisions' sha1s. @@ -459,7 +444,6 @@ @app.route('/browse/revision//history//') -@set_renderers(HTMLRenderer) def browse_revision_history(sha1_git_root, sha1_git): """Display information about revision sha1_git, limited to the sub-graph of all transitive parents of sha1_git_root. @@ -501,7 +485,6 @@ @app.route('/browse/revision//directory/') @app.route('/browse/revision//directory//') -@set_renderers(HTMLRenderer) def browse_revision_directory(sha1_git, path=None): """Browse directory from revision with sha1_git. @@ -538,7 +521,6 @@ @app.route('/browse/revision/' '/history/' '/directory//') -@set_renderers(HTMLRenderer) def browse_revision_history_directory(sha1_git_root, sha1_git, path=None): """Return information about directory pointed to by the revision defined as: revision sha1_git, limited to the sub-graph of all @@ -639,7 +621,6 @@ '/ts/' '/history/' '/directory//') -@set_renderers(HTMLRenderer) def browse_directory_through_revision_with_origin_history( origin_id, branch_name="refs/heads/master", @@ -690,7 +671,6 @@ @app.route('/browse/revision' '/origin/' '/ts//') -@set_renderers(HTMLRenderer) def browse_revision_with_origin(origin_id, branch_name="refs/heads/master", ts=None): @@ -739,7 +719,6 @@ '/branch/' '/ts/' '/history//') -@set_renderers(HTMLRenderer) def browse_revision_history_through_origin(origin_id, branch_name='refs/heads/master', ts=None, @@ -806,7 +785,6 @@ '/branch/' '/ts/' '/directory//') -@set_renderers(HTMLRenderer) def browse_revision_directory_through_origin(origin_id, branch_name='refs/heads/master', ts=None, @@ -845,7 +823,6 @@ @app.route('/browse/entity/') @app.route('/browse/entity//') -@set_renderers(HTMLRenderer) def browse_entity(uuid): env = {'entities': [], 'message': None} diff --git a/swh/web/ui/views/main.py b/swh/web/ui/views/main.py --- a/swh/web/ui/views/main.py +++ b/swh/web/ui/views/main.py @@ -6,12 +6,9 @@ import flask from ..main import app -from flask.ext.api.decorators import set_renderers -from flask.ext.api.renderers import HTMLRenderer @app.route('/') -@set_renderers(HTMLRenderer) def homepage(): """Home page @@ -22,6 +19,5 @@ @app.route('/about/') -@set_renderers(HTMLRenderer) def about(): return flask.render_template('about.html')