diff options
28 files changed, 57 insertions, 507 deletions
| diff --git a/.travis.yml b/.travis.yml index 7ebe715a..456f8e9c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,18 +10,15 @@ env:    - DJANGO="https://www.djangoproject.com/download/1.6a1/tarball/"    - DJANGO="django==1.5.1 --use-mirrors"    - DJANGO="django==1.4.5 --use-mirrors" -  - DJANGO="django==1.3.7 --use-mirrors"  install:    - pip install $DJANGO -  - pip install defusedxml==0.3 +  - pip install defusedxml==0.3 --use-mirrors +  - pip install django-filter==0.6 --use-mirrors    - "if [[ ${TRAVIS_PYTHON_VERSION::1} != '3' ]]; then pip install oauth2==1.5.211 --use-mirrors; fi"    - "if [[ ${TRAVIS_PYTHON_VERSION::1} != '3' ]]; then pip install django-oauth-plus==2.0 --use-mirrors; fi"    - "if [[ ${TRAVIS_PYTHON_VERSION::1} != '3' ]]; then pip install django-oauth2-provider==0.2.4 --use-mirrors; fi"    - "if [[ ${TRAVIS_PYTHON_VERSION::1} != '3' ]]; then pip install django-guardian==1.1.1 --use-mirrors; fi" -  - "if [[ ${DJANGO::11} == 'django==1.3' ]]; then pip install django-filter==0.5.4 --use-mirrors; fi" -  - "if [[ ${DJANGO::11} == 'django==1.3' ]]; then pip install six --use-mirrors; fi" -  - "if [[ ${DJANGO::11} != 'django==1.3' ]]; then pip install django-filter==0.6 --use-mirrors; fi"    - export PYTHONPATH=.  script: @@ -31,10 +28,5 @@ matrix:    exclude:      - python: "3.2"        env: DJANGO="django==1.4.5 --use-mirrors" -    - python: "3.2" -      env: DJANGO="django==1.3.7 --use-mirrors"      - python: "3.3"        env: DJANGO="django==1.4.5 --use-mirrors" -    - python: "3.3" -      env: DJANGO="django==1.3.7 --use-mirrors" - diff --git a/rest_framework/authentication.py b/rest_framework/authentication.py index cf001a24..db5cce40 100644 --- a/rest_framework/authentication.py +++ b/rest_framework/authentication.py @@ -6,8 +6,8 @@ import base64  from django.contrib.auth import authenticate  from django.core.exceptions import ImproperlyConfigured +from django.middleware.csrf import CsrfViewMiddleware  from rest_framework import exceptions, HTTP_HEADER_ENCODING -from rest_framework.compat import CsrfViewMiddleware  from rest_framework.compat import oauth, oauth_provider, oauth_provider_store  from rest_framework.compat import oauth2_provider, provider_now  from rest_framework.authtoken.models import Token diff --git a/rest_framework/compat.py b/rest_framework/compat.py index 1238f043..f048b10a 100644 --- a/rest_framework/compat.py +++ b/rest_framework/compat.py @@ -5,25 +5,19 @@ versions of django/python, and compatibility wrappers around optional packages.  # flake8: noqa  from __future__ import unicode_literals -  import django  from django.core.exceptions import ImproperlyConfigured  from django.conf import settings +  # Try to import six from Django, fallback to external `six` package.  try:      from django.utils import six  except ImportError:      import six -# location of patterns, url, include changes in 1.4 onwards -try: -    from django.conf.urls import patterns, url, include -except ImportError: -    from django.conf.urls.defaults import patterns, url, include - -# Handle django.utils.encoding rename: -# smart_unicode -> smart_text +# 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 @@ -41,13 +35,15 @@ try:  except ImportError:      from django.http import HttpResponse as HttpResponseBase +  # django-filter is optional  try:      import django_filters  except ImportError:      django_filters = None -# guardian is optional + +# django-guardian is optional  try:      import guardian  except ImportError: @@ -80,14 +76,6 @@ except ImportError:          Image = None -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 - -  # Django 1.5 add support for custom auth user model  if django.VERSION >= (1, 5):      AUTH_USER_MODEL = settings.AUTH_USER_MODEL @@ -95,46 +83,13 @@ else:      AUTH_USER_MODEL = 'auth.User' +# 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 _View -    from django.utils.decorators import classonlymethod -    from django.utils.functional import update_wrapper - -    class View(_View): -        # 1.3 does not include head method in base View class -        # See: https://code.djangoproject.com/ticket/15668 -        @classonlymethod -        def as_view(cls, **initkwargs): -            """ -            Main entry point for a request-response process. -            """ -            # sanitize keyword arguments -            for key in initkwargs: -                if key in cls.http_method_names: -                    raise TypeError("You tried to pass in the %s method name as a " -                                    "keyword argument to %s(). Don't do that." -                                    % (key, cls.__name__)) -                if not hasattr(cls, key): -                    raise TypeError("%s() received an invalid keyword %r" % ( -                        cls.__name__, key)) - -            def view(request, *args, **kwargs): -                self = cls(**initkwargs) -                if hasattr(self, 'get') and not hasattr(self, 'head'): -                    self.head = self.get -                return self.dispatch(request, *args, **kwargs) - -            # take name and docstring from class -            update_wrapper(view, cls, updated=()) - -            # and possible attributes set by decorators -            # like csrf_exempt from dispatch -            update_wrapper(view, cls.dispatch, assigned=()) -            return view - -        # _allowed_methods only present from 1.5 onwards +    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)] @@ -144,316 +99,16 @@ if 'patch' not in View.http_method_names:      View.http_method_names = View.http_method_names + ['patch'] -# PUT, DELETE do not require CSRF until 1.4.  They should.  Make it better. -if django.VERSION >= (1, 4): -    from django.middleware.csrf import CsrfViewMiddleware -else: -    import hashlib -    import re -    import random -    import logging - -    from django.conf import settings -    from django.core.urlresolvers import get_callable - -    try: -        from logging import NullHandler -    except ImportError: -        class NullHandler(logging.Handler): -            def emit(self, record): -                pass - -    logger = logging.getLogger('django.request') - -    if not logger.handlers: -        logger.addHandler(NullHandler()) - -    def same_origin(url1, url2): -        """ -        Checks if two URLs are 'same-origin' -        """ -        p1, p2 = urlparse.urlparse(url1), urlparse.urlparse(url2) -        return p1[0:2] == p2[0:2] - -    def constant_time_compare(val1, val2): -        """ -        Returns True if the two strings are equal, False otherwise. - -        The time taken is independent of the number of characters that match. -        """ -        if len(val1) != len(val2): -            return False -        result = 0 -        for x, y in zip(val1, val2): -            result |= ord(x) ^ ord(y) -        return result == 0 - -    # Use the system (hardware-based) random number generator if it exists. -    if hasattr(random, 'SystemRandom'): -        randrange = random.SystemRandom().randrange -    else: -        randrange = random.randrange - -    _MAX_CSRF_KEY = 18446744073709551616      # 2 << 63 - -    REASON_NO_REFERER = "Referer checking failed - no Referer." -    REASON_BAD_REFERER = "Referer checking failed - %s does not match %s." -    REASON_NO_CSRF_COOKIE = "CSRF cookie not set." -    REASON_BAD_TOKEN = "CSRF token missing or incorrect." - -    def _get_failure_view(): -        """ -        Returns the view to be used for CSRF rejections -        """ -        return get_callable(settings.CSRF_FAILURE_VIEW) - -    def _get_new_csrf_key(): -        return hashlib.md5("%s%s" % (randrange(0, _MAX_CSRF_KEY), settings.SECRET_KEY)).hexdigest() - -    def get_token(request): -        """ -        Returns the the CSRF token required for a POST form. The token is an -        alphanumeric value. - -        A side effect of calling this function is to make the the csrf_protect -        decorator and the CsrfViewMiddleware add a CSRF cookie and a 'Vary: Cookie' -        header to the outgoing response.  For this reason, you may need to use this -        function lazily, as is done by the csrf context processor. -        """ -        request.META["CSRF_COOKIE_USED"] = True -        return request.META.get("CSRF_COOKIE", None) - -    def _sanitize_token(token): -        # Allow only alphanum, and ensure we return a 'str' for the sake of the post -        # processing middleware. -        token = re.sub('[^a-zA-Z0-9]', '', str(token.decode('ascii', 'ignore'))) -        if token == "": -            # In case the cookie has been truncated to nothing at some point. -            return _get_new_csrf_key() -        else: -            return token - -    class CsrfViewMiddleware(object): -        """ -        Middleware that requires a present and correct csrfmiddlewaretoken -        for POST requests that have a CSRF cookie, and sets an outgoing -        CSRF cookie. - -        This middleware should be used in conjunction with the csrf_token template -        tag. -        """ -        # The _accept and _reject methods currently only exist for the sake of the -        # requires_csrf_token decorator. -        def _accept(self, request): -            # Avoid checking the request twice by adding a custom attribute to -            # request.  This will be relevant when both decorator and middleware -            # are used. -            request.csrf_processing_done = True -            return None - -        def _reject(self, request, reason): -            return _get_failure_view()(request, reason=reason) - -        def process_view(self, request, callback, callback_args, callback_kwargs): - -            if getattr(request, 'csrf_processing_done', False): -                return None - -            try: -                csrf_token = _sanitize_token(request.COOKIES[settings.CSRF_COOKIE_NAME]) -                # Use same token next time -                request.META['CSRF_COOKIE'] = csrf_token -            except KeyError: -                csrf_token = None -                # Generate token and store it in the request, so it's available to the view. -                request.META["CSRF_COOKIE"] = _get_new_csrf_key() - -            # Wait until request.META["CSRF_COOKIE"] has been manipulated before -            # bailing out, so that get_token still works -            if getattr(callback, 'csrf_exempt', False): -                return None - -            # Assume that anything not defined as 'safe' by RC2616 needs protection. -            if request.method not in ('GET', 'HEAD', 'OPTIONS', 'TRACE'): -                if getattr(request, '_dont_enforce_csrf_checks', False): -                    # Mechanism to turn off CSRF checks for test suite.  It comes after -                    # the creation of CSRF cookies, so that everything else continues to -                    # work exactly the same (e.g. cookies are sent etc), but before the -                    # any branches that call reject() -                    return self._accept(request) - -                if request.is_secure(): -                    # Suppose user visits http://example.com/ -                    # An active network attacker,(man-in-the-middle, MITM) sends a -                    # POST form which targets https://example.com/detonate-bomb/ and -                    # submits it via javascript. -                    # -                    # The attacker will need to provide a CSRF cookie and token, but -                    # that is no problem for a MITM and the session independent -                    # nonce we are using. So the MITM can circumvent the CSRF -                    # protection. This is true for any HTTP connection, but anyone -                    # using HTTPS expects better!  For this reason, for -                    # https://example.com/ we need additional protection that treats -                    # http://example.com/ as completely untrusted.  Under HTTPS, -                    # Barth et al. found that the Referer header is missing for -                    # same-domain requests in only about 0.2% of cases or less, so -                    # we can use strict Referer checking. -                    referer = request.META.get('HTTP_REFERER') -                    if referer is None: -                        logger.warning('Forbidden (%s): %s' % (REASON_NO_REFERER, request.path), -                            extra={ -                                'status_code': 403, -                                'request': request, -                            } -                        ) -                        return self._reject(request, REASON_NO_REFERER) - -                    # Note that request.get_host() includes the port -                    good_referer = 'https://%s/' % request.get_host() -                    if not same_origin(referer, good_referer): -                        reason = REASON_BAD_REFERER % (referer, good_referer) -                        logger.warning('Forbidden (%s): %s' % (reason, request.path), -                            extra={ -                                'status_code': 403, -                                'request': request, -                            } -                        ) -                        return self._reject(request, reason) - -                if csrf_token is None: -                    # No CSRF cookie. For POST requests, we insist on a CSRF cookie, -                    # and in this way we can avoid all CSRF attacks, including login -                    # CSRF. -                    logger.warning('Forbidden (%s): %s' % (REASON_NO_CSRF_COOKIE, request.path), -                        extra={ -                            'status_code': 403, -                            'request': request, -                        } -                    ) -                    return self._reject(request, REASON_NO_CSRF_COOKIE) - -                # check non-cookie token for match -                request_csrf_token = "" -                if request.method == "POST": -                    request_csrf_token = request.POST.get('csrfmiddlewaretoken', '') - -                if request_csrf_token == "": -                    # Fall back to X-CSRFToken, to make things easier for AJAX, -                    # and possible for PUT/DELETE -                    request_csrf_token = request.META.get('HTTP_X_CSRFTOKEN', '') - -                if not constant_time_compare(request_csrf_token, csrf_token): -                    logger.warning('Forbidden (%s): %s' % (REASON_BAD_TOKEN, request.path), -                        extra={ -                            'status_code': 403, -                            'request': request, -                        } -                    ) -                    return self._reject(request, REASON_BAD_TOKEN) - -            return self._accept(request) - -# timezone support is new in Django 1.4 -try: -    from django.utils import timezone -except ImportError: -    timezone = None - -# dateparse is ALSO new in Django 1.4 -try: -    from django.utils.dateparse import parse_date, parse_datetime, parse_time -except ImportError: -    import datetime -    import re - -    date_re = re.compile( -        r'(?P<year>\d{4})-(?P<month>\d{1,2})-(?P<day>\d{1,2})$' -    ) - -    datetime_re = re.compile( -        r'(?P<year>\d{4})-(?P<month>\d{1,2})-(?P<day>\d{1,2})' -        r'[T ](?P<hour>\d{1,2}):(?P<minute>\d{1,2})' -        r'(?::(?P<second>\d{1,2})(?:\.(?P<microsecond>\d{1,6})\d{0,6})?)?' -        r'(?P<tzinfo>Z|[+-]\d{1,2}:\d{1,2})?$' -    ) - -    time_re = re.compile( -        r'(?P<hour>\d{1,2}):(?P<minute>\d{1,2})' -        r'(?::(?P<second>\d{1,2})(?:\.(?P<microsecond>\d{1,6})\d{0,6})?)?' -    ) - -    def parse_date(value): -        match = date_re.match(value) -        if match: -            kw = dict((k, int(v)) for k, v in match.groupdict().iteritems()) -            return datetime.date(**kw) - -    def parse_time(value): -        match = time_re.match(value) -        if match: -            kw = match.groupdict() -            if kw['microsecond']: -                kw['microsecond'] = kw['microsecond'].ljust(6, '0') -            kw = dict((k, int(v)) for k, v in kw.iteritems() if v is not None) -            return datetime.time(**kw) - -    def parse_datetime(value): -        """Parse datetime, but w/o the timezone awareness in 1.4""" -        match = datetime_re.match(value) -        if match: -            kw = match.groupdict() -            if kw['microsecond']: -                kw['microsecond'] = kw['microsecond'].ljust(6, '0') -            kw = dict((k, int(v)) for k, v in kw.iteritems() if v is not None) -            return datetime.datetime(**kw) - - -# smart_urlquote is new on Django 1.4 -try: -    from django.utils.html import smart_urlquote -except ImportError: -    import re -    from django.utils.encoding import smart_str -    try: -        from urllib.parse import quote, urlsplit, urlunsplit -    except ImportError:     # Python 2 -        from urllib import quote -        from urlparse import urlsplit, urlunsplit - -    unquoted_percents_re = re.compile(r'%(?![0-9A-Fa-f]{2})') - -    def smart_urlquote(url): -        "Quotes a URL if it isn't already quoted." -        # Handle IDN before quoting. -        scheme, netloc, path, query, fragment = urlsplit(url) -        try: -            netloc = netloc.encode('idna').decode('ascii')  # IDN -> ACE -        except UnicodeError:  # invalid domain part -            pass -        else: -            url = urlunsplit((scheme, netloc, path, query, fragment)) - -        # An URL is considered unquoted if it contains no % characters or -        # contains a % not followed by two hexadecimal digits. See #9655. -        if '%' not in url or unquoted_percents_re.search(url): -            # See http://bugs.python.org/issue2637 -            url = quote(smart_str(url), safe=b'!*\'();:@&=+$,/?#[]~') - -        return force_text(url) - - -# RequestFactory only provide `generic` from 1.5 onwards - +# 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_or_smart_bytes  except ImportError: -    # In 1.3 and 1.4 the test client just uses smart_str +    # 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): @@ -478,6 +133,7 @@ class RequestFactory(DjangoRequestFactory):          r.update(extra)          return self.request(**r) +  # Markdown is optional  try:      import markdown @@ -492,7 +148,6 @@ try:          safe_mode = False          md = markdown.Markdown(extensions=extensions, safe_mode=safe_mode)          return md.convert(text) -  except ImportError:      apply_markdown = None @@ -510,14 +165,16 @@ try:  except ImportError:      etree = None -# OAuth is optional + +# OAuth2 is optional  try:      # Note: The `oauth2` package actually provides oauth1.0a support.  Urg.      import oauth2 as oauth  except ImportError:      oauth = None -# OAuth is optional + +# OAuthProvider is optional  try:      import oauth_provider      from oauth_provider.store import store as oauth_provider_store @@ -525,6 +182,7 @@ except (ImportError, ImproperlyConfigured):      oauth_provider = None      oauth_provider_store = None +  # OAuth 2 support is optional  try:      import provider.oauth2 as oauth2_provider @@ -542,8 +200,6 @@ try:          # Any other supported version does use timezone aware datetimes          from django.utils.timezone import now as provider_now  except ImportError: -    import traceback -    traceback.print_exc()      oauth2_provider = None      oauth2_provider_models = None      oauth2_provider_forms = None @@ -551,7 +207,8 @@ except ImportError:      oauth2_constants = None      provider_now = None -# Handle lazy strings + +# Handle lazy strings across Py2/Py3  from django.utils.functional import Promise  if six.PY3: diff --git a/rest_framework/fields.py b/rest_framework/fields.py index b3a9b0df..f340510d 100644 --- a/rest_framework/fields.py +++ b/rest_framework/fields.py @@ -18,12 +18,14 @@ from django.conf import settings  from django.db.models.fields import BLANK_CHOICE_DASH  from django.http import QueryDict  from django.forms import widgets +from django.utils import timezone  from django.utils.encoding import is_protected_type  from django.utils.translation import ugettext_lazy as _  from django.utils.datastructures import SortedDict +from django.utils.dateparse import parse_date, parse_datetime, parse_time  from rest_framework import ISO_8601  from rest_framework.compat import ( -    timezone, parse_date, parse_datetime, parse_time, BytesIO, six, smart_text, +    BytesIO, six, smart_text,      force_text, is_non_str_iterable  )  from rest_framework.settings import api_settings diff --git a/rest_framework/routers.py b/rest_framework/routers.py index 1c7a8158..790299cc 100644 --- a/rest_framework/routers.py +++ b/rest_framework/routers.py @@ -17,9 +17,9 @@ from __future__ import unicode_literals  import itertools  from collections import namedtuple +from django.conf.urls import patterns, url  from django.core.exceptions import ImproperlyConfigured  from rest_framework import views -from rest_framework.compat import patterns, url  from rest_framework.response import Response  from rest_framework.reverse import reverse  from rest_framework.urlpatterns import format_suffix_patterns diff --git a/rest_framework/runtests/settings.py b/rest_framework/runtests/settings.py index be721658..12aa73e7 100644 --- a/rest_framework/runtests/settings.py +++ b/rest_framework/runtests/settings.py @@ -93,10 +93,7 @@ INSTALLED_APPS = (      'django.contrib.sessions',      'django.contrib.sites',      'django.contrib.messages', -    # Uncomment the next line to enable the admin: -    # 'django.contrib.admin', -    # Uncomment the next line to enable admin documentation: -    # 'django.contrib.admindocs', +    'django.contrib.staticfiles',      'rest_framework',      'rest_framework.authtoken',      'rest_framework.tests', diff --git a/rest_framework/runtests/urls.py b/rest_framework/runtests/urls.py index ed5baeae..dff71011 100644 --- a/rest_framework/runtests/urls.py +++ b/rest_framework/runtests/urls.py @@ -1,7 +1,7 @@  """  Blank URLConf just to keep runtests.py happy.  """ -from rest_framework.compat import patterns +from django.conf.urls import patterns  urlpatterns = patterns('',  ) diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py index f1775762..9e3881a2 100644 --- a/rest_framework/serializers.py +++ b/rest_framework/serializers.py @@ -18,7 +18,7 @@ from decimal import Decimal  from django.db import models  from django.forms import widgets  from django.utils.datastructures import SortedDict -from rest_framework.compat import get_concrete_model, six +from rest_framework.compat import six  # Note: We do the following so that users of the framework can use this style:  # @@ -575,7 +575,7 @@ class ModelSerializer(Serializer):          cls = self.opts.model          assert cls is not None, \                  "Serializer class '%s' is missing 'model' Meta option" % self.__class__.__name__ -        opts = get_concrete_model(cls)._meta +        opts = cls._meta.concrete_model._meta          ret = SortedDict()          nested = bool(self.opts.depth) @@ -784,7 +784,7 @@ class ModelSerializer(Serializer):          Return a list of field names to exclude from model validation.          """          cls = self.opts.model -        opts = get_concrete_model(cls)._meta +        opts = cls._meta.concrete_model._meta          exclusions = [field.name for field in opts.fields + opts.many_to_many]          for field_name, field in self.fields.items(): diff --git a/rest_framework/templates/rest_framework/base.html b/rest_framework/templates/rest_framework/base.html index 2776d550..47377d51 100644 --- a/rest_framework/templates/rest_framework/base.html +++ b/rest_framework/templates/rest_framework/base.html @@ -1,4 +1,5 @@  {% load url from future %} +{% load staticfiles %}  {% load rest_framework %}  <!DOCTYPE html>  <html> diff --git a/rest_framework/templates/rest_framework/login_base.html b/rest_framework/templates/rest_framework/login_base.html index be9a0072..be83c2f5 100644 --- a/rest_framework/templates/rest_framework/login_base.html +++ b/rest_framework/templates/rest_framework/login_base.html @@ -1,4 +1,5 @@  {% load url from future %} +{% load staticfiles %}  {% load rest_framework %}  <html> diff --git a/rest_framework/templatetags/rest_framework.py b/rest_framework/templatetags/rest_framework.py index e9c1cdd5..55f36149 100644 --- a/rest_framework/templatetags/rest_framework.py +++ b/rest_framework/templatetags/rest_framework.py @@ -2,97 +2,14 @@ from __future__ import unicode_literals, absolute_import  from django import template  from django.core.urlresolvers import reverse, NoReverseMatch  from django.http import QueryDict -from django.utils.html import escape +from django.utils.html import escape, smart_urlquote  from django.utils.safestring import SafeData, mark_safe -from rest_framework.compat import urlparse, force_text, six, smart_urlquote +from rest_framework.compat import urlparse, force_text, six  import re, string  register = template.Library() -# Note we don't use 'load staticfiles', because we need a 1.3 compatible -# version, so instead we include the `static` template tag ourselves. - -# When 1.3 becomes unsupported by REST framework, we can instead start to -# use the {% load staticfiles %} tag, remove the following code, -# and add a dependency that `django.contrib.staticfiles` must be installed. - -# Note: We can't put this into the `compat` module because the compat import -# from rest_framework.compat import ... -# conflicts with this rest_framework template tag module. - -try:  # Django 1.5+ -    from django.contrib.staticfiles.templatetags.staticfiles import StaticFilesNode - -    @register.tag('static') -    def do_static(parser, token): -        return StaticFilesNode.handle_token(parser, token) - -except ImportError: -    try:  # Django 1.4 -        from django.contrib.staticfiles.storage import staticfiles_storage - -        @register.simple_tag -        def static(path): -            """ -            A template tag that returns the URL to a file -            using staticfiles' storage backend -            """ -            return staticfiles_storage.url(path) - -    except ImportError:  # Django 1.3 -        from urlparse import urljoin -        from django import template -        from django.templatetags.static import PrefixNode - -        class StaticNode(template.Node): -            def __init__(self, varname=None, path=None): -                if path is None: -                    raise template.TemplateSyntaxError( -                        "Static template nodes must be given a path to return.") -                self.path = path -                self.varname = varname - -            def url(self, context): -                path = self.path.resolve(context) -                return self.handle_simple(path) - -            def render(self, context): -                url = self.url(context) -                if self.varname is None: -                    return url -                context[self.varname] = url -                return '' - -            @classmethod -            def handle_simple(cls, path): -                return urljoin(PrefixNode.handle_simple("STATIC_URL"), path) - -            @classmethod -            def handle_token(cls, parser, token): -                """ -                Class method to parse prefix node and return a Node. -                """ -                bits = token.split_contents() - -                if len(bits) < 2: -                    raise template.TemplateSyntaxError( -                        "'%s' takes at least one argument (path to file)" % bits[0]) - -                path = parser.compile_filter(bits[1]) - -                if len(bits) >= 2 and bits[-2] == 'as': -                    varname = bits[3] -                else: -                    varname = None - -                return cls(varname, path) - -        @register.tag('static') -        def do_static_13(parser, token): -            return StaticNode.handle_token(parser, token) - -  def replace_query_param(url, key, val):      """      Given a URL and a key/val pair, set or replace an item in the query diff --git a/rest_framework/tests/test_authentication.py b/rest_framework/tests/test_authentication.py index a44813b6..e9a817c0 100644 --- a/rest_framework/tests/test_authentication.py +++ b/rest_framework/tests/test_authentication.py @@ -1,4 +1,5 @@  from __future__ import unicode_literals +from django.conf.urls import patterns, url, include  from django.contrib.auth.models import User  from django.http import HttpResponse  from django.test import TestCase @@ -18,7 +19,6 @@ from rest_framework.authentication import (      OAuth2Authentication  )  from rest_framework.authtoken.models import Token -from rest_framework.compat import patterns, url, include  from rest_framework.compat import oauth2_provider, oauth2_provider_models, oauth2_provider_scope  from rest_framework.compat import oauth, oauth_provider  from rest_framework.test import APIRequestFactory, APIClient diff --git a/rest_framework/tests/test_breadcrumbs.py b/rest_framework/tests/test_breadcrumbs.py index 41ddf2ce..33740cbb 100644 --- a/rest_framework/tests/test_breadcrumbs.py +++ b/rest_framework/tests/test_breadcrumbs.py @@ -1,6 +1,6 @@  from __future__ import unicode_literals +from django.conf.urls import patterns, url  from django.test import TestCase -from rest_framework.compat import patterns, url  from rest_framework.utils.breadcrumbs import get_breadcrumbs  from rest_framework.views import APIView diff --git a/rest_framework/tests/test_filters.py b/rest_framework/tests/test_filters.py index 379db29d..9697c5ee 100644 --- a/rest_framework/tests/test_filters.py +++ b/rest_framework/tests/test_filters.py @@ -1,12 +1,13 @@  from __future__ import unicode_literals  import datetime  from decimal import Decimal +from django.conf.urls import patterns, url  from django.db import models  from django.core.urlresolvers import reverse  from django.test import TestCase  from django.utils import unittest  from rest_framework import generics, serializers, status, filters -from rest_framework.compat import django_filters, patterns, url +from rest_framework.compat import django_filters  from rest_framework.test import APIRequestFactory  from rest_framework.tests.models import BasicModel diff --git a/rest_framework/tests/test_htmlrenderer.py b/rest_framework/tests/test_htmlrenderer.py index 8957a43c..6c570dfd 100644 --- a/rest_framework/tests/test_htmlrenderer.py +++ b/rest_framework/tests/test_htmlrenderer.py @@ -1,11 +1,11 @@  from __future__ import unicode_literals  from django.core.exceptions import PermissionDenied +from django.conf.urls import patterns, url  from django.http import Http404  from django.test import TestCase  from django.template import TemplateDoesNotExist, Template  import django.template.loader  from rest_framework import status -from rest_framework.compat import patterns, url  from rest_framework.decorators import api_view, renderer_classes  from rest_framework.renderers import TemplateHTMLRenderer  from rest_framework.response import Response diff --git a/rest_framework/tests/test_hyperlinkedserializers.py b/rest_framework/tests/test_hyperlinkedserializers.py index 61e613d7..ea7f70f2 100644 --- a/rest_framework/tests/test_hyperlinkedserializers.py +++ b/rest_framework/tests/test_hyperlinkedserializers.py @@ -1,8 +1,8 @@  from __future__ import unicode_literals  import json +from django.conf.urls import patterns, url  from django.test import TestCase  from rest_framework import generics, status, serializers -from rest_framework.compat import patterns, url  from rest_framework.test import APIRequestFactory  from rest_framework.tests.models import (      Anchor, BasicModel, ManyToManyModel, BlogPost, BlogPostComment, @@ -24,7 +24,7 @@ class BlogPostCommentSerializer(serializers.ModelSerializer):  class PhotoSerializer(serializers.Serializer):      description = serializers.CharField() -    album_url = serializers.HyperlinkedRelatedField(source='album', view_name='album-detail', queryset=Album.objects.all(), lookup_field='title', slug_url_kwarg='title') +    album_url = serializers.HyperlinkedRelatedField(source='album', view_name='album-detail', queryset=Album.objects.all(), lookup_field='title')      def restore_object(self, attrs, instance=None):          return Photo(**attrs) diff --git a/rest_framework/tests/test_relations_hyperlink.py b/rest_framework/tests/test_relations_hyperlink.py index 3c4d39af..fa6b01ac 100644 --- a/rest_framework/tests/test_relations_hyperlink.py +++ b/rest_framework/tests/test_relations_hyperlink.py @@ -1,7 +1,7 @@  from __future__ import unicode_literals +from django.conf.urls import patterns, url  from django.test import TestCase  from rest_framework import serializers -from rest_framework.compat import patterns, url  from rest_framework.test import APIRequestFactory  from rest_framework.tests.models import (      BlogPost, diff --git a/rest_framework/tests/test_renderers.py b/rest_framework/tests/test_renderers.py index df6f4aa6..9d1dd77e 100644 --- a/rest_framework/tests/test_renderers.py +++ b/rest_framework/tests/test_renderers.py @@ -2,12 +2,13 @@  from __future__ import unicode_literals  from decimal import Decimal +from django.conf.urls import patterns, url, include  from django.core.cache import cache  from django.test import TestCase  from django.utils import unittest  from django.utils.translation import ugettext_lazy as _  from rest_framework import status, permissions -from rest_framework.compat import yaml, etree, patterns, url, include, six, StringIO +from rest_framework.compat import yaml, etree, six, StringIO  from rest_framework.response import Response  from rest_framework.views import APIView  from rest_framework.renderers import BaseRenderer, JSONRenderer, YAMLRenderer, \ diff --git a/rest_framework/tests/test_request.py b/rest_framework/tests/test_request.py index 969d8024..d6363425 100644 --- a/rest_framework/tests/test_request.py +++ b/rest_framework/tests/test_request.py @@ -2,13 +2,13 @@  Tests for content parsing, and form-overloaded content parsing.  """  from __future__ import unicode_literals +from django.conf.urls import patterns  from django.contrib.auth.models import User  from django.contrib.auth import authenticate, login, logout  from django.contrib.sessions.middleware import SessionMiddleware  from django.test import TestCase  from rest_framework import status  from rest_framework.authentication import SessionAuthentication -from rest_framework.compat import patterns  from rest_framework.parsers import (      BaseParser,      FormParser, diff --git a/rest_framework/tests/test_response.py b/rest_framework/tests/test_response.py index eea3c641..1c4c551c 100644 --- a/rest_framework/tests/test_response.py +++ b/rest_framework/tests/test_response.py @@ -1,7 +1,7 @@  from __future__ import unicode_literals +from django.conf.urls import patterns, url, include  from django.test import TestCase  from rest_framework.tests.models import BasicModel, BasicModelSerializer -from rest_framework.compat import patterns, url, include  from rest_framework.response import Response  from rest_framework.views import APIView  from rest_framework import generics diff --git a/rest_framework/tests/test_reverse.py b/rest_framework/tests/test_reverse.py index 690a30b1..320b125d 100644 --- a/rest_framework/tests/test_reverse.py +++ b/rest_framework/tests/test_reverse.py @@ -1,6 +1,6 @@  from __future__ import unicode_literals +from django.conf.urls import patterns, url  from django.test import TestCase -from rest_framework.compat import patterns, url  from rest_framework.reverse import reverse  from rest_framework.test import APIRequestFactory diff --git a/rest_framework/tests/test_routers.py b/rest_framework/tests/test_routers.py index 3f456fef..1c34648f 100644 --- a/rest_framework/tests/test_routers.py +++ b/rest_framework/tests/test_routers.py @@ -1,9 +1,9 @@  from __future__ import unicode_literals +from django.conf.urls import patterns, url, include  from django.db import models  from django.test import TestCase  from django.core.exceptions import ImproperlyConfigured  from rest_framework import serializers, viewsets, permissions -from rest_framework.compat import include, patterns, url  from rest_framework.decorators import detail_route, list_route  from rest_framework.response import Response  from rest_framework.routers import SimpleRouter, DefaultRouter diff --git a/rest_framework/tests/test_testing.py b/rest_framework/tests/test_testing.py index 48b8956b..c08dd493 100644 --- a/rest_framework/tests/test_testing.py +++ b/rest_framework/tests/test_testing.py @@ -1,9 +1,9 @@  # -- coding: utf-8 --  from __future__ import unicode_literals +from django.conf.urls import patterns, url  from django.contrib.auth.models import User  from django.test import TestCase -from rest_framework.compat import patterns, url  from rest_framework.decorators import api_view  from rest_framework.response import Response  from rest_framework.test import APIClient, APIRequestFactory, force_authenticate diff --git a/rest_framework/tests/test_urlpatterns.py b/rest_framework/tests/test_urlpatterns.py index 8132ec4c..e0060e69 100644 --- a/rest_framework/tests/test_urlpatterns.py +++ b/rest_framework/tests/test_urlpatterns.py @@ -1,9 +1,9 @@  from __future__ import unicode_literals  from collections import namedtuple +from django.conf.urls import patterns, url, include  from django.core import urlresolvers  from django.test import TestCase  from rest_framework.test import APIRequestFactory -from rest_framework.compat import patterns, url, include  from rest_framework.urlpatterns import format_suffix_patterns diff --git a/rest_framework/urlpatterns.py b/rest_framework/urlpatterns.py index d9143bb4..a62530c7 100644 --- a/rest_framework/urlpatterns.py +++ b/rest_framework/urlpatterns.py @@ -1,6 +1,6 @@  from __future__ import unicode_literals +from django.conf.urls import url, include  from django.core.urlresolvers import RegexURLResolver -from rest_framework.compat import url, include  from rest_framework.settings import api_settings diff --git a/rest_framework/urls.py b/rest_framework/urls.py index 9c4719f1..87ec0f0a 100644 --- a/rest_framework/urls.py +++ b/rest_framework/urls.py @@ -13,7 +13,7 @@ your authentication settings include `SessionAuthentication`.      )  """  from __future__ import unicode_literals -from rest_framework.compat import patterns, url +from django.conf.urls import patterns, url  template_name = {'template_name': 'rest_framework/login.html'} diff --git a/rest_framework/utils/encoders.py b/rest_framework/utils/encoders.py index 7efd5417..13a85550 100644 --- a/rest_framework/utils/encoders.py +++ b/rest_framework/utils/encoders.py @@ -2,9 +2,10 @@  Helper classes for parsers.  """  from __future__ import unicode_literals +from django.utils import timezone  from django.utils.datastructures import SortedDict  from django.utils.functional import Promise -from rest_framework.compat import timezone, force_text +from rest_framework.compat import force_text  from rest_framework.serializers import DictWithMetadata, SortedDictWithMetadata  import datetime  import decimal @@ -1,6 +1,6 @@  [tox]  downloadcache = {toxworkdir}/cache/ -envlist = py3.3-django1.6,py3.2-django1.6,py2.7-django1.6,py2.6-django1.6,py3.3-django1.5,py3.2-django1.5,py2.7-django1.5,py2.6-django1.5,py2.7-django1.4,py2.6-django1.4,py2.7-django1.3,py2.6-django1.3 +envlist = py3.3-django1.6,py3.2-django1.6,py2.7-django1.6,py2.6-django1.6,py3.3-django1.5,py3.2-django1.5,py2.7-django1.5,py2.6-django1.5,py2.7-django1.4,py2.6-django1.4  [testenv]  commands = {envpython} rest_framework/runtests/runtests.py @@ -88,23 +88,3 @@ deps = django==1.4.3         oauth2==1.5.211         django-oauth2-provider==0.2.3         django-guardian==1.1.1 - -[testenv:py2.7-django1.3] -basepython = python2.7 -deps = django==1.3.5 -       django-filter==0.5.4 -       defusedxml==0.3 -       django-oauth-plus==2.0 -       oauth2==1.5.211 -       django-oauth2-provider==0.2.3 -       django-guardian==1.1.1 - -[testenv:py2.6-django1.3] -basepython = python2.6 -deps = django==1.3.5 -       django-filter==0.5.4 -       defusedxml==0.3 -       django-oauth-plus==2.0 -       oauth2==1.5.211 -       django-oauth2-provider==0.2.3 -       django-guardian==1.1.1 | 
