Commits

Lynn Rees  committed 9f825a6

[svn]

  • Participants
  • Parent commits 99d2620
  • Branches wsgiform
  • Tags svn.37

Comments (0)

Files changed (4)

File branches/0.3/setup.py

 setup(name='wsgiform',
       version='0.3',
       description='''WSGI form parsing and validation middleware''',
-      long_description='''WSGI middleware for validating form
-submissions and parsing them into dictionaries, individual WSGI 'environ'
-dictionary entries, FieldStorage instances, or keyword arguments passed to
-a WSGI application. Supports data escaping and sterilization.
+      long_description='''WSGI middleware for validating form submissions
+and parsing them into dictionaries, individual WSGI 'environ' dictionary
+entries, cgi.FieldStorage instances, or keyword arguments passed to a
+WSGI application. Supports automatically escaping and sterilizing form
+submissions.
 
 # Simple wsgiform usage format example:
 

File branches/0.3/wsgiform/form.py

 # 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.
 
-'''WSGI middleware for parsing form submissions into dictionaries, individual
-'environ' entries, FieldStorage instances, or keyword arguments that can be
-passed in the 'environ' dictionary to a WSGI application. Features include
-form validation, escaping, and data sterilization.
+'''WSGI middleware for validating form submissions and parsing them into
+dictionaries, individual WSGI 'environ' dictionary entries, cgi.FieldStorage
+instances, or keyword arguments passed to a WSGI application. Supports
+automatically escaping and sterilizing form submissions.
 '''
 
 import cgi
+import urllib
 from StringIO import StringIO
 from wsgiform.util import escapeform, hyperform, sterileform, getinput
 
     return ['Data in field(s) %s was invalid.' %
             ' '.join(environ['wsgiform.error'])]
 
+def validate(qdict, validators, environ, strict=False):
+    '''Validates form data.
+
+    qdict Dictionary of validators where the key is a form field
+    environ A WSGI environment dictionary
+    validators An iterable with validators
+    strict Keys w/out validators are form errors (default: False)
+    '''
+    errors = []
+    # Validate each form entry
+    for key, value in qdict.iteritems():
+        try:
+            # Accumulate any errors
+            if not validators[key](value): errors.append(key)
+        except KeyError:
+            if strict: errors.append(key)
+    # Put any errors in environ error entry and fail
+    if errors:
+        environ['wsgiform.error'] = errors
+        return False        
+    return True
+
 def form(**kw):
     '''Decorator for form parsing.'''
     def decorator(application):
     entries, FieldStorage instances, or keyword arguments that can be passed to
     WSGI applications in the environ dictionary.
     '''
-
+    
     environ = None
+    # Environ key styles
     keys = {'fieldstorage':'wsgiform.fieldstorage', 'dict':'wsgiform.dict',
         'kwargs':'wsgize.kwargs', 'environ':'wsgiform.%s',
         'routing_args':'wsgiorg.routing_args'}
+    # Data sterilizing functions
     funclist = {'escape':escapeform, 'hyperescape':hyperform,
         'sterilize':sterileform} 
 
         self.key = self.keys.get(self.style)
         # Dictionary of validating functions where keywords == form field names 
         self.validators = kw.get('validators', {})
-        # Stop on errors or empty fields
+        # Function to validate form submissions
+        self.validate = kw.get('validfunc', validate)
+        # WSGI application to handle form validation errors
+        self.handler = kw.get('errapp', _errorapp)
+        # Stop on errors, empty fields, and validation errors
         self.strict = kw.get('strict', False)
         # Function to escape metachars
         self.func = self.funclist[(kw.get('func', 'escape'))]
-        # WSGI application to handle form validation errors
-        self.handler = kw.get('errapp', _errorapp)
         
-    def __call__(self, env, start_response):
-        # Store form submissions in cgi.FieldStorage
+    def __call__(self, env, start_response):       
+        qdict = self.func(env, self.strict)            
+        # Validate form if validators present            
+        if self.validators:
+            if not self.validate(qdict, self.validators, env, self.strict):
+                return self.handler(env, start_response)
         if self.style == 'fieldstorage':
-            # Preserve wsgi.input
-            wsginput = StringIO(getinput(env['wsgi.input']))
-            env[self.key] = cgi.FieldStorage(fp=wsginput, environ=env,
-                keep_blank_values=self.strict, strict_parsing=self.strict)
-        # Handle dictionary variants
+            # Reparse query back into a query string
+            cginput = StringIO(urllib.urlencode(qdict))
+            # Put into cgi.FieldStorage instance
+            qdict = cgi.FieldStorage(fp=cginput, environ=env)
+        # Store form submissions as individual environ entries
+        if self.style == 'environ':
+            for k, v in qdict.iteritems(): env[self.key % k] = v
+        # Store form submissions using wsgi.routing_vars style
+        elif self.style == 'routing_args':
+            args, kwargs = env.get(self.key, ((), {}))
+            env[self.key] = (args, kwargs.update(qdict))
+        # Store form submissions as kwargs, dict, or cgi.FieldStorage
         else:
-            qdict, self.environ = self.func(env, self.strict), env
-            # Validate form if required            
-            if self.validators and not self.validate(qdict):
-                return self.handler(env, start_response)
-            # Store form submissions as individual environ entries
-            if self.style == 'environ':
-                for k, v in qdict.iteritems(): env[self.key % k] = v
-            # Store form submissions using wsgi.routing_vars style
-            elif self.style == 'routing_args':
-                args, kwargs = env.get(self.key, ((), {}))
-                env[self.key] = (args, kwargs.update(qdict))
-            # Store form submissions as kwargs or dict
-            else:
-                env[self.key] = qdict
-        return self.application(env, start_response)
-    
-    def validate(self, qdict):
-        '''Validates form data.
-
-        qdict Dictionary of validators where the key is a form field       
-        '''
-        errors = []
-        # Validate each form entry
-        for key, value in qdict.iteritems():
-            try:
-                # Accumulate any errors
-                if not self.validators[key](value): errors.append(key)
-            except KeyError:
-                errors.append(key)
-        # Put any errors in environ error entry and fail
-        if errors:
-            self.environ['wsgiform.error'] = errors
-            return False
-        return True
+            env[self.key] = qdict
+        return self.application(env, start_response)

File branches/0.3/wsgiform/tests/test_validators.py

-#This file was originally generated by PyScripter's unitest wizard
+# Copyright (c) 2006 L. C. Rees
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+#
+#    1. Redistributions of source code must retain the above copyright notice,
+#       this list of conditions and the following disclaimer.
+#
+#    2. 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.
+#
+#    3. Neither the name of WsgiForm nor the names of its contributors may be
+#       used to endorse or promote products derived from this software without
+#       specific prior written permission.
+#
+# 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 OWNER 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.
 
 import unittest
-import validators
+import wsgiform.validators as validators
 
 class TestValidators(unittest.TestCase):
 

File branches/0.3/wsgiform/tests/test_wsgiform.py

 '''Unit tests for wsgiform.'''
 
 import wsgiform.form as wsgiform
+from wsgiform.validators import getvalidator
 import unittest
 import StringIO
 import copy
     def dummy_app(self, environ, func):
         return environ
 
-    def dummy_sr(self):
+    def dummy_sr(self, status, headers, exc_info=None):
         pass
 
-    def testfieldstorage(self):
+    def test_fieldstorage(self):
         '''Parses form data into a FieldStorage instance.'''
         form = wsgiform.WsgiForm(self.dummy_app, style='fieldstorage')
         env = form(copy.deepcopy(self.test_env), self.dummy_sr)
         self.assertEqual(env['wsgiform.fieldstorage'].getfirst('num'), '12121')
 
-    def testdictionary(self):
+    def test_dictionary(self):
         '''Parses form data into a dictionary.'''
         form = wsgiform.WsgiForm(self.dummy_app, style='dict')
         env = form(copy.deepcopy(self.test_env), self.dummy_sr)
         self.assertEqual(env['wsgiform.dict']['num'], '12121')
 
-    def testkwargs(self):
+    def test_kwargs(self):
         '''Parses form data into keyword arguments.'''
         form = wsgiform.WsgiForm(self.dummy_app, style='kwargs')
         env = form(copy.deepcopy(self.test_env), self.dummy_sr)
         self.assertEqual(env['wsgize.kwargs']['num'], '12121')
 
-    def testenviron(self):
+    def test_environ(self):
         '''Parses form data into individual environ entries.'''
         form = wsgiform.WsgiForm(self.dummy_app, style='environ')
         env = form(copy.deepcopy(self.test_env), self.dummy_sr)
         self.assertEqual(env['wsgiform.num'], '12121')
 
-    def testdict_escape(self):
+    def test_escape(self):
         '''Parses form data into a dictionary with HTML escaping. '''
-        form = wsgiform.WsgiForm(self.dummy_app, style='dict', func='escape')
-        env = form(copy.deepcopy(self.test_env), self.dummy_sr)
-        self.assertEqual(env['wsgiform.dict']['name'], '<tag id="Test">This is a 'test' & another.</tag>')         
-
-    def testkwargs_escape(self):
-        '''Parses form data into keyword arguments with HTML escaping.'''
-        form = wsgiform.WsgiForm(self.dummy_app, style='kwargs', func='escape')
-        env = form(copy.deepcopy(self.test_env), self.dummy_sr)
-        self.assertEqual(env['wsgize.kwargs']['name'], '<tag id="Test">This is a 'test' & another.</tag>')
-
-    def testenviron_escape(self):
-        '''Parses form data into individual environ entries with HTML escaping.'''
-        form = wsgiform.WsgiForm(self.dummy_app, style='environ', func='escape')
-        env = form(copy.deepcopy(self.test_env), self.dummy_sr)
-        self.assertEqual(env['wsgiform.name'], '<tag id="Test">This is a 'test' & another.</tag>')
-
-    def testdict_strictescape(self):
-        '''Parses form data into a dictionary with strict HTML escaping.'''
-        form = wsgiform.WsgiForm(self.dummy_app, style='dict', func='escape', strict=True)
+        form = wsgiform.WsgiForm(self.dummy_app)
         env = form(copy.deepcopy(self.test_env), self.dummy_sr)
         self.assertEqual(env['wsgiform.dict']['name'], '<tag id="Test">This is a 'test' & another.</tag>')
 
-    def testkwargs_strictescape(self):
-        '''Parses form data into keyword arguments with strict HTML escaping.'''
-        form = wsgiform.WsgiForm(self.dummy_app, style='kwargs', func='escape', strict=True)
+    def test_hyperescape(self):
+        '''Parses form data into a dictionary with HTML hyperescaping. '''
+        form = wsgiform.WsgiForm(self.dummy_app, func='hyperescape')
         env = form(copy.deepcopy(self.test_env), self.dummy_sr)
-        self.assertEqual(env['wsgize.kwargs']['name'], '<tag id="Test">This is a 'test' & another.</tag>')
+        self.assertEqual(env['wsgiform.dict']['name'], '<tag id="Test">This is a 'test' & another.</tag>')                 
 
-    def testenviron_strictescape(self):
-        '''Parses form data into individual environ entries with strict HTML escaping.'''
-        form = wsgiform.WsgiForm(self.dummy_app, style='environ', func='escape', strict=True)
+    def test_strictescape(self):
+        '''Parses form data into a dictionary with strict HTML escaping.'''
+        form = wsgiform.WsgiForm(self.dummy_app, strict=True)
         env = form(copy.deepcopy(self.test_env), self.dummy_sr)
-        self.assertEqual(env['wsgiform.name'], '<tag id="Test">This is a 'test' & another.</tag>')
+        self.assertEqual(env['wsgiform.dict']['name'], '<tag id="Test">This is a 'test' & another.</tag>')
 
-    def testdict_sterilize(self):
+    def test_stricthyperescape(self):
+        '''Parses form data into a dictionary with strict HTML hyperescaping.'''
+        form = wsgiform.WsgiForm(self.dummy_app, func='hyperescape', strict=True)
+        env = form(copy.deepcopy(self.test_env), self.dummy_sr)
+        self.assertEqual(env['wsgiform.dict']['name'], '<tag id="Test">This is a 'test' & another.</tag>')        
+
+    def test_sterilize(self):
         '''Parses form data into a dictionary with data sterilization.'''
-        form = wsgiform.WsgiForm(self.dummy_app, style='dict', func='sterilize')
+        form = wsgiform.WsgiForm(self.dummy_app, func='sterilize')
         env = form(copy.deepcopy(self.test_env), self.dummy_sr)
         self.assertEqual(env['wsgiform.dict']['name'], 'tag idTestThis is a test  another.tag')
 
-    def testkwargs_sterilize(self):
-        '''Parses form data into keyword arguments with data sterilization.'''
-        form = wsgiform.WsgiForm(self.dummy_app, style='kwargs', func='sterilize')
-        env = form(copy.deepcopy(self.test_env), self.dummy_sr)
-        self.assertEqual(env['wsgize.kwargs']['name'], 'tag idTestThis is a test  another.tag')
-
-    def testenviron_sterilize(self):
-        '''Parses form data into individual environ entries with data sterilization.'''
-        form = wsgiform.WsgiForm(self.dummy_app, style='environ', func='sterilize')
-        env = form(copy.deepcopy(self.test_env), self.dummy_sr)
-        self.assertEqual(env['wsgiform.name'], 'tag idTestThis is a test  another.tag')
-
-    def testdict_strictsterilize(self):
+    def test_strictsterilize(self):
         '''Parses form data into a dictionary with strict data sterilization.'''
-        form = wsgiform.WsgiForm(self.dummy_app, style='dict', func='sterilize', strict=True)
+        form = wsgiform.WsgiForm(self.dummy_app, func='sterilize', strict=True)
         env = form(copy.deepcopy(self.test_env), self.dummy_sr)
         self.assertEqual(env['wsgiform.dict']['name'], 'tag idTestThis is a test  another.tag')
 
-    def testkwargs_strictsterilize(self):
-        '''Parses form data into keyword arguments with strict data sterilization.'''
-        form = wsgiform.WsgiForm(self.dummy_app, style='kwargs', func='sterilize', strict=True)
+    def test_validation(self):
+        '''Tests data validation.'''
+        vdict = {'num':getvalidator(('number', ('range', 10000, 15000)))}
+        form = wsgiform.WsgiForm(self.dummy_app, func='sterilize', validators=vdict)
         env = form(copy.deepcopy(self.test_env), self.dummy_sr)
-        self.assertEqual(env['wsgize.kwargs']['name'], 'tag idTestThis is a test  another.tag')
+        self.assertEqual(env['wsgiform.dict']['name'], 'tag idTestThis is a test  another.tag')
 
-    def testenviron_strictsterilize(self):
-        '''Parses form data into individual environ entries with strict data sterilization.'''
-        form = wsgiform.WsgiForm(self.dummy_app, style='environ', func='sterilize', strict=True)
-        env = form(copy.deepcopy(self.test_env), self.dummy_sr)
-        self.assertEqual(env['wsgiform.name'], 'tag idTestThis is a test  another.tag')
+    def test_validation_false(self):
+        '''Tests bad data validation.'''
+        vdict = {'num':getvalidator(('float', ('range', 1000, 1500)))}
+        form = wsgiform.WsgiForm(self.dummy_app, func='sterilize', validators=vdict)
+        iteble = form(copy.deepcopy(self.test_env), self.dummy_sr)
+        self.assertEqual(iteble[0], 'Data in field(s) num was invalid.')        
 
+    def test_validation_strict(self):    
+        '''Tests strict data validation.'''
+        vdict = {'num':getvalidator(('number', ('range', 10000, 15000)))}
+        form = wsgiform.WsgiForm(self.dummy_app, func='sterilize', strict=True, validators=vdict)
+        iteble = form(copy.deepcopy(self.test_env), self.dummy_sr)
+        self.assertEqual(iteble[0], 'Data in field(s) str1 name Submit state was invalid.')        
+    
 
 if __name__ == '__main__': unittest.main()