diff --git a/docs/source/quick_start.rst b/docs/source/quick_start.rst
index 5eeadcf..d8c2aa9 100644
--- a/docs/source/quick_start.rst
+++ b/docs/source/quick_start.rst
@@ -157,15 +157,9 @@ This is how it looks in the project template
(``test_project.controllers.root.RootController``)::
from pecan import expose
- from formencode import Schema, validators as v
from webob.exc import status_map
- class SampleForm(Schema):
- name = v.String(not_empty=True)
- age = v.Int(not_empty=True)
-
-
class RootController(object):
@expose(
@@ -175,13 +169,7 @@ This is how it looks in the project template
def index(self):
return dict()
- @index.when(
- method = 'POST',
- template = 'success.html',
- schema = SampleForm(),
- error_handler = '/index',
- htmlfill = dict(auto_insert_errors = True, prefix_error = False)
- )
+ @index.when(method='POST')
def index_post(self, name, age):
return dict(name=name)
diff --git a/docs/source/routing.rst b/docs/source/routing.rst
index 9fc0dd5..f936254 100644
--- a/docs/source/routing.rst
+++ b/docs/source/routing.rst
@@ -178,11 +178,6 @@ parameters, some of which can impact routing.
expose(template = None,
content_type = 'text/html',
- schema = None,
- json_schema = None,
- variable_decode = False,
- error_handler = None,
- htmlfill = None,
generic = False)
diff --git a/docs/source/validation_n_errors.rst b/docs/source/validation_n_errors.rst
index 4543371..facc4d6 100644
--- a/docs/source/validation_n_errors.rst
+++ b/docs/source/validation_n_errors.rst
@@ -2,166 +2,5 @@
Validation and Error Handling
=============================
-Pecan provides a variety of tools to help you handle common form validation and
-error handling activities, like:
-
-* Validating the presence of submitted form contents with a schema.
-* Transforming strings from form submissions into useful Python objects.
-* Simplifying the process of re-displaying form values and associated error messages inline.
-
-Rather than re-inventing the wheel, Pecan uses `FormEncode `_ for schemas and form validation.
-
-Writing and Applying Schemas
-------------------------------
-Here's a simple example of a schema and how to apply it to a controller method using
-Pecan's ``expose`` decorator::
-
- from pecan import expose
- from formencode import Schema, validators as v
-
- class SimpleSchema(Schema):
- username = v.String(not_empty=True)
- password = v.String(not_empty=True)
-
- class LoginController(object):
-
- @expose(schema=SimpleSchema)
- def login(self, **kw):
- if authenticate(
- kw['username'],
- kw['password']
- ):
- set_cookie()
- return dict()
-
-Validating JSON Content
-------------------------------
-In addition to simple form arguments, Pecan also makes it easy to validate JSON request bodies.
-Often, especially in AJAX requests, the request content is encoded as JSON in the request body.
-Pecan's validation can handle the decoding for you and apply schema validation to the decoded
-data structure::
-
- from pecan import expose
- from formencode import Schema, validators as v
- from myproject.lib import authenticate
-
- class JSONSchema(Schema):
- """
- This schema would decode a JSON request body
- that looked like:
-
- {
- 'username' : 'pecan',
- 'password' : 'dotpy
- }
-
- """
- username = v.String(not_empty=True)
- password = v.String(not_empty=True)
-
- class LoginController(object):
-
- @expose(json_schema=JSONSchema)
- def login(self, **kw):
- authenticate(
- kw['username'],
- kw['password']
- )
- return dict()
-
-Handling Schema Failures
-------------------------------
-When schema validation fails, the validation errors from FormEncode are applied to Pecan's
-request object (``pecan.request.pecan``) as a dictionary.
-The key where the actual errors go is ``validation_errors`` and this can be
-inspected by your controller methods to react to errors appropiately::
-
- from pecan import expose, request
- from myproject.schemas import SimpleSchema
-
- class LoginController(object):
-
- @expose(schema=SimpleSchema)
- def login(self, **kw):
- if request.pecan['validation_errors']:
- pass # Don't Panic!
- return dict()
-
-Error Handlers and Template Filling
-------------------------------
-When schema validation fails, Pecan allows you to redirect to another controller internally
-for error handling via the `error_handler` keyword argument to ``@expose()``.
-This is especially useful when used in combination with generic
-controller methods::
-
- from pecan import request, expose
- from formencode import Schema, validators as v
-
- class ProfileSchema(Schema):
- name = v.String(not_empty=True)
- email = v.String(not_empty=True)
-
- class ProfileController(object):
-
- @expose(generic=True)
- def index(self):
- pass
-
- @index.when(method="GET", template='profile.html')
- def index_get(self):
- """
- This method will be called to render the original template.
- It will also be used for generating a form pre-filled with values
- when schema failures occur.
- """
- return dict()
-
- @index.when(method="POST", schema=ProfileSchema(), error_handler=lambda: request.path)
- def index_post(self, **kw):
- """
- This method will do something with POST arguments.
- If the schema validation fails, an internal redirect will
- cause the `profile.html` template to be rendered via the
- ``index_get`` method.
- """
-
- name = kw.get('name')
- email = ke.get('email')
-
- redirect('/profile')
-
-In this example, when form validation errors occur (for example, the email provided is invalid),
-Pecan will handle pre-filling the form values in ``profile.html`` for you. Additionally, inline
-errors will be appended to the template using FormEncode's ``htmlfill``.
-
-Bypassing ``htmlfill``
-------------------------------
-Sometimes you want certain fields in your templates to be ignored (i.e., not pre-filled) by ``htmlfill``.
-A perfect use case for this is password and hidden input fields. The default Pecan template namespace
-includes a built-in function, ``static``, which allows you to enforce a static value for form fields,
-preventing ``htmlfill`` from filling in submitted form variables::
-
-
-
-Working with ``variabledecode``
-------------------------------
-Pecan also lets you take advantage of FormEncode's ``variabledecode`` for transforming flat HTML form
-submissions into nested structures::
-
- from pecan import expose
- from myproject import SimpleSchema
-
- class ProfileController(object):
-
- @expose(schema=SimpleSchema(), variable_decode=True)
- def index(self):
- return dict()
+TODO: Rewrite this (potentially as an extension/cookbook) to illustrate the
+new technique (without formencode).
diff --git a/pecan/core.py b/pecan/core.py
index 35cae46..261c83d 100644
--- a/pecan/core.py
+++ b/pecan/core.py
@@ -7,8 +7,6 @@ from webob import Request, Response, exc
from threading import local
from itertools import chain
from mimetypes import guess_type, add_type
-from formencode import htmlfill, Invalid, variabledecode
-from formencode.schema import merge_dicts
from paste.recursive import ForwardRequestException
from urlparse import urlsplit, urlunsplit
@@ -121,8 +119,7 @@ def redirect(location=None, internal=False, code=None, headers={},
def error_for(field):
'''
A convenience function for fetching the validation error for a
- particular field in a form. Useful within templates when not using
- ``htmlfill`` for forms.
+ particular field in a form.
:param field: The name of the field to get the error for.
'''
@@ -130,22 +127,6 @@ def error_for(field):
return request.pecan['validation_errors'].get(field, '')
-def static(name, value):
- '''
- When using ``htmlfill`` validation support, this function indicates
- that ``htmlfill`` should not fill in a value for this field, and
- should instead use the value specified.
-
- :param name: The name of the field.
- :param value: The value to specify.
- '''
-
- if 'pecan.params' not in request.environ:
- request.environ['pecan.params'] = dict(request.params)
- request.environ['pecan.params'][name] = value
- return value
-
-
def render(template, namespace):
'''
Render the specified template using the Pecan rendering framework
@@ -176,14 +157,11 @@ class ValidationException(ForwardRequestException):
location = cfg['error_handler']
if callable(location):
location = location()
- merge_dicts(request.pecan['validation_errors'], errors)
if 'pecan.params' not in request.environ:
request.environ['pecan.params'] = dict(request.params)
request.environ[
'pecan.validation_errors'
] = request.pecan['validation_errors']
- if cfg.get('htmlfill') is not None:
- request.environ['pecan.htmlfill'] = cfg['htmlfill']
request.environ['REQUEST_METHOD'] = 'GET'
request.environ['pecan.validation_redirected'] = True
ForwardRequestException.__init__(self, location)
@@ -407,7 +385,6 @@ class Pecan(object):
renderer = self.renderers.get('json', self.template_path)
else:
namespace['error_for'] = error_for
- namespace['static'] = static
if ':' in template:
renderer = self.renderers.get(
template.split(':')[0],
@@ -416,45 +393,6 @@ class Pecan(object):
template = template.split(':')[1]
return renderer.render(template, namespace)
- def validate(self, schema, params, json=False, error_handler=None,
- htmlfill=None, variable_decode=None):
- '''
- Performs validation against a schema for any passed params,
- including support for ``JSON``.
-
- :param schema: A ``formencode`` ``Schema`` object to validate against.
- :param params: The dictionary of parameters to validate.
- :param json: A boolean, indicating whether or not the validation should
- validate against JSON content.
- :param error_handler: The path to a controller which will handle
- errors. If not specified, validation errors will raise a
- ``ValidationException``.
- :param htmlfill: Specifies whether or not to use htmlfill.
- :param variable_decode: Indicates whether or not to decode variables
- when using htmlfill.
- '''
-
- try:
- to_validate = params
- if json:
- to_validate = loads(request.body)
- if variable_decode is not None:
- to_validate = variabledecode.variable_decode(
- to_validate, **variable_decode
- )
- params = schema.to_python(to_validate)
- except Invalid, e:
- kwargs = {}
- if variable_decode is not None:
- kwargs['encode_variables'] = True
- kwargs.update(variable_decode)
- request.pecan['validation_errors'] = e.unpack_errors(**kwargs)
- if error_handler is not None:
- raise ValidationException()
- if json:
- params = dict(data=params)
- return params or {}
-
def handle_request(self):
'''
The main request handler for Pecan applications.
@@ -524,20 +462,8 @@ class Pecan(object):
# handle "before" hooks
self.handle_hooks('before', state)
- # fetch and validate any parameters
+ # fetch any parameters
params = dict(request.params)
- if 'schema' in cfg:
- params = self.validate(
- cfg['schema'],
- params,
- json=cfg['validate_json'],
- error_handler=cfg.get('error_handler'),
- htmlfill=cfg.get('htmlfill'),
- variable_decode=cfg.get('variable_decode')
- )
- elif 'pecan.validation_errors' in request.environ:
- errors = request.environ.pop('pecan.validation_errors')
- request.pecan['validation_errors'] = errors
# fetch the arguments for the controller
args, kwargs = self.get_args(
@@ -575,23 +501,8 @@ class Pecan(object):
request.pecan['content_type'] = 'application/json'
result = self.render(template, result)
- # pass the response through htmlfill (items are popped out of the
- # environment even if htmlfill won't run for proper cleanup)
- _htmlfill = cfg.get('htmlfill')
- if _htmlfill is None and 'pecan.htmlfill' in request.environ:
- _htmlfill = request.environ.pop('pecan.htmlfill')
if 'pecan.params' in request.environ:
params = request.environ.pop('pecan.params')
- if request.pecan['validation_errors'] and _htmlfill is not None and \
- request.pecan['content_type'] == 'text/html':
- errors = request.pecan['validation_errors']
- result = htmlfill.render(
- result,
- defaults=params,
- errors=errors,
- text_as_default=True,
- **_htmlfill
- )
# If we are in a test request put the namespace where it can be
# accessed directly
diff --git a/pecan/decorators.py b/pecan/decorators.py
index 2ae86eb..aaf144c 100644
--- a/pecan/decorators.py
+++ b/pecan/decorators.py
@@ -20,11 +20,6 @@ def when_for(controller):
def expose(template=None,
content_type='text/html',
- schema=None,
- json_schema=None,
- variable_decode=False,
- error_handler=None,
- htmlfill=None,
generic=False):
'''
@@ -34,14 +29,6 @@ def expose(template=None,
:param template: The path to a template, relative to the base template
directory.
:param content_type: The content-type to use for this template.
- :param schema: A ``formencode`` ``Schema`` object to use for validation.
- :param json_schema: A ``formencode`` ``Schema`` object to use for
- validation of JSON POST/PUT content.
- :param variable_decode: A boolean indicating if you want to use
- ``htmlfill``'s variable decode capability of transforming flat HTML form
- structures into nested ones.
- :param htmlfill: Indicates whether or not you want to use ``htmlfill`` for
- this controller.
:param generic: A boolean which flags this as a "generic" controller, which
uses generic functions based upon ``simplegeneric`` generic functions.
Allows you to split a single controller into multiple paths based upon HTTP
@@ -70,30 +57,8 @@ def expose(template=None,
# store the arguments for this controller method
cfg['argspec'] = getargspec(f)
- # store the schema
- cfg['error_handler'] = error_handler
- if schema is not None:
- cfg['schema'] = schema
- cfg['validate_json'] = False
- elif json_schema is not None:
- cfg['schema'] = json_schema
- cfg['validate_json'] = True
-
- # store the variable decode configuration
- if isinstance(variable_decode, dict) or variable_decode == True:
- _variable_decode = dict(dict_char='.', list_char='-')
- if isinstance(variable_decode, dict):
- _variable_decode.update(variable_decode)
- cfg['variable_decode'] = _variable_decode
-
- # store the htmlfill configuration
- if isinstance(htmlfill, dict) or htmlfill == True or \
- schema is not None:
- _htmlfill = dict(auto_insert_errors=False)
- if isinstance(htmlfill, dict):
- _htmlfill.update(htmlfill)
- cfg['htmlfill'] = _htmlfill
return f
+
return decorate
diff --git a/pecan/templates/project/+package+/controllers/root.py b/pecan/templates/project/+package+/controllers/root.py
index 1be22c0..bc1e72b 100644
--- a/pecan/templates/project/+package+/controllers/root.py
+++ b/pecan/templates/project/+package+/controllers/root.py
@@ -1,24 +1,14 @@
from pecan import expose, redirect
-from formencode import Schema, validators as v
from webob.exc import status_map
-class SearchForm(Schema):
- q = v.String(not_empty=True)
-
-
class RootController(object):
@expose(generic=True, template='index.html')
def index(self):
return dict()
- @index.when(
- method='POST',
- schema=SearchForm(),
- error_handler='/index',
- htmlfill=dict(auto_insert_errors=True)
- )
+ @index.when(method='POST')
def index_post(self, q):
redirect('http://pecan.readthedocs.org/en/latest/search.html?q=%s' % q)
diff --git a/pecan/tests/templates/form_login.html b/pecan/tests/templates/form_login.html
deleted file mode 100644
index b159f3e..0000000
--- a/pecan/tests/templates/form_login.html
+++ /dev/null
@@ -1,2 +0,0 @@
-
-
\ No newline at end of file
diff --git a/pecan/tests/test_hooks.py b/pecan/tests/test_hooks.py
index 94b52c5..a7d7025 100644
--- a/pecan/tests/test_hooks.py
+++ b/pecan/tests/test_hooks.py
@@ -7,7 +7,6 @@ from pecan.hooks import (
from pecan.configuration import Config
from pecan.decorators import transactional, after_commit, after_rollback
from unittest import TestCase
-from formencode import Schema, validators
from webtest import TestApp
@@ -329,125 +328,6 @@ class TestHooks(TestCase):
assert run_hook[4] == 'after1'
assert run_hook[5] == 'after2'
- def test_hooks_with_validation(self):
- run_hook = []
-
- class RegistrationSchema(Schema):
- first_name = validators.String(not_empty=True)
- last_name = validators.String(not_empty=True)
- email = validators.Email()
- username = validators.PlainText()
- password = validators.String()
- password_confirm = validators.String()
- age = validators.Int()
- chained_validators = [
- validators.FieldsMatch('password', 'password_confirm')
- ]
-
- class SimpleHook(PecanHook):
- def on_route(self, state):
- run_hook.append('on_route')
-
- def before(self, state):
- run_hook.append('before')
-
- def after(self, state):
- run_hook.append('after')
-
- def on_error(self, state, e):
- run_hook.append('error')
-
- class RootController(object):
- @expose()
- def errors(self, *args, **kwargs):
- run_hook.append('inside')
- return 'errors'
-
- @expose(schema=RegistrationSchema())
- def index(self, first_name,
- last_name,
- email,
- username,
- password,
- password_confirm,
- age):
- run_hook.append('inside')
- return str(len(request.pecan['validation_errors']) > 0)
-
- @expose(schema=RegistrationSchema(), error_handler='/errors')
- def with_handler(self, first_name,
- last_name,
- email,
- username,
- password,
- password_confirm,
- age):
- run_hook.append('inside')
- return str(len(request.pecan['validation_errors']) > 0)
-
- # test that the hooks get properly run with no validation errors
- app = TestApp(make_app(RootController(), hooks=[SimpleHook()]))
- r = app.post('/', dict(
- first_name='Jonathan',
- last_name='LaCour',
- email='jonathan@cleverdevil.org',
- username='jlacour',
- password='123456',
- password_confirm='123456',
- age='31'
- ))
- assert r.status_int == 200
- assert r.body == 'False'
- assert len(run_hook) == 4
- assert run_hook[0] == 'on_route'
- assert run_hook[1] == 'before'
- assert run_hook[2] == 'inside'
- assert run_hook[3] == 'after'
- run_hook = []
-
- # test that the hooks get properly run with validation errors
- app = TestApp(make_app(RootController(), hooks=[SimpleHook()]))
- r = app.post('/', dict(
- first_name='Jonathan',
- last_name='LaCour',
- email='jonathan@cleverdevil.org',
- username='jlacour',
- password='654321',
- password_confirm='123456',
- age='31'
- ))
- assert r.status_int == 200
- assert r.body == 'True'
- assert len(run_hook) == 4
- assert run_hook[0] == 'on_route'
- assert run_hook[1] == 'before'
- assert run_hook[2] == 'inside'
- assert run_hook[3] == 'after'
- run_hook = []
-
- # test that the hooks get properly run with validation errors
- # and an error handler
- app = TestApp(make_app(RootController(), hooks=[SimpleHook()]))
- r = app.post('/with_handler', dict(
- first_name='Jonathan',
- last_name='LaCour',
- email='jonathan@cleverdevil.org',
- username='jlacour',
- password='654321',
- password_confirm='123456',
- age='31'
- ))
- assert r.status_int == 200
- assert r.body == 'errors'
- assert len(run_hook) == 7
- assert run_hook[0] == 'on_route'
- assert run_hook[1] == 'before'
- assert run_hook[2] == 'after'
- assert run_hook[3] == 'on_route'
- assert run_hook[4] == 'before'
- assert run_hook[5] == 'inside'
- assert run_hook[6] == 'after'
-
class TestTransactionHook(TestCase):
def test_transaction_hook(self):
diff --git a/pecan/tests/test_rest.py b/pecan/tests/test_rest.py
index 0a007c5..86e56ea 100644
--- a/pecan/tests/test_rest.py
+++ b/pecan/tests/test_rest.py
@@ -7,8 +7,6 @@ try:
except:
from json import dumps, loads # noqa
-import formencode
-
class TestRestController(TestCase):
@@ -856,58 +854,3 @@ class TestRestController(TestCase):
r = app.get('/foos/bars/bazs/final/named')
assert r.status_int == 200
assert r.body == 'NAMED'
-
- def test_rest_with_validation_redirects(self):
- """
- Fixing a bug:
-
- When schema validation fails, pecan can use an internal redirect
- (paste.recursive.ForwardRequestException) to send you to another
- controller to "handle" the display of error messages. Additionally,
- pecan overwrites the request method as GET.
-
- In some circumstances, RestController's special `_method` parameter
- prevents the redirected request from routing to the appropriate
- `error_handler` controller.
- """
-
- class SampleSchema(formencode.Schema):
- name = formencode.validators.String()
-
- class UserController(RestController):
-
- @expose()
- def get_one(self, id):
- return "FORM VALIDATION FAILED"
-
- @expose(
- schema=SampleSchema(),
- error_handler=lambda: request.path
- )
- def put(self, id):
- raise AssertionError("Schema validation should fail.")
-
- @expose(
- schema=SampleSchema(),
- error_handler=lambda: request.path
- )
- def delete(self, id):
- raise AssertionError("Schema validation should fail.")
-
- class RootController(object):
- users = UserController()
-
- # create the app
- app = TestApp(make_app(RootController()))
-
- # create the app
- app = TestApp(make_app(RootController()))
-
- # test proper internal redirection
- r = app.post('/users/1?_method=put')
- assert r.status_int == 200
- assert r.body == "FORM VALIDATION FAILED"
-
- r = app.post('/users/1?_method=delete')
- assert r.status_int == 200
- assert r.body == "FORM VALIDATION FAILED"
diff --git a/pecan/tests/test_static.py b/pecan/tests/test_static.py
deleted file mode 100644
index b423707..0000000
--- a/pecan/tests/test_static.py
+++ /dev/null
@@ -1,27 +0,0 @@
-import os
-from pecan import expose, make_app
-from unittest import TestCase
-from webtest import TestApp
-
-
-class TestStatic(TestCase):
-
- def test_simple_static(self):
- class RootController(object):
- @expose()
- def index(self):
- return 'Hello, World!'
-
- # make sure Cascade is working properly
- text = os.path.join(os.path.dirname(__file__), 'static/text.txt')
- static_root = os.path.join(os.path.dirname(__file__), 'static')
-
- app = TestApp(make_app(RootController(), static_root=static_root))
- response = app.get('/index.html')
- assert response.status_int == 200
- assert response.body == 'Hello, World!'
-
- # get a static resource
- response = app.get('/text.txt')
- assert response.status_int == 200
- assert response.body == open(text, 'rb').read()
diff --git a/pecan/tests/test_validation.py b/pecan/tests/test_validation.py
deleted file mode 100644
index df138a9..0000000
--- a/pecan/tests/test_validation.py
+++ /dev/null
@@ -1,885 +0,0 @@
-from formencode import ForEach, Schema, validators
-from webtest import TestApp
-from unittest import TestCase
-
-import os.path
-
-from pecan import make_app, expose, request, ValidationException
-from pecan.templating import _builtin_renderers as builtin_renderers
-
-try:
- from simplejson import dumps
-except ImportError:
- from json import dumps # noqa
-
-
-class TestValidation(TestCase):
-
- template_path = os.path.join(os.path.dirname(__file__), 'templates')
-
- def test_simple_validation(self):
- class RegistrationSchema(Schema):
- first_name = validators.String(not_empty=True)
- last_name = validators.String(not_empty=True)
- email = validators.Email()
- username = validators.PlainText()
- password = validators.String()
- password_confirm = validators.String()
- age = validators.Int()
- chained_validators = [
- validators.FieldsMatch('password', 'password_confirm')
- ]
-
- class RootController(object):
- @expose(schema=RegistrationSchema())
- def index(self, first_name,
- last_name,
- email,
- username,
- password,
- password_confirm,
- age):
- assert isinstance(last_name, unicode)
- assert isinstance(first_name, unicode)
- assert age == 31
- assert isinstance(age, int)
- return 'Success!'
-
- @expose(json_schema=RegistrationSchema())
- def json(self, data):
- assert data['age'] == 31
- assert isinstance(data['age'], int)
- return 'Success!'
-
- # test form submissions
- app = TestApp(make_app(RootController()))
- r = app.post('/', dict(
- first_name='Jonathan',
- last_name='LaCour',
- email='jonathan@cleverdevil.org',
- username='jlacour',
- password='123456',
- password_confirm='123456',
- age='31'
- ))
- assert r.status_int == 200
- assert r.body == 'Success!'
-
- # test JSON submissions
- r = app.post('/json', dumps(dict(
- first_name='Jonathan',
- last_name='LaCour',
- email='jonathan@cleverdevil.org',
- username='jlacour',
- password='123456',
- password_confirm='123456',
- age='31'
- )), [('content-type', 'application/json')])
- assert r.status_int == 200
- assert r.body == 'Success!'
-
- def test_validation_with_none_type(self):
- """
- formencode schemas can be configured to return NoneType in some
- circumstances. We use the output of formencode's validate() to
- determine wildcard arguments, so we should protect ourselves
- from this scenario.
- """
- class RegistrationSchema(Schema):
- if_empty = None
- first_name = validators.String(not_empty=True)
- last_name = validators.String(not_empty=True)
-
- class RootController(object):
- @expose(schema=RegistrationSchema())
- def index(self, **kw):
- assert 'first_name' not in kw
- assert 'last_name' not in kw
- return 'Success!'
-
- # test form submissions
- app = TestApp(make_app(RootController()))
- r = app.post('/', dict())
- assert r.status_int == 200
- assert r.body == 'Success!'
-
- def test_simple_failure(self):
- class RegistrationSchema(Schema):
- first_name = validators.String(not_empty=True)
- last_name = validators.String(not_empty=True)
- email = validators.Email()
- username = validators.PlainText()
- password = validators.String()
- password_confirm = validators.String()
- age = validators.Int()
- chained_validators = [
- validators.FieldsMatch('password', 'password_confirm')
- ]
-
- class RootController(object):
-
- @expose()
- def errors(self, *args, **kwargs):
- assert len(request.pecan['validation_errors']) > 0
- return 'There was an error!'
-
- @expose(schema=RegistrationSchema())
- def index(self, first_name,
- last_name,
- email,
- username,
- password,
- password_confirm,
- age):
- assert len(request.pecan['validation_errors']) > 0
- return 'Success!'
-
- @expose(schema=RegistrationSchema(), error_handler='/errors')
- def with_handler(self, first_name,
- last_name,
- email,
- username,
- password,
- password_confirm,
- age):
- assert len(request.pecan['validation_errors']) > 0
- return 'Success!'
-
- @expose(json_schema=RegistrationSchema())
- def json(self, data):
- assert len(request.pecan['validation_errors']) > 0
- return 'Success!'
-
- @expose(json_schema=RegistrationSchema(), error_handler='/errors')
- def json_with_handler(self, data):
- assert len(request.pecan['validation_errors']) > 0
- return 'Success!'
-
- # test without error handler
- app = TestApp(make_app(RootController()))
- r = app.post('/', dict(
- first_name='Jonathan',
- last_name='LaCour',
- email='jonathan@cleverdevil.org',
- username='jlacour',
- password='123456',
- password_confirm='654321',
- age='31'
- ))
- assert r.status_int == 200
- assert r.body == 'Success!'
-
- # test with error handler
- r = app.post('/with_handler', dict(
- first_name='Jonathan',
- last_name='LaCour',
- email='jonathan@cleverdevil.org',
- username='jlacour',
- password='123456',
- password_confirm='654321',
- age='31'
- ))
- assert r.status_int == 200
- assert r.body == 'There was an error!'
-
- # test JSON without error handler
- r = app.post('/json', dumps(dict(
- first_name='Jonathan',
- last_name='LaCour',
- email='jonathan@cleverdevil.org',
- username='jlacour',
- password='123456',
- password_confirm='654321',
- age='31'
- )), [('content-type', 'application/json')])
- assert r.status_int == 200
- assert r.body == 'Success!'
-
- # test JSON with error handler
- r = app.post('/json_with_handler', dumps(dict(
- first_name='Jonathan',
- last_name='LaCour',
- email='jonathan@cleverdevil.org',
- username='jlacour',
- password='123456',
- password_confirm='654321',
- age='31'
- )), [('content-type', 'application/json')])
- assert r.status_int == 200
- assert r.body == 'There was an error!'
-
- def test_with_variable_decode(self):
-
- class ColorSchema(Schema):
- colors = ForEach(validators.String(not_empty=True))
-
- class RootController(object):
-
- @expose()
- def errors(self, *args, **kwargs):
- errs = ', '.join(request.pecan['validation_errors'].keys())
- return 'Error with %s!' % errs
-
- @expose(schema=ColorSchema(),
- variable_decode=True)
- def index(self, **kwargs):
- if request.pecan['validation_errors']:
- return ', '.join(request.pecan['validation_errors'].keys())
- else:
- return 'Success!'
-
- @expose(schema=ColorSchema(),
- error_handler='/errors',
- variable_decode=True)
- def with_handler(self, **kwargs):
- if request.pecan['validation_errors']:
- return ', '.join(request.pecan['validation_errors'].keys())
- else:
- return 'Success!'
-
- @expose(json_schema=ColorSchema(),
- variable_decode=True)
- def json(self, data):
- if request.pecan['validation_errors']:
- return ', '.join(request.pecan['validation_errors'].keys())
- else:
- return 'Success!'
-
- @expose(json_schema=ColorSchema(),
- error_handler='/errors',
- variable_decode=True)
- def json_with_handler(self, data):
- if request.pecan['validation_errors']:
- return ', '.join(request.pecan['validation_errors'].keys())
- else:
- return 'Success!'
-
- @expose(schema=ColorSchema(),
- variable_decode=dict())
- def custom(self, **kwargs):
- if request.pecan['validation_errors']:
- return ', '.join(request.pecan['validation_errors'].keys())
- else:
- return 'Success!'
-
- @expose(schema=ColorSchema(),
- error_handler='/errors',
- variable_decode=dict())
- def custom_with_handler(self, **kwargs):
- if request.pecan['validation_errors']:
- return ', '.join(request.pecan['validation_errors'].keys())
- else:
- return 'Success!'
-
- @expose(json_schema=ColorSchema(),
- variable_decode=dict())
- def custom_json(self, data):
- if request.pecan['validation_errors']:
- return ', '.join(request.pecan['validation_errors'].keys())
- else:
- return 'Success!'
-
- @expose(json_schema=ColorSchema(),
- error_handler='/errors',
- variable_decode=dict())
- def custom_json_with_handler(self, data):
- if request.pecan['validation_errors']:
- return ', '.join(request.pecan['validation_errors'].keys())
- else:
- return 'Success!'
-
- @expose(schema=ColorSchema(),
- variable_decode=dict(dict_char='-', list_char='.'))
- def alternate(self, **kwargs):
- if request.pecan['validation_errors']:
- return ', '.join(request.pecan['validation_errors'].keys())
- else:
- return 'Success!'
-
- @expose(schema=ColorSchema(),
- error_handler='/errors',
- variable_decode=dict(dict_char='-', list_char='.'))
- def alternate_with_handler(self, **kwargs):
- if request.pecan['validation_errors']:
- return ', '.join(request.pecan['validation_errors'].keys())
- else:
- return 'Success!'
-
- @expose(json_schema=ColorSchema(),
- variable_decode=dict(dict_char='-', list_char='.'))
- def alternate_json(self, data):
- if request.pecan['validation_errors']:
- return ', '.join(request.pecan['validation_errors'].keys())
- else:
- return 'Success!'
-
- @expose(json_schema=ColorSchema(),
- error_handler='/errors',
- variable_decode=dict(dict_char='-', list_char='.'))
- def alternate_json_with_handler(self, data):
- if request.pecan['validation_errors']:
- return ', '.join(request.pecan['validation_errors'].keys())
- else:
- return 'Success!'
-
- # test without error handler
- app = TestApp(make_app(RootController()))
- r = app.post('/', {
- 'colors-0': 'blue',
- 'colors-1': 'red'
- })
- assert r.status_int == 200
- assert r.body == 'Success!'
-
- # test failure without error handler
- r = app.post('/', {
- 'colors-0': 'blue',
- 'colors-1': ''
- })
- assert r.status_int == 200
- assert r.body == 'colors-1'
-
- # test with error handler
- r = app.post('/with_handler', {
- 'colors-0': 'blue',
- 'colors-1': 'red'
- })
- assert r.status_int == 200
- assert r.body == 'Success!'
-
- # test failure with error handler
- r = app.post('/with_handler', {
- 'colors-0': '',
- 'colors-1': 'red'
- })
- assert r.status_int == 200
- assert r.body == 'Error with colors-0!'
-
- # test JSON without error handler
- r = app.post('/json', dumps({
- 'colors-0': 'blue',
- 'colors-1': 'red'
- }), [('content-type', 'application/json')])
- assert r.status_int == 200
- assert r.body == 'Success!'
-
- # test JSON failure without error handler
- r = app.post('/json', dumps({
- 'colors-0': 'blue',
- 'colors-1': ''
- }), [('content-type', 'application/json')])
- assert r.status_int == 200
- assert r.body == 'colors-1'
-
- # test JSON with error handler
- r = app.post('/json_with_handler', dumps({
- 'colors-0': 'blue',
- 'colors-1': 'red'
- }), [('content-type', 'application/json')])
- assert r.status_int == 200
- assert r.body == 'Success!'
-
- # test JSON failure with error handler
- r = app.post('/json_with_handler', dumps({
- 'colors-0': '',
- 'colors-1': 'red'
- }), [('content-type', 'application/json')])
- assert r.status_int == 200
- assert r.body == 'Error with colors-0!'
-
- # test custom without error handler
- r = app.post('/custom', {
- 'colors-0': 'blue',
- 'colors-1': 'red'
- })
- assert r.status_int == 200
- assert r.body == 'Success!'
-
- # test custom failure without error handler
- r = app.post('/custom', {
- 'colors-0': 'blue',
- 'colors-1': ''
- })
- assert r.status_int == 200
- assert r.body == 'colors-1'
-
- # test custom with error handler
- r = app.post('/custom_with_handler', {
- 'colors-0': 'blue',
- 'colors-1': 'red'
- })
- assert r.status_int == 200
- assert r.body == 'Success!'
-
- # test custom failure with error handler
- r = app.post('/custom_with_handler', {
- 'colors-0': '',
- 'colors-1': 'red'
- })
- assert r.status_int == 200
- assert r.body == 'Error with colors-0!'
-
- # test custom JSON without error handler
- r = app.post('/custom_json', dumps({
- 'colors-0': 'blue',
- 'colors-1': 'red'
- }), [('content-type', 'application/json')])
- assert r.status_int == 200
- assert r.body == 'Success!'
-
- # test custom JSON failure without error handler
- r = app.post('/custom_json', dumps({
- 'colors-0': 'blue',
- 'colors-1': ''
- }), [('content-type', 'application/json')])
- assert r.status_int == 200
- assert r.body == 'colors-1'
-
- # test custom JSON with error handler
- r = app.post('/custom_json_with_handler', dumps({
- 'colors-0': 'blue',
- 'colors-1': 'red'
- }), [('content-type', 'application/json')])
- assert r.status_int == 200
- assert r.body == 'Success!'
-
- # test custom JSON failure with error handler
- r = app.post('/custom_json_with_handler', dumps({
- 'colors-0': '',
- 'colors-1': 'red'
- }), [('content-type', 'application/json')])
- assert r.status_int == 200
- assert r.body == 'Error with colors-0!'
-
- # test alternate without error handler
- r = app.post('/alternate', {
- 'colors.0': 'blue',
- 'colors.1': 'red'
- })
- assert r.status_int == 200
- assert r.body == 'Success!'
-
- # test alternate failure without error handler
- r = app.post('/alternate', {
- 'colors.0': 'blue',
- 'colors.1': ''
- })
- assert r.status_int == 200
- assert r.body == 'colors.1'
-
- # test alternate with error handler
- r = app.post('/alternate_with_handler', {
- 'colors.0': 'blue',
- 'colors.1': 'red'
- })
- assert r.status_int == 200
- assert r.body == 'Success!'
-
- # test alternate failure with error handler
- r = app.post('/alternate_with_handler', {
- 'colors.0': '',
- 'colors.1': 'red'
- })
- assert r.status_int == 200
- assert r.body == 'Error with colors.0!'
-
- # test alternate JSON without error handler
- r = app.post('/alternate_json', dumps({
- 'colors.0': 'blue',
- 'colors.1': 'red'
- }), [('content-type', 'application/json')])
- assert r.status_int == 200
- assert r.body == 'Success!'
-
- # test alternate JSON failure without error handler
- r = app.post('/alternate_json', dumps({
- 'colors.0': 'blue',
- 'colors.1': ''
- }), [('content-type', 'application/json')])
- assert r.status_int == 200
- assert r.body == 'colors.1'
-
- # test alternate JSON with error handler
- r = app.post('/alternate_json_with_handler', dumps({
- 'colors.0': 'blue',
- 'colors.1': 'red'
- }), [('content-type', 'application/json')])
- assert r.status_int == 200
- assert r.body == 'Success!'
-
- # test alternate JSON failure with error handler
- r = app.post('/alternate_json_with_handler', dumps({
- 'colors.0': '',
- 'colors.1': 'red'
- }), [('content-type', 'application/json')])
- assert r.status_int == 200
- assert r.body == 'Error with colors.0!'
-
- def test_htmlfill(self):
-
- if 'mako' not in builtin_renderers:
- return
-
- class ColorSchema(Schema):
- colors = ForEach(validators.String(not_empty=True))
-
- class NameSchema(Schema):
- name = validators.String(not_empty=True)
-
- class RootController(object):
-
- @expose(template='mako:form_colors.html',
- schema=ColorSchema(),
- variable_decode=True)
- def index(self, **kwargs):
- if request.pecan['validation_errors']:
- return dict()
- else:
- return dict(data=kwargs)
-
- @expose(schema=ColorSchema(),
- error_handler='/errors_with_handler',
- variable_decode=True)
- def with_handler(self, **kwargs):
- return ', '.join(kwargs['colors'])
-
- @expose('mako:form_colors.html')
- def errors_with_handler(self):
- return dict()
-
- @expose(template='mako:form_name.html',
- schema=NameSchema(),
- htmlfill=dict(auto_insert_errors=True))
- def with_errors(self, **kwargs):
- return kwargs
-
- @expose(schema=NameSchema(),
- error_handler='/errors_with_handler_and_errors',
- htmlfill=dict(auto_insert_errors=True))
- def with_handler_and_errors(self, **kwargs):
- return kwargs['name']
-
- @expose('mako:form_name.html')
- def errors_with_handler_and_errors(self):
- return dict()
-
- @expose(template='json',
- schema=NameSchema(),
- htmlfill=dict(auto_insert_errors=True))
- def json(self, **kwargs):
- if request.pecan['validation_errors']:
- return dict(
- error_with=request.pecan['validation_errors'].keys()
- )
- else:
- return kwargs
-
- def _get_contents(filename):
- return open(os.path.join(self.template_path, filename), 'r').read()
-
- # test without error handler
- app = TestApp(
- make_app(RootController(), template_path=self.template_path)
- )
- r = app.post('/', {
- 'colors-0': 'blue',
- 'colors-1': 'red'
- })
- assert r.status_int == 200
- assert r.body == _get_contents('form_colors_valid.html')
-
- # test failure without error handler
- r = app.post('/', {
- 'colors-0': 'blue',
- 'colors-1': ''
- })
- assert r.status_int == 200
- assert r.body == _get_contents('form_colors_invalid.html')
-
- # test with error handler
- r = app.post('/with_handler', {
- 'colors-0': 'blue',
- 'colors-1': 'red'
- })
- assert r.status_int == 200
- assert r.body == 'blue, red'
-
- # test failure with error handler
- r = app.post('/with_handler', {
- 'colors-0': 'blue',
- 'colors-1': ''
- })
- assert r.status_int == 200
- assert r.body == _get_contents('form_colors_invalid.html')
-
- # test with errors
- r = app.post('/with_errors', {
- 'name': 'Yoann'
- })
- assert r.status_int == 200
- assert r.body == _get_contents('form_name_valid.html')
-
- # test failure with errors
- r = app.post('/with_errors', {
- 'name': ''
- })
- assert r.status_int == 200
- assert r.body == _get_contents('form_name_invalid.html')
-
- # test with error handler and errors
- r = app.post('/with_handler_and_errors', {
- 'name': 'Yoann'
- })
- assert r.status_int == 200
- assert r.body == 'Yoann'
-
- # test failure with error handler and errors
- r = app.post('/with_handler_and_errors', {
- 'name': ''
- })
- assert r.status_int == 200
- assert r.body == _get_contents('form_name_invalid.html')
-
- # test JSON
- r = app.post('/json', {
- 'name': 'Yoann'
- })
- assert r.status_int == 200
- assert r.body == dumps(dict(name='Yoann'))
-
- # test JSON failure
- r = app.post('/json', {
- 'name': ''
- })
- assert r.status_int == 200
- assert r.body == dumps(dict(error_with=['name']))
-
- def test_htmlfill_static(self):
-
- if 'mako' not in builtin_renderers:
- return
-
- class LoginSchema(Schema):
- username = validators.String(not_empty=True)
- password = validators.String(not_empty=True)
-
- class RootController(object):
-
- @expose(template='mako:form_login.html',
- schema=LoginSchema())
- def index(self, **kwargs):
- if request.pecan['validation_errors']:
- return dict()
- else:
- return dict(data=kwargs)
-
- @expose(schema=LoginSchema(),
- error_handler='/errors_with_handler')
- def with_handler(self, **kwargs):
- return '%s:%s' % (kwargs['username'], kwargs['password'])
-
- @expose('mako:form_login.html')
- def errors_with_handler(self):
- return dict()
-
- def _get_contents(filename):
- return open(os.path.join(self.template_path, filename), 'r').read()
-
- # test without error handler
- app = TestApp(
- make_app(RootController(), template_path=self.template_path)
- )
- r = app.post('/', {
- 'username': 'ryan',
- 'password': 'password'
- })
- assert r.status_int == 200
- assert r.body == _get_contents('form_login_valid.html')
-
- # test failure without error handler
- r = app.post('/', {
- 'username': 'ryan',
- 'password': ''
- })
- assert r.status_int == 200
- assert r.body == _get_contents('form_login_invalid.html')
-
- # test with error handler
- r = app.post('/with_handler', {
- 'username': 'ryan',
- 'password': 'password'
- })
- assert r.status_int == 200
- assert r.body == 'ryan:password'
-
- # test failure with error handler
- r = app.post('/with_handler', {
- 'username': 'ryan',
- 'password': ''
- })
- assert r.status_int == 200
- assert r.body == _get_contents('form_login_invalid.html')
-
- def test_error_for(self):
-
- if 'mako' not in builtin_renderers:
- return
-
- class ColorSchema(Schema):
- colors = ForEach(validators.String(not_empty=True))
-
- class RootController(object):
-
- @expose(template='mako:error_for.html')
- def errors(self, *args, **kwargs):
- return dict()
-
- @expose(template='mako:error_for.html',
- schema=ColorSchema(),
- variable_decode=True)
- def index(self, **kwargs):
- return dict()
-
- @expose(schema=ColorSchema(),
- error_handler='/errors',
- variable_decode=True)
- def with_handler(self, **kwargs):
- return dict()
-
- @expose(template='mako:error_for.html',
- json_schema=ColorSchema(),
- variable_decode=True)
- def json(self, data):
- return dict()
-
- @expose(json_schema=ColorSchema(),
- error_handler='/errors',
- variable_decode=True)
- def json_with_handler(self, data):
- return dict()
-
- # test without error handler
- app = TestApp(
- make_app(RootController(), template_path=self.template_path)
- )
- r = app.post('/', {
- 'colors-0': 'blue',
- 'colors-1': 'red'
- })
- assert r.status_int == 200
- assert r.body == ''
-
- # test failure without error handler
- r = app.post('/', {
- 'colors-0': 'blue',
- 'colors-1': ''
- })
- assert r.status_int == 200
- assert r.body == 'Please enter a value'
-
- # test failure with error handler
- r = app.post('/with_handler', {
- 'colors-0': 'blue',
- 'colors-1': ''
- })
- assert r.status_int == 200
- assert r.body == 'Please enter a value'
-
- # test JSON failure without error handler
- r = app.post('/json', dumps({
- 'colors-0': 'blue',
- 'colors-1': ''
- }), [('content-type', 'application/json')])
- assert r.status_int == 200
- assert r.body == 'Please enter a value'
-
- # test JSON failure with error handler
- r = app.post('/json_with_handler', dumps({
- 'colors-0': 'blue',
- 'colors-1': ''
- }), [('content-type', 'application/json')])
- assert r.status_int == 200
- assert r.body == 'Please enter a value'
-
- def test_callable_error_handler(self):
-
- class ColorSchema(Schema):
- colors = ForEach(validators.String(not_empty=True))
-
- class RootController(object):
-
- @expose()
- def errors(self, *args, **kwargs):
- return 'There was an error!'
-
- @expose(schema=ColorSchema(),
- error_handler=lambda: '/errors',
- variable_decode=True)
- def index(self, **kwargs):
- return 'Success!'
-
- # test with error handler
- app = TestApp(make_app(RootController()))
- r = app.post('/', {
- 'colors-0': 'blue',
- 'colors-1': 'red'
- })
- assert r.status_int == 200
- assert r.body == 'Success!'
-
- # test with error handler
- r = app.post('/', {
- 'colors-0': 'blue',
- 'colors-1': ''
- })
- assert r.status_int == 200
- assert r.body == 'There was an error!'
-
- def test_validation_exception(self):
-
- if 'mako' not in builtin_renderers:
- return
-
- class NameSchema(Schema):
- name = validators.String(not_empty=True)
-
- class SubController(object):
- @expose()
- def _route(self, *args):
- raise ValidationException('/success')
-
- class RootController(object):
-
- sub = SubController()
-
- @expose('mako:form_name.html')
- def errors_name(self):
- return dict()
-
- @expose(schema=NameSchema(),
- error_handler='/errors_name',
- htmlfill=dict(auto_insert_errors=True))
- def name(self, name):
- raise ValidationException(
- errors={'name': 'Names must be unique'}
- )
-
- @expose()
- def success(self):
- return 'Success!'
-
- def _get_contents(filename):
- return open(os.path.join(self.template_path, filename), 'r').read()
-
- # test exception with no controller
- app = TestApp(
- make_app(RootController(), template_path=self.template_path)
- )
- r = app.get('/sub')
- assert r.status_int == 200
- assert r.body == 'Success!'
-
- # test exception with additional errors
- r = app.post('/name', {'name': 'Yoann'})
- assert r.status_int == 200
- assert r.body == _get_contents('form_name_invalid_custom.html')
diff --git a/setup.py b/setup.py
index e79e2f0..bcdd92b 100644
--- a/setup.py
+++ b/setup.py
@@ -14,7 +14,6 @@ requirements = [
"Mako >= 0.4.0",
"Paste >= 1.7.5.1",
"PasteScript >= 1.7.3",
- "formencode >= 1.2.2",
"WebTest >= 1.2.2"
]