Index: django/contrib/localflavor/br/forms.py
===================================================================
--- django/contrib/localflavor/br/forms.py	(revision 5724)
+++ django/contrib/localflavor/br/forms.py	(working copy)
@@ -1,24 +1,63 @@
 # -*- coding: utf-8 -*-
 """
 BR-specific Form helpers
+
+Usage:
+
+>>> from django.contrib.localflavor.br.forms import *
+>>> from django import newforms as forms
+>>> class BRForm(forms.Form):
+...   zipcode = BRZipCodeField()
+...   phone = BRPhoneNumberField()
+...   state = BRStateChoiceField()
+...   cpf = BRCPFField()
+...   cnpj = BRCNPJField()
+... 
+>>> f = BRForm()
+>>> print f.as_ul()
+...
+>>> print f.as_table()
+...
+>>> print f.as_p()
+
+
+>>> data = {'zipcode': '12345-123',
+...         'phone': '41 3562 3464',
+...         'state': 'PR',
+...         'cpf': '663.256.017-26',
+...         'cnpj': '64.132.916/0001-88'}
+>>> f = BRForm(data)
+>>> f.is_valid()
+True
 """
 
+import re
+from django.utils.translation import ugettext
+from django.utils.encoding import smart_unicode
 from django.newforms import ValidationError
-from django.newforms.fields import Field, RegexField, CharField, Select, EMPTY_VALUES
-from django.utils.encoding import smart_unicode
-from django.utils.translation import ugettext
-import re
+from django.newforms.fields import (Field, RegexField,
+                                    CharField, Select,
+                                    EMPTY_VALUES)
 
 phone_digits_re = re.compile(r'^(\d{2})[-\.]?(\d{4})[-\.]?(\d{4})$')
 
 class BRZipCodeField(RegexField):
+    """
+    A field that checks brazilian zipcode format.
+    """
     def __init__(self, *args, **kwargs):
+        error = ugettext('Enter a zip code in the format XXXXX-XXX.')
         super(BRZipCodeField, self).__init__(r'^\d{5}-\d{3}$',
-            max_length=None, min_length=None,
-            error_message=ugettext('Enter a zip code in the format XXXXX-XXX.'),
-                    *args, **kwargs)
+                                             max_length=None,
+                                             min_length=None,
+                                             error_message=error,
+                                             *args,
+                                             **kwargs)
 
 class BRPhoneNumberField(Field):
+    """
+    A field that checks brazilian phone number format.
+    """
     def clean(self, value):
         super(BRPhoneNumberField, self).clean(value)
         if value in EMPTY_VALUES:
@@ -27,18 +66,38 @@
         m = phone_digits_re.search(value)
         if m:
             return u'%s-%s-%s' % (m.group(1), m.group(2), m.group(3))
-        raise ValidationError(ugettext('Phone numbers must be in XX-XXXX-XXXX format.'))
+        raise ValidationError(ugettext('Phone numbers must be'
+                                       ' in  XX-XXXX-XXXX format.'))
 
-class BRStateSelect(Select):
+class BRStateChoiceField(Field):
     """
-    A Select widget that uses a list of Brazilian states/territories
-    as its choices.
+    A choice field that uses a list of Brazilian states as its choices.
     """
-    def __init__(self, attrs=None):
+    widget = Select
+
+    def __init__(self, required=True, widget=None, label=None,
+                 initial=None, help_text=None):
+        super(BRStateChoiceField, self).__init__(required, widget, label,
+                                                 initial, help_text)
         from br_states import STATE_CHOICES
-        super(BRStateSelect, self).__init__(attrs, choices=STATE_CHOICES)
+        self.widget.choices = STATE_CHOICES
 
+    def clean(self, value):
+        value = super(BRStateChoiceField, self).clean(value)
+        if value in EMPTY_VALUES:
+            value = u''
+        value = smart_unicode(value)
+        if value == u'':
+            return value
+        valid_values = set([smart_unicode(k) for k, v in self.widget.choices])
+        if value not in valid_values:
+            raise ValidationError(ugettext(u'Select a valid brazilian state.'
+                                           ' That state is not one'
+                                           ' of the available states.'))
+        return value
 
+
+
 def DV_maker(v):
     if v >= 2:
         return 11 - v
@@ -46,74 +105,92 @@
 
 class BRCPFField(CharField):
     """
-    This field validate a CPF number or a CPF string. A CPF number is
-    compounded by XXX.XXX.XXX-VD. The two last digits are check digits.
+    This field validate a CPF(Natural Persons Register) number or  string.
+    A CPF is compounded by XXX.XXX.XXX-VD. The two last digits are check
+    digits.
+    Value can be either a string in the format XXX.XXX.XXX-XX or an
+    11-digit number.
 
     More information:
     http://en.wikipedia.org/wiki/Cadastro_de_Pessoas_F%C3%ADsicas
     """
     def __init__(self, *args, **kwargs):
-        super(BRCPFField, self).__init__(max_length=14, min_length=11, *args, **kwargs)
+        super(BRCPFField, self).__init__(max_length=14, min_length=11,
+                                         *args, **kwargs)
 
     def clean(self, value):
-        """
-        Value can be either a string in the format XXX.XXX.XXX-XX or an
-        11-digit number.
-        """
         value = super(BRCPFField, self).clean(value)
         if value in EMPTY_VALUES:
             return u''
         orig_value = value[:]
         if not value.isdigit():
-            value = re.sub("[-\.]", "", value)
+            value = re.sub('[-\.]', '', value)
         try:
             int(value)
         except ValueError:
-            raise ValidationError(ugettext("This field requires only numbers."))
+            raise ValidationError(ugettext('This field requires'
+                                           ' only numbers.'))
         if len(value) != 11:
-            raise ValidationError(ugettext("This field requires at most 11 digits or 14 characters."))
+            raise ValidationError(ugettext('This field requires at most'
+                                           ' 11 digits or 14 characters.'))
         orig_dv = value[-2:]
 
-        new_1dv = sum([i * int(value[idx]) for idx, i in enumerate(range(10, 1, -1))])
+        new_1dv = sum([i * int(value[idx]) \
+                       for idx, i in enumerate(range(10, 1, -1))])
         new_1dv = DV_maker(new_1dv % 11)
         value = value[:-2] + str(new_1dv) + value[-1]
-        new_2dv = sum([i * int(value[idx]) for idx, i in enumerate(range(11, 1, -1))])
+        new_2dv = sum([i * int(value[idx]) \
+                       for idx, i in enumerate(range(11, 1, -1))])
         new_2dv = DV_maker(new_2dv % 11)
         value = value[:-1] + str(new_2dv)
         if value[-2:] != orig_dv:
-            raise ValidationError(ugettext("Invalid CPF number."))
+            raise ValidationError(ugettext('Invalid CPF number.'))
 
         return orig_value
 
 class BRCNPJField(Field):
+    """
+    This field validate a CNPJ(National Registry of Corporations).
+    Value can be either a string in the format XX.XXX.XXX/XXXX-XX or a
+    group of 14 characters.
+
+    """
     def clean(self, value):
-        """
-        Value can be either a string in the format XX.XXX.XXX/XXXX-XX or a
-        group of 14 characters.
-        """
         value = super(BRCNPJField, self).clean(value)
         if value in EMPTY_VALUES:
             return u''
         orig_value = value[:]
         if not value.isdigit():
-            value = re.sub("[-/\.]", "", value)
+            value = re.sub('[-/\.]', '', value)
         try:
             int(value)
         except ValueError:
-            raise ValidationError("This field requires only numbers.")
+            raise ValidationError('This field requires only numbers.')
         if len(value) != 14:
-            raise ValidationError(
-                ugettext("This field requires at least 14 digits"))
+            raise ValidationError(ugettext('This field requires at'
+                                           ' least 14 digits'))
         orig_dv = value[-2:]
 
-        new_1dv = sum([i * int(value[idx]) for idx, i in enumerate(range(5, 1, -1) + range(9, 1, -1))])
+        new_1dv = sum([i * int(value[idx]) \
+                       for idx, i in enumerate(range(5, 1, -1) +
+                                               range(9, 1, -1))])
         new_1dv = DV_maker(new_1dv % 11)
         value = value[:-2] + str(new_1dv) + value[-1]
-        new_2dv = sum([i * int(value[idx]) for idx, i in enumerate(range(6, 1, -1) + range(9, 1, -1))])
+        new_2dv = sum([i * int(value[idx]) \
+                       for idx, i in enumerate(range(6, 1, -1) +
+                                               range(9, 1, -1))])
         new_2dv = DV_maker(new_2dv % 11)
         value = value[:-1] + str(new_2dv)
         if value[-2:] != orig_dv:
-            raise ValidationError(ugettext("Invalid CNPJ number."))
+            raise ValidationError(ugettext('Invalid CNPJ number.'))
 
         return orig_value
 
+# Widgets
+class BRStateSelect(Select):
+    """
+    A Select widget that uses a list of Brazilian states as its choices.
+    """
+    def __init__(self, attrs=None):
+        from br_states import STATE_CHOICES
+        super(BRStateSelect, self).__init__(attrs, choices=STATE_CHOICES)
Index: tests/regressiontests/forms/localflavor.py
===================================================================
--- tests/regressiontests/forms/localflavor.py	(revision 5724)
+++ tests/regressiontests/forms/localflavor.py	(working copy)
@@ -980,6 +980,48 @@
 >>> w.render('states', 'PR')
 u'<select name="states">\n<option value="AC">Acre</option>\n<option value="AL">Alagoas</option>\n<option value="AP">Amap\xe1</option>\n<option value="AM">Amazonas</option>\n<option value="BA">Bahia</option>\n<option value="CE">Cear\xe1</option>\n<option value="DF">Distrito Federal</option>\n<option value="ES">Esp\xedrito Santo</option>\n<option value="GO">Goi\xe1s</option>\n<option value="MA">Maranh\xe3o</option>\n<option value="MT">Mato Grosso</option>\n<option value="MS">Mato Grosso do Sul</option>\n<option value="MG">Minas Gerais</option>\n<option value="PA">Par\xe1</option>\n<option value="PB">Para\xedba</option>\n<option value="PR" selected="selected">Paran\xe1</option>\n<option value="PE">Pernambuco</option>\n<option value="PI">Piau\xed</option>\n<option value="RJ">Rio de Janeiro</option>\n<option value="RN">Rio Grande do Norte</option>\n<option value="RS">Rio Grande do Sul</option>\n<option value="RO">Rond\xf4nia</option>\n<option value="RR">Roraima</option>\n<option value="SC">Santa Catarina</option>\n<option value="SP">S\xe3o Paulo</option>\n<option value="SE">Sergipe</option>\n<option value="TO">Tocantins</option>\n</select>'
 
+# BRStateChoiceField #########################################################
+>>> from django.contrib.localflavor.br.forms import BRStateChoiceField
+>>> f = BRStateChoiceField()
+>>> for s, n in f.widget.choices:
+...   f.clean(s)
+... 
+u'AC'
+u'AL'
+u'AP'
+u'AM'
+u'BA'
+u'CE'
+u'DF'
+u'ES'
+u'GO'
+u'MA'
+u'MT'
+u'MS'
+u'MG'
+u'PA'
+u'PB'
+u'PR'
+u'PE'
+u'PI'
+u'RJ'
+u'RN'
+u'RS'
+u'RO'
+u'RR'
+u'SC'
+u'SP'
+u'SE'
+u'TO'
+>>> f.clean('')
+Traceback (most recent call last):
+...
+ValidationError: [u'This field is required.']
+>>> f.clean('pr')
+Traceback (most recent call last):
+...
+ValidationError: [u'Select a valid brazilian state. That state is not one of the available states.']
+
 # DEZipCodeField ##############################################################
 
 >>> from django.contrib.localflavor.de.forms import DEZipCodeField
