2012-09-20 16:06:27 +04:00
|
|
|
"""
|
2012-10-27 13:32:49 +04:00
|
|
|
The `compat` module provides support for backwards compatibility with older
|
2015-08-07 00:51:35 +03:00
|
|
|
versions of Django/Python, and compatibility wrappers around optional packages.
|
2012-09-20 16:06:27 +04:00
|
|
|
"""
|
2022-07-14 15:20:36 +03:00
|
|
|
import django
|
2024-06-23 16:54:08 +03:00
|
|
|
from django.db import models
|
|
|
|
from django.db.models.constants import LOOKUP_SEP
|
|
|
|
from django.db.models.sql.query import Node
|
2015-08-07 00:51:35 +03:00
|
|
|
from django.views.generic import View
|
2015-06-18 16:38:29 +03:00
|
|
|
|
2017-10-16 10:33:31 +03:00
|
|
|
|
2014-12-16 19:37:32 +03:00
|
|
|
def unicode_http_header(value):
|
|
|
|
# Coerce HTTP header value to unicode.
|
2018-09-17 11:39:59 +03:00
|
|
|
if isinstance(value, bytes):
|
2014-12-16 19:37:32 +03:00
|
|
|
return value.decode('iso-8859-1')
|
|
|
|
return value
|
|
|
|
|
|
|
|
|
2017-10-05 21:41:38 +03:00
|
|
|
# django.contrib.postgres requires psycopg2
|
2014-11-06 15:00:30 +03:00
|
|
|
try:
|
2015-01-23 19:27:23 +03:00
|
|
|
from django.contrib.postgres import fields as postgres_fields
|
2014-12-04 05:11:42 +03:00
|
|
|
except ImportError:
|
2015-01-23 19:27:23 +03:00
|
|
|
postgres_fields = None
|
2014-11-06 15:00:30 +03:00
|
|
|
|
|
|
|
|
2019-07-17 16:17:45 +03:00
|
|
|
# coreapi is required for CoreAPI schema generation
|
2016-10-21 17:00:25 +03:00
|
|
|
try:
|
|
|
|
import coreapi
|
2018-01-08 12:45:29 +03:00
|
|
|
except ImportError:
|
2016-10-21 17:00:25 +03:00
|
|
|
coreapi = None
|
2019-07-17 16:17:45 +03:00
|
|
|
|
|
|
|
# uritemplate is required for OpenAPI and CoreAPI schema generation
|
|
|
|
try:
|
|
|
|
import uritemplate
|
|
|
|
except ImportError:
|
2016-10-21 17:00:25 +03:00
|
|
|
uritemplate = None
|
|
|
|
|
|
|
|
|
2017-03-03 18:24:37 +03:00
|
|
|
# coreschema is optional
|
|
|
|
try:
|
|
|
|
import coreschema
|
|
|
|
except ImportError:
|
|
|
|
coreschema = None
|
|
|
|
|
|
|
|
|
2018-10-03 17:28:04 +03:00
|
|
|
# pyyaml is optional
|
|
|
|
try:
|
|
|
|
import yaml
|
|
|
|
except ImportError:
|
|
|
|
yaml = None
|
|
|
|
|
2024-03-19 01:29:02 +03:00
|
|
|
# inflection is optional
|
|
|
|
try:
|
|
|
|
import inflection
|
|
|
|
except ImportError:
|
|
|
|
inflection = None
|
|
|
|
|
2018-10-03 17:28:04 +03:00
|
|
|
|
2016-10-10 15:03:46 +03:00
|
|
|
# requests is optional
|
|
|
|
try:
|
|
|
|
import requests
|
|
|
|
except ImportError:
|
|
|
|
requests = None
|
|
|
|
|
|
|
|
|
2013-04-09 21:22:39 +04:00
|
|
|
# PATCH method is not implemented by Django
|
|
|
|
if 'patch' not in View.http_method_names:
|
|
|
|
View.http_method_names = View.http_method_names + ['patch']
|
|
|
|
|
2012-12-16 22:11:59 +04:00
|
|
|
|
2019-06-01 00:11:58 +03:00
|
|
|
# Markdown is optional (version 3.0+ required)
|
2012-09-20 16:06:27 +04:00
|
|
|
try:
|
|
|
|
import markdown
|
|
|
|
|
2019-05-09 05:34:38 +03:00
|
|
|
HEADERID_EXT_PATH = 'markdown.extensions.toc'
|
|
|
|
LEVEL_PARAM = 'baselevel'
|
2015-08-07 00:51:35 +03:00
|
|
|
|
2012-09-20 16:06:27 +04:00
|
|
|
def apply_markdown(text):
|
|
|
|
"""
|
|
|
|
Simple wrapper around :func:`markdown.markdown` to set the base level
|
|
|
|
of '#' style headers to <h2>.
|
|
|
|
"""
|
2015-11-09 20:47:00 +03:00
|
|
|
extensions = [HEADERID_EXT_PATH]
|
2015-11-05 19:52:31 +03:00
|
|
|
extension_configs = {
|
2015-11-09 20:47:00 +03:00
|
|
|
HEADERID_EXT_PATH: {
|
2016-10-10 15:03:46 +03:00
|
|
|
LEVEL_PARAM: '2'
|
2015-11-05 19:52:31 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
md = markdown.Markdown(
|
|
|
|
extensions=extensions, extension_configs=extension_configs
|
|
|
|
)
|
2017-10-02 12:44:29 +03:00
|
|
|
md_filter_add_syntax_highlight(md)
|
2012-09-20 16:06:27 +04:00
|
|
|
return md.convert(text)
|
|
|
|
except ImportError:
|
|
|
|
apply_markdown = None
|
2017-03-09 19:50:00 +03:00
|
|
|
markdown = None
|
2012-09-20 16:06:27 +04:00
|
|
|
|
|
|
|
|
2017-03-09 17:49:51 +03:00
|
|
|
try:
|
|
|
|
import pygments
|
|
|
|
from pygments.formatters import HtmlFormatter
|
2020-08-17 23:26:56 +03:00
|
|
|
from pygments.lexers import TextLexer, get_lexer_by_name
|
2017-03-09 17:49:51 +03:00
|
|
|
|
|
|
|
def pygments_highlight(text, lang, style):
|
|
|
|
lexer = get_lexer_by_name(lang, stripall=False)
|
|
|
|
formatter = HtmlFormatter(nowrap=True, style=style)
|
|
|
|
return pygments.highlight(text, lexer, formatter)
|
|
|
|
|
|
|
|
def pygments_css(style):
|
|
|
|
formatter = HtmlFormatter(style=style)
|
|
|
|
return formatter.get_style_defs('.highlight')
|
|
|
|
|
|
|
|
except ImportError:
|
|
|
|
pygments = None
|
|
|
|
|
|
|
|
def pygments_highlight(text, lang, style):
|
|
|
|
return text
|
|
|
|
|
|
|
|
def pygments_css(style):
|
|
|
|
return None
|
|
|
|
|
2017-10-02 12:44:29 +03:00
|
|
|
if markdown is not None and pygments is not None:
|
|
|
|
# starting from this blogpost and modified to support current markdown extensions API
|
|
|
|
# https://zerokspot.com/weblog/2008/06/18/syntax-highlighting-in-markdown-with-pygments/
|
|
|
|
|
|
|
|
import re
|
|
|
|
|
2020-08-17 23:26:56 +03:00
|
|
|
from markdown.preprocessors import Preprocessor
|
|
|
|
|
2017-10-02 12:44:29 +03:00
|
|
|
class CodeBlockPreprocessor(Preprocessor):
|
|
|
|
pattern = re.compile(
|
2017-11-10 11:41:03 +03:00
|
|
|
r'^\s*``` *([^\n]+)\n(.+?)^\s*```', re.M | re.S)
|
2017-10-02 12:44:29 +03:00
|
|
|
|
|
|
|
formatter = HtmlFormatter()
|
|
|
|
|
|
|
|
def run(self, lines):
|
|
|
|
def repl(m):
|
|
|
|
try:
|
|
|
|
lexer = get_lexer_by_name(m.group(1))
|
|
|
|
except (ValueError, NameError):
|
|
|
|
lexer = TextLexer()
|
2017-11-10 11:41:03 +03:00
|
|
|
code = m.group(2).replace('\t', ' ')
|
2017-10-02 12:44:29 +03:00
|
|
|
code = pygments.highlight(code, lexer, self.formatter)
|
2017-11-10 11:41:03 +03:00
|
|
|
code = code.replace('\n\n', '\n \n').replace('\n', '<br />').replace('\\@', '@')
|
2017-10-02 12:44:29 +03:00
|
|
|
return '\n\n%s\n\n' % code
|
|
|
|
ret = self.pattern.sub(repl, "\n".join(lines))
|
|
|
|
return ret.split("\n")
|
|
|
|
|
|
|
|
def md_filter_add_syntax_highlight(md):
|
2019-06-01 00:11:58 +03:00
|
|
|
md.preprocessors.register(CodeBlockPreprocessor(), 'highlight', 40)
|
2017-10-02 12:44:29 +03:00
|
|
|
return True
|
|
|
|
else:
|
|
|
|
def md_filter_add_syntax_highlight(md):
|
|
|
|
return False
|
2017-03-22 07:01:07 +03:00
|
|
|
|
|
|
|
|
2024-01-11 15:34:46 +03:00
|
|
|
if django.VERSION >= (5, 1):
|
|
|
|
# Django 5.1+: use the stock ip_address_validators function
|
|
|
|
# Note: Before Django 5.1, ip_address_validators returns a tuple containing
|
|
|
|
# 1) the list of validators and 2) the error message. Starting from
|
|
|
|
# Django 5.1 ip_address_validators only returns the list of validators
|
|
|
|
from django.core.validators import ip_address_validators
|
2024-06-23 16:54:08 +03:00
|
|
|
|
|
|
|
def get_referenced_base_fields_from_q(q):
|
|
|
|
return q.referenced_base_fields
|
|
|
|
|
2024-01-11 15:34:46 +03:00
|
|
|
else:
|
|
|
|
# Django <= 5.1: create a compatibility shim for ip_address_validators
|
|
|
|
from django.core.validators import \
|
|
|
|
ip_address_validators as _ip_address_validators
|
|
|
|
|
|
|
|
def ip_address_validators(protocol, unpack_ipv4):
|
|
|
|
return _ip_address_validators(protocol, unpack_ipv4)[0]
|
|
|
|
|
2024-06-23 16:54:08 +03:00
|
|
|
# Django < 5.1: create a compatibility shim for Q.referenced_base_fields
|
|
|
|
# https://github.com/django/django/blob/5.1a1/django/db/models/query_utils.py#L179
|
|
|
|
def _get_paths_from_expression(expr):
|
|
|
|
if isinstance(expr, models.F):
|
|
|
|
yield expr.name
|
|
|
|
elif hasattr(expr, "flatten"):
|
|
|
|
for child in expr.flatten():
|
|
|
|
if isinstance(child, models.F):
|
|
|
|
yield child.name
|
|
|
|
elif isinstance(child, models.Q):
|
|
|
|
yield from _get_children_from_q(child)
|
|
|
|
|
|
|
|
def _get_children_from_q(q):
|
|
|
|
for child in q.children:
|
|
|
|
if isinstance(child, Node):
|
|
|
|
yield from _get_children_from_q(child)
|
|
|
|
elif isinstance(child, tuple):
|
|
|
|
lhs, rhs = child
|
|
|
|
yield lhs
|
|
|
|
if hasattr(rhs, "resolve_expression"):
|
|
|
|
yield from _get_paths_from_expression(rhs)
|
|
|
|
elif hasattr(child, "resolve_expression"):
|
|
|
|
yield from _get_paths_from_expression(child)
|
|
|
|
|
|
|
|
def get_referenced_base_fields_from_q(q):
|
|
|
|
return {
|
|
|
|
child.split(LOOKUP_SEP, 1)[0] for child in _get_children_from_q(q)
|
|
|
|
}
|
|
|
|
|
2024-01-11 15:34:46 +03:00
|
|
|
|
2014-12-04 04:50:25 +03:00
|
|
|
# `separators` argument to `json.dumps()` differs between 2.x and 3.x
|
2018-01-08 18:22:32 +03:00
|
|
|
# See: https://bugs.python.org/issue22767
|
2019-04-30 18:53:44 +03:00
|
|
|
SHORT_SEPARATORS = (',', ':')
|
|
|
|
LONG_SEPARATORS = (', ', ': ')
|
|
|
|
INDENT_SEPARATORS = (',', ': ')
|