Inactive

Project Summary

  Analyzed 7 days ago based on code collected 7 days ago.

Formosa is a library to process user input from untrusted sources, a problem admittedly already widely addressed by countless other libraries. Formosa, however, brings a number of things to the table not found in other form processing libraries, in hopes of solving very common sources of grief for developers.

Dramatis PersonaeFields translate zero or more values from a mapping (typically strings from a MultiDict given in an HTTP request) into a single atomic Python value of a particular type (or None). Examples of fields include String, Int, Date, etc.

Validators implement higher-order constraints on a translated mapping of values. They execute only if all fields were successfully translated.

Forms are built from fields and validators. They first translate fields into a dict of type-safe Python values, or raise an ErrorSet exception if input is invalid. If input is valid, they pass the dict of values to a set of validators for higher-order validation. If everything is well-formed, the translated and validated input is returned.

Sub-forms are fields that are also forms. They are described below.

Error sets are possibly nested collections of error messages raised during translation or validation. Errors may target (i.e span) any number of fields, from zero to many. This, as far as I know, is absent from all other form processing libraries, but is extremely helpful, especially for higher-order constraints.

Use CasesBasic OverviewThe following implements a form asking a user for his or her first and last names, optional e-mail address, favorite number (from 1 to 10), and optional comments:

from formosa import Form, ErrorSet, fields

form = Form({'first_name': fields.String('first_name', max_length=30),
'last_name': fields.String('last_name', max_length=30),
'email': fields.Email('email', allow_empty=True),
'favorite_number': fields.Int('favorite_number', min=1, max=10),
'comments': fields.String('comments', max_length=1000)})
try:
result = form.translate(request.POST)
except ErrorSet, errors:
print 'Oops!\n%s' % errors
else:
print 'Hello, %s!' % result['first_name']Formosa comes with a wide variety of fields, and fields are extremely simple to implement (the protocol consists of a single method). An examples of custom fields one might implement as part of a project include subclasses of Choice or MultiChoice for domain classes.

Higher-Order Declarative ConstraintsHave you ever needed to express a constraint on your input along the lines of the following?

I want to assert two values are always given in ascending order. I want values x, y, and z to map to unique values in columns a, b, c in a database table. I want no more than a certain number of values from a set of fields to be entered -- or that a certain minimum number of fields are entered, too. If given, I want these fields to have the same (or different) value. Enter validators. Validators are objects that implement constraints across mappings of multiple translated values, so they can have much farther-reaching logic than simple fields (which are limited in scope to single values without some sort of heroics). Validators follow a very simple protocol, and those bundled with Formosa include:

Same, Different: A set of fields must all have the same or different values AtLeast, AtMost: At least, or at most a certain number of fields may or must be given Ascending, Descending: Enforce inequality constraints across multiple fields (strict or not) EqualTo: A set of fields must be equal to a given value. OnlyIf: A set of fields may have values only if every field in another set does. IfAnyThen: If any field from one set has been specified, then each field from another set must have a value. Unique: Assert that a set of values are unique to a corresponding set of SQLAlchemy model properties. For example, the following implements a form where the start date must precede the end date, via the Ascending validator. This pretty clearly demonstrates just how simple such declarative constraints make things:

>>> form = Form({'start_date': fields.Date('start_date'),
... 'end_date': fields.Date('end_date')},
... [validators.Ascending(['start_date', 'end_date'])])
>>> form.translate({'start_date': '11/26/2008', 'end_date': '11/24/2008'})
Traceback (most recent call last):
...
formosa.ErrorSet: * Values are not in ascending order [end_date, start_date]Sub-FormsAnother common problem is to have sub-forms nested inside a larger form, where the sub-form is optional. (This pattern translates to nullable foreign keys in relational databases.) A very widespread example of this is optional mailing addresses: if any of the fields of the address is given, the whole sub-form should be validated, but if no field is given, its fields should be ignored (even if they're required in terms of the sub-form). Here's an example:

>>> form = Form({'first_name': fields.String('first_name', max_length=30),
... 'last_name': fields.String('last_name', max_length=30),
... 'address': fields.SubForm({'street1': fields.String('street1', False, 30),
... 'street2': fields.String('street2', True, 30),
... 'city': fields.String('city', False, 20),
... 'state': fields.String('state', False, 2, 2),
... 'zip': fields.ZipCode('zip')}),
... 'notes': fields.String('notes', True, 1000)})
>>> input = dict(first_name='Nick', last_name='Murphy', street1='123 Test St.', city='Testville', state='AZ', zip='85712')
>>> output = form.translate(input)
>>> from pprint import pprint
>>> pprint(output)
{'address': {'city': 'Testville',
'state': 'AZ',
'street1': '123 Test St.',
'street2': None,
'zip': '85712'},
'first_name': 'Nick',
'last_name': 'Murphy',
'notes': None}Here, address is translated to None if none of its fields are entered. Otherwise, it is processed like a top-level form. Assuming translation and validation succeeds, it is translated into a dict of values mapped to 'address' in the parent form. Note that ErrorSet is nestable, so any errors during translation or validation are also percolated back to the parent.

AvailabilityFormosa is released under the MIT license, so please use, share and enjoy. It also comes with a very extensive unit test suite, offering full test coverage, so you can be assured of its correctness. You can check out the most recent copy from the Google Code Subversion repository as follows:

$ svn checkout http://formosa.googlecode.com/svn/trunk formosa

Share

In a Nutshell, formosa...

 

Activity

30 Day Summary Apr 17 2013 — May 17 2013

12 Month Summary May 17 2012 — May 17 2013

Community

Ratings

Be the first to rate this project
 
Click to add your rating
 
Review this Project!
 
 
 

Creative Commons License Copyright © 2013 Black Duck Software, Inc. and its contributors, Some Rights Reserved. Unless otherwise marked, this work is licensed under a Creative Commons Attribution 3.0 Unported License . Ohloh ® and the Ohloh logo are trademarks of Black Duck Software, Inc. in the United States and/or other jurisdictions. All other trademarks are the property of their respective holders.