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
|
|
|
"""
|
2013-06-27 00:18:13 +04:00
|
|
|
|
2012-11-22 03:20:49 +04:00
|
|
|
from __future__ import unicode_literals
|
2015-06-18 16:38:29 +03:00
|
|
|
|
2016-06-02 16:39:10 +03:00
|
|
|
import inspect
|
|
|
|
|
2015-06-18 16:38:29 +03:00
|
|
|
import django
|
2016-06-02 16:39:10 +03:00
|
|
|
from django.apps import apps
|
2014-12-16 19:37:32 +03:00
|
|
|
from django.conf import settings
|
2017-11-10 11:41:03 +03:00
|
|
|
from django.core import validators
|
|
|
|
from django.core.exceptions import ImproperlyConfigured
|
2017-11-14 11:55:59 +03:00
|
|
|
from django.db import models
|
2015-06-25 23:55:51 +03:00
|
|
|
from django.utils import six
|
2015-08-07 00:51:35 +03:00
|
|
|
from django.views.generic import View
|
2015-06-18 16:38:29 +03:00
|
|
|
|
2016-10-10 15:03:46 +03:00
|
|
|
try:
|
2017-11-10 11:41:03 +03:00
|
|
|
from django.urls import ( # noqa
|
|
|
|
URLPattern,
|
|
|
|
URLResolver,
|
2016-10-10 15:03:46 +03:00
|
|
|
)
|
|
|
|
except ImportError:
|
2017-11-10 11:41:03 +03:00
|
|
|
# Will be removed in Django 2.0
|
|
|
|
from django.urls import ( # noqa
|
|
|
|
RegexURLPattern as URLPattern,
|
|
|
|
RegexURLResolver as URLResolver,
|
2016-10-10 15:03:46 +03:00
|
|
|
)
|
|
|
|
|
|
|
|
|
2017-10-16 10:33:31 +03:00
|
|
|
def get_regex_pattern(urlpattern):
|
|
|
|
if hasattr(urlpattern, 'pattern'):
|
|
|
|
# Django 2.0
|
|
|
|
return urlpattern.pattern.regex.pattern
|
|
|
|
else:
|
|
|
|
# Django < 2.0
|
|
|
|
return urlpattern.regex.pattern
|
|
|
|
|
|
|
|
|
|
|
|
def make_url_resolver(regex, urlpatterns):
|
|
|
|
try:
|
|
|
|
# Django 2.0
|
|
|
|
from django.urls.resolvers import RegexPattern
|
2017-11-10 11:41:03 +03:00
|
|
|
return URLResolver(RegexPattern(regex), urlpatterns)
|
2017-10-16 10:33:31 +03:00
|
|
|
|
|
|
|
except ImportError:
|
|
|
|
# Django < 2.0
|
2017-11-10 11:41:03 +03:00
|
|
|
return URLResolver(regex, urlpatterns)
|
2017-10-16 10:33:31 +03:00
|
|
|
|
|
|
|
|
2014-12-15 14:55:17 +03:00
|
|
|
def unicode_repr(instance):
|
|
|
|
# Get the repr of an instance, but ensure it is a unicode string
|
|
|
|
# on both python 3 (already the case) and 2 (not the case).
|
|
|
|
if six.PY2:
|
2015-01-21 16:03:37 +03:00
|
|
|
return repr(instance).decode('utf-8')
|
2014-12-15 14:55:17 +03:00
|
|
|
return repr(instance)
|
|
|
|
|
|
|
|
|
|
|
|
def unicode_to_repr(value):
|
|
|
|
# Coerce a unicode string to the correct repr return type, depending on
|
|
|
|
# the Python version. We wrap all our `__repr__` implementations with
|
|
|
|
# this and then use unicode throughout internally.
|
|
|
|
if six.PY2:
|
|
|
|
return value.encode('utf-8')
|
|
|
|
return value
|
|
|
|
|
|
|
|
|
2014-12-16 19:37:32 +03:00
|
|
|
def unicode_http_header(value):
|
|
|
|
# Coerce HTTP header value to unicode.
|
|
|
|
if isinstance(value, six.binary_type):
|
|
|
|
return value.decode('iso-8859-1')
|
|
|
|
return value
|
|
|
|
|
|
|
|
|
2015-08-20 13:35:32 +03:00
|
|
|
def distinct(queryset, base):
|
|
|
|
if settings.DATABASES[queryset.db]["ENGINE"] == "django.db.backends.oracle":
|
|
|
|
# distinct analogue for Oracle users
|
|
|
|
return base.filter(pk__in=set(queryset.values_list('pk', flat=True)))
|
|
|
|
return queryset.distinct()
|
|
|
|
|
|
|
|
|
2016-06-02 16:39:10 +03:00
|
|
|
def _resolve_model(obj):
|
|
|
|
"""
|
|
|
|
Resolve supplied `obj` to a Django model class.
|
|
|
|
|
|
|
|
`obj` must be a Django model class itself, or a string
|
|
|
|
representation of one. Useful in situations like GH #1225 where
|
|
|
|
Django may not have resolved a string-based reference to a model in
|
|
|
|
another model's foreign key definition.
|
|
|
|
|
|
|
|
String representations should have the format:
|
|
|
|
'appname.ModelName'
|
|
|
|
"""
|
|
|
|
if isinstance(obj, six.string_types) and len(obj.split('.')) == 2:
|
|
|
|
app_name, model_name = obj.split('.')
|
|
|
|
resolved_model = apps.get_model(app_name, model_name)
|
|
|
|
if resolved_model is None:
|
|
|
|
msg = "Django did not return a model for {0}.{1}"
|
|
|
|
raise ImproperlyConfigured(msg.format(app_name, model_name))
|
|
|
|
return resolved_model
|
|
|
|
elif inspect.isclass(obj) and issubclass(obj, models.Model):
|
|
|
|
return obj
|
|
|
|
raise ValueError("{0} is not a Django model".format(obj))
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
2016-10-21 17:00:25 +03:00
|
|
|
# coreapi is optional (Note that uritemplate is a dependency of coreapi)
|
|
|
|
try:
|
|
|
|
import coreapi
|
|
|
|
import uritemplate
|
|
|
|
except (ImportError, SyntaxError):
|
|
|
|
# SyntaxError is possible under python 3.2
|
|
|
|
coreapi = None
|
|
|
|
uritemplate = None
|
|
|
|
|
|
|
|
|
2017-03-03 18:24:37 +03:00
|
|
|
# coreschema is optional
|
|
|
|
try:
|
|
|
|
import coreschema
|
|
|
|
except ImportError:
|
|
|
|
coreschema = None
|
|
|
|
|
|
|
|
|
2015-08-27 16:25:44 +03:00
|
|
|
# django-crispy-forms is optional
|
|
|
|
try:
|
|
|
|
import crispy_forms
|
|
|
|
except ImportError:
|
|
|
|
crispy_forms = None
|
|
|
|
|
|
|
|
|
2016-10-10 15:03:46 +03:00
|
|
|
# requests is optional
|
|
|
|
try:
|
|
|
|
import requests
|
|
|
|
except ImportError:
|
|
|
|
requests = None
|
|
|
|
|
|
|
|
|
2014-07-28 09:37:30 +04:00
|
|
|
# Django-guardian is optional. Import only if guardian is in INSTALLED_APPS
|
|
|
|
# Fixes (#1712). We keep the try/except for the test suite.
|
|
|
|
guardian = None
|
2015-09-03 16:22:13 +03:00
|
|
|
try:
|
2015-10-09 06:03:19 +03:00
|
|
|
if 'guardian' in settings.INSTALLED_APPS:
|
2017-11-10 11:41:03 +03:00
|
|
|
import guardian # noqa
|
2015-09-03 16:22:13 +03:00
|
|
|
except ImportError:
|
|
|
|
pass
|
2013-09-23 19:48:25 +04:00
|
|
|
|
|
|
|
|
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
|
|
|
|
2012-09-20 16:06:27 +04:00
|
|
|
# Markdown is optional
|
|
|
|
try:
|
|
|
|
import markdown
|
|
|
|
|
2015-11-09 20:47:00 +03:00
|
|
|
if markdown.version <= '2.2':
|
|
|
|
HEADERID_EXT_PATH = 'headerid'
|
2016-10-10 15:03:46 +03:00
|
|
|
LEVEL_PARAM = 'level'
|
|
|
|
elif markdown.version < '2.6':
|
2015-11-09 20:47:00 +03:00
|
|
|
HEADERID_EXT_PATH = 'markdown.extensions.headerid'
|
2016-10-10 15:03:46 +03:00
|
|
|
LEVEL_PARAM = 'level'
|
|
|
|
else:
|
|
|
|
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
|
2017-10-18 21:53:05 +03:00
|
|
|
from pygments.lexers import get_lexer_by_name, TextLexer
|
2017-03-09 17:49:51 +03:00
|
|
|
from pygments.formatters import HtmlFormatter
|
|
|
|
|
|
|
|
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/
|
|
|
|
|
|
|
|
from markdown.preprocessors import Preprocessor
|
|
|
|
import re
|
|
|
|
|
|
|
|
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):
|
|
|
|
md.preprocessors.add('highlight', CodeBlockPreprocessor(), "_begin")
|
|
|
|
return True
|
|
|
|
else:
|
|
|
|
def md_filter_add_syntax_highlight(md):
|
|
|
|
return False
|
2017-03-22 07:01:07 +03:00
|
|
|
|
2017-11-10 11:41:03 +03:00
|
|
|
# pytz is required from Django 1.11. Remove when dropping Django 1.10 support.
|
2017-03-22 07:01:07 +03:00
|
|
|
try:
|
2017-11-14 11:55:59 +03:00
|
|
|
import pytz # noqa
|
2017-03-22 07:01:07 +03:00
|
|
|
from pytz.exceptions import InvalidTimeError
|
|
|
|
except ImportError:
|
|
|
|
InvalidTimeError = Exception
|
|
|
|
|
|
|
|
|
2014-12-04 04:50:25 +03:00
|
|
|
# `separators` argument to `json.dumps()` differs between 2.x and 3.x
|
2014-10-30 19:53:12 +03:00
|
|
|
# See: http://bugs.python.org/issue22767
|
|
|
|
if six.PY3:
|
|
|
|
SHORT_SEPARATORS = (',', ':')
|
|
|
|
LONG_SEPARATORS = (', ', ': ')
|
2015-01-19 17:41:10 +03:00
|
|
|
INDENT_SEPARATORS = (',', ': ')
|
2014-10-30 19:53:12 +03:00
|
|
|
else:
|
|
|
|
SHORT_SEPARATORS = (b',', b':')
|
|
|
|
LONG_SEPARATORS = (b', ', b': ')
|
2015-01-19 17:41:10 +03:00
|
|
|
INDENT_SEPARATORS = (b',', b': ')
|
2015-06-01 19:20:53 +03:00
|
|
|
|
2015-04-29 16:08:52 +03:00
|
|
|
|
2017-09-26 11:02:20 +03:00
|
|
|
class CustomValidatorMessage(object):
|
|
|
|
"""
|
|
|
|
We need to avoid evaluation of `lazy` translated `message` in `django.core.validators.BaseValidator.__init__`.
|
|
|
|
https://github.com/django/django/blob/75ed5900321d170debef4ac452b8b3cf8a1c2384/django/core/validators.py#L297
|
2017-10-05 21:41:38 +03:00
|
|
|
|
2017-09-26 11:02:20 +03:00
|
|
|
Ref: https://github.com/encode/django-rest-framework/pull/5452
|
|
|
|
"""
|
2017-11-10 11:41:03 +03:00
|
|
|
|
2017-09-26 11:02:20 +03:00
|
|
|
def __init__(self, *args, **kwargs):
|
|
|
|
self.message = kwargs.pop('message', self.message)
|
|
|
|
super(CustomValidatorMessage, self).__init__(*args, **kwargs)
|
|
|
|
|
2017-11-10 11:41:03 +03:00
|
|
|
|
|
|
|
class MinValueValidator(CustomValidatorMessage, validators.MinValueValidator):
|
2017-09-26 11:02:20 +03:00
|
|
|
pass
|
|
|
|
|
2017-11-10 11:41:03 +03:00
|
|
|
|
|
|
|
class MaxValueValidator(CustomValidatorMessage, validators.MaxValueValidator):
|
2017-09-26 11:02:20 +03:00
|
|
|
pass
|
|
|
|
|
2017-11-10 11:41:03 +03:00
|
|
|
|
|
|
|
class MinLengthValidator(CustomValidatorMessage, validators.MinLengthValidator):
|
2017-09-26 11:02:20 +03:00
|
|
|
pass
|
|
|
|
|
2017-11-10 11:41:03 +03:00
|
|
|
|
|
|
|
class MaxLengthValidator(CustomValidatorMessage, validators.MaxLengthValidator):
|
2017-09-26 11:02:20 +03:00
|
|
|
pass
|
2015-11-18 17:25:58 +03:00
|
|
|
|
2017-11-10 11:41:03 +03:00
|
|
|
|
2017-10-05 12:43:49 +03:00
|
|
|
def authenticate(request=None, **credentials):
|
|
|
|
from django.contrib.auth import authenticate
|
|
|
|
if django.VERSION < (1, 11):
|
|
|
|
return authenticate(**credentials)
|
|
|
|
else:
|
|
|
|
return authenticate(request=request, **credentials)
|