diff --git a/README.md b/README.md
index 5d0306bb6..1abb17bfd 100644
--- a/README.md
+++ b/README.md
@@ -53,7 +53,7 @@ Add `'rest_framework'` to your `INSTALLED_APPS` setting.
Let's take a look at a quick example of using REST framework to build a simple model-backed API for accessing users and groups.
-Startup up a new project like so...
+Startup up a new project like so...
pip install django
pip install djangorestframework
@@ -79,7 +79,7 @@ class UserViewSet(viewsets.ModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
-
+
# Routers provide a way of automatically determining the URL conf.
router = routers.DefaultRouter()
router.register(r'users', UserViewSet)
@@ -100,7 +100,7 @@ Add the following to your `settings.py` module:
```python
INSTALLED_APPS = (
... # Make sure to include the default installed apps here.
- 'rest_framework',
+ 'rest_framework',
)
REST_FRAMEWORK = {
@@ -123,10 +123,10 @@ You can also interact with the API using command line tools such as [`curl`](htt
$ curl -H 'Accept: application/json; indent=4' -u admin:password http://127.0.0.1:8000/users/
[
{
- "url": "http://127.0.0.1:8000/users/1/",
- "username": "admin",
- "email": "admin@example.com",
- "is_staff": true,
+ "url": "http://127.0.0.1:8000/users/1/",
+ "username": "admin",
+ "email": "admin@example.com",
+ "is_staff": true,
}
]
@@ -134,10 +134,10 @@ Or to create a new user:
$ curl -X POST -d username=new -d email=new@example.com -d is_staff=false -H 'Accept: application/json; indent=4' -u admin:password http://127.0.0.1:8000/users/
{
- "url": "http://127.0.0.1:8000/users/2/",
- "username": "new",
- "email": "new@example.com",
- "is_staff": false,
+ "url": "http://127.0.0.1:8000/users/2/",
+ "username": "new",
+ "email": "new@example.com",
+ "is_staff": false,
}
# Documentation & Support
@@ -159,24 +159,24 @@ Send a description of the issue via email to [rest-framework-security@googlegrou
Copyright (c) 2011-2014, Tom Christie
All rights reserved.
-Redistribution and use in source and binary forms, with or without
+Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
-Redistributions of source code must retain the above copyright notice, this
+Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
-Redistributions in binary form must reproduce the above copyright notice, this
-list of conditions and the following disclaimer in the documentation and/or
+Redistributions in binary form must reproduce the above copyright notice, this
+list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
-ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
-FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
-CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
-OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -215,6 +215,5 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
[urlobject]: https://github.com/zacharyvoase/urlobject
[markdown]: http://pypi.python.org/pypi/Markdown/
[pyyaml]: http://pypi.python.org/pypi/PyYAML
-[defusedxml]: https://pypi.python.org/pypi/defusedxml
[django-filter]: http://pypi.python.org/pypi/django-filter
[security-mail]: mailto:rest-framework-security@googlegroups.com
diff --git a/docs/api-guide/parsers.md b/docs/api-guide/parsers.md
index 73e3a7057..32819146e 100644
--- a/docs/api-guide/parsers.md
+++ b/docs/api-guide/parsers.md
@@ -78,18 +78,6 @@ Requires the `pyyaml` package to be installed.
**.media_type**: `application/yaml`
-## XMLParser
-
-Parses REST framework's default style of `XML` request content.
-
-Note that the `XML` markup language is typically used as the base language for more strictly defined domain-specific languages, such as `RSS`, `Atom`, and `XHTML`.
-
-If you are considering using `XML` for your API, you may want to consider implementing a custom renderer and parser for your specific requirements, and using an existing domain-specific media-type, or creating your own custom XML-based media-type.
-
-Requires the `defusedxml` package to be installed.
-
-**.media_type**: `application/xml`
-
## FormParser
Parses HTML form content. `request.data` will be populated with a `QueryDict` of data.
@@ -161,7 +149,7 @@ By default this will include the following keys: `view`, `request`, `args`, `kwa
## Example
-The following is an example plaintext parser that will populate the `request.data` property with a string representing the body of the request.
+The following is an example plaintext parser that will populate the `request.data` property with a string representing the body of the request.
class PlainTextParser(BaseParser):
"""
diff --git a/docs/api-guide/renderers.md b/docs/api-guide/renderers.md
index 035ec1d27..47bf2e601 100644
--- a/docs/api-guide/renderers.md
+++ b/docs/api-guide/renderers.md
@@ -145,20 +145,6 @@ Note that non-ascii characters will not be character escaped. For example:
**.charset**: `utf-8`
-## XMLRenderer
-
-Renders REST framework's default style of `XML` response content.
-
-Note that the `XML` markup language is used typically used as the base language for more strictly defined domain-specific languages, such as `RSS`, `Atom`, and `XHTML`.
-
-If you are considering using `XML` for your API, you may want to consider implementing a custom renderer and parser for your specific requirements, and using an existing domain-specific media-type, or creating your own custom XML-based media-type.
-
-**.media_type**: `application/xml`
-
-**.format**: `'.xml'`
-
-**.charset**: `utf-8`
-
## TemplateHTMLRenderer
Renders data to HTML, using Django's standard template rendering.
diff --git a/docs/index.md b/docs/index.md
index dc7bb6d08..ac5d5a016 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -55,7 +55,6 @@ The following packages are optional:
* [Markdown][markdown] (2.1.0+) - Markdown support for the browsable API.
* [PyYAML][yaml] (3.10+) - YAML content-type support.
-* [defusedxml][defusedxml] (0.3+) - XML content-type support.
* [django-filter][django-filter] (0.5.4+) - Filtering support.
* [django-restframework-oauth][django-restframework-oauth] package for OAuth 1.0a and 2.0 support.
* [django-guardian][django-guardian] (1.1.1+) - Object level permissions support.
@@ -256,7 +255,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
[eventbrite]: https://www.eventbrite.co.uk/about/
[markdown]: http://pypi.python.org/pypi/Markdown/
[yaml]: http://pypi.python.org/pypi/PyYAML
-[defusedxml]: https://pypi.python.org/pypi/defusedxml
[django-filter]: http://pypi.python.org/pypi/django-filter
[django-restframework-oauth]: https://github.com/jlafon/django-rest-framework-oauth
[django-guardian]: https://github.com/lukaszb/django-guardian
diff --git a/requirements-test.txt b/requirements-test.txt
index 19b2c610a..2fb859da7 100644
--- a/requirements-test.txt
+++ b/requirements-test.txt
@@ -7,7 +7,6 @@ flake8==2.2.2
# Optional packages
markdown>=2.1.0
PyYAML>=3.10
-defusedxml>=0.3
django-guardian==1.2.4
django-filter>=0.5.4
Pillow==2.3.0
diff --git a/rest_framework/compat.py b/rest_framework/compat.py
index da2496d86..06da37701 100644
--- a/rest_framework/compat.py
+++ b/rest_framework/compat.py
@@ -244,13 +244,6 @@ except ImportError:
yaml = None
-# XML is optional
-try:
- import defusedxml.ElementTree as etree
-except ImportError:
- etree = None
-
-
# `seperators` argument to `json.dumps()` differs between 2.x and 3.x
# See: http://bugs.python.org/issue22767
if six.PY3:
diff --git a/rest_framework/parsers.py b/rest_framework/parsers.py
index ccb82f03b..6d0e932bd 100644
--- a/rest_framework/parsers.py
+++ b/rest_framework/parsers.py
@@ -12,12 +12,10 @@ from django.http import QueryDict
from django.http.multipartparser import MultiPartParser as DjangoMultiPartParser
from django.http.multipartparser import MultiPartParserError, parse_header, ChunkIter
from django.utils import six
-from rest_framework.compat import etree, yaml, force_text, urlparse
+from rest_framework.compat import yaml, force_text, urlparse
from rest_framework.exceptions import ParseError
from rest_framework import renderers
import json
-import datetime
-import decimal
class DataAndFiles(object):
@@ -136,78 +134,6 @@ class MultiPartParser(BaseParser):
raise ParseError('Multipart form parse error - %s' % six.text_type(exc))
-class XMLParser(BaseParser):
- """
- XML parser.
- """
-
- media_type = 'application/xml'
-
- def parse(self, stream, media_type=None, parser_context=None):
- """
- Parses the incoming bytestream as XML and returns the resulting data.
- """
- assert etree, 'XMLParser requires defusedxml to be installed'
-
- parser_context = parser_context or {}
- encoding = parser_context.get('encoding', settings.DEFAULT_CHARSET)
- parser = etree.DefusedXMLParser(encoding=encoding)
- try:
- tree = etree.parse(stream, parser=parser, forbid_dtd=True)
- except (etree.ParseError, ValueError) as exc:
- raise ParseError('XML parse error - %s' % six.text_type(exc))
- data = self._xml_convert(tree.getroot())
-
- return data
-
- def _xml_convert(self, element):
- """
- convert the xml `element` into the corresponding python object
- """
-
- children = list(element)
-
- if len(children) == 0:
- return self._type_convert(element.text)
- else:
- # if the fist child tag is list-item means all children are list-item
- if children[0].tag == "list-item":
- data = []
- for child in children:
- data.append(self._xml_convert(child))
- else:
- data = {}
- for child in children:
- data[child.tag] = self._xml_convert(child)
-
- return data
-
- def _type_convert(self, value):
- """
- Converts the value returned by the XMl parse into the equivalent
- Python type
- """
- if value is None:
- return value
-
- try:
- return datetime.datetime.strptime(value, '%Y-%m-%d %H:%M:%S')
- except ValueError:
- pass
-
- try:
- return int(value)
- except ValueError:
- pass
-
- try:
- return decimal.Decimal(value)
- except decimal.InvalidOperation:
- pass
-
- return value
-
-
class FileUploadParser(BaseParser):
"""
Parser for file upload data.
diff --git a/rest_framework/renderers.py b/rest_framework/renderers.py
index e87d16d0d..dd49ae828 100644
--- a/rest_framework/renderers.py
+++ b/rest_framework/renderers.py
@@ -16,11 +16,8 @@ from django.http.multipartparser import parse_header
from django.template import Context, RequestContext, loader, Template
from django.test.client import encode_multipart
from django.utils import six
-from django.utils.xmlutils import SimplerXMLGenerator
from rest_framework import exceptions, serializers, status, VERSION
-from rest_framework.compat import (
- SHORT_SEPARATORS, LONG_SEPARATORS, StringIO, smart_text, yaml
-)
+from rest_framework.compat import SHORT_SEPARATORS, LONG_SEPARATORS, yaml
from rest_framework.exceptions import ParseError
from rest_framework.settings import api_settings
from rest_framework.request import is_form_media_type, override_method
@@ -140,55 +137,6 @@ class JSONPRenderer(JSONRenderer):
return callback.encode(self.charset) + b'(' + json + b');'
-class XMLRenderer(BaseRenderer):
- """
- Renderer which serializes to XML.
- """
-
- media_type = 'application/xml'
- format = 'xml'
- charset = 'utf-8'
-
- def render(self, data, accepted_media_type=None, renderer_context=None):
- """
- Renders `data` into serialized XML.
- """
- if data is None:
- return ''
-
- stream = StringIO()
-
- xml = SimplerXMLGenerator(stream, self.charset)
- xml.startDocument()
- xml.startElement("root", {})
-
- self._to_xml(xml, data)
-
- xml.endElement("root")
- xml.endDocument()
- return stream.getvalue()
-
- def _to_xml(self, xml, data):
- if isinstance(data, (list, tuple)):
- for item in data:
- xml.startElement("list-item", {})
- self._to_xml(xml, item)
- xml.endElement("list-item")
-
- elif isinstance(data, dict):
- for key, value in six.iteritems(data):
- xml.startElement(key, {})
- self._to_xml(xml, value)
- xml.endElement(key)
-
- elif data is None:
- # Don't output any value
- pass
-
- else:
- xml.characters(smart_text(data))
-
-
class YAMLRenderer(BaseRenderer):
"""
Renderer which serializes to YAML.
diff --git a/tests/test_parsers.py b/tests/test_parsers.py
index 3f2672df0..32fb05955 100644
--- a/tests/test_parsers.py
+++ b/tests/test_parsers.py
@@ -1,15 +1,11 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
-from rest_framework.compat import StringIO
from django import forms
from django.core.files.uploadhandler import MemoryFileUploadHandler
from django.test import TestCase
-from django.utils import unittest
-from rest_framework.compat import etree
+from rest_framework.compat import StringIO
from rest_framework.parsers import FormParser, FileUploadParser
-from rest_framework.parsers import XMLParser
-import datetime
class Form(forms.Form):
@@ -31,62 +27,6 @@ class TestFormParser(TestCase):
self.assertEqual(Form(data).is_valid(), True)
-class TestXMLParser(TestCase):
- def setUp(self):
- self._input = StringIO(
- ''
- ''
- '121.0'
- 'dasd'
- ''
- '2011-12-25 12:45:00'
- ''
- )
- self._data = {
- 'field_a': 121,
- 'field_b': 'dasd',
- 'field_c': None,
- 'field_d': datetime.datetime(2011, 12, 25, 12, 45, 00)
- }
- self._complex_data_input = StringIO(
- ''
- ''
- '2011-12-25 12:45:00'
- ''
- '1first'
- '2second'
- ''
- 'name'
- ''
- )
- self._complex_data = {
- "creation_date": datetime.datetime(2011, 12, 25, 12, 45, 00),
- "name": "name",
- "sub_data_list": [
- {
- "sub_id": 1,
- "sub_name": "first"
- },
- {
- "sub_id": 2,
- "sub_name": "second"
- }
- ]
- }
-
- @unittest.skipUnless(etree, 'defusedxml not installed')
- def test_parse(self):
- parser = XMLParser()
- data = parser.parse(self._input)
- self.assertEqual(data, self._data)
-
- @unittest.skipUnless(etree, 'defusedxml not installed')
- def test_complex_data_parse(self):
- parser = XMLParser()
- data = parser.parse(self._complex_data_input)
- self.assertEqual(data, self._complex_data)
-
-
class TestFileUploadParser(TestCase):
def setUp(self):
class MockRequest(object):
diff --git a/tests/test_renderers.py b/tests/test_renderers.py
index 416d7f224..1eec37dc3 100644
--- a/tests/test_renderers.py
+++ b/tests/test_renderers.py
@@ -6,19 +6,18 @@ from django.conf.urls import patterns, url, include
from django.core.cache import cache
from django.db import models
from django.test import TestCase
-from django.utils import six, unittest
+from django.utils import six
from django.utils.translation import ugettext_lazy as _
from rest_framework import status, permissions
-from rest_framework.compat import yaml, etree, StringIO, BytesIO
+from rest_framework.compat import yaml, BytesIO
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework.renderers import BaseRenderer, JSONRenderer, YAMLRenderer, \
- XMLRenderer, JSONPRenderer, BrowsableAPIRenderer
-from rest_framework.parsers import YAMLParser, XMLParser
+ JSONPRenderer, BrowsableAPIRenderer
+from rest_framework.parsers import YAMLParser
from rest_framework.settings import api_settings
from rest_framework.test import APIRequestFactory
from collections import MutableMapping
-import datetime
import json
import pickle
import re
@@ -501,104 +500,6 @@ if yaml:
self.assertEqual(content.strip(), 'countries: [United Kingdom, France, EspaƱa]'.encode('utf-8'))
-class XMLRendererTestCase(TestCase):
- """
- Tests specific to the XML Renderer
- """
-
- _complex_data = {
- "creation_date": datetime.datetime(2011, 12, 25, 12, 45, 00),
- "name": "name",
- "sub_data_list": [
- {
- "sub_id": 1,
- "sub_name": "first"
- },
- {
- "sub_id": 2,
- "sub_name": "second"
- }
- ]
- }
-
- def test_render_string(self):
- """
- Test XML rendering.
- """
- renderer = XMLRenderer()
- content = renderer.render({'field': 'astring'}, 'application/xml')
- self.assertXMLContains(content, 'astring')
-
- def test_render_integer(self):
- """
- Test XML rendering.
- """
- renderer = XMLRenderer()
- content = renderer.render({'field': 111}, 'application/xml')
- self.assertXMLContains(content, '111')
-
- def test_render_datetime(self):
- """
- Test XML rendering.
- """
- renderer = XMLRenderer()
- content = renderer.render({
- 'field': datetime.datetime(2011, 12, 25, 12, 45, 00)
- }, 'application/xml')
- self.assertXMLContains(content, '2011-12-25 12:45:00')
-
- def test_render_float(self):
- """
- Test XML rendering.
- """
- renderer = XMLRenderer()
- content = renderer.render({'field': 123.4}, 'application/xml')
- self.assertXMLContains(content, '123.4')
-
- def test_render_decimal(self):
- """
- Test XML rendering.
- """
- renderer = XMLRenderer()
- content = renderer.render({'field': Decimal('111.2')}, 'application/xml')
- self.assertXMLContains(content, '111.2')
-
- def test_render_none(self):
- """
- Test XML rendering.
- """
- renderer = XMLRenderer()
- content = renderer.render({'field': None}, 'application/xml')
- self.assertXMLContains(content, '')
-
- def test_render_complex_data(self):
- """
- Test XML rendering.
- """
- renderer = XMLRenderer()
- content = renderer.render(self._complex_data, 'application/xml')
- self.assertXMLContains(content, 'first')
- self.assertXMLContains(content, 'second')
-
- @unittest.skipUnless(etree, 'defusedxml not installed')
- def test_render_and_parse_complex_data(self):
- """
- Test XML rendering.
- """
- renderer = XMLRenderer()
- content = StringIO(renderer.render(self._complex_data, 'application/xml'))
-
- parser = XMLParser()
- complex_data_out = parser.parse(content)
- error_msg = "complex data differs!IN:\n %s \n\n OUT:\n %s" % (repr(self._complex_data), repr(complex_data_out))
- self.assertEqual(self._complex_data, complex_data_out, error_msg)
-
- def assertXMLContains(self, xml, string):
- self.assertTrue(xml.startswith('\n'))
- self.assertTrue(xml.endswith(''))
- self.assertTrue(string in xml, '%r not in %r' % (string, xml))
-
-
# Tests for caching issue, #346
class CacheRenderTest(TestCase):
"""