Commit f4f237e3 by Tom Christie

3.2, 3.3 compat

parent d9c7b1c5
__version__ = '2.1.17'
VERSION = __version__ # synonym
# Header encoding (see RFC5987)
HTTP_HEADER_ENCODING = 'iso-8859-1'
"""
Provides a set of pluggable authentication policies.
"""
from __future__ import unicode_literals
from django.contrib.auth import authenticate
from django.utils.encoding import DjangoUnicodeDecodeError
from rest_framework import exceptions
from rest_framework import exceptions, HTTP_HEADER_ENCODING
from rest_framework.compat import CsrfViewMiddleware
from rest_framework.compat import smart_text
from rest_framework.authtoken.models import Token
......@@ -43,23 +44,25 @@ class BasicAuthentication(BaseAuthentication):
Returns a `User` if a correct username and password have been supplied
using HTTP Basic authentication. Otherwise returns `None`.
"""
auth = request.META.get('HTTP_AUTHORIZATION', '').split()
auth = request.META.get('HTTP_AUTHORIZATION', b'')
if type(auth) == type(''):
# Work around django test client oddness
auth = auth.encode(HTTP_HEADER_ENCODING)
auth = auth.split()
if not auth or auth[0].lower() != "basic":
if not auth or auth[0].lower() != b'basic':
return None
if len(auth) != 2:
raise exceptions.AuthenticationFailed('Invalid basic header')
encoding = api_settings.HTTP_HEADER_ENCODING
try:
auth_parts = base64.b64decode(auth[1].encode(encoding)).partition(':')
except TypeError:
auth_parts = base64.b64decode(auth[1]).decode(HTTP_HEADER_ENCODING).partition(':')
except (TypeError, UnicodeDecodeError):
raise exceptions.AuthenticationFailed('Invalid basic header')
try:
userid = smart_text(auth_parts[0])
password = smart_text(auth_parts[2])
userid, password = auth_parts[0], auth_parts[2]
except DjangoUnicodeDecodeError:
raise exceptions.AuthenticationFailed('Invalid basic header')
......
......@@ -311,7 +311,7 @@ class SlugRelatedField(RelatedField):
return self.queryset.get(**{self.slug_field: data})
except ObjectDoesNotExist:
raise ValidationError(self.error_messages['does_not_exist'] %
(self.slug_field, unicode(data)))
(self.slug_field, smart_text(data)))
except (TypeError, ValueError):
msg = self.error_messages['invalid']
raise ValidationError(msg)
......
......@@ -212,7 +212,7 @@ class BaseSerializer(Field):
reverted_data = {}
if data is not None and not isinstance(data, dict):
self._errors['non_field_errors'] = [u'Invalid data']
self._errors['non_field_errors'] = ['Invalid data']
return None
for field_name, field in self.fields.items():
......@@ -287,7 +287,7 @@ class BaseSerializer(Field):
"""
Deserialize primitives -> objects.
"""
if hasattr(data, '__iter__') and not isinstance(data, dict):
if hasattr(data, '__iter__') and not isinstance(data, (dict, six.text_type)):
# TODO: error data when deserializing lists
return [self.from_native(item, None) for item in data]
......@@ -525,7 +525,7 @@ class ModelSerializer(Serializer):
"""
try:
instance.full_clean(exclude=self.get_validation_exclusions())
except ValidationError, err:
except ValidationError as err:
self._errors = err.message_dict
return None
return instance
......
......@@ -75,9 +75,6 @@ DEFAULTS = {
'URL_FORMAT_OVERRIDE': 'format',
'FORMAT_SUFFIX_KWARG': 'format',
# Header encoding (see RFC5987)
'HTTP_HEADER_ENCODING': 'iso-8859-1',
}
......
from __future__ import unicode_literals
from django.contrib.auth.models import User
from django.http import HttpResponse
from django.test import Client, TestCase
from rest_framework import HTTP_HEADER_ENCODING
from rest_framework import permissions
from rest_framework.authtoken.models import Token
from rest_framework.authentication import TokenAuthentication, BasicAuthentication, SessionAuthentication
......@@ -41,13 +44,17 @@ class BasicAuthTests(TestCase):
def test_post_form_passing_basic_auth(self):
"""Ensure POSTing json over basic auth with correct credentials passes and does not require CSRF"""
auth = 'Basic %s' % base64.encodestring('%s:%s' % (self.username, self.password)).encode('iso-8859-1').strip().decode('iso-8859-1')
credentials = ('%s:%s' % (self.username, self.password))
base64_credentials = base64.b64encode(credentials.encode(HTTP_HEADER_ENCODING)).decode(HTTP_HEADER_ENCODING)
auth = 'Basic %s' % base64_credentials
response = self.csrf_client.post('/basic/', {'example': 'example'}, HTTP_AUTHORIZATION=auth)
self.assertEqual(response.status_code, 200)
def test_post_json_passing_basic_auth(self):
"""Ensure POSTing form over basic auth with correct credentials passes and does not require CSRF"""
auth = 'Basic %s' % base64.encodestring('%s:%s' % (self.username, self.password)).encode('iso-8859-1').strip().decode('iso-8859-1')
credentials = ('%s:%s' % (self.username, self.password))
base64_credentials = base64.b64encode(credentials.encode(HTTP_HEADER_ENCODING)).decode(HTTP_HEADER_ENCODING)
auth = 'Basic %s' % base64_credentials
response = self.csrf_client.post('/basic/', json.dumps({'example': 'example'}), 'application/json', HTTP_AUTHORIZATION=auth)
self.assertEqual(response.status_code, 200)
......
......@@ -86,16 +86,16 @@ class TestGenericRelations(TestCase):
serializer = TagSerializer(Tag.objects.all())
expected = [
{
'tag': u'django',
'tagged_item': u'Bookmark: https://www.djangoproject.com/'
'tag': 'django',
'tagged_item': 'Bookmark: https://www.djangoproject.com/'
},
{
'tag': u'python',
'tagged_item': u'Bookmark: https://www.djangoproject.com/'
'tag': 'python',
'tagged_item': 'Bookmark: https://www.djangoproject.com/'
},
{
'tag': u'reminder',
'tagged_item': u'Note: Remember the milk'
'tag': 'reminder',
'tagged_item': 'Note: Remember the milk'
}
]
self.assertEquals(serializer.data, expected)
......@@ -218,11 +218,11 @@ class HyperlinkedForeignKeyTests(TestCase):
self.assertEquals(serializer.data, expected)
def test_foreign_key_update_incorrect_type(self):
data = {'url': '/foreignkeysource/1/', 'name': u'source-1', 'target': 2}
data = {'url': '/foreignkeysource/1/', 'name': 'source-1', 'target': 2}
instance = ForeignKeySource.objects.get(pk=1)
serializer = ForeignKeySourceSerializer(instance, data=data)
self.assertFalse(serializer.is_valid())
self.assertEquals(serializer.errors, {'target': [u'Incorrect type. Expected url string, received int.']})
self.assertEquals(serializer.errors, {'target': ['Incorrect type. Expected url string, received int.']})
def test_reverse_foreign_key_update(self):
data = {'url': '/foreignkeytarget/2/', 'name': 'target-2', 'sources': ['/foreignkeysource/1/', '/foreignkeysource/3/']}
......@@ -439,7 +439,7 @@ class HyperlinkedNullableOneToOneTests(TestCase):
queryset = OneToOneTarget.objects.all()
serializer = NullableOneToOneTargetSerializer(queryset)
expected = [
{'url': '/onetoonetarget/1/', 'name': u'target-1', 'nullable_source': '/nullableonetoonesource/1/'},
{'url': '/onetoonetarget/2/', 'name': u'target-2', 'nullable_source': None},
{'url': '/onetoonetarget/1/', 'name': 'target-1', 'nullable_source': '/nullableonetoonesource/1/'},
{'url': '/onetoonetarget/2/', 'name': 'target-2', 'nullable_source': None},
]
self.assertEquals(serializer.data, expected)
......@@ -109,7 +109,7 @@ class NestedNullableOneToOneTests(TestCase):
queryset = OneToOneTarget.objects.all()
serializer = NullableOneToOneTargetSerializer(queryset)
expected = [
{'id': 1, 'name': u'target-1', 'nullable_source': {'id': 1, 'name': u'source-1', 'target': 1}},
{'id': 2, 'name': u'target-2', 'nullable_source': None},
{'id': 1, 'name': 'target-1', 'nullable_source': {'id': 1, 'name': 'source-1', 'target': 1}},
{'id': 2, 'name': 'target-2', 'nullable_source': None},
]
self.assertEquals(serializer.data, expected)
......@@ -198,11 +198,11 @@ class PKForeignKeyTests(TestCase):
self.assertEquals(serializer.data, expected)
def test_foreign_key_update_incorrect_type(self):
data = {'id': 1, 'name': u'source-1', 'target': 'foo'}
data = {'id': 1, 'name': 'source-1', 'target': 'foo'}
instance = ForeignKeySource.objects.get(pk=1)
serializer = ForeignKeySourceSerializer(instance, data=data)
self.assertFalse(serializer.is_valid())
self.assertEquals(serializer.errors, {'target': [u'Incorrect type. Expected pk value, received str.']})
self.assertEquals(serializer.errors, {'target': ['Incorrect type. Expected pk value, received str.']})
def test_reverse_foreign_key_update(self):
data = {'id': 2, 'name': 'target-2', 'sources': [1, 3]}
......@@ -415,7 +415,7 @@ class PKNullableOneToOneTests(TestCase):
queryset = OneToOneTarget.objects.all()
serializer = NullableOneToOneTargetSerializer(queryset)
expected = [
{'id': 1, 'name': u'target-1', 'nullable_source': 1},
{'id': 2, 'name': u'target-2', 'nullable_source': None},
{'id': 1, 'name': 'target-1', 'nullable_source': 1},
{'id': 2, 'name': 'target-2', 'nullable_source': None},
]
self.assertEquals(serializer.data, expected)
......@@ -236,17 +236,17 @@ class ValidationTests(TestCase):
data = ['i am', 'a', 'list']
serializer = CommentSerializer(self.comment, data=data)
self.assertEquals(serializer.is_valid(), False)
self.assertEquals(serializer.errors, {'non_field_errors': [u'Invalid data']})
self.assertEquals(serializer.errors, {'non_field_errors': ['Invalid data']})
data = 'and i am a string'
serializer = CommentSerializer(self.comment, data=data)
self.assertEquals(serializer.is_valid(), False)
self.assertEquals(serializer.errors, {'non_field_errors': [u'Invalid data']})
self.assertEquals(serializer.errors, {'non_field_errors': ['Invalid data']})
data = 42
serializer = CommentSerializer(self.comment, data=data)
self.assertEquals(serializer.is_valid(), False)
self.assertEquals(serializer.errors, {'non_field_errors': [u'Invalid data']})
self.assertEquals(serializer.errors, {'non_field_errors': ['Invalid data']})
def test_cross_field_validation(self):
......@@ -300,7 +300,7 @@ class ValidationTests(TestCase):
}
serializer = ActionItemSerializerCustomRestore(data=data)
self.assertEquals(serializer.is_valid(), False)
self.assertEquals(serializer.errors, {'title': [u'Ensure this value has at most 200 characters (it has 201).']})
self.assertEquals(serializer.errors, {'title': ['Ensure this value has at most 200 characters (it has 201).']})
def test_default_modelfield_max_length_exceeded(self):
data = {
......@@ -340,7 +340,7 @@ class CustomValidationTests(TestCase):
serializer = self.CommentSerializerWithFieldValidator(data=data)
self.assertFalse(serializer.is_valid())
self.assertEquals(serializer.errors, {'content': [u'Test not in value']})
self.assertEquals(serializer.errors, {'content': ['Test not in value']})
def test_missing_data(self):
"""
......@@ -352,7 +352,7 @@ class CustomValidationTests(TestCase):
}
serializer = self.CommentSerializerWithFieldValidator(data=incomplete_data)
self.assertFalse(serializer.is_valid())
self.assertEquals(serializer.errors, {'content': [u'This field is required.']})
self.assertEquals(serializer.errors, {'content': ['This field is required.']})
def test_wrong_data(self):
"""
......@@ -365,7 +365,7 @@ class CustomValidationTests(TestCase):
}
serializer = self.CommentSerializerWithFieldValidator(data=wrong_data)
self.assertFalse(serializer.is_valid())
self.assertEquals(serializer.errors, {'email': [u'Enter a valid e-mail address.']})
self.assertEquals(serializer.errors, {'email': ['Enter a valid e-mail address.']})
class PositiveIntegerAsChoiceTests(TestCase):
......
from django.test.client import RequestFactory, FakePayload
from django.test.client import MULTIPART_CONTENT
from urlparse import urlparse
from rest_framework.compat import urlparse
class RequestFactory(RequestFactory):
......@@ -14,7 +14,7 @@ class RequestFactory(RequestFactory):
patch_data = self._encode_data(data, content_type)
parsed = urlparse(path)
parsed = urlparse.urlparse(path)
r = {
'CONTENT_LENGTH': len(patch_data),
'CONTENT_TYPE': content_type,
......
......@@ -139,7 +139,7 @@
# raise errors on unexpected request data"""
# content = {'qwerty': 'uiop', 'extra': 'extra'}
# validator.allow_unknown_form_fields = True
# self.assertEqual({'qwerty': u'uiop'},
# self.assertEqual({'qwerty': 'uiop'},
# validator.validate_request(content, None),
# "Resource didn't accept unknown fields.")
# validator.allow_unknown_form_fields = False
......
[tox]
downloadcache = {toxworkdir}/cache/
envlist = py2.7-django1.5,py2.7-django1.4,py2.7-django1.3,py2.6-django1.5,py2.6-django1.4,py2.6-django1.3
envlist = py3.3-django1.5,py3.2-django1.5,py2.7-django1.5,py2.7-django1.4,py2.7-django1.3,py2.6-django1.5,py2.6-django1.4,py2.6-django1.3
[testenv]
commands = {envpython} rest_framework/runtests/runtests.py
[testenv:py2.7-django1.5]
basepython = python2.7
deps = https://github.com/django/django/zipball/master
[testenv:py3.3-django1.5]
basepython = python3.3
deps = https://www.djangoproject.com/download/1.5c1/tarball/
django-filter==0.5.4
[testenv:py2.7-django1.4]
basepython = python2.7
deps = django==1.4.3
[testenv:py3.2-django1.5]
basepython = python3.2
deps = https://www.djangoproject.com/download/1.5c1/tarball/
django-filter==0.5.4
[testenv:py2.7-django1.3]
[testenv:py2.7-django1.5]
basepython = python2.7
deps = django==1.3.5
deps = https://www.djangoproject.com/download/1.5c1/tarball/
django-filter==0.5.4
[testenv:py2.6-django1.5]
basepython = python2.6
deps = https://github.com/django/django/zipball/master
deps = https://www.djangoproject.com/download/1.5c1/tarball/
django-filter==0.5.4
[testenv:py2.7-django1.4]
basepython = python2.7
deps = django==1.4.3
django-filter==0.5.4
[testenv:py2.6-django1.4]
......@@ -30,7 +35,14 @@ basepython = python2.6
deps = django==1.4.3
django-filter==0.5.4
[testenv:py2.7-django1.3]
basepython = python2.7
deps = django==1.3.5
django-filter==0.5.4
six
[testenv:py2.6-django1.3]
basepython = python2.6
deps = django==1.3.5
django-filter==0.5.4
six
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment