compat.py 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  1. """
  2. The `compat` module provides support for backwards compatibility with older
  3. versions of Django/Python, and compatibility wrappers around optional packages.
  4. """
  5. from django.conf import settings
  6. from django.views.generic import View
  7. def unicode_http_header(value):
  8. # Coerce HTTP header value to unicode.
  9. if isinstance(value, bytes):
  10. return value.decode('iso-8859-1')
  11. return value
  12. def distinct(queryset, base):
  13. if settings.DATABASES[queryset.db]["ENGINE"] == "django.db.backends.oracle":
  14. # distinct analogue for Oracle users
  15. return base.filter(pk__in=set(queryset.values_list('pk', flat=True)))
  16. return queryset.distinct()
  17. # django.contrib.postgres requires psycopg2
  18. try:
  19. from django.contrib.postgres import fields as postgres_fields
  20. except ImportError:
  21. postgres_fields = None
  22. # coreapi is required for CoreAPI schema generation
  23. try:
  24. import coreapi
  25. except ImportError:
  26. coreapi = None
  27. # uritemplate is required for OpenAPI and CoreAPI schema generation
  28. try:
  29. import uritemplate
  30. except ImportError:
  31. uritemplate = None
  32. # coreschema is optional
  33. try:
  34. import coreschema
  35. except ImportError:
  36. coreschema = None
  37. # pyyaml is optional
  38. try:
  39. import yaml
  40. except ImportError:
  41. yaml = None
  42. # requests is optional
  43. try:
  44. import requests
  45. except ImportError:
  46. requests = None
  47. # PATCH method is not implemented by Django
  48. if 'patch' not in View.http_method_names:
  49. View.http_method_names = View.http_method_names + ['patch']
  50. # Markdown is optional (version 3.0+ required)
  51. try:
  52. import markdown
  53. HEADERID_EXT_PATH = 'markdown.extensions.toc'
  54. LEVEL_PARAM = 'baselevel'
  55. def apply_markdown(text):
  56. """
  57. Simple wrapper around :func:`markdown.markdown` to set the base level
  58. of '#' style headers to <h2>.
  59. """
  60. extensions = [HEADERID_EXT_PATH]
  61. extension_configs = {
  62. HEADERID_EXT_PATH: {
  63. LEVEL_PARAM: '2'
  64. }
  65. }
  66. md = markdown.Markdown(
  67. extensions=extensions, extension_configs=extension_configs
  68. )
  69. md_filter_add_syntax_highlight(md)
  70. return md.convert(text)
  71. except ImportError:
  72. apply_markdown = None
  73. markdown = None
  74. try:
  75. import pygments
  76. from pygments.formatters import HtmlFormatter
  77. from pygments.lexers import TextLexer, get_lexer_by_name
  78. def pygments_highlight(text, lang, style):
  79. lexer = get_lexer_by_name(lang, stripall=False)
  80. formatter = HtmlFormatter(nowrap=True, style=style)
  81. return pygments.highlight(text, lexer, formatter)
  82. def pygments_css(style):
  83. formatter = HtmlFormatter(style=style)
  84. return formatter.get_style_defs('.highlight')
  85. except ImportError:
  86. pygments = None
  87. def pygments_highlight(text, lang, style):
  88. return text
  89. def pygments_css(style):
  90. return None
  91. if markdown is not None and pygments is not None:
  92. # starting from this blogpost and modified to support current markdown extensions API
  93. # https://zerokspot.com/weblog/2008/06/18/syntax-highlighting-in-markdown-with-pygments/
  94. import re
  95. from markdown.preprocessors import Preprocessor
  96. class CodeBlockPreprocessor(Preprocessor):
  97. pattern = re.compile(
  98. r'^\s*``` *([^\n]+)\n(.+?)^\s*```', re.M | re.S)
  99. formatter = HtmlFormatter()
  100. def run(self, lines):
  101. def repl(m):
  102. try:
  103. lexer = get_lexer_by_name(m.group(1))
  104. except (ValueError, NameError):
  105. lexer = TextLexer()
  106. code = m.group(2).replace('\t', ' ')
  107. code = pygments.highlight(code, lexer, self.formatter)
  108. code = code.replace('\n\n', '\n&nbsp;\n').replace('\n', '<br />').replace('\\@', '@')
  109. return '\n\n%s\n\n' % code
  110. ret = self.pattern.sub(repl, "\n".join(lines))
  111. return ret.split("\n")
  112. def md_filter_add_syntax_highlight(md):
  113. md.preprocessors.register(CodeBlockPreprocessor(), 'highlight', 40)
  114. return True
  115. else:
  116. def md_filter_add_syntax_highlight(md):
  117. return False
  118. # `separators` argument to `json.dumps()` differs between 2.x and 3.x
  119. # See: https://bugs.python.org/issue22767
  120. SHORT_SEPARATORS = (',', ':')
  121. LONG_SEPARATORS = (', ', ': ')
  122. INDENT_SEPARATORS = (',', ': ')