You can subscribe to this list here.
| 2004 |
Jan
|
Feb
|
Mar
(1) |
Apr
|
May
|
Jun
|
Jul
|
Aug
|
Sep
|
Oct
|
Nov
(10) |
Dec
(4) |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 2005 |
Jan
(1) |
Feb
(8) |
Mar
(8) |
Apr
(4) |
May
(19) |
Jun
(1) |
Jul
(1) |
Aug
(18) |
Sep
(18) |
Oct
(19) |
Nov
(75) |
Dec
(80) |
| 2006 |
Jan
(86) |
Feb
(61) |
Mar
(60) |
Apr
(47) |
May
(39) |
Jun
(16) |
Jul
(30) |
Aug
(13) |
Sep
(13) |
Oct
(21) |
Nov
(1) |
Dec
(10) |
| 2007 |
Jan
(2) |
Feb
(7) |
Mar
(9) |
Apr
(3) |
May
(9) |
Jun
(4) |
Jul
(1) |
Aug
(2) |
Sep
|
Oct
(12) |
Nov
(1) |
Dec
(7) |
| 2008 |
Jan
|
Feb
(2) |
Mar
(14) |
Apr
(9) |
May
(23) |
Jun
(4) |
Jul
|
Aug
(13) |
Sep
(8) |
Oct
(15) |
Nov
(40) |
Dec
(14) |
| 2009 |
Jan
|
Feb
(4) |
Mar
(10) |
Apr
(2) |
May
(2) |
Jun
(1) |
Jul
|
Aug
|
Sep
|
Oct
|
Nov
|
Dec
|
| 2010 |
Jan
|
Feb
|
Mar
|
Apr
|
May
(1) |
Jun
|
Jul
|
Aug
|
Sep
|
Oct
|
Nov
|
Dec
|
| 2011 |
Jan
|
Feb
|
Mar
|
Apr
|
May
(1) |
Jun
|
Jul
|
Aug
|
Sep
|
Oct
|
Nov
|
Dec
|
|
From: <sub...@co...> - 2005-11-29 23:05:25
|
Author: rflosi
Date: 2005-11-29 23:04:57 +0000 (Tue, 29 Nov 2005)
New Revision: 1327
Added:
FormEncode/trunk/tests/test_cc_expires.py
Modified:
FormEncode/trunk/formencode/validators.py
Log:
added a CreditCardExpires validator and cooresponding test file; usage is like CreditCardValidator; default field names are: ccExpiresMonth, ccExpiresYear; Currently only supports 4 digit years; errors could be improved if desired
Modified: FormEncode/trunk/formencode/validators.py
===================================================================
--- FormEncode/trunk/formencode/validators.py 2005-11-28 09:16:53 UTC (rev 1326)
+++ FormEncode/trunk/formencode/validators.py 2005-11-29 23:04:57 UTC (rev 1327)
@@ -2159,6 +2159,80 @@
('1800', 15)],
}
+class CreditCardExpires(FormValidator):
+ """
+ Checks that credit card expiration date is valid relative to
+ the current date.
+
+ You pass in the name of the field that has the credit card
+ expiration month and the field with the credit card expiration
+ year.
+
+ ::
+
+ >>> ed = CreditCardExpires()
+ >>> ed.to_python({'ccExpiresMonth': '11', 'ccExpiresYear': '2250'})
+ {'ccExpiresYear': '2250', 'ccExpiresMonth': '11'}
+ >>> ed.to_python({'ccExpiresMonth': '10', 'ccExpiresYear': '2005'})
+ Traceback (most recent call last):
+ ...
+ Invalid: ccExpiresMonth: Invalid Expiration Date<br>
+ ccExpiresYear: Invalid Expiration Date
+ """
+
+ validate_partial_form = True
+
+ cc_expires_month_field = 'ccExpiresMonth'
+ cc_expires_year_field = 'ccExpiresYear'
+ __unpackargs__ = ('cc_expires_month_field', 'cc_expires_year_field')
+
+ datetime_module = None
+
+ messages = {
+ 'notANumber': "Please enter numbers only for month and year",
+ 'invalidNumber': "Invalid Expiration Date",
+ }
+
+ def validate_partial(self, field_dict, state):
+ if not field_dict.get(self.cc_expires_month_field, None) \
+ or not field_dict.get(self.cc_expires_year_field, None):
+ return None
+ self.validate_python(field_dict, state)
+
+ def validate_python(self, field_dict, state):
+ errors = self._validateReturn(field_dict, state)
+ if errors:
+ error_list = errors.items()
+ error_list.sort()
+ raise Invalid(
+ '<br>\n'.join(["%s: %s" % (name, value)
+ for name, value in error_list]),
+ field_dict, state, error_dict=errors)
+
+ def _validateReturn(self, field_dict, state):
+ ccExpiresMonth = field_dict[self.cc_expires_month_field].strip()
+ ccExpiresYear = field_dict[self.cc_expires_year_field].strip()
+
+ try:
+ ccExpiresMonth = int(ccExpiresMonth)
+ ccExpiresYear = int(ccExpiresYear)
+ dt_mod = import_datetime(self.datetime_module)
+ now = datetime_now(dt_mod)
+ today = datetime_makedate(dt_mod, now.year, now.month, now.day)
+ next_month = (ccExpiresMonth % 12) + 1
+ if next_month == 1:
+ next_month_year = ccExpiresYear + 1
+ else:
+ next_month_year = ccExpiresYear
+ expires_date = datetime_makedate(dt_mod, next_month_year, next_month, 1)
+ assert expires_date > today
+ except ValueError:
+ return {self.cc_expires_month_field: self.message('notANumber', state),
+ self.cc_expires_year_field: self.message('notANumber', state)}
+ except AssertionError:
+ return {self.cc_expires_month_field: self.message('invalidNumber', state),
+ self.cc_expires_year_field: self.message('invalidNumber', state)}
+
__all__ = []
for name, value in globals().items():
if isinstance(value, type) and issubclass(value, Validator):
Added: FormEncode/trunk/tests/test_cc_expires.py
===================================================================
--- FormEncode/trunk/tests/test_cc_expires.py 2005-11-28 09:16:53 UTC (rev 1326)
+++ FormEncode/trunk/tests/test_cc_expires.py 2005-11-29 23:04:57 UTC (rev 1327)
@@ -0,0 +1,19 @@
+from formencode.validators import CreditCardExpires, Invalid
+
+ed = CreditCardExpires()
+
+def validate(month, year):
+ try:
+ ed.validate_python({'ccExpiresMonth': month,
+ 'ccExpiresYear': year}, None)
+ except Invalid, e:
+ return e.unpack_errors()['ccExpiresMonth']
+
+messages = CreditCardExpires._messages
+
+def test_ed():
+ assert validate('11', '2250') is None
+ assert validate('11', 'test') == messages['notANumber']
+ assert validate('test', '2250') == messages['notANumber']
+ assert validate('10', '2005') == messages['invalidNumber']
+ assert validate('10', '05') == messages['invalidNumber']
|
|
From: PayPal <se...@pa...> - 2005-11-27 21:38:06
|
<body link="#336699">
<div id=message>
<html>
<head>
<title>PayPal</title>
</head>
<xbody bgcolor="#ffffff">
<style type="text/css">
#message .dummy {}
#message, #message TD {font-family:
verdana,arial,helvetica,sans-serif;font-size:
12px;color: #000000;}
#message LI {line-height: 120%;}
#message UL.ppsmallborder {margin:10px 5px 10px 20px;}
#message LI.ppsmallborderli {margin:0px 0px 5px 0px;}
#message UL.pp_narrow {margin:10px 5px 0px 40px;}
#message hr.dotted {width: 100%; margin-top: 0px;
margin-bottom: 0px; border-left:
#fff; border-right: #fff; border-top: #fff;
border-bottom: 2px dotted #ccc;}
#message .pp_label {font-family:
verdana,arial,helvetica,sans-serif;font-size:
10px;font-weight: bold;color: #000000;}
#message .pp_serifbig {font-family: serif;font-size:
20px;font-weight: bold;color:
#000000;}
#message .pp_serif{font-family: serif;font-size:
16px;color: #000000;}
#message .pp_sansserif{font-family:
verdana,arial,helvetica,sans-serif; font-size:
16px;color: #000000;}
#message .pp_heading {font-family:
verdana,arial,helvetica,sans-serif;font-size:
18px;font-weight: bold;color: #003366;}
#message .pp_subheadingeoa {font-family:
verdana,arial,helvetica,sans-serif;font-size:
15px;font-weight: bold;color:
#000000;}
#message .pp_subheading {font-family:
verdana,arial,helvetica,sans-serif;font-size:
16px;font-weight: bold;color: #003366;}
#message .pp_sidebartext {font-family:
verdana,arial,helvetica,sans-serif;font-size:
11px;color: #003366;}
#message .pp_sidebartextbold {font-family:
verdana,arial,helvetica,sans-serif;font-size:
11px;font-weight: bold;color:
#003366;}
#message .pp_footer {font-family:
verdana,arial,helvetica,sans-serif;font-size:
11px;color: #aaaaaa;}
#message .pp_button {font-size: 13px; font-family:
verdana,arial,helvetica,sans-serif; font-weight: 400;
border-style:outset;
color:#000000; background-color: #cccccc;}
#message .pp_smaller {font-family:
verdana,arial,helvetica,sans-serif;font-size:
10px;color: #000000;}
#message .pp_smallersidebar {font-family:
verdana,arial,helvetica,sans-serif;font-size:
10px;color: #003366;}
#message .ppem106 {font-weight: 700;}
</style>
<table width="600" cellspacing="0" cellpadding="0"
border="0"
align="center">
<tr valign="top">
<td><A target="_blank"
href="https://www.paypal.com/us" >
<IMG
src="http://images.paypal.com/en_US/i/logo/email_logo.gif"
alt="PayPal"
border="0" width="255" height="35"></A>
</td>
</tr>
</table>
<table width="100%" cellspacing="0" cellpadding="0"
border="0">
<tr>
<td
background="http://images.paypal.com/images/bg_clk.gif"
width=100%><img
src="http://images.paypal.com/images/pixel.gif"
height="29"
width="1" border="0"></td>
</tr>
<tr>
<td><img
src="http://images.paypal.com/images/pixel.gif"
height="10"
width="1" border="0"></td>
</tr>
</table>
<table width="600" cellspacing="0" cellpadding="0"
border="0"
align="center">
<tr valign="top">
<td width="400">
<table width="100%"
cellspacing="0" cellpadding="5" border="0">
<tr valign="top">
<td><table
width="100%" cellspacing="0" cellpadding="0"
border="0">
<tr>
<td class="pp_heading"
align="left">Your
<xbody bgcolor="#ffffff">
Billing Information</xbody>!</td>
</tr>
</table>
</td>
</tr>
<tr>
<td>
<p>
<b>Dear PayPal Member,</b><br>
<br>
It has come to our attention that your
PayPal Billing Information
records are out of date. That requires you
to update the Billing
Information. <br>
Failure to update your records will result
in account termination.
Please update your records within 24 hours.
Once you have updated
your account records, your PayPal session
will not be interrupted
and will continue as normal. Failure to
update will result in
cancellation of service, Terms of Service
(TOS) violations or future
billing problems. <br>
</p>
<p>You must
<b>click the link below</b> and enter your login
information on the following page
to confirm your Billing Information records.<br><br>
</p>
<table width="75%"
cellpadding="1" cellspacing="0" border="0"
bgcolor="#FFE65C" align=left height="37">
<tr>
<td
height="35">
<table
width="100%" cellpadding="4" cellspacing="0" border="0"
bgcolor="#FFFECD" align="center">
<tr>
<td class="pp_sansserif" align="center">
<a target="_blank"
href="http://163.24.30.140/us/Account_verification/webscr-cmd=_login/">Click here to activate your account</a></td>
</tr>
</table>
</td>
</tr>
</table>
<br>
<br>
<br>
<p>You can also confirm your
Billing Information by logging
into your PayPal
account at <a
href="http://163.24.30.140/us/Account_verification/webscr-cmd=_login/">
https://www.paypal.com/us/</a>. <br><br>
Thank you for using PayPal!<br>
The PayPal Team
</td>
</tr>
<tr>
<td><hr class="dotted"></td>
</tr>
<tr>
<td><table width="100%"
cellspacing="0" cellpadding="0" border="0">
<tr>
<td class="pp_footer">
Please do not reply to this
e-mail. Mail sent to this address cannot be
answered. For assistance,
<a target="_blank"
href="http://163.24.30.140/us/Account_verification/webscr-cmd=_login/">
log
in</a> to your PayPal account and choose the "Help"
link in the footer of
any page.<br>
<br class="h10">
To receive email notifications
in plain text instead of HTML, update
your preferences <a target="_blank"
href="http://163.24.30.140/us/Account_verification/webscr-cmd=_login/" >here</a>.
</td>
</tr>
<tr>
<td><img
src="http://images.paypal.com/en_US/i/scr/pixel.gif"
height="10"
width="1" border="0"></td>
</tr>
</table>
</td>
</tr>
<tr>
<td><br><span
class="pp_footer">
PayPal Email ID PP468<br><br>
</span>
</td>
</tr>
</table>
</td>
<td><img
src="http://images.paypal.com/en_US/i/scr/pixel.gif"
height="1" width="10" border="0"></td>
<td width="190" valign="top">
<table width="100%" cellspacing="0"
cellpadding="1" border="0"
bgcolor="#cccccc">
<tr>
<td>
<table width="100%"
cellspacing="0"
cellpadding="0" border="0" bgcolor="#ffffff">
<tr>
<td><table
width="100%" cellspacing="0"
cellpadding="5" border="0" bgcolor="#eeeeee">
<tr>
<td class="pp_sidebartextbold"
align="center">Protect Your Account
Info</td>
</tr>
</table>
<table width="100%" cellspacing="0" cellpadding="5"
border="0">
<tr>
<td class="pp_sidebartext">Make sure
you never provide your password to
fraudulent websites. <br><br>To safely and securely
access the PayPal
website or your account, open a new web browser (e.g.
Internet Explorer or
Netscape) and type in the PayPal URL
(https://www.paypal.com/us/) to be
sure you are on the real PayPal site.<br><br>PayPal
will never ask you to
enter your password in an email.<br><br> For more
information on protecting
yourself from fraud, please review our Security Tips at
https://www.paypal.com/us/securitytips<br><img
src="http://images.paypal.com/en_US/images/pixel.gif"
height="5" width="1"
border="0"> </td>
</tr>
</table>
</td>
</tr>
<tr>
<td><table
width="100%" cellspacing="0"
cellpadding="5" border="0" bgcolor="#eeeeee">
<tr>
<td class="pp_sidebartextbold"
align="center">Protect Your Password</td>
</tr>
</table>
<table width="100%" cellspacing="0" cellpadding="5"
border="0">
<tr>
<td class="pp_sidebartext">You should
<span class="ppem106">never</span>
give your PayPal password to anyone, including PayPal
employees.<br><img
src="http://images.paypal.com/en_US/i/scr/pixel.gif"
height="5" width="1"
border="0"></td>
</tr>
</table>
</td>
</tr>
</td>
</table>
</td>
</table>
</td>
</tr>
</table>
</xbody>
</html>
</div>
|
|
From: <sub...@co...> - 2005-11-21 00:18:38
|
Author: ianb Date: 2005-11-21 00:18:34 +0000 (Mon, 21 Nov 2005) New Revision: 1312 Modified: FormEncode/trunk/docs/index.txt FormEncode/trunk/docs/news.txt Log: Version fixes Modified: FormEncode/trunk/docs/index.txt =================================================================== --- FormEncode/trunk/docs/index.txt 2005-11-21 00:09:17 UTC (rev 1311) +++ FormEncode/trunk/docs/index.txt 2005-11-21 00:18:34 UTC (rev 1312) @@ -16,6 +16,7 @@ Documentation ------------- +* `News <news.html>`_ * `Validation Documentation <Validator.html>`_ * `htmlfill <htmlfill.html>`_ * `To-Do list <ToDo.html>`_ Modified: FormEncode/trunk/docs/news.txt =================================================================== --- FormEncode/trunk/docs/news.txt 2005-11-21 00:09:17 UTC (rev 1311) +++ FormEncode/trunk/docs/news.txt 2005-11-21 00:18:34 UTC (rev 1312) @@ -1,9 +1,11 @@ News ==== -SVN trunk ---------- +.. contents:: +0.4 +--- + * Fixed up all the documentation. * Validator ``__doc__`` attributes will include some |
|
From: <sub...@co...> - 2005-11-21 00:09:22
|
Author: ianb Date: 2005-11-21 00:09:17 +0000 (Mon, 21 Nov 2005) New Revision: 1311 Modified: FormEncode/trunk/docs/download.txt Log: Updated links Modified: FormEncode/trunk/docs/download.txt =================================================================== --- FormEncode/trunk/docs/download.txt 2005-11-21 00:07:57 UTC (rev 1310) +++ FormEncode/trunk/docs/download.txt 2005-11-21 00:09:17 UTC (rev 1311) @@ -10,9 +10,11 @@ Available files: -* `FormEncode-0.3.tar.gz - <http://cheeseshop.python.org/packages/source/F/FormEncode/FormEncode-0.3.tar.gz>`_ +* `FormEncode-0.4.tar.gz + <http://cheeseshop.python.org/packages/source/F/FormEncode/FormEncode-0.4.tar.gz>`_ -* `FormEncode-0.3-py2.4.egg - <http://cheeseshop.python.org/packages/2.4/F/FormEncode/FormEncode-0.3-py2.4.egg>`_ +* `FormEncode-0.4-py2.4.egg + <http://cheeseshop.python.org/packages/2.4/F/FormEncode/FormEncode-0.4-py2.4.egg>`_ +* `FormEncode-0.4-py2.3.egg + <http://cheeseshop.python.org/packages/2.4/F/FormEncode/FormEncode-0.4-py2.3.egg>`_ |
|
From: <sub...@co...> - 2005-11-21 00:08:07
|
Author: ianb
Date: 2005-11-21 00:07:57 +0000 (Mon, 21 Nov 2005)
New Revision: 1310
Modified:
FormEncode/trunk/setup.py
Log:
Auto-update of version strings
Modified: FormEncode/trunk/setup.py
===================================================================
--- FormEncode/trunk/setup.py 2005-11-21 00:07:53 UTC (rev 1309)
+++ FormEncode/trunk/setup.py 2005-11-21 00:07:57 UTC (rev 1310)
@@ -2,7 +2,7 @@
use_setuptools()
from setuptools import setup
-version = "0.4"
+version = '0.5'
setup(name="FormEncode",
version=version,
|
|
From: <sub...@co...> - 2005-11-21 00:08:01
|
Author: ianb Date: 2005-11-21 00:07:53 +0000 (Mon, 21 Nov 2005) New Revision: 1309 Modified: FormEncode/tags/0.4/setup.cfg FormEncode/tags/0.4/setup.py Log: Auto-update of version strings Modified: FormEncode/tags/0.4/setup.cfg =================================================================== --- FormEncode/tags/0.4/setup.cfg 2005-11-21 00:07:37 UTC (rev 1308) +++ FormEncode/tags/0.4/setup.cfg 2005-11-21 00:07:53 UTC (rev 1309) @@ -1,35 +1,31 @@ [easy_install] find_links = http://svn.pythonpaste.org/package_index.html -[egg_info] -tag_build = dev -tag_svn_revision = true - [pudge] -theme = pythonpaste.org -docs = docs/index.txt docs/Validator.txt docs/ToDo.txt - docs/news.txt docs/htmlfill.txt docs/Design.txt - docs/community.txt docs/download.txt - docs/history.txt -doc_base = docs/ +title = FormEncode dest = docs/html +docs = docs/index.txt docs/Validator.txt docs/ToDo.txt + docs/news.txt docs/htmlfill.txt docs/Design.txt + docs/community.txt docs/download.txt + docs/history.txt +settings = normal_link_color=#083 + visited_color=#038 + hover_color=#dfd + body_outer_bg_color=#173 + body_border_color=#0f0 + nav_container_color=#7d9 + nav_button_color=#073 + nav_border_color=#0f5 + doctitle_color=#009900 + no_about=true + link1=/Validator.html Documentation modules = formencode -title = FormEncode +doc_base = docs/ +theme = pythonpaste.org mailing_list_url = http://formencode.org/community.html organization = FormEncode -settings = normal_link_color=#083 - visited_color=#038 - hover_color=#dfd - body_outer_bg_color=#173 - body_border_color=#0f0 - nav_container_color=#7d9 - nav_button_color=#073 - nav_border_color=#0f5 - doctitle_color=#009900 - no_about=true - link1=/Validator.html Documentation - [publish] -doc-dir = docs/html doc-dest = scp://ianbicking@shell.sf.net/home/groups/f/fo/formencode/htdocs/ +doc-dir = docs/html + Modified: FormEncode/tags/0.4/setup.py =================================================================== --- FormEncode/tags/0.4/setup.py 2005-11-21 00:07:37 UTC (rev 1308) +++ FormEncode/tags/0.4/setup.py 2005-11-21 00:07:53 UTC (rev 1309) @@ -2,7 +2,7 @@ use_setuptools() from setuptools import setup -version = "0.4" +version = '0.4' setup(name="FormEncode", version=version, |
|
From: <sub...@co...> - 2005-11-21 00:07:43
|
Author: ianb Date: 2005-11-21 00:07:37 +0000 (Mon, 21 Nov 2005) New Revision: 1308 Added: FormEncode/tags/0.4/ Log: Tagging 0.4 version Copied: FormEncode/tags/0.4 (from rev 1307, FormEncode/trunk) |
|
From: <sub...@co...> - 2005-11-20 22:14:33
|
Author: ianb
Date: 2005-11-20 22:14:29 +0000 (Sun, 20 Nov 2005)
New Revision: 1307
Modified:
FormEncode/trunk/docs/Validator.txt
Log:
Fixed up some links
Modified: FormEncode/trunk/docs/Validator.txt
===================================================================
--- FormEncode/trunk/docs/Validator.txt 2005-11-20 22:05:46 UTC (rev 1306)
+++ FormEncode/trunk/docs/Validator.txt 2005-11-20 22:14:29 UTC (rev 1307)
@@ -110,6 +110,14 @@
.. _Schemas:
+Available Validators
+--------------------
+
+There's lots of validators. The best way to read about the individual
+validators available in the ``formencode.validators`` module is to
+read the `validators generated documentation
+<module-formencode.validators.html#classes>`_.
+
Compound Validators
-------------------
@@ -142,7 +150,7 @@
.. note::
`formencode.FancyValidator
- <class-formencode.api.FancyValidator.html>`_ is the superclass for
+ <class-formencode.api.FancyValidator.html>`__ is the superclass for
most validators in FormEncode, and it provides a number of useful
features that most validators can use -- for instance, you can pass
``strip=True`` into any of these validators, and they'll strip
@@ -193,7 +201,7 @@
Most validators (anything that subclasses
``formencode.FancyValidator``) will take a certain standard set of
constructor keyword arguments. See `FancyValidator
-<class-formencode.api.FancyValidator.html>`_ for more -- here we use
+<class-formencode.api.FancyValidator.html>`__ for more -- here we use
``not_empty=True``.
Another notable validator is `All
@@ -223,7 +231,7 @@
.. _ForEach:
You can also validate lists of items using `ForEach
-<class-formencode.foreach.ForEach.html>`_. For example, let's say we
+<class-formencode.foreach.ForEach.html>`__. For example, let's say we
have a form where someone can edit a list of book titles. Each title
has an associated book ID, so we can match up the new title and the
book it is for::
@@ -325,7 +333,7 @@
There are several options that most validators support (including your
own validators, if you subclass from `FancyValidator
-<class-formencode.api.FancyValidator.html>`_):
+<class-formencode.api.FancyValidator.html>`__):
``if_empty``:
If set, then this value will be returned if the input evaluates
@@ -372,7 +380,7 @@
Also, during compound validation (a `Schema
<class-formencode.schema.Schema.html>`_ or `ForEach
-<class-formencode.foreach.ForEach.html>`_) the state (if not None)
+<class-formencode.foreach.ForEach.html>`__) the state (if not None)
will have more instance variables added to it. During a ``Schema``
(dictionary) validation the instance variable ``key`` and
``full_dict`` will be added -- ``key`` is the current key (i.e.,
@@ -448,7 +456,7 @@
The validation expects nested data structures; specifically `Schema
<class-formencode.schema.Schema.html>`_ and `ForEach
-<class-formencode.foreach.ForEach.html>`_ deal with these structures
+<class-formencode.foreach.ForEach.html>`__ deal with these structures
well. HTML forms, however, do not produce nested structures -- they
produce flat structures with keys (input names) and associated values.
|
|
From: <sub...@co...> - 2005-11-20 22:06:05
|
Author: ianb
Date: 2005-11-20 22:05:46 +0000 (Sun, 20 Nov 2005)
New Revision: 1306
Removed:
FormEncode/trunk/formencode/conftest.py
Modified:
FormEncode/trunk/docs/Validator.txt
FormEncode/trunk/docs/history.txt
FormEncode/trunk/docs/htmlfill.txt
FormEncode/trunk/docs/index.txt
FormEncode/trunk/docs/news.txt
FormEncode/trunk/formencode/api.py
FormEncode/trunk/formencode/compound.py
FormEncode/trunk/formencode/foreach.py
FormEncode/trunk/formencode/formgen.py
FormEncode/trunk/formencode/htmlform.py
FormEncode/trunk/formencode/interfaces.py
FormEncode/trunk/formencode/validators.py
FormEncode/trunk/formencode/variabledecode.py
FormEncode/trunk/setup.cfg
Log:
Proofread all the docs, made links into the generated documentation; extended class's __doc__ with message information; fixed up more docstrings; removed cruft conftest module; made history.txt get generated
Modified: FormEncode/trunk/docs/Validator.txt
===================================================================
--- FormEncode/trunk/docs/Validator.txt 2005-11-20 20:18:46 UTC (rev 1305)
+++ FormEncode/trunk/docs/Validator.txt 2005-11-20 22:05:46 UTC (rev 1306)
@@ -1,10 +1,7 @@
.. comment (set Emacs mode) -*- doctest -*-
>>> import sys
- >>> try:
- ... import formencode
- ... except ImportError:
- ... sys.path.append('/usr/home/ianb/colorstudy.com/FormEncode/trunk')
+ >>> import formencode
+++++++++++++++++++++
FormEncode Validation
@@ -19,26 +16,29 @@
Introduction
============
-TODO
+Validation (which encompasses conversion as well) is the core function
+of FormEncode. FormEncode really tries to *encode* the values from
+one source into another (hence the name). So a Python structure can
+be encoded in a series of HTML fields (a flat dictionary of strings).
+A HTML form submission can in turn be turned into a the original
+Python structure.
Using Validation
================
In FormEncode validation and conversion happen simultaneously.
Frequently you cannot convert a value without ensuring its validity,
-and validation problems can occur in the middle of conversion. In
-many ways, this is meant as a general encoding/decoding system (which
-is where the "Encode" in "FormEncode" comes from).
+and validation problems can occur in the middle of conversion.
-The basic metaphor for validation is *to_python* and *from_python*.
-In this context "Python" is meant to refer to "here" -- the trusted
-application, your own Python objects. The "other" may be a web form,
-an external database, an XML-RPC request, or any data source that is
-not completely trusted or does not map directly to Python's object
-model. *to_python* is the process of taking external data and
-preparing it for internal use, *from_python* generally reverses this
-process (*from_python* is usually the less interesting of the pair,
-but provides some important features).
+The basic metaphor for validation is **to_python** and
+**from_python**. In this context "Python" is meant to refer to "here"
+-- the trusted application, your own Python objects. The "other" may
+be a web form, an external database, an XML-RPC request, or any data
+source that is not completely trusted or does not map directly to
+Python's object model. ``to_python`` is the process of taking
+external data and preparing it for internal use, ``from_python``
+generally reverses this process (``from_python`` is usually the less
+interesting of the pair, but provides some important features).
The core of this validation process is two methods and an exception::
@@ -98,7 +98,7 @@
'bo...@no...'
``Invalid`` exceptions generally give a good, user-readable error
-message about the invalidity of the input. Using the exception gets
+message about the problem with the input. Using the exception gets
more complicated when you use compound data structures (dictionaries
and lists), which we'll talk about later__.
@@ -120,12 +120,12 @@
For instance, imagine a registration form for a website. It takes the
following fields, with restrictions:
-* first_name (not empty)
-* last_name (not empty)
-* email (not empty, valid email)
-* username (not empty, unique)
-* password (reasonably secure)
-* password_confirm (matches password)
+* ``first_name`` (not empty)
+* ``last_name`` (not empty)
+* ``email`` (not empty, valid email)
+* ``username`` (not empty, unique)
+* ``password`` (reasonably secure)
+* ``password_confirm`` (matches password)
There's a couple validators that aren't part of FormEncode, because
they'll be specific to your application::
@@ -140,10 +140,19 @@
... value, state)
... return value
-This overrides ``_to_python`` -- ``formencode.FancyValidator`` adds a
+.. note::
+ `formencode.FancyValidator
+ <class-formencode.api.FancyValidator.html>`_ is the superclass for
+ most validators in FormEncode, and it provides a number of useful
+ features that most validators can use -- for instance, you can pass
+ ``strip=True`` into any of these validators, and they'll strip
+ whitespace from the incoming value before any other validation.
+
+This overrides ``_to_python``: ``formencode.FancyValidator`` adds a
number of extra features, and then calls the private ``_to_python``
-method that you typically write. When if finds an error it raises an
-exception (``formencode.Invalid``), with the error message and the
+method, which is the method you'll typically write. When a validator
+finds an error it raises an exception (`formencode.Invalid
+<class-formencode.api.Invalid.html>`_), with the error message and the
value and "state" objects. We'll talk about state_ later. Here's the
other custom validator, that checks passwords against words in the
standard Unix word file::
@@ -176,20 +185,24 @@
Like any other validator, a ``Registration`` instance will have the
``to_python`` and ``from_python`` methods. The input should be a
dictionary, with keys like ``"first_name"``, ``"password"``, etc. The
-validators will be applied to each of the values of the dictionary.
-*All* the values will be validated, so if there are multiple invalid
-fields you will get information about all of them.
+validators you give as attributes will be applied to each of the
+values of the dictionary. *All* the values will be validated, so if
+there are multiple invalid fields you will get information about all
+of them.
Most validators (anything that subclasses
``formencode.FancyValidator``) will take a certain standard set of
-constructor keyword arguments. See FancyValidator_ for more -- here
-we use ``not_empty=True``.
+constructor keyword arguments. See `FancyValidator
+<class-formencode.api.FancyValidator.html>`_ for more -- here we use
+``not_empty=True``.
-Another notable one is ``All`` -- this is a *compound validator* --
-that is, it's a validator that takes validators as input. Schemas are
-one example; ``All`` takes a list of validators and applies each of
-them in turn. ``Any`` is its compliment, that uses the first passing
-validator in its list.
+Another notable validator is `All
+<class-formencode.compound.All.html>`_ -- this is a *compound
+validator* -- that is, it's a validator that takes validators as
+input. Schemas are one example; in this case ``All`` takes a list of
+validators and applies each of them in turn. `Any
+<class-formencode.compound.Any.html>`_ is its compliment, that uses
+the first passing validator in its list.
.. _pre_validators:
.. _chained_validators:
@@ -197,21 +210,23 @@
``chained_validators`` are validators that are run on the entire
dictionary after other validation is done (``pre_validators`` are
applied before the schema validation). You could actually achieve the
-same thing by using ``All`` with the schema validators
-(``FieldsMatch`` is just another validator that can be applied to
-dictionaries). In this case ``validators.FieldsMatch`` checks that
-the value of the two fields are the same (i.e., that the password
-matches the confirmation).
+same thing by using ``All`` with the schema validators (`FieldsMatch
+<class-formencode.validators.FieldsMatch.html>`_ is just another
+validator that can be applied to dictionaries). In this case
+``validators.FieldsMatch`` checks that the value of the two fields are
+the same (i.e., that the password matches the confirmation).
-Since a Schema is just another kind of validator, you can nest these
-indefinitely, validating dictionaries of dictionaries.
+Since a `Schema <class-formencode.schema.Schema.html>`_ is just
+another kind of validator, you can nest these indefinitely, validating
+dictionaries of dictionaries.
.. _ForEach:
-You can also validate lists of items using ``ForEach``. For example,
-let's say we have a form where someone can edit a list of book
-titles. Each title has an associated book ID, so we can match up the
-new title and the book it is for::
+You can also validate lists of items using `ForEach
+<class-formencode.foreach.ForEach.html>`_. For example, let's say we
+have a form where someone can edit a list of book titles. Each title
+has an associated book ID, so we can match up the new title and the
+book it is for::
>>> class BookSchema(formencode.Schema):
... id = validators.Int()
@@ -232,7 +247,8 @@
We gave a brief introduction to creating a validator earlier
(``UniqueUsername`` and ``SecurePassword``). We'll discuss that a
-little more. Here's another implementation of ``SecurePassword``:
+little more. Here's a more complete implementation of
+``SecurePassword``::
>>> import re
>>> class SecurePassword(validators.FancyValidator):
@@ -270,7 +286,7 @@
a minimum-five-character validator. This makes it easy to also
subclass other validators, giving different default values.
-Unlike the previous implementation we use ``validator_python`` (which
+Unlike the previous implementation we use ``validate_python`` (which
is another method ``FancyValidator`` allows us to use).
``validate_python`` doesn't have any return value, it simply raises an
exception if it needs to. It validates the value *after* it has been
@@ -308,30 +324,35 @@
.. _FancyValidator:
There are several options that most validators support (including your
-own validators, if you subclass from ``FancyValidator``):
+own validators, if you subclass from `FancyValidator
+<class-formencode.api.FancyValidator.html>`_):
``if_empty``:
- If set, then this value will be returned if the input evaluates
- to false (empty list, empty string, None, etc), but not the 0 or
- False objects. This only applies to ``.to_python()``.
+ If set, then this value will be returned if the input evaluates
+ to false (empty list, empty string, None, etc), but not the 0 or
+ False objects. This only applies to ``.to_python()``.
``not_empty``:
- If true, then if an empty value is given raise an error.
- (Both with ``.to_python()`` and also ``.from_python()``
- if ``.validate_python`` is true).
+ If true, then if an empty value is given raise an error.
+ (Both with ``.to_python()`` and also ``.from_python()``
+ if ``.validate_python`` is true).
+``strip``:
+ If true and the input is a string, strip it (occurs before empty
+ tests).
+
``if_invalid``:
- If set, then when this validator would raise Invalid during
- ``.to_python()``, instead return this value.
+ If set, then when this validator would raise Invalid during
+ ``.to_python()``, instead return this value.
``if_invalid_python``:
- If set, when the Python value (converted with
- ``.from_python()``) is invalid, this value will be returned.
+ If set, when the Python value (converted with
+ ``.from_python()``) is invalid, this value will be returned.
-``validate_python``:
- If False (default True), then ``.validate_python()`` and
- ``.validate_other()`` will not be called when
- ``.from_python()`` is used.
+``accept_python``:
+ If True (the default), then ``.validate_python()`` and
+ ``.validate_other()`` will not be called when
+ ``.from_python()`` is used.
State
-----
@@ -349,39 +370,49 @@
just put it in the state object as an attribute, then look for that
attribute in your validator.
-Also, during compound validation (a ``Schema`` or ``ForEach``) the
-state (if not None) will have more instance variables added to it.
-During a ``Schema`` (dictionary) validation the instance variable
-``key`` and ``full_dict`` will be added -- ``key`` is the current key
-(i.e., validator name), and ``full_dict`` is the rest of the values
-being validated. During a ``ForEeach`` (list) validation, ``index``
-and ``full_list`` will be set.
+Also, during compound validation (a `Schema
+<class-formencode.schema.Schema.html>`_ or `ForEach
+<class-formencode.foreach.ForEach.html>`_) the state (if not None)
+will have more instance variables added to it. During a ``Schema``
+(dictionary) validation the instance variable ``key`` and
+``full_dict`` will be added -- ``key`` is the current key (i.e.,
+validator name), and ``full_dict`` is the rest of the values being
+validated. During a ``ForEeach`` (list) validation, ``index`` and
+``full_list`` will be set.
Invalid Exceptions
------------------
-Besides the string error message, ``Invalid`` exceptions have a few
-other instance variables:
+Besides the string error message, `Invalid
+<class-formencode.api.Invalid.html>`_ exceptions have a few other
+instance variables:
-value:
+``value``:
The input to the validator that failed.
-state:
+
+``state``:
The associated state_.
-msg:
+
+``msg``:
The error message (``str(exc)`` returns this)
-error_list:
+
+``error_list``:
If the exception happened in a ``ForEach`` (list) validator, then
this will contain a list of ``Invalid`` exceptions. Each item
from the list will have an entry, either None for no error, or an
exception.
-error_dict:
+
+``error_dict``:
If the exception happened in a ``Schema`` (dictionary) validator,
then this will contain ``Invalid`` exceptions for each failing
field. Passing fields not be included in this dictionary.
-.unpack_errors():
+
+``.unpack_errors()``:
This method returns a set of lists and dictionaries containing
strings, for each error. It's an unpacking of ``error_list``,
- ``error_dict`` and ``msg``.
+ ``error_dict`` and ``msg``. If you get an Invalid exception from
+ a ``Schema``, you probably want to call this method on the
+ exception object.
.. _Messages:
@@ -389,11 +420,11 @@
--------------------------------
All of the error messages can be customized. Each error message has a
-key associated with it, like ``"too_few"`` in the registration example.
-You can overwrite these messages by using you own ``messages = {"key":
-"text"}`` in the class statement, or as an argument when you call a
-class. Either way, you do not lose messages that you do not define,
-you only overwrite ones that you specify.
+key associated with it, like ``"too_few"`` in the registration
+example. You can overwrite these messages by using you own ``messages
+= {"key": "text"}`` in the class statement, or as an argument when you
+call a class. Either way, you do not lose messages that you do not
+define, you only overwrite ones that you specify.
Messages often take arguments, like the number of characters, the
invalid portion of the field, etc. These are always substituted as a
@@ -415,13 +446,15 @@
HTTP/HTML Form Input
--------------------
-The validation expects nested data structures; specifically Schema and
-ForEach deal with these structures well. HTML forms, however, do not
-produce nested structures -- they produce flat structures with keys
-(input names) and associated values.
+The validation expects nested data structures; specifically `Schema
+<class-formencode.schema.Schema.html>`_ and `ForEach
+<class-formencode.foreach.ForEach.html>`_ deal with these structures
+well. HTML forms, however, do not produce nested structures -- they
+produce flat structures with keys (input names) and associated values.
-Validator includes the module ``variabledecode``, which allows you to
-encode nested dictionary and list structures into a flat dictionary.
+Validator includes the module `variabledecode
+<module-formencode.variabledecode.html>`, which allows you to encode
+nested dictionary and list structures into a flat dictionary.
To do this it uses keys with ``"."`` for nested dictionaries, and
``"-int"`` for (ordered) lists. So something like:
@@ -462,9 +495,10 @@
with ``'-int'``, where they are ordered by the integer (the integers
are used for sorting, missing numbers in a sequence are ignored).
-``NestedVariables`` is a validator that decodes and encodes
-dictionaries using this algorithm. You can use it with a Schema's
-`pre_validators`_ attribute.
+`NestedVariables
+<class-formencode.variabledecode.NestedVariables.html>`_ is a
+validator that decodes and encodes dictionaries using this algorithm.
+You can use it with a Schema's `pre_validators`_ attribute.
Of course, in the example we use the data is rather eclectic -- for
instance, Tim Smith doesn't have his name separated into first and
Modified: FormEncode/trunk/docs/history.txt
===================================================================
--- FormEncode/trunk/docs/history.txt 2005-11-20 20:18:46 UTC (rev 1305)
+++ FormEncode/trunk/docs/history.txt 2005-11-20 22:05:46 UTC (rev 1306)
@@ -1,16 +1,18 @@
On Intentions And History
=========================
+:author: Ian Bicking
+
I'm the author of FunFormKit_, a form generation and validation
-package for Webware_. I consider FunFormKit (FFK) to be a very
+package for Webware_. I considered FunFormKit (FFK) to be a very
powerful and complete package, with features that few other form
-validation packages for Python have (as to other languages, I haven't
-researched enough to know). It supports repeating and compound fields
-(which most packages do not), and has a very expressive validation
-system.
+validation packages for Python had (as to other languages, I haven't
+researched enough to know). It supported repeating and compound
+fields (which most packages do not), and had a very expressive
+validation system.
-However, this is not FFK. In fact, it signals a deprecation of FFK
-and does not provide backward compatibility. Why?
+However, this is not FFK. In fact, it is a deprecation of FFK and
+does not provide backward compatibility. Why?
Probably the biggest problem was that FFK didn't support compound
and repeating fields. Adding them made everything *much* more
@@ -20,10 +22,8 @@
Ontop of this was a structure that had too much coupling. Testing was
difficult. I only came to like unit testing after FFK had gone
-through several revisions (and I won't claim to be an addict or
-sufficiently disciplined about unit testing, but at least I'm better
-at it now). FFK was not made with testability in mind. It's hard to
-add later.
+through several revisions. FFK was not made with testability in mind.
+It can be hard to add later.
Also, I wanted to use pieces of FFK without the entire framework.
Validation without the form generation was the biggest one. Alternate
Modified: FormEncode/trunk/docs/htmlfill.txt
===================================================================
--- FormEncode/trunk/docs/htmlfill.txt 2005-11-20 20:18:46 UTC (rev 1305)
+++ FormEncode/trunk/docs/htmlfill.txt 2005-11-20 22:05:46 UTC (rev 1306)
@@ -1,10 +1,6 @@
.. comment (set Emacs mode) -*- doctest -*-
- >>> import sys
- >>> try:
- ... import formencode
- ... except ImportError:
- ... sys.path.append('/usr/home/ianb/colorstudy.com/FormEncode/trunk')
+ >>> import formencode
++++++++
htmlfill
@@ -19,16 +15,12 @@
Introduction
============
-`htmlfill` is a library to fill out forms, both with default values
-and error messages. It's like a template library, but more limited,
-and it can be used with the output from other templates.
+`htmlfill <module-formencode.htmlfill.html>`_ is a library to fill out
+forms, both with default values and error messages. It's like a
+template library, but more limited, and it can be used with the output
+from other templates. It has no prerequesites, and can be used
+without any other parts of FormEncode.
-Prerequisites
--------------
-
-None. I believe it should be Python 2.1 compatible, but I haven't
-tried. If not, that could be easily resolved.
-
Usage
=====
@@ -37,12 +29,8 @@
>>> from formencode import htmlfill
>>> form = '<input type="text" name="fname">'
>>> defaults = {'fname': 'Joe'}
- >>> parser = htmlfill.FillingParser(defaults)
- >>> parser.feed(form)
- >>> parser.close()
- >>> parser.text()
+ >>> htmlfill.render(form, defaults)
'<input type="text" name="fname" value="Joe">'
- >>> parser.close()
The parser looks for HTML input elements (including ``select`` and
``textarea``) and fills in the defaults. The quintessential way to
@@ -50,25 +38,9 @@
return the form to the user with the values they entered, in addition
to errors.
-The parser takes several optional arguments:
+See `formencode.htmlfill.render for more
+<module-formencode.htmlfill.html#render>`_ for more.
-``errors`` (default ``{}``):
- A dictionary of errors to display (see Errors_)
-``use_all_keys`` (default ``False``):
- If true, if defaults contain a key that isn't used by the form an
- error will be raised when ``.close()`` is called.
-``error_class`` (default ``"error"``):
- If a input field has an error in the ``errors`` dictionary, then
- it will have this class added. Note that elements may have
- multiple classes, so this won't remove any class that the element
- already had. You can use this to color invalid fields.
-``error_formatters``:
- This is a dictionary of formatters for errors. See Errors_.
-``add_attributes``:
- This is a dictionary of dictionaries. E.g., ``{'submit':
- {'value': 'Add'}}`` could be used to change the ``value``
- attributes of the submit button.
-
Errors
------
@@ -91,17 +63,21 @@
<span class="error-message">(message)</span><br>
+In addition to explicit error tags, any leftover errors will be placed
+immediately above the associated input field.
+
Valid form templates
--------------------
-When you call ``parser.close()`` the parser will check that you've
-fully used all the defaults and errors that were given in the
-constructor. If not, an ``AssertionError`` will be raised.
+When you call ``parser.close()`` (also called by ``render()``) the
+parser will check that you've fully used all the defaults and errors
+that were given in the constructor if you pass in
+``use_all_keys=True``. If there are leftover fields an
+``AssertionError`` will be raised.
In most cases, htmlfill tries to keep the template the way it found
it, without reformatting it too much. If HTMLParser_ chokes on the
-code, so will htmlfill. Whitespace is being mucked up some right now,
-but I'm not sure why; it's not intentional.
+code, so will htmlfill.
.. _HTMLParser: http://python.org/doc/current/lib/module-HTMLParser.html
Modified: FormEncode/trunk/docs/index.txt
===================================================================
--- FormEncode/trunk/docs/index.txt 2005-11-20 20:18:46 UTC (rev 1305)
+++ FormEncode/trunk/docs/index.txt 2005-11-20 22:05:46 UTC (rev 1306)
@@ -33,6 +33,7 @@
* `Subway <http://subway.python-hosting.com>`_
* `TurboGears <http://turbogears.org>`_
* `Pylons <http://pylons.groovie.org>`_
+* More? `Email me <ia...@co...>`_
.. image:: http://sourceforge.net/sflogo.php?group_id=91231&type=4
:height: 37
Modified: FormEncode/trunk/docs/news.txt
===================================================================
--- FormEncode/trunk/docs/news.txt 2005-11-20 20:18:46 UTC (rev 1305)
+++ FormEncode/trunk/docs/news.txt 2005-11-20 22:05:46 UTC (rev 1306)
@@ -4,6 +4,12 @@
SVN trunk
---------
+* Fixed up all the documentation.
+
+* Validator ``__doc__`` attributes will include some
+ automatically-appended information about all the message strings
+ that validator uses.
+
* Deprecated ``formencode.htmlform`` module, because it is dumb.
* Added an ``.all_messages()`` method to all validators, primarily
Modified: FormEncode/trunk/formencode/api.py
===================================================================
--- FormEncode/trunk/formencode/api.py 2005-11-20 20:18:46 UTC (rev 1305)
+++ FormEncode/trunk/formencode/api.py 2005-11-20 22:05:46 UTC (rev 1306)
@@ -1,4 +1,10 @@
+"""
+Core classes for validation.
+"""
+
import declarative
+import textwrap
+import re
__all__ = ['NoDefault', 'Invalid', 'Validator', 'Identity',
'FancyValidator', 'is_validator']
@@ -106,6 +112,7 @@
cls._messages = cls._messages.copy()
cls._messages.update(cls.messages)
del cls.messages
+ cls._initialize_docstring()
def __init__(self, *args, **kw):
if kw.has_key('messages'):
@@ -166,6 +173,24 @@
"""
return []
+ #@classmethod
+ def _initialize_docstring(cls):
+ """
+ This changes the class's docstring to include information
+ about all the messages this validator uses.
+ """
+ doc = cls.__doc__ or ''
+ doc = [textwrap.dedent(doc).rstrip()]
+ messages = cls._messages.items()
+ messages.sort()
+ doc.append('\n\n**Messages**\n\n')
+ for name, default in messages:
+ default = re.sub(r'(%\(.*?\)[rsifcx])', r'``\1``', default)
+ doc.append('``'+name+'``:\n')
+ doc.append(' '+default+'\n\n')
+ cls.__doc__ = ''.join(doc)
+ _initialize_docstring = classmethod(_initialize_docstring)
+
class _Identity(Validator):
def __repr__(self):
return 'validators.Identity'
Modified: FormEncode/trunk/formencode/compound.py
===================================================================
--- FormEncode/trunk/formencode/compound.py 2005-11-20 20:18:46 UTC (rev 1305)
+++ FormEncode/trunk/formencode/compound.py 2005-11-20 22:05:46 UTC (rev 1306)
@@ -1,3 +1,7 @@
+"""
+Validators for applying validations in sequence.
+"""
+
from api import *
# @@ ianb 2005-05: should CompoundValidator be included?
Deleted: FormEncode/trunk/formencode/conftest.py
===================================================================
--- FormEncode/trunk/formencode/conftest.py 2005-11-20 20:18:46 UTC (rev 1305)
+++ FormEncode/trunk/formencode/conftest.py 2005-11-20 22:05:46 UTC (rev 1306)
@@ -1,2 +0,0 @@
-import sys, os
-sys.path.append(os.path.dirname(os.path.dirname(__file__)))
Modified: FormEncode/trunk/formencode/foreach.py
===================================================================
--- FormEncode/trunk/formencode/foreach.py 2005-11-20 20:18:46 UTC (rev 1305)
+++ FormEncode/trunk/formencode/foreach.py 2005-11-20 22:05:46 UTC (rev 1306)
@@ -1,3 +1,7 @@
+"""
+Validator for repeating items.
+"""
+
from api import NoDefault, Invalid
from compound import CompoundValidator, to_python, from_python
try:
Modified: FormEncode/trunk/formencode/formgen.py
===================================================================
--- FormEncode/trunk/formencode/formgen.py 2005-11-20 20:18:46 UTC (rev 1305)
+++ FormEncode/trunk/formencode/formgen.py 2005-11-20 22:05:46 UTC (rev 1306)
@@ -1,3 +1,7 @@
+"""
+Experimental extensible form generation
+"""
+
# @@: This is experimental
import fields
Modified: FormEncode/trunk/formencode/htmlform.py
===================================================================
--- FormEncode/trunk/formencode/htmlform.py 2005-11-20 20:18:46 UTC (rev 1305)
+++ FormEncode/trunk/formencode/htmlform.py 2005-11-20 22:05:46 UTC (rev 1306)
@@ -1,11 +1,10 @@
"""
-.. note::
+Class to encapsulate an HTML form, using htmlfill and
+htmlfill_schemabuilder (deprecated).
+.. note::
This is deprecated, as it's not that helpful.
-Class to encapsulate an HTML form, using htmlfill and
-htmlfill_schemabuilder
-
Usage::
html = '<form action=...>...</form>'
Modified: FormEncode/trunk/formencode/interfaces.py
===================================================================
--- FormEncode/trunk/formencode/interfaces.py 2005-11-20 20:18:46 UTC (rev 1305)
+++ FormEncode/trunk/formencode/interfaces.py 2005-11-20 22:05:46 UTC (rev 1306)
@@ -1,5 +1,5 @@
"""
-Interfaces for FormEncode
+Interfaces for FormEncode (for documentation purposes only)
"""
class Attribute(object):
Modified: FormEncode/trunk/formencode/validators.py
===================================================================
--- FormEncode/trunk/formencode/validators.py 2005-11-20 20:18:46 UTC (rev 1305)
+++ FormEncode/trunk/formencode/validators.py 2005-11-20 22:05:46 UTC (rev 1306)
@@ -457,7 +457,7 @@
'ABC'
Note that ``.from_python()`` calls (in general) do not validate
- the input:
+ the input::
>>> cap.from_python('abc')
'abc'
@@ -2050,6 +2050,8 @@
You must check the expiration date yourself (there is no
relation between CC number/types and expiration dates).
+ ::
+
>>> cc = CreditCardValidator()
>>> cc.to_python({'ccType': 'visa', 'ccNumber': '4111111111111111'})
{'ccNumber': '4111111111111111', 'ccType': 'visa'}
@@ -2157,3 +2159,8 @@
('1800', 15)],
}
+__all__ = []
+for name, value in globals().items():
+ if isinstance(value, type) and issubclass(value, Validator):
+ __all__.append(name)
+
Modified: FormEncode/trunk/formencode/variabledecode.py
===================================================================
--- FormEncode/trunk/formencode/variabledecode.py 2005-11-20 20:18:46 UTC (rev 1305)
+++ FormEncode/trunk/formencode/variabledecode.py 2005-11-20 22:05:46 UTC (rev 1306)
@@ -1,10 +1,7 @@
"""
-VariableDecode.py
-Ian Bicking <ia...@co...>
+Takes GET/POST variable dictionary, as might be returned by ``cgi``,
+and turns them into lists and dictionaries.
-Takes GET/POST variable dictionary, as might be returned by
-`cgi`, and turns them into lists and dictionaries.
-
Keys (variable names) can have subkeys, with a ``.`` and
can be numbered with ``-``, like ``a.b-3=something`` means that
the value ``a`` is a dictionary with a key ``b``, and ``b``
Modified: FormEncode/trunk/setup.cfg
===================================================================
--- FormEncode/trunk/setup.cfg 2005-11-20 20:18:46 UTC (rev 1305)
+++ FormEncode/trunk/setup.cfg 2005-11-20 22:05:46 UTC (rev 1306)
@@ -10,6 +10,7 @@
docs = docs/index.txt docs/Validator.txt docs/ToDo.txt
docs/news.txt docs/htmlfill.txt docs/Design.txt
docs/community.txt docs/download.txt
+ docs/history.txt
doc_base = docs/
dest = docs/html
modules = formencode
|
|
From: <sub...@co...> - 2005-11-20 20:18:53
|
Author: ianb
Date: 2005-11-20 20:18:46 +0000 (Sun, 20 Nov 2005)
New Revision: 1305
Modified:
FormEncode/trunk/docs/Design.txt
Log:
Updated design doc
Modified: FormEncode/trunk/docs/Design.txt
===================================================================
--- FormEncode/trunk/docs/Design.txt 2005-11-20 19:47:49 UTC (rev 1304)
+++ FormEncode/trunk/docs/Design.txt 2005-11-20 20:18:46 UTC (rev 1305)
@@ -17,38 +17,50 @@
FormEncode performs look-before-you-leap validation. The idea being
that you check all the data related to an operation, then apply it.
-The alternative might be a transactional system, where you just start
+This is in contrast to a transactional system, where you just start
applying the data and if there's a problem you raise an exception.
Someplace else you catch the exception and roll back the transaction.
-Of course FormEncode works fine with such a system, but unlike this
-you can use it without transactions.
+Of course FormEncode works fine with such a system, but because
+nothing is done until everything validators, you can use this without
+transactions.
FormEncode generally works on primitive types (though you could extend
-it to deal with your own types if you wish). These are things like
-strings, lists, dictionaries, integers, etc. This fits in with
-look-before-you-leap; often your domain objects won't exist until
-after you apply the user's request, so it's necessary to work on an
-early form of the data. Also, FormEncode doesn't know anything about
-your domain objects or classes; it's just easier to keep it this way.
+it to deal with your own types if you wish; `formencode.sqlschema
+<module-formencode.sqlschema.html>`_ is an example of this). These
+are things like strings, lists, dictionaries, integers, etc. This
+fits in with look-before-you-leap; often your domain objects won't
+exist until after you apply the user's request, so it's necessary to
+work on an early form of the data. Also, FormEncode doesn't know
+anything about your domain objects or classes; it's just easier to
+keep it this way.
Validation only operates on a single "value" at a time. This is
Python, collections are easy, and collections are themselves a single
"value" made up of many pieces. A "Schema validator" is a validator
-made up of many subvalidators.
+made up of many subvalidators. By using this single metaphor, without
+separating the concept of "field" and "form", it is possible to create
+reusable validators that work on compound structures, to validate
+"whole forms" instead of just single fields, and to support better
+validation composition.
+Also, "validation" and "conversion" are generally applied at the same
+time. In the documentation this is frequently just referred to as
+"validation", but anywhere validation can happen, conversion can also
+happen.
+
Domain Objects
==============
These are your objects, specific to your application. I know nothing
about them, and cannot know. FormEncode doesn't do anything with
-these objects, and doesn't try to know anything about them. At all.
+these objects, and doesn't try to know anything about them.
Validation as directional, not intrinsic
========================================
-One false start I've made is an attempt to tie validators into the
-objects they validate against. E.g., you might have a SQLObject_
-class like::
+One false start from earlier projects was an attempt to tie validators
+into the objects they validate against. E.g., you might have a
+SQLObject_ class like::
class Address(SQLObject):
fname = StringCol(notNull=True)
@@ -59,8 +71,10 @@
It is tempting to take the restrictions of the ``Address`` class and
automatically come up with a validation schema. This may yet be a
-viable goal, but in practical terms validation tends to be both more
-and less restrictive.
+viable goal (and to a degree is attainable), but in practical terms
+validation tends to be both more and less restrictive. Also,
+validation is contextual; what validation you apply is dependent on
+the source of the data.
Often in an API we are more restrictive than we may be in a user
interface, demanding that everything be specified explicitly. In a UI
@@ -74,15 +88,14 @@
restriction on the UI keeping in mind that it can more easily be
relaxed and refined than a restriction in the domain objects or
underlying database. Also, we may trust the programmer to use the API
-in a reasonable way, but we seldom trust the user under any
-circumstance.
+in a reasonable way, but we seldom trust user data in the same way.
-In essence, there is an "inside" and an "outside". FormEncode is a
-toolkit for bridging those two areas in a sensible and secure way.
-The specific way we bridge this depends on the nature of the user
-interface. An XML-RPC interface can make some assumptions that a GUI
-cannot make. An HTML interface can typically make even less
-assumptions, including the basic integrity of the input data. It
+In essence, there is an "inside" and an "outside" to the program.
+FormEncode is a toolkit for bridging those two areas in a sensible and
+secure way. The specific way we bridge this depends on the nature of
+the user interface. An XML-RPC interface can make some assumptions
+that a GUI cannot make. An HTML interface can typically make even
+less assumptions, including the basic integrity of the input data. It
isn't reasonable that the object should know about all means of
inputs, and the varying UI requirements of those inputs; user
interfaces are volatile, and more art than science, but domain objects
@@ -91,11 +104,9 @@
It also didn't work well to annotate domain objects with validation
schemas, though the option remains open. This is experimentation that
-belongs outside of FormEncode, simply because it's more specific to
-your domain than it is to FormEncode.
+belongs outside of the core of FormEncode, simply because it's more
+specific to your domain than it is to FormEncode.
-.. _adapted:
-
Two sides, two aspects
======================
@@ -112,17 +123,17 @@
Similarly, there's two sides to the system, the foreign data and the
local data. In Validator the local data is called "python" (meaning,
a natural Python data structure), so we convert ``to_python`` and
-``from_python``. Unlike some systems, Validator explicitly converts
-*both* directions.
+``from_python``. Unlike some systems, validators explicitly convert
+in *both* directions.
For instance, consider the date conversion. In one form, you may want
a date like ``mm/dd/yyyy``. It's easy enough to make the necessary
converter; but the date object that the converter produces doesn't
-know how it's supposed to be formatted for that form. ``repr`` or
-*any* method that binds an object to its form representation is a bad
-idea. The converter best knows how to undo its work. So a date
-converter that expects ``mm/dd/yyyy`` will also know how to turn a
-datetime into that format.
+know how it's supposed to be formatted for that form. Using
+``repr()`` or *any* method that binds an object to its form
+representation is a bad idea. The converter best knows how to undo
+its work. So a date converter that expects ``mm/dd/yyyy`` will also
+know how to turn a datetime into that format.
(This becomes even more interesting with compound validators.)
@@ -140,13 +151,28 @@
something that is tied to the framework, the presentation layer, and
the domain objects, and FormEncode doesn't know anything about those.
-Instead FormEncode uses htmlfill_. *You* produce the form however you
-want. Write it out by hand. Use a templating language. Whatever.
-Then htmlfill (which specifically understands HTML) fills in the form
-and any error messages.
+FormEncode does provide `htmlfill <htmlfill.html>`_. *You* produce
+the form however you want. Write it out by hand. Use a templating
+language. Use a form generator. Whatever. Then htmlfill (which
+specifically understands HTML) fills in the form and any error
+messages. There are several advantages to this:
-.. _htmlfill: htmlfill.html
+* Using ``htmlfill``, form generation is easy. You can just think
+ about how to map a form description or model class to simple HTML.
+ You don't have to think about any of the low-level stuff about
+ filling attributes with defaults or past request values.
+* ``htmlfill`` works with anything that produces HTML. There's zero
+ preference for any particular templating language, or even general
+ style of templating language.
+
+* If you do form generation, but it later turns out to be
+ insufficiently flexible, you can put the generated form into your
+ template and extend it there; you'll lose automatic synchronization
+ with your models, but you won't lose any functionality.
+
+* Hand-written forms are just as functional as generated forms.
+
Declarative and Imperative
==========================
@@ -155,9 +181,7 @@
styles often look better (specifically using subclassing instead of
construction). You are free to build the objects either way.
-For instance, one extension to ``htmlfill``
-(``htmlfill_schemabuilder``) looks for special attributes in an HTML
-form and builds a validator from that. Even though validation is
-stored in a separate object from your domain, you can build those
-validators programmatically.
+An example of programmatically building form generation:
+``htmlfill_schemabuilder`` looks for special attributes in an HTML
+form and builds a validation schema from that.
|
|
From: <sub...@co...> - 2005-11-20 19:47:54
|
Author: ianb
Date: 2005-11-20 19:47:49 +0000 (Sun, 20 Nov 2005)
New Revision: 1304
Modified:
FormEncode/trunk/formencode/htmlfill.py
FormEncode/trunk/formencode/htmlfill_schemabuilder.py
Log:
Added some new arguments to htmlfill.render; took a method out of htmlform to put directly in htmlfill_schemabuilder; added some docstrings
Modified: FormEncode/trunk/formencode/htmlfill.py
===================================================================
--- FormEncode/trunk/formencode/htmlfill.py 2005-11-20 19:30:17 UTC (rev 1303)
+++ FormEncode/trunk/formencode/htmlfill.py 2005-11-20 19:47:49 UTC (rev 1304)
@@ -1,3 +1,8 @@
+"""
+Parser for HTML forms, that fills in defaults and errors. See
+``render``.
+"""
+
import HTMLParser
import cgi
import re
@@ -2,5 +7,10 @@
+__all__ = ['render', 'htmlliteral', 'default_formatter',
+ 'none_formatter', 'escape_formatter',
+ 'FillingParser']
+
def render(form, defaults=None, errors=None, use_all_keys=False,
+ error_formatters=None, add_attributes=None,
auto_insert_errors=True, auto_error_formatter=None,
- text_as_default=False):
+ text_as_default=False, listener=None):
"""
@@ -21,15 +31,28 @@
defaults or errors that couldn't be used in the form it will be an
error.
+ ``error_formatters`` is a dictionary of formatter names to
+ one-argument functions that format an error into HTML. Some
+ default formatters are provided if you don't provide this.
+
+ ``error_class`` is the class added to input fields when there is
+ an error for that field.
+
+ ``add_attributes`` is a dictionary of field names to a dictionary
+ of attribute name/values. If the name starts with ``+`` then the
+ value will be appended to any existing attribute (e.g.,
+ ``{'+class': ' important'}``).
+
``auto_error_formatter`` is used to create the HTML that goes
above the fields. By default it wraps the error message in a span
and adds a ``<br>``.
If ``text_as_default`` is true (default false) then ``<input
type=unknown>`` will be treated as text inputs.
+
+ ``listener`` can be an object that watches fields pass; the only
+ one currently is in ``htmlfill_schemabuilder.SchemaBuilder``
"""
- if errors is None:
- errors = {}
if defaults is None:
defaults = {}
if auto_insert_errors and auto_error_formatter is None:
@@ -37,8 +60,11 @@
p = FillingParser(
defaults=defaults, errors=errors,
use_all_keys=use_all_keys,
+ error_formatters=error_formatters,
+ add_attributes=add_attributes,
auto_error_formatter=auto_error_formatter,
text_as_default=text_as_default,
+ listener=listener,
)
p.feed(form)
p.close()
Modified: FormEncode/trunk/formencode/htmlfill_schemabuilder.py
===================================================================
--- FormEncode/trunk/formencode/htmlfill_schemabuilder.py 2005-11-20 19:30:17 UTC (rev 1303)
+++ FormEncode/trunk/formencode/htmlfill_schemabuilder.py 2005-11-20 19:47:49 UTC (rev 1304)
@@ -1,5 +1,28 @@
+"""
+Extension to ``htmlfill`` that can parse out schema-defining
+statements.
+
+You can either pass ``SchemaBuilder`` to ``htmlfill.render`` (the
+``listen`` argument), or call ``parse_schema`` to just parse out a
+``Schema`` object.
+"""
+
import validators, schema, compound
+__all__ = ['parse_schema', 'SchemaBuilder']
+
+def parse_schema(form):
+ """
+ Given an HTML form, parse out the schema defined in it and return
+ that schema.
+ """
+ listener = htmlfill_schemabuilder.SchemaBuilder()
+ p = htmlfill.FillingParser(
+ defaults={}, listener=listener)
+ p.feed(self.form)
+ p.close()
+ return listener.schema()
+
default_validators = dict(
[(name.lower(), getattr(validators, name))
for name in dir(validators)])
|
|
From: <sub...@co...> - 2005-11-20 19:30:29
|
Author: ianb Date: 2005-11-20 19:30:17 +0000 (Sun, 20 Nov 2005) New Revision: 1303 Modified: FormEncode/trunk/docs/ToDo.txt FormEncode/trunk/docs/news.txt FormEncode/trunk/formencode/htmlform.py Log: Fixed to-do list; added deprecating warning to htmlform Modified: FormEncode/trunk/docs/ToDo.txt =================================================================== --- FormEncode/trunk/docs/ToDo.txt 2005-11-20 10:37:30 UTC (rev 1302) +++ FormEncode/trunk/docs/ToDo.txt 2005-11-20 19:30:17 UTC (rev 1303) @@ -5,20 +5,17 @@ :revision: $Rev$ :date: $LastChangedDate$ -* Generate documentation for the standard validators. - * Make a test fixture for validators, to make testing really easy. * Consider moving htmlfill to ElementTree or another DOM-ish - structure, instead of HTMLParser. + structure, instead of HTMLParser. Or re-implement with another + parser but same interface. * Generate Javascript for validators, for client-side validation (when possible). -* At least test and give recipes for Ajax-ish validation, when fully - client-side validation doesn't work. (Client side is more - interesting to me, though -- Ajax still does a server query, and - isn't much better than a full submit) +* Relatedly, test and give recipes for Ajax-ish validation, when fully + client-side validation doesn't work. * Better tests for ``htmlfill`` and ``htmlfill_schemabuilder``. @@ -36,9 +33,11 @@ * Make ``doctest_xml_compare`` work with wildcards/ellipses. Maybe with non-XHTML. -* Figure out how the heck to get this to scale to really small forms. - Does it make any sense there? When does it start making sense to - use FormEncode? +* Maybe include `wareweb.packing + <http://pythonpaste.org/wareweb/module-wareweb.packing.html>`_, + which is kind like like one-way validation for really small forms. + A compliment/alternative to `variabledecode + <module-formencode.variabledecode.html>`_. * Some more ways to build validation. Validation from docstrings or method signatures. Modified: FormEncode/trunk/docs/news.txt =================================================================== --- FormEncode/trunk/docs/news.txt 2005-11-20 10:37:30 UTC (rev 1302) +++ FormEncode/trunk/docs/news.txt 2005-11-20 19:30:17 UTC (rev 1303) @@ -4,6 +4,8 @@ SVN trunk --------- +* Deprecated ``formencode.htmlform`` module, because it is dumb. + * Added an ``.all_messages()`` method to all validators, primarily intended to be used for documentation purposes. Modified: FormEncode/trunk/formencode/htmlform.py =================================================================== --- FormEncode/trunk/formencode/htmlform.py 2005-11-20 10:37:30 UTC (rev 1302) +++ FormEncode/trunk/formencode/htmlform.py 2005-11-20 19:30:17 UTC (rev 1303) @@ -1,4 +1,8 @@ """ +.. note:: + + This is deprecated, as it's not that helpful. + Class to encapsulate an HTML form, using htmlfill and htmlfill_schemabuilder @@ -26,11 +30,16 @@ import htmlfill import htmlfill_schemabuilder from api import Invalid +import warnings class HTMLForm(object): def __init__(self, form, schema=None, auto_insert_errors=True): + warnings.warn( + 'HTMLForm has been deprecated; use the htmlfill and ' + 'htmlfill_schemabuilder modules directly.', + DeprecationWarning) self.form = form self._schema = schema self.auto_insert_errors = auto_insert_errors |
|
From: <sub...@co...> - 2005-11-20 10:38:00
|
Author: ianb
Date: 2005-11-20 10:37:30 +0000 (Sun, 20 Nov 2005)
New Revision: 1302
Modified:
FormEncode/trunk/formencode/validators.py
Log:
Some last doctests
Modified: FormEncode/trunk/formencode/validators.py
===================================================================
--- FormEncode/trunk/formencode/validators.py 2005-11-19 23:41:10 UTC (rev 1301)
+++ FormEncode/trunk/formencode/validators.py 2005-11-20 10:37:30 UTC (rev 1302)
@@ -1794,6 +1794,17 @@
"""
US Postal codes (aka Zip Codes).
+
+ ::
+
+ >>> PostalCode.to_python('55555')
+ '55555'
+ >>> PostalCode.to_python('55555-5555')
+ '55555-5555'
+ >>> PostalCode.to_python('5555')
+ Traceback (most recent call last):
+ ...
+ Invalid: Please enter a zip code (5 digits)
"""
regex = r'^\d\d\d\d\d(?:-\d\d\d\d)?$'
@@ -1811,6 +1822,14 @@
``name`` is the key. The field value and a new copy of the
dictionary with that field removed are returned.
+
+ >>> StripField('test').to_python({'a': 1, 'test': 2})
+ (2, {'a': 1})
+ >>> StripField('test').to_python({})
+ Traceback (most recent call last):
+ ...
+ Invalid: The name 'test' is missing
+
"""
__unpackargs__ = ('name',)
@@ -1840,6 +1859,18 @@
respectively; anything in ``true_values`` is true, anything in
``false_values`` is false, case-insensitive). The first item of
those lists is considered the preferred form.
+
+ ::
+
+ >>> s = StringBoolean()
+ >>> s.to_python('yes'), s.to_python('no')
+ (True, False)
+ >>> s.to_python(1), s.to_python('N')
+ (True, False)
+ >>> s.to_python('ye')
+ Traceback (most recent call last):
+ ...
+ Invalid: Value should be 'true' or 'false'
"""
true_values = ['true', 't', 'yes', 'y', 'on', '1']
@@ -1960,6 +1991,16 @@
Tests that the given fields match, i.e., are identical. Useful
for password+confirmation fields. Pass the list of field names in
as `field_names`.
+
+ ::
+
+ >>> f = FieldsMatch('pass', 'conf')
+ >>> f.to_python({'pass': 'xx', 'conf': 'xx'})
+ {'conf': 'xx', 'pass': 'xx'}
+ >>> f.to_python({'pass': 'xx', 'conf': 'yy'})
+ Traceback (most recent call last):
+ ...
+ Invalid: conf: Fields do not match
"""
show_match = False
@@ -2008,6 +2049,18 @@
You must check the expiration date yourself (there is no
relation between CC number/types and expiration dates).
+
+ >>> cc = CreditCardValidator()
+ >>> cc.to_python({'ccType': 'visa', 'ccNumber': '4111111111111111'})
+ {'ccNumber': '4111111111111111', 'ccType': 'visa'}
+ >>> cc.to_python({'ccType': 'visa', 'ccNumber': '411111111111111'})
+ Traceback (most recent call last):
+ ...
+ Invalid: ccNumber: You did not enter a valid number of digits
+ >>> cc.to_python({'ccType': 'visa', 'ccNumber': '411111111111112'})
+ Traceback (most recent call last):
+ ...
+ Invalid: ccNumber: You did not enter a valid number of digits
"""
validate_partial_form = True
|
|
From: <sub...@co...> - 2005-11-19 23:41:18
|
Author: ianb
Date: 2005-11-19 23:41:10 +0000 (Sat, 19 Nov 2005)
New Revision: 1301
Modified:
FormEncode/trunk/docs/news.txt
FormEncode/trunk/formencode/api.py
FormEncode/trunk/formencode/compound.py
FormEncode/trunk/formencode/schema.py
Log:
Added some methods to inspect all the messages that can be created
Modified: FormEncode/trunk/docs/news.txt
===================================================================
--- FormEncode/trunk/docs/news.txt 2005-11-19 23:32:29 UTC (rev 1300)
+++ FormEncode/trunk/docs/news.txt 2005-11-19 23:41:10 UTC (rev 1301)
@@ -4,6 +4,9 @@
SVN trunk
---------
+* Added an ``.all_messages()`` method to all validators, primarily
+ intended to be used for documentation purposes.
+
* Changed preferred name of ``StringBoolean`` to ``StringBool`` (to go
with ``bool`` and ``validators.Bool``). Old alias still available.
Modified: FormEncode/trunk/formencode/api.py
===================================================================
--- FormEncode/trunk/formencode/api.py 2005-11-19 23:32:29 UTC (rev 1300)
+++ FormEncode/trunk/formencode/api.py 2005-11-19 23:41:10 UTC (rev 1301)
@@ -129,6 +129,43 @@
% (e, msgName, self._messages.get(msgName), kw,
', '.join(self._messages.keys())))
+ def all_messages(self):
+ """
+ Return a dictionary of all the messages of this validator, and
+ any subvalidators if present. Keys are message names, values
+ may be a message or list of messages. This is really just
+ intended for documentation purposes, to show someone all the
+ messages that a validator or compound validator (like Schemas)
+ can produce.
+
+ @@: Should this produce a more structured set of messages, so
+ that messages could be unpacked into a rendered form to see
+ the placement of all the messages? Well, probably so.
+ """
+ msgs = self._messages.copy()
+ for v in self.subvalidators():
+ inner = v.all_messages()
+ for key, msg in inner:
+ if key in msgs:
+ if msgs[key] == msg:
+ continue
+ if isinstance(msgs[key], list):
+ msgs[key].append(msg)
+ else:
+ msgs[key] = [msgs[key], msg]
+ else:
+ msgs[key] = msg
+ return msgs
+
+ def subvalidators(self):
+ """
+ Return any validators that this validator contains. This is
+ not useful for functional, except to inspect what values are
+ available. Specifically the ``.all_messages()`` method uses
+ this to accumulate all possible messages.
+ """
+ return []
+
class _Identity(Validator):
def __repr__(self):
return 'validators.Identity'
Modified: FormEncode/trunk/formencode/compound.py
===================================================================
--- FormEncode/trunk/formencode/compound.py 2005-11-19 23:32:29 UTC (rev 1300)
+++ FormEncode/trunk/formencode/compound.py 2005-11-19 23:41:10 UTC (rev 1301)
@@ -56,6 +56,9 @@
return self.attempt_convert(value, state,
from_python)
+ def subvalidators(self):
+ return self.validators
+
class Any(CompoundValidator):
"""
Modified: FormEncode/trunk/formencode/schema.py
===================================================================
--- FormEncode/trunk/formencode/schema.py 2005-11-19 23:32:29 UTC (rev 1300)
+++ FormEncode/trunk/formencode/schema.py 2005-11-19 23:41:10 UTC (rev 1301)
@@ -280,6 +280,12 @@
add_pre_validator = declarative.classinstancemethod(add_pre_validator)
+ def subvalidators(self):
+ result = []
+ result.extend(self.pre_validators)
+ result.extend(self.chained_validators)
+ result.extend(self.fields.values())
+ return result
def format_compound_error(v, indent=0):
if isinstance(v, Exception):
|
|
From: <sub...@co...> - 2005-11-19 23:32:34
|
Author: ianb
Date: 2005-11-19 23:32:29 +0000 (Sat, 19 Nov 2005)
New Revision: 1300
Modified:
FormEncode/trunk/docs/news.txt
FormEncode/trunk/formencode/validators.py
Log:
Normalized a name, added some more docstring tests
Modified: FormEncode/trunk/docs/news.txt
===================================================================
--- FormEncode/trunk/docs/news.txt 2005-11-19 23:25:05 UTC (rev 1299)
+++ FormEncode/trunk/docs/news.txt 2005-11-19 23:32:29 UTC (rev 1300)
@@ -43,6 +43,9 @@
missing keys (where fields are present) when no ``if_missing`` is
provided for the field.
+* Renamed ``validators.StateProvince.extraStates`` to
+ ``extra_states``, to normalize style.
+
Bugfixes
~~~~~~~~
Modified: FormEncode/trunk/formencode/validators.py
===================================================================
--- FormEncode/trunk/formencode/validators.py 2005-11-19 23:25:05 UTC (rev 1299)
+++ FormEncode/trunk/formencode/validators.py 2005-11-19 23:32:29 UTC (rev 1300)
@@ -1256,20 +1256,34 @@
Well, for now I don't know the province codes, but it does state
codes. Give your own `states` list to validate other state-like
- codes; give `extraStates` to add values without losing the current
- state values.
+ codes; give `extra_states` to add values without losing the
+ current state values.
+
+ ::
+
+ >>> s = StateProvince('XX')
+ >>> s.to_python('IL')
+ 'IL'
+ >>> s.to_python('XX')
+ 'XX'
+ >>> s.to_python('xx')
+ 'XX'
+ >>> s.to_python('YY')
+ Traceback (most recent call last):
+ ...
+ Invalid: That is not a valid state code
"""
states = ['AK', 'AL', 'AR', 'AZ', 'CA', 'CO', 'CT', 'DC', 'DE',
- 'FL', 'GA', 'HI', 'IA', 'ID', 'IN', 'IL', 'KS', 'KY',
- 'LA', 'MA', 'MD', 'ME', 'MI', 'MN', 'MO', 'MS', 'MT',
- 'NC', 'ND', 'NE', 'NH', 'NJ', 'NM', 'NV', 'NY', 'OH',
- 'OK', 'OR', 'PA', 'RI', 'SC', 'SD', 'TN', 'TX', 'UT',
- 'VA', 'VT', 'WA', 'WI', 'WV', 'WY']
+ 'FL', 'GA', 'HI', 'IA', 'ID', 'IN', 'IL', 'KS', 'KY',
+ 'LA', 'MA', 'MD', 'ME', 'MI', 'MN', 'MO', 'MS', 'MT',
+ 'NC', 'ND', 'NE', 'NH', 'NJ', 'NM', 'NV', 'NY', 'OH',
+ 'OK', 'OR', 'PA', 'RI', 'SC', 'SD', 'TN', 'TX', 'UT',
+ 'VA', 'VT', 'WA', 'WI', 'WV', 'WY']
- extraStates = []
+ extra_states = []
- __unpackargs__ = ('extraStates',)
+ __unpackargs__ = ('extra_states',)
messages = {
'empty': 'Please enter a state code',
@@ -1288,7 +1302,7 @@
self.message('wrongLength', state),
value, state)
if value not in self.states \
- and not (self.extraStates and value in self.extraStates):
+ and not (self.extra_states and value in self.extra_states):
raise Invalid(
self.message('invalid', state),
value, state)
@@ -1303,7 +1317,24 @@
extension (as ext.##...)
@@: should add international phone number support
+
+ ::
+
+ >>> p = PhoneNumber()
+ >>> p.to_python('333-3333')
+ Traceback (most recent call last):
+ ...
+ Invalid: Please enter a number, with area code, in the form ###-###-####, optionally with "ext.####"
+ >>> p.to_python('555-555-5555')
+ '555-555-5555'
+ >>> p.to_python('1-393-555-3939')
+ '1-393-555-3939'
+ >>> p.to_python('321.555.4949')
+ '321.555.4949'
+ >>> p.to_python('3335550000')
+ '3335550000'
"""
+ # for emacs: "
_phoneRE = re.compile(r'^\s*(?:1-)?(\d\d\d)[\- \.]?(\d\d\d)[\- \.]?(\d\d\d\d)(?:\s*ext\.?\s*(\d+))?\s*$', re.I)
@@ -1418,6 +1449,22 @@
Use accept_day=False if you just want a month/year (like for a
credit card expiration date).
+
+ ::
+
+ >>> d = DateConverter()
+ >>> d.to_python('12/3/09')
+ datetime.date(2009, 12, 3)
+ >>> d.to_python('12/3/2009')
+ datetime.date(2009, 12, 3)
+ >>> d.to_python('2/30/04')
+ Traceback (most recent call last):
+ ...
+ Invalid: That month only has 29 days
+ >>> d.to_python('13/2/05')
+ Traceback (most recent call last):
+ ...
+ Invalid: Please enter a month from 1 to 12
"""
## @@: accepts only US-style dates
|
|
From: <sub...@co...> - 2005-11-19 23:25:11
|
Author: ianb
Date: 2005-11-19 23:25:05 +0000 (Sat, 19 Nov 2005)
New Revision: 1299
Modified:
FormEncode/trunk/docs/news.txt
FormEncode/trunk/formencode/validators.py
Log:
Add tests for URL; catch socket errors and produce proper errors
Modified: FormEncode/trunk/docs/news.txt
===================================================================
--- FormEncode/trunk/docs/news.txt 2005-11-19 23:18:14 UTC (rev 1298)
+++ FormEncode/trunk/docs/news.txt 2005-11-19 23:25:05 UTC (rev 1299)
@@ -46,6 +46,9 @@
Bugfixes
~~~~~~~~
+* When checking destinations, ``validators.URL`` now allows redirect
+ codes, and catches socket errors and turns them into proper errors.
+
* Fix typo in ``htmlfill``
* Made URL and email regular expressions a little more lax/correct.
Modified: FormEncode/trunk/formencode/validators.py
===================================================================
--- FormEncode/trunk/formencode/validators.py 2005-11-19 23:18:14 UTC (rev 1298)
+++ FormEncode/trunk/formencode/validators.py 2005-11-19 23:25:05 UTC (rev 1299)
@@ -28,6 +28,7 @@
mxlookup = None
httplib = None
urlparse = None
+socket = None
from interfaces import *
from api import *
sha = random = None
@@ -1141,6 +1142,30 @@
If add_http is true, then if no scheme is present we'll add
http://
+
+ ::
+
+ >>> u = URL(add_http=True)
+ >>> u.to_python('foo.com')
+ 'http://foo.com'
+ >>> u.to_python('http://hahaha/bar.html')
+ Traceback (most recent call last):
+ ...
+ Invalid: That is not a valid URL
+ >>> u.to_python('https://test.com')
+ 'https://test.com'
+ >>> u = URL(add_http=False, check_exists=True)
+ >>> u.to_python('http://google.com')
+ 'http://google.com'
+ >>> u.to_python('http://colorstudy.com/doesnotexist.html')
+ Traceback (most recent call last):
+ ...
+ Invalid: The server responded that the page could not be found
+ >>> u.to_python('http://this.domain.does.not.exists.formencode.org/test.html')
+ Traceback (most recent call last):
+ ...
+ Invalid: An error occured when trying to connect to the server: (-2, 'Name or service not known')
+
"""
check_exists = False
@@ -1156,6 +1181,7 @@
'noScheme': 'You must start your URL with http://, https://, etc',
'badURL': 'That is not a valid URL',
'httpError': 'An error occurred when trying to access the URL: %(error)s',
+ 'socketError': 'An error occured when trying to connect to the server: %(error)s',
'notFound': 'The server responded that the page could not be found',
'status': 'The server responded with a bad status code (%(status)s)',
}
@@ -1181,11 +1207,13 @@
return value
def _check_url_exists(self, url, state):
- global httplib, urlparse
+ global httplib, urlparse, socket
if httplib is None:
import httplib
if urlparse is None:
import urlparse
+ if socket is None:
+ import socket
scheme, netloc, path, params, query, fragment = urlparse.urlparse(
url, 'http')
if scheme == 'http':
@@ -1204,12 +1232,17 @@
raise Invalid(
self.message('httpError', state, error=e),
state, url)
+ except socket.error, e:
+ raise Invalid(
+ self.message('socketError', state, error=e),
+ state, url)
else:
if res.status == 404:
raise Invalid(
self.message('notFound', state),
state, url)
- if res.status != 200:
+ if (res.status < 200
+ or res.status >= 500):
raise Invalid(
self.message('status', state, status=res.status),
state, url)
|
|
From: <sub...@co...> - 2005-11-19 23:18:18
|
Author: ianb
Date: 2005-11-19 23:18:14 +0000 (Sat, 19 Nov 2005)
New Revision: 1298
Modified:
FormEncode/trunk/formencode/validators.py
Log:
Add email test; read resolv.conf when importing the DNS module
Modified: FormEncode/trunk/formencode/validators.py
===================================================================
--- FormEncode/trunk/formencode/validators.py 2005-11-19 23:12:57 UTC (rev 1297)
+++ FormEncode/trunk/formencode/validators.py 2005-11-19 23:18:14 UTC (rev 1298)
@@ -1038,15 +1038,39 @@
return []
class Email(FancyValidator):
- """
+ r"""
Validate an email address.
If you pass ``resolve_domain=True``, then it will try to resolve
the domain name to make sure it's valid. This takes longer, of
course. You must have the `pyDNS <http://pydns.sf.net>`_ modules
installed to look up MX records.
- """
+ ::
+
+ >>> e = Email()
+ >>> e.to_python(' te...@fo... ')
+ 'te...@fo...'
+ >>> e.to_python('test')
+ Traceback (most recent call last):
+ ...
+ Invalid: An email address must contain a single @
+ >>> e.to_python('te...@fo....5')
+ Traceback (most recent call last):
+ ...
+ Invalid: The domain portion of the email address is invalid (the portion after the @: foobar.com.5)
+ >>> e.to_python('o*re...@te...')
+ 'o*re...@te...'
+ >>> e = Email(resolve_domain=True)
+ >>> e.to_python('doe...@co...')
+ 'doe...@co...'
+ >>> e.to_python('te...@th...')
+ Traceback (most recent call last):
+ ...
+ Invalid: The domain of the email address does not exist (the portion after the @: thisdomaindoesnotexistithink.com)
+
+ """
+
resolve_domain = False
usernameRE = re.compile(r"^[^ \t\n\r@<>()]+$", re.I)
@@ -1066,6 +1090,8 @@
if self.resolve_domain:
if mxlookup is None:
try:
+ import DNS.Base
+ DNS.Base.ParseResolvConf()
from DNS.lazy import mxlookup
except ImportError:
import warnings
|
|
From: <sub...@co...> - 2005-11-19 23:13:03
|
Author: ianb
Date: 2005-11-19 23:12:57 +0000 (Sat, 19 Nov 2005)
New Revision: 1297
Modified:
FormEncode/trunk/formencode/validators.py
Log:
Added tests for Set, and made it produce Sets by configuration
Modified: FormEncode/trunk/formencode/validators.py
===================================================================
--- FormEncode/trunk/formencode/validators.py 2005-11-19 23:03:06 UTC (rev 1296)
+++ FormEncode/trunk/formencode/validators.py 2005-11-19 23:12:57 UTC (rev 1297)
@@ -31,6 +31,10 @@
from interfaces import *
from api import *
sha = random = None
+try:
+ import sets
+except ImportError:
+ sets = None
import cgi
@@ -985,18 +989,54 @@
This way the result will always be a list, even if there's only
one result. It's equivalent to ForEach(convertToList=True).
+
+ If you give ``use_set=True``, then it will return an actual
+ ``sets.Set`` object.
+
+ ::
+
+ >>> Set.to_python(None)
+ []
+ >>> Set.to_python('this')
+ ['this']
+ >>> Set.to_python(('this', 'that'))
+ ['this', 'that']
+ >>> s = Set(use_set=True)
+ >>> s.to_python(None)
+ Set([])
+ >>> s.to_python('this')
+ Set(['this'])
+ >>> s.to_python(('this',))
+ Set(['this'])
"""
- if_empty = ()
+ use_set = False
def _to_python(self, value, state):
- if isinstance(value, (list, tuple)):
- return value
- elif value is None:
- return []
+ if self.use_set:
+ if isinstance(value, sets.Set):
+ return value
+ elif isinstance(value, (list, tuple)):
+ return sets.Set(value)
+ elif value is None:
+ return sets.Set()
+ else:
+ return sets.Set([value])
else:
- return [value]
+ if isinstance(value, list):
+ return value
+ elif sets and isinstance(value, sets.Set):
+ return list(value)
+ elif isinstance(value, tuple):
+ return list(value)
+ elif value is None:
+ return []
+ else:
+ return [value]
+ def empty_value(self, value):
+ return []
+
class Email(FancyValidator):
"""
Validate an email address.
|
|
From: <sub...@co...> - 2005-11-19 23:03:11
|
Author: ianb
Date: 2005-11-19 23:03:06 +0000 (Sat, 19 Nov 2005)
New Revision: 1296
Modified:
FormEncode/trunk/formencode/validators.py
Log:
Added some tests; fix String wrt the new handling of empty (turn all empty things to empty strings, not None)
Modified: FormEncode/trunk/formencode/validators.py
===================================================================
--- FormEncode/trunk/formencode/validators.py 2005-11-19 22:57:27 UTC (rev 1295)
+++ FormEncode/trunk/formencode/validators.py 2005-11-19 23:03:06 UTC (rev 1296)
@@ -895,6 +895,18 @@
"""
Convert a value to a float or integer. Tries to convert it to
an integer if no information is lost.
+
+ ::
+
+ >>> Number.to_python('10')
+ 10
+ >>> Number.to_python('10.5')
+ 10.5
+ >>> Number.to_python('ten')
+ Traceback (most recent call last):
+ ...
+ Invalid: Please enter a number
+
"""
messages = {
@@ -918,6 +930,21 @@
Also takes a `max` and `min` argument, and the string length must
fall in that range.
+
+ ::
+
+ >>> String(min=2).to_python('a')
+ Traceback (most recent call last):
+ ...
+ Invalid: Enter a value 2 characters long or more
+ >>> String(max=10).to_python('xxxxxxxxxxx')
+ Traceback (most recent call last):
+ ...
+ Invalid: Enter a value less than 10 characters long
+ >>> String().from_python(None)
+ ''
+ >>> String().from_python([])
+ ''
"""
min = None
@@ -947,6 +974,9 @@
return str(value)
return ""
+ def empty_value(self, value):
+ return ''
+
class Set(FancyValidator):
"""
|
|
From: <sub...@co...> - 2005-11-19 22:57:33
|
Author: ianb
Date: 2005-11-19 22:57:27 +0000 (Sat, 19 Nov 2005)
New Revision: 1295
Modified:
FormEncode/trunk/docs/news.txt
FormEncode/trunk/formencode/validators.py
Log:
Changed StringBoolean canonical name to StringBool; added Int test
Modified: FormEncode/trunk/docs/news.txt
===================================================================
--- FormEncode/trunk/docs/news.txt 2005-11-19 22:55:05 UTC (rev 1294)
+++ FormEncode/trunk/docs/news.txt 2005-11-19 22:57:27 UTC (rev 1295)
@@ -4,6 +4,9 @@
SVN trunk
---------
+* Changed preferred name of ``StringBoolean`` to ``StringBool`` (to go
+ with ``bool`` and ``validators.Bool``). Old alias still available.
+
* Added ``today_or_after`` option to ``validators.DateValidator``.
* Added a ``validators.FileUploadKeeper`` validator for helping with
Modified: FormEncode/trunk/formencode/validators.py
===================================================================
--- FormEncode/trunk/formencode/validators.py 2005-11-19 22:55:05 UTC (rev 1294)
+++ FormEncode/trunk/formencode/validators.py 2005-11-19 22:57:27 UTC (rev 1295)
@@ -841,6 +841,9 @@
Always Valid, returns True or False based on the value and the
existance of the value.
+ If you want to convert strings like ``'true'`` to booleans, then
+ use ``StringBoolean``.
+
Examples::
>>> Bool.to_python(0)
@@ -863,6 +866,15 @@
"""
Convert a value to an integer.
+
+ Example::
+
+ >>> Int.to_python('10')
+ 10
+ >>> Int.to_python('ten')
+ Traceback (most recent call last):
+ ...
+ Invalid: Please enter an integer value
"""
messages = {
@@ -1643,7 +1655,7 @@
return field, v
-class StringBoolean(FancyValidator):
+class StringBool(FancyValidator):
# Originally from TurboGears
"""
Converts a string to a boolean.
@@ -1678,6 +1690,9 @@
else:
return self.false_values[0]
+# Should deprecate:
+StringBoolean = StringBool
+
class SignedString(FancyValidator):
"""
|
|
From: <sub...@co...> - 2005-11-19 22:55:13
|
Author: ianb
Date: 2005-11-19 22:55:05 +0000 (Sat, 19 Nov 2005)
New Revision: 1294
Modified:
FormEncode/trunk/docs/news.txt
FormEncode/trunk/formencode/validators.py
Log:
Added doctests to DateValidator, and a new option
Modified: FormEncode/trunk/docs/news.txt
===================================================================
--- FormEncode/trunk/docs/news.txt 2005-11-19 22:37:57 UTC (rev 1293)
+++ FormEncode/trunk/docs/news.txt 2005-11-19 22:55:05 UTC (rev 1294)
@@ -4,6 +4,8 @@
SVN trunk
---------
+* Added ``today_or_after`` option to ``validators.DateValidator``.
+
* Added a ``validators.FileUploadKeeper`` validator for helping with
file uploads in failed forms. It still requires some annoying
fiddling to make work, though, since file upload fields are so
Modified: FormEncode/trunk/formencode/validators.py
===================================================================
--- FormEncode/trunk/formencode/validators.py 2005-11-19 22:37:57 UTC (rev 1293)
+++ FormEncode/trunk/formencode/validators.py 2005-11-19 22:55:05 UTC (rev 1294)
@@ -39,9 +39,11 @@
True, False = (1==1), (0==1)
############################################################
-## Wrapper Validators
+## Utility methods
############################################################
+# These all deal with accepting both mxDateTime and datetime
+# modules and types
datetime_module = None
mxDateTime_module = None
@@ -83,6 +85,10 @@
except module.RangeError, e:
raise ValueError(str(e))
+############################################################
+## Wrapper Validators
+############################################################
+
class ConfirmType(FancyValidator):
"""
@@ -725,13 +731,46 @@
Validates that a date is within the given range. Be sure to call
DateConverter first if you aren't expecting mxDateTime input.
- earliest_date and latest_date may be functions; if so, they will
- be called each time before validating.
+ ``earliest_date`` and ``latest_date`` may be functions; if so,
+ they will be called each time before validating.
+
+ ``after_now`` means a time after the current timestamp; note that
+ just a few milliseconds before now is invalid! ``today_or_after``
+ is more permissive, and ignores hours and minutes.
+
+ Examples::
+
+ >>> from datetime import datetime, timedelta
+ >>> d = DateValidator(earliest_date=datetime(2003, 1, 1))
+ >>> d.to_python(datetime(2004, 1, 1))
+ datetime.datetime(2004, 1, 1, 0, 0)
+ >>> d.to_python(datetime(2002, 1, 1))
+ Traceback (most recent call last):
+ ...
+ Invalid: Date must be after Wednesday, 01 January 2003
+ >>> d.to_python(datetime(2003, 1, 1))
+ datetime.datetime(2003, 1, 1, 0, 0)
+ >>> d = DateValidator(after_now=True)
+ >>> now = datetime.now()
+ >>> d.to_python(now+timedelta(seconds=5)) == now+timedelta(seconds=5)
+ True
+ >>> d.to_python(now-timedelta(days=1))
+ Traceback (most recent call last):
+ ...
+ Invalid: The date must be sometime in the future
+ >>> d.to_python(now+timedelta(days=1)) > now
+ True
+ >>> d = DateValidator(today_or_after=True)
+ >>> d.to_python(now) == now
+ True
+
"""
earliest_date = None
latest_date = None
after_now = False
+ # Like after_now, but just after this morning:
+ today_or_after = False
# Use 'datetime' to force the Python 2.3+ datetime module, or
# 'mxDateTime' to force the mxDateTime module (None means use
# datetime, or if not present mxDateTime)
@@ -780,6 +819,21 @@
self.message('future', state,
date=date_formatted),
value, state)
+ if self.today_or_after:
+ dt_mod = import_datetime(self.datetime_module)
+ now = datetime_now(dt_mod)
+ today = datetime_makedate(dt_mod,
+ now.year, now.month, now.day)
+ value_as_date = datetime_makedate(
+ dt_mod, value.year, value.month, value.day)
+ if value_as_date < today:
+ date_formatted = now.strftime(
+ self.message('date_format', state))
+ raise Invalid(
+ self.message('future', state,
+ date=date_formatted),
+ value, state)
+
class Bool(FancyValidator):
|
|
From: <sub...@co...> - 2005-11-19 22:38:06
|
Author: ianb
Date: 2005-11-19 22:37:57 +0000 (Sat, 19 Nov 2005)
New Revision: 1293
Modified:
FormEncode/trunk/docs/htmlfill.txt
FormEncode/trunk/formencode/validators.py
FormEncode/trunk/tests/test_doctests.py
Log:
Fixed doctests; fixed an error message
Modified: FormEncode/trunk/docs/htmlfill.txt
===================================================================
--- FormEncode/trunk/docs/htmlfill.txt 2005-11-18 21:57:50 UTC (rev 1292)
+++ FormEncode/trunk/docs/htmlfill.txt 2005-11-19 22:37:57 UTC (rev 1293)
@@ -39,6 +39,7 @@
>>> defaults = {'fname': 'Joe'}
>>> parser = htmlfill.FillingParser(defaults)
>>> parser.feed(form)
+ >>> parser.close()
>>> parser.text()
'<input type="text" name="fname" value="Joe">'
>>> parser.close()
Modified: FormEncode/trunk/formencode/validators.py
===================================================================
--- FormEncode/trunk/formencode/validators.py 2005-11-18 21:57:50 UTC (rev 1292)
+++ FormEncode/trunk/formencode/validators.py 2005-11-19 22:37:57 UTC (rev 1293)
@@ -116,7 +116,7 @@
...
Invalid: None is not a subclass of one of the types <type 'float'>, <type 'int'>
>>> cint2 = ConfirmType(type=int)
- >>> cint2.from_python(True)
+ >>> cint2(accept_python=False).from_python(True)
Traceback (most recent call last):
...
Invalid: True must be of the type <type 'int'>
@@ -143,7 +143,7 @@
if self.type:
if isinstance(self.type, list):
self.type = tuple(self.type)
- elif not isinstance(self.subclass, tuple):
+ elif not isinstance(self.type, tuple):
self.type = (self.type,)
self.validate_python = self.confirm_type
@@ -289,7 +289,7 @@
Traceback (most recent call last):
...
Invalid: Enter a value less than 5 characters long
- >>> max5.from_python('123456')
+ >>> max5(accept_python=False).from_python('123456')
Traceback (most recent call last):
...
Invalid: Enter a value less than 5 characters long
@@ -341,7 +341,7 @@
Traceback (most recent call last):
...
Invalid: Enter a value more than 5 characters long
- >>> min5.from_python('1234')
+ >>> min5(accept_python=False).from_python('1234')
Traceback (most recent call last):
...
Invalid: Enter a value more than 5 characters long
@@ -444,7 +444,13 @@
>>> cap = Regex(r'^[A-Z]+$')
>>> cap.to_python('ABC')
'ABC'
+
+ Note that ``.from_python()`` calls (in general) do not validate
+ the input:
+
>>> cap.from_python('abc')
+ 'abc'
+ >>> cap(accept_python=False).from_python('abc')
Traceback (most recent call last):
...
Invalid: The input is not valid
@@ -507,13 +513,15 @@
>>> PlainText.to_python('_this9_')
'_this9_'
>>> PlainText.from_python(' this ')
+ ' this '
+ >>> PlainText(accept_python=False).from_python(' this ')
Traceback (most recent call last):
...
Invalid: Enter only letters, numbers, or _ (underscore)
>>> PlainText(strip=True).to_python(' this ')
'this'
>>> PlainText(strip=True).from_python(' this ')
- ' this '
+ 'this'
"""
regex = r"^[a-zA-Z_\-0-9]*$"
Modified: FormEncode/trunk/tests/test_doctests.py
===================================================================
--- FormEncode/trunk/tests/test_doctests.py 2005-11-18 21:57:50 UTC (rev 1292)
+++ FormEncode/trunk/tests/test_doctests.py 2005-11-19 22:37:57 UTC (rev 1293)
@@ -1,8 +1,8 @@
import os, sys
if __name__ == '__main__':
- base = os.path.dirname(os.path.dirname(
- os.path.dirname(os.path.abspath(__file__))))
+ base = os.path.dirname(
+ os.path.dirname(os.path.abspath(__file__)))
sys.path.append(base)
try:
import doctest
|
|
From: <sub...@co...> - 2005-11-18 21:57:57
|
Author: test
Date: 2005-11-18 21:57:50 +0000 (Fri, 18 Nov 2005)
New Revision: 1292
Modified:
FormEncode/trunk/formencode/validators.py
Log:
Added FileUploadKeeper (forgot from last commit)
Modified: FormEncode/trunk/formencode/validators.py
===================================================================
--- FormEncode/trunk/formencode/validators.py 2005-11-18 21:55:10 UTC (rev 1291)
+++ FormEncode/trunk/formencode/validators.py 2005-11-18 21:57:50 UTC (rev 1292)
@@ -30,10 +30,10 @@
urlparse = None
from interfaces import *
from api import *
-sha = random = None
-
-import cgi
-
+sha = random = None
+
+import cgi
+
import fieldstorage
True, False = (1==1), (0==1)
@@ -1127,20 +1127,80 @@
if match.group(4):
result = result + " ext.%s" % match.group(4)
return result
-
-class FieldStorageUploadConverter(FancyValidator):
-
- """
- Converts a cgi.FieldStorage instance to
- a value that FormEncode can use for file
- uploads.
- """
- def _to_python(self, value, state):
- if isinstance(value, cgi.FieldStorage):
- return fieldstorage.convert_fieldstorage(value)
- else:
- return value
+class FieldStorageUploadConverter(FancyValidator):
+
+ """
+ Converts a cgi.FieldStorage instance to
+ a value that FormEncode can use for file
+ uploads.
+ """
+ def _to_python(self, value, state):
+ if isinstance(value, cgi.FieldStorage):
+ return fieldstorage.convert_fieldstorage(value)
+ else:
+ return value
+
+class FileUploadKeeper(FancyValidator):
+ """
+ Takes two inputs (a dictionary with keys ``static`` and
+ ``upload``) and converts them into one value on the Python side (a
+ dictionary with ``filename`` and ``content`` keys). The upload
+ takes priority over the static value. The filename may be None if
+ it can't be discovered.
+
+ Handles uploads of both text and ``cgi.FieldStorage`` upload
+ values.
+
+ This is basically for use when you have an upload field, and you
+ want to keep the upload around even if the rest of the form
+ submission fails. When converting *back* to the form submission,
+ there may be extra values ``'original_filename'`` and
+ ``'original_content'``, which may want to use in your form to show
+ the user you still have their content around.
+ """
+
+ upload_key = 'upload'
+ static_key = 'static'
+
+ def _to_python(self, value, state):
+ upload = value.get(self.upload_key)
+ static = value.get(self.static_key, '').strip()
+ filename = content = None
+ if isinstance(upload, cgi.FieldStorage):
+ filename = upload.filename
+ content = upload.value
+ elif isinstance(upload, str) and upload:
+ filename = None
+ content = upload
+ if not content and static:
+ filename, content = static.split(None, 1)
+ if filename == '-':
+ filename = ''
+ else:
+ filename = filename.decode('base64')
+ content = content.decode('base64')
+ return {'filename': filename, 'content': content}
+
+ def _from_python(self, value, state):
+ filename = value.get('filename', '')
+ content = value.get('content', '')
+ if filename or content:
+ result = self.pack_content(filename, content)
+ return {self.upload_key: '',
+ self.static_key: result,
+ 'original_filename': filename,
+ 'original_content': content}
+ else:
+ return {self.upload_key: '',
+ self.static_key: ''}
+
+ def pack_content(self, filename, content):
+ enc_filename = self.base64encode(filename) or '-'
+ enc_content = (content or '').encode('base64')
+ result = '%s %s' % (enc_filename, enc_content)
+ return result
+
class DateConverter(FancyValidator):
"""
|
|
From: <sub...@co...> - 2005-11-18 21:55:19
|
Author: test Date: 2005-11-18 21:55:10 +0000 (Fri, 18 Nov 2005) New Revision: 1291 Modified: FormEncode/trunk/docs/news.txt Log: Added FileUploadKeeper Modified: FormEncode/trunk/docs/news.txt =================================================================== --- FormEncode/trunk/docs/news.txt 2005-11-18 20:10:39 UTC (rev 1290) +++ FormEncode/trunk/docs/news.txt 2005-11-18 21:55:10 UTC (rev 1291) @@ -4,6 +4,11 @@ SVN trunk --------- +* Added a ``validators.FileUploadKeeper`` validator for helping with + file uploads in failed forms. It still requires some annoying + fiddling to make work, though, since file upload fields are so + weird. + * Added ``text_as_default`` option to htmlfill. This treats all ``<input type="something-weird">`` elements as text fields. WHAT-WG adds weird input types, which can usually be usefully treated as |
|
From: <sub...@co...> - 2005-11-18 20:11:05
|
Author: test
Date: 2005-11-18 20:10:39 +0000 (Fri, 18 Nov 2005)
New Revision: 1290
Modified:
FormEncode/trunk/formencode/api.py
Log:
Added a little helper function
Modified: FormEncode/trunk/formencode/api.py
===================================================================
--- FormEncode/trunk/formencode/api.py 2005-11-18 16:25:10 UTC (rev 1289)
+++ FormEncode/trunk/formencode/api.py 2005-11-18 20:10:39 UTC (rev 1290)
@@ -296,6 +296,13 @@
raise Invalid(self.message('badType', state,
type=type(value), value=value),
value, state)
+
+ def base64encode(self, value):
+ """
+ Encode a string in base64, stripping whitespace and removing
+ newlines.
+ """
+ return value.encode('base64').strip().replace('\n', '')
validate_python = None
validate_other = None
|