Skip to content

Commit

Permalink
Merge pull request #80 from gchin/master
Browse files Browse the repository at this point in the history
Add `validation_context` setting which allows the user to specify a contextmanager to custom handle request/response validation exceptions + docs
  • Loading branch information
striglia committed Jan 29, 2015
2 parents 383d358 + 97bd662 commit 95a5a1c
Show file tree
Hide file tree
Showing 5 changed files with 71 additions and 25 deletions.
6 changes: 6 additions & 0 deletions docs/changelog.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
Changelog
=========

1.4.0 (2015-01-27)
++++++++++++++++++
* Added `validation_context_path` setting which allows the user to specify a
path to a contextmanager to custom handle request/response validation
exceptions.

1.3.0 (2014-12-02)
++++++++++++++++++
* Now throws RequestValidationError and ResponseValidationError instead of
Expand Down
6 changes: 6 additions & 0 deletions docs/configuration.rst
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,12 @@ A few relevant settings for your `Pyramid .ini file <http://docs.pylonsproject.o
# Default: [r'^/static/?', r'^/api-docs/?']
pyramid_swagger.exclude_paths = [r'^/static/?', r'^/api-docs/?']
# Path to contextmanager to handle request/response validation
# exceptions. This should be a dotted python name as per
# http://docs.pylonsproject.org/projects/pyramid/en/latest/glossary.html#term-dotted-python-name
# Default: None
pyramid_swagger.validation_context_path = 'path.to.user.defined.contextmanager'
Note that, equivalently, you can add these during webapp configuration:

.. code-block:: python
Expand Down
31 changes: 25 additions & 6 deletions pyramid_swagger/tween.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,27 @@ def noop_context(request, response=None):
yield


def _get_validation_context(registry):
validation_context_path = registry.settings.get(
'pyramid_swagger.validation_context_path',
)

if validation_context_path:
m = re.match(
'(?P<module_path>.*)\.(?P<contextmanager_name>.*)',
validation_context_path,
)
module_path = m.group('module_path')
contextmanager_name = m.group('contextmanager_name')

return getattr(
__import__(module_path, fromlist=contextmanager_name),
contextmanager_name,
)
else:
return noop_context


def validation_tween_factory(handler, registry):
"""Pyramid tween for performing validation.
Expand All @@ -80,20 +101,18 @@ def validator_tween(request):
should_exclude_path(settings.exclude_paths, request.path):
return handler(request)

validation_context = _get_validation_context(registry)

try:
schema_data, resolver = schema.schema_and_resolver_for_request(
request)
except PathNotMatchedError as exc:
if settings.validate_path:
raise RequestValidationError(str(exc))
with validation_context(request):
raise RequestValidationError(str(exc))
else:
return handler(request)

validation_context = registry.settings.get(
'pyramid_swagger.validation_context',
noop_context,
)

if settings.validate_request:
with validation_context(request):
_validate_request(
Expand Down
26 changes: 18 additions & 8 deletions tests/acceptance/request_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,17 @@ def test_app(**overrides):
return TestApp(main({}, **settings))


@contextmanager
def validation_context(request, response=None):
try:
yield
except Exception:
raise exception_response(206)


validation_ctx_path = 'tests.acceptance.request_test.validation_context'


def test_400_if_required_query_args_absent(test_app):
assert test_app.get(
'/sample/path_arg1/resource',
Expand Down Expand Up @@ -154,14 +165,13 @@ def test_200_skip_validation_when_disabled():
.status_code == 200


def test_path_validation_context():
assert test_app(**{'pyramid_swagger.validation_context_path': validation_ctx_path}) \
.get('/does_not_exist') \
.status_code == 206


def test_request_validation_context():
@contextmanager
def validation_context(request, response=None):
try:
yield
except Exception:
raise exception_response(206)

assert test_app(**{'pyramid_swagger.validation_context': validation_context}) \
assert test_app(**{'pyramid_swagger.validation_context_path': validation_ctx_path}) \
.get('/get_with_non_string_query_args', params={}) \
.status_code == 206
27 changes: 16 additions & 11 deletions tests/acceptance/response_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,21 @@
from webtest import AppError


class CustomResponseValidationException(Exception):
pass


@contextmanager
def validation_context(request, response=None):
try:
yield
except Exception:
raise CustomResponseValidationException


validation_ctx_path = 'tests.acceptance.response_test.validation_context'


def get_registry(settings):
registry = Registry('testing')
config = Configurator(registry=registry)
Expand Down Expand Up @@ -161,16 +176,6 @@ def test_app_error_if_path_not_in_spec_and_path_validation_disabled():


def test_response_validation_context():
class CustomResponseValidationException(Exception):
pass

@contextmanager
def validation_context(request, response=None):
try:
yield
except Exception:
raise CustomResponseValidationException

request = pyramid.testing.DummyRequest(
method='GET',
path='/sample/path_arg1/resource',
Expand All @@ -186,5 +191,5 @@ def validation_context(request, response=None):
_validate_against_tween(
request,
response=response,
**{'pyramid_swagger.validation_context': validation_context}
**{'pyramid_swagger.validation_context_path': validation_ctx_path}
)

0 comments on commit 95a5a1c

Please sign in to comment.