""" The `compat` module provides support for backwards compatibility with older versions of django/python, and compatibility wrappers around optional packages. """ # flake8: noqa from __future__ import unicode_literals import django import inspect from django.core.exceptions import ImproperlyConfigured from django.conf import settings from django.utils import six # Handle django.utils.encoding rename in 1.5 onwards. # smart_unicode -> smart_text # force_unicode -> force_text try: from django.utils.encoding import smart_text except ImportError: from django.utils.encoding import smart_unicode as smart_text try: from django.utils.encoding import force_text except ImportError: from django.utils.encoding import force_unicode as force_text # HttpResponseBase only exists from 1.5 onwards try: from django.http.response import HttpResponseBase except ImportError: from django.http import HttpResponse as HttpResponseBase # django-filter is optional try: import django_filters except ImportError: django_filters = None if django.VERSION >= (1, 6): def clean_manytomany_helptext(text): return text else: # Up to version 1.5 many to many fields automatically suffix # the `help_text` attribute with hardcoded text. def clean_manytomany_helptext(text): if text.endswith(' Hold down "Control", or "Command" on a Mac, to select more than one.'): text = text[:-69] return text # 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 if 'guardian' in settings.INSTALLED_APPS: try: import guardian import guardian.shortcuts # Fixes #1624 except ImportError: pass # cStringIO only if it's available, otherwise StringIO try: import cStringIO.StringIO as StringIO except ImportError: StringIO = six.StringIO BytesIO = six.BytesIO # urlparse compat import (Required because it changed in python 3.x) try: from urllib import parse as urlparse except ImportError: import urlparse # UserDict moves in Python 3 try: from UserDict import UserDict from UserDict import DictMixin except ImportError: from collections import UserDict from collections import MutableMapping as DictMixin # Try to import PIL in either of the two ways it can end up installed. try: from PIL import Image except ImportError: try: import Image except ImportError: Image = None def get_model_name(model_cls): try: return model_cls._meta.model_name except AttributeError: # < 1.6 used module_name instead of model_name return model_cls._meta.module_name def get_concrete_model(model_cls): try: return model_cls._meta.concrete_model except AttributeError: # 1.3 does not include concrete model return model_cls # View._allowed_methods only present from 1.5 onwards if django.VERSION >= (1, 5): from django.views.generic import View else: from django.views.generic import View as DjangoView class View(DjangoView): def _allowed_methods(self): return [m.upper() for m in self.http_method_names if hasattr(self, m)] # MinValueValidator and MaxValueValidator only accept `message` in 1.8+ if django.VERSION >= (1, 8): from django.core.validators import MinValueValidator, MaxValueValidator else: from django.core.validators import MinValueValidator as DjangoMinValueValidator from django.core.validators import MaxValueValidator as DjangoMaxValueValidator class MinValueValidator(DjangoMinValueValidator): def __init__(self, *args, **kwargs): self.message = kwargs.pop('message', self.message) super(MinValueValidator, self).__init__(*args, **kwargs) class MaxValueValidator(DjangoMaxValueValidator): def __init__(self, *args, **kwargs): self.message = kwargs.pop('message', self.message) super(MaxValueValidator, self).__init__(*args, **kwargs) # URLValidator only accept `message` in 1.6+ if django.VERSION >= (1, 6): from django.core.validators import URLValidator else: from django.core.validators import URLValidator as DjangoURLValidator class URLValidator(DjangoURLValidator): def __init__(self, *args, **kwargs): self.message = kwargs.pop('message', self.message) super(URLValidator, self).__init__(*args, **kwargs) # EmailValidator requires explicit regex prior to 1.6+ if django.VERSION >= (1, 6): from django.core.validators import EmailValidator else: from django.core.validators import EmailValidator as DjangoEmailValidator from django.core.validators import email_re class EmailValidator(DjangoEmailValidator): def __init__(self, *args, **kwargs): super(EmailValidator, self).__init__(email_re, *args, **kwargs) # PATCH method is not implemented by Django if 'patch' not in View.http_method_names: View.http_method_names = View.http_method_names + ['patch'] # RequestFactory only provides `generic` from 1.5 onwards from django.test.client import RequestFactory as DjangoRequestFactory from django.test.client import FakePayload try: # In 1.5 the test client uses force_bytes from django.utils.encoding import force_bytes as force_bytes_or_smart_bytes except ImportError: # In 1.4 the test client just uses smart_str from django.utils.encoding import smart_str as force_bytes_or_smart_bytes class RequestFactory(DjangoRequestFactory): def generic(self, method, path, data='', content_type='application/octet-stream', **extra): parsed = urlparse.urlparse(path) data = force_bytes_or_smart_bytes(data, settings.DEFAULT_CHARSET) r = { 'PATH_INFO': self._get_path(parsed), 'QUERY_STRING': force_text(parsed[4]), 'REQUEST_METHOD': str(method), } if data: r.update({ 'CONTENT_LENGTH': len(data), 'CONTENT_TYPE': str(content_type), 'wsgi.input': FakePayload(data), }) elif django.VERSION <= (1, 4): # For 1.3 we need an empty WSGI payload r.update({ 'wsgi.input': FakePayload('') }) r.update(extra) return self.request(**r) # Markdown is optional try: import markdown def apply_markdown(text): """ Simple wrapper around :func:`markdown.markdown` to set the base level of '#' style headers to