Skip to content

Commit

Permalink
Clarify situation of extract_json_data
Browse files Browse the repository at this point in the history
  • Loading branch information
leplatrem committed Oct 25, 2016
1 parent 88d0cdb commit 6339c2f
Show file tree
Hide file tree
Showing 8 changed files with 92 additions and 29 deletions.
5 changes: 4 additions & 1 deletion CHANGES.txt
Expand Up @@ -6,7 +6,10 @@ CHANGELOG
2.1.0 (unreleased)
==================

- Nothing changed yet.
**Internal changes**

- Deprecate ``cornice.util.extract_json_data()`` and ``cornice.util.extract_form_urlencoded_data()``
in favor of ``cornice.validators.extract_cstruct()``


2.0.1 (2016-10-24)
Expand Down
24 changes: 7 additions & 17 deletions cornice/util.py
Expand Up @@ -2,6 +2,7 @@
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
# You can obtain one at http://mozilla.org/MPL/2.0/.
import sys
import warnings

import json
import simplejson
Expand Down Expand Up @@ -144,26 +145,15 @@ def match_content_type_header(func, context, request):


def extract_json_data(request):
if request.body:
try:
body = simplejson.loads(request.body)
if isinstance(body, dict):
return body
request.errors.add(
'body', None,
"Invalid JSON: Should be a JSON object, got %s" % body
)
return {}
except ValueError as e:
request.errors.add(
'body', None,
"Invalid JSON request body: %s" % e)
return {}
else:
return {}
warnings.warn("Use ``cornice.validators.extract_cstruct()`` instead",
DeprecationWarning)
from cornice.validators import extract_cstruct
return extract_cstruct(request)['body']


def extract_form_urlencoded_data(request):
warnings.warn("Use ``cornice.validators.extract_cstruct()`` instead",
DeprecationWarning)
return request.POST


Expand Down
11 changes: 11 additions & 0 deletions cornice/validators/__init__.py
Expand Up @@ -19,6 +19,17 @@


def extract_cstruct(request):
"""
Extract attributes from the specified `request` such as body, url, path,
method, querystring, headers, cookies, and returns them in a single dict
object.
:param request: Current request
:type request: :class:`~pyramid:pyramid.request.Request`
:returns: A mapping containing most request attributes.
:rtype: dict
"""
if request.content_type == 'application/x-www-form-urlencoded':
body = request.POST.mixed()

Expand Down
48 changes: 40 additions & 8 deletions cornice/validators/_colander.py
Expand Up @@ -3,30 +3,62 @@
# You can obtain one at http://mozilla.org/MPL/2.0/.


def body_validator(request, **kwargs):
def body_validator(request, schema=None, deserializer=None, **kwargs):
"""
Validate the body against the schema defined on the service.
The content of the body is deserialized, validated and stored in the
``request.validated`` attribute.
.. note::
If no schema is defined, this validator does nothing.
:param request: Current request
:type request: :class:`~pyramid:pyramid.request.Request`
:param schema: The Colander schema class
:param deserializer: Optional deserializer, defaults to
:func:`cornice.validators.extract_cstruct`
"""
import colander

schema = kwargs.get('schema')
if schema:
if schema is not None:
class RequestSchema(colander.MappingSchema):
body = schema()

def deserialize(self, cstruct=colander.null):
appstruct = super(RequestSchema, self).deserialize(cstruct)
return appstruct['body']
kwargs['schema'] = RequestSchema
return validator(request, **kwargs)
schema = RequestSchema
return validator(request, schema, deserializer, **kwargs)


def validator(request, schema=None, deserializer=None, **kwargs):
"""
Validate the full request against the schema defined on the service.
def validator(request, deserializer=None, **kw):
Each attribute of the request is deserialized, validated and stored in the
``request.validated`` attribute
(eg. body in ``request.validated['body']``).
.. note::
If no schema is defined, this validator does nothing.
:param request: Current request
:type request: :class:`~pyramid:pyramid.request.Request`
:param schema: The Colander schema class
:param deserializer: Optional deserializer, defaults to
:func:`cornice.validators.extract_cstruct`
"""
import colander
from cornice.validators import extract_cstruct

if deserializer is None:
deserializer = extract_cstruct

schema = kw.get('schema')

if schema is None:
raise TypeError('This validator cannot work without a schema')

Expand Down
8 changes: 8 additions & 0 deletions docs/source/api.rst
Expand Up @@ -22,6 +22,14 @@ Resource
.. autofunction:: cornice.resource.add_resource


Validation
==========

.. autofunction:: cornice.validators.extract_cstruct
.. autofunction:: cornice.validators.colander_body_validator
.. autofunction:: cornice.validators.colander_validator


Errors
======

Expand Down
2 changes: 1 addition & 1 deletion docs/source/conf.py
Expand Up @@ -7,7 +7,7 @@
print("please install the 'mozilla-sphinx-theme' distribution")

sys.path.insert(0, os.path.abspath("../..")) # include cornice from the source
extensions = []
extensions = ['sphinx.ext.autodoc']

templates_path = ['_templates']
source_suffix = '.rst'
Expand Down
6 changes: 4 additions & 2 deletions docs/source/upgrading.rst
Expand Up @@ -216,16 +216,18 @@ accessed via the validator kwargs:

.. code-block:: python
from cornice.validators import extract_cstruct
def my_validator(request, deserializer=None, **kwargs):
if deserializer is None:
deserializer = extract_json_data
deserializer = extract_cstruct
data = deserializer(request)
...
.. note::

The built-in ``colander_validator`` supports custom deserializers and defaults
to the built-in JSON deserializer.
to the built-in JSON deserializer ``cornice.validators.extract_cstruct``.

.. note::

Expand Down
17 changes: 17 additions & 0 deletions tests/test_util.py
@@ -0,0 +1,17 @@
import mock
import unittest

from cornice import util


class TestDeprecatedUtils(unittest.TestCase):

def test_extract_json_data_is_deprecated(self):
with mock.patch('cornice.util.warnings') as mocked:
util.extract_json_data(mock.MagicMock())
self.assertTrue(mocked.warn.called)

def test_extract_form_urlencoded_data_is_deprecated(self):
with mock.patch('cornice.util.warnings') as mocked:
util.extract_form_urlencoded_data(mock.MagicMock())
self.assertTrue(mocked.warn.called)

0 comments on commit 6339c2f

Please sign in to comment.