doc_utils.py 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. # -*- coding: utf-8 -*-
  2. """
  3. (c) 2014-2020 - Copyright Red Hat Inc
  4. Authors:
  5. Ralph Bean <rbean@redhat.com>
  6. Pierre-Yves Chibon <pingou@pingoured.fr>
  7. Julen Landa Alustiza <jlanda@fedoraproject.org>
  8. """
  9. from __future__ import absolute_import, unicode_literals
  10. import textwrap
  11. import docutils
  12. import docutils.core
  13. import docutils.examples
  14. try:
  15. from markupsafe import escape
  16. except ImportError:
  17. from jinja2 import escape
  18. import kitchen.text.converters as ktc
  19. from markupsafe import Markup
  20. import pagure.lib.encoding_utils
  21. import pagure.lib.query
  22. from pagure.config import config as pagure_config
  23. def modify_rst(rst, view_file_url=None):
  24. """Downgrade some of our rst directives if docutils is too old."""
  25. if view_file_url:
  26. rst = rst.replace(".. image:: ", ".. image:: %s" % view_file_url)
  27. # We catch Exception if we want :-p
  28. # pylint: disable=broad-except
  29. try:
  30. # The rst features we need were introduced in this version
  31. minimum = [0, 9]
  32. version = [int(cpt) for cpt in docutils.__version__.split(".")]
  33. # If we're at or later than that version, no need to downgrade
  34. if version >= minimum:
  35. return rst
  36. except Exception: # pragma: no cover
  37. # If there was some error parsing or comparing versions, run the
  38. # substitutions just to be safe.
  39. pass
  40. # On Fedora this will never work as the docutils version is to recent
  41. # Otherwise, make code-blocks into just literal blocks.
  42. substitutions = {".. code-block:: javascript": "::"} # pragma: no cover
  43. for old, new in substitutions.items(): # pragma: no cover
  44. rst = rst.replace(old, new)
  45. return rst # pragma: no cover
  46. def modify_html(html):
  47. """Perform style substitutions where docutils doesn't do what we want."""
  48. substitutions = {
  49. '<tt class="docutils literal">': "<code>",
  50. "</tt>": "</code>",
  51. "$$FLAG_STATUSES_COMMAS$$": ", ".join(
  52. sorted(pagure_config["FLAG_STATUSES_LABELS"].keys())
  53. ),
  54. "$$FLAG_SUCCESS$$": pagure_config["FLAG_SUCCESS"],
  55. "$$FLAG_FAILURE$$": pagure_config["FLAG_FAILURE"],
  56. "$$FLAG_PENDING$$": pagure_config["FLAG_PENDING"],
  57. }
  58. for old, new in substitutions.items():
  59. html = html.replace(old, new)
  60. return html
  61. def convert_doc(rst_string, view_file_url=None):
  62. """Utility to load an RST file and turn it into fancy HTML."""
  63. rst = modify_rst(rst_string, view_file_url)
  64. overrides = {"report_level": "quiet"}
  65. try:
  66. html = docutils.core.publish_parts(
  67. source=rst, writer_name="html", settings_overrides=overrides
  68. )
  69. except Exception:
  70. return "<pre>%s</pre>" % escape(rst)
  71. else:
  72. html_string = html["html_body"]
  73. html_string = modify_html(html_string)
  74. html_string = Markup(html_string)
  75. return html_string
  76. def convert_readme(content, ext, view_file_url=None):
  77. """Convert the provided content according to the extension of the file
  78. provided.
  79. """
  80. output = pagure.lib.encoding_utils.decode(ktc.to_bytes(content))
  81. safe = False
  82. if ext and ext in [".rst"]:
  83. safe = True
  84. output = convert_doc(output, view_file_url)
  85. elif ext and ext in [".mk", ".md", ".markdown"]:
  86. output = pagure.lib.query.text2markdown(output, readme=True)
  87. safe = True
  88. elif not ext or (ext and ext in [".text", ".txt"]):
  89. safe = True
  90. output = "<pre>%s</pre>" % escape(output)
  91. return output, safe
  92. def load_doc(endpoint):
  93. """Utility to load an RST file and turn it into fancy HTML."""
  94. rst = modify_rst(textwrap.dedent(endpoint.__doc__))
  95. api_docs = docutils.examples.html_body(rst)
  96. api_docs = modify_html(api_docs)
  97. api_docs = Markup(api_docs)
  98. return api_docs
  99. def load_doc_title(endpoint):
  100. """Utility to load docstring title from a method"""
  101. rst = modify_rst(textwrap.dedent(endpoint.__doc__))
  102. parts = docutils.examples.html_parts(rst)
  103. fragment = parts["title"]
  104. return fragment
  105. def load_doc_title_and_name(endpoint):
  106. """Utility to load the HTML doc version and the title from a method."""
  107. result = {
  108. "doc": load_doc(endpoint),
  109. "title": load_doc_title(endpoint),
  110. "name": endpoint.__name__,
  111. }
  112. return result