doc_utils.py 4.2 KB

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