=== modified file 'django/newforms/extras/widgets.py'
--- django/newforms/extras/widgets.py	2007-11-13 02:22:10 +0000
+++ django/newforms/extras/widgets.py	2007-11-21 21:28:06 +0000
@@ -20,9 +20,9 @@
     day_field = '%s_day'
     year_field = '%s_year'
 
-    def __init__(self, attrs=None, years=None):
+    def __init__(self, attrs=None, outer_attrs=None, years=None):
         # years is an optional list/tuple of years to use in the "year" select box.
-        self.attrs = attrs or {}
+        super(SelectDateWidget, self).__init__(attrs, outer_attrs)
         if years:
             self.years = years
         else:

=== modified file 'django/newforms/forms.py'
--- django/newforms/forms.py	2007-11-13 02:22:10 +0000
+++ django/newforms/forms.py	2007-11-21 21:53:24 +0000
@@ -131,6 +131,14 @@
                     top_errors.extend(['(Hidden field %s) %s' % (name, force_unicode(e)) for e in bf_errors])
                 hidden_fields.append(unicode(bf))
             else:
+                outer_attrs = bf.field.widget.outer_attrs
+                outer_class_list = outer_attrs.pop('class', '').split()
+                if bf.field.required:
+                    outer_class_list.append('required')
+                if bf_errors:
+                    outer_class_list.append('error')
+                if outer_class_list:
+                    outer_attrs['class'] = ' '.join(outer_class_list)
                 if errors_on_separate_row and bf_errors:
                     output.append(error_row % force_unicode(bf_errors))
                 if bf.label:
@@ -146,7 +154,8 @@
                     help_text = help_text_html % force_unicode(field.help_text)
                 else:
                     help_text = u''
-                output.append(normal_row % {'errors': force_unicode(bf_errors), 'label': force_unicode(label), 'field': unicode(bf), 'help_text': help_text})
+                output.append(normal_row % {'errors': force_unicode(bf_errors), 'label': force_unicode(label),
+                    'field': unicode(bf), 'help_text': help_text, 'outer_attrs': flatatt(outer_attrs)})
         if top_errors:
             output.insert(0, error_row % top_errors)
         if hidden_fields: # Insert any hidden fields in the last row.
@@ -161,15 +170,15 @@
 
     def as_table(self):
         "Returns this form rendered as HTML <tr>s -- excluding the <table></table>."
-        return self._html_output(u'<tr><th>%(label)s</th><td>%(errors)s%(field)s%(help_text)s</td></tr>', u'<tr><td colspan="2">%s</td></tr>', '</td></tr>', u'<br />%s', False)
+        return self._html_output(u'<tr%(outer_attrs)s><th>%(label)s</th><td>%(errors)s%(field)s%(help_text)s</td></tr>', u'<tr><td colspan="2">%s</td></tr>', '</td></tr>', u'<br />%s', False)
 
     def as_ul(self):
         "Returns this form rendered as HTML <li>s -- excluding the <ul></ul>."
-        return self._html_output(u'<li>%(errors)s%(label)s %(field)s%(help_text)s</li>', u'<li>%s</li>', '</li>', u' %s', False)
+        return self._html_output(u'<li%(outer_attrs)s>%(errors)s%(label)s %(field)s%(help_text)s</li>', u'<li>%s</li>', '</li>', u' %s', False)
 
     def as_p(self):
         "Returns this form rendered as HTML <p>s."
-        return self._html_output(u'<p>%(label)s %(field)s%(help_text)s</p>', u'%s', '</p>', u' %s', True)
+        return self._html_output(u'<p%(outer_attrs)s>%(label)s %(field)s%(help_text)s</p>', u'%s', '</p>', u' %s', True)
 
     def non_field_errors(self):
         """

=== modified file 'django/newforms/widgets.py'
--- django/newforms/widgets.py	2007-11-13 02:22:10 +0000
+++ django/newforms/widgets.py	2007-11-21 21:28:06 +0000
@@ -130,15 +130,20 @@
     is_hidden = False          # Determines whether this corresponds to an <input type="hidden">.
     needs_multipart_form = False # Determines does this widget need multipart-encrypted form
 
-    def __init__(self, attrs=None):
+    def __init__(self, attrs=None, outer_attrs=None):
         if attrs is not None:
             self.attrs = attrs.copy()
         else:
             self.attrs = {}
+        if outer_attrs is not None:
+            self.outer_attrs = outer_attrs.copy()
+        else:
+            self.outer_attrs = {}
 
     def __deepcopy__(self, memo):
         obj = copy.copy(self)
         obj.attrs = self.attrs.copy()
+        obj.outer_attrs = self.outer_attrs.copy()
         memo[id(self)] = obj
         return obj
 
@@ -197,8 +202,8 @@
 class PasswordInput(Input):
     input_type = 'password'
 
-    def __init__(self, attrs=None, render_value=True):
-        super(PasswordInput, self).__init__(attrs)
+    def __init__(self, attrs=None, outer_attrs=None, render_value=True):
+        super(PasswordInput, self).__init__(attrs, outer_attrs)
         self.render_value = render_value
 
     def render(self, name, value, attrs=None):
@@ -214,8 +219,8 @@
     A widget that handles <input type="hidden"> for fields that have a list
     of values.
     """
-    def __init__(self, attrs=None, choices=()):
-        super(MultipleHiddenInput, self).__init__(attrs)
+    def __init__(self, attrs=None, outer_attrs=None, choices=()):
+        super(MultipleHiddenInput, self).__init__(attrs, outer_attrs)
         # choices can be any iterable
         self.choices = choices
 
@@ -241,11 +246,12 @@
         return files.get(name, None)
 
 class Textarea(Widget):
-    def __init__(self, attrs=None):
+    def __init__(self, attrs=None, outer_attrs=None):
         # The 'rows' and 'cols' attributes are required for HTML correctness.
-        self.attrs = {'cols': '40', 'rows': '10'}
+        default_attrs = {'cols': '40', 'rows': '10'}
         if attrs:
-            self.attrs.update(attrs)
+            default_attrs.update(attrs)
+        super(Textarea, self, default_attrs, outer_attrs)
 
     def render(self, name, value, attrs=None):
         if value is None: value = ''
@@ -257,8 +263,8 @@
     input_type = 'text'
     format = '%Y-%m-%d %H:%M:%S'     # '2006-10-25 14:30:59'
 
-    def __init__(self, attrs=None, format=None):
-        super(DateTimeInput, self).__init__(attrs)
+    def __init__(self, attrs=None, outer_attrs=None, format=None):
+        super(DateTimeInput, self).__init__(attrs, outer_attrs)
         if format:
             self.format = format
 
@@ -270,8 +276,8 @@
         return super(DateTimeInput, self).render(name, value, attrs)
 
 class CheckboxInput(Widget):
-    def __init__(self, attrs=None, check_test=bool):
-        super(CheckboxInput, self).__init__(attrs)
+    def __init__(self, attrs=None, outer_attrs=None, check_test=bool):
+        super(CheckboxInput, self).__init__(attrs, outer_attrs)
         # check_test is a callable that takes a value and returns True
         # if the checkbox should be checked for that value.
         self.check_test = check_test
@@ -296,8 +302,8 @@
         return super(CheckboxInput, self).value_from_datadict(data, files, name)
 
 class Select(Widget):
-    def __init__(self, attrs=None, choices=()):
-        super(Select, self).__init__(attrs)
+    def __init__(self, attrs=None, outer_attrs=None, choices=()):
+        super(Select, self).__init__(attrs, outer_attrs)
         # choices can be any iterable, but we may need to render this widget
         # multiple times. Thus, collapse it into a list so it can be consumed
         # more than once.
@@ -319,9 +325,9 @@
     """
     A Select Widget intended to be used with NullBooleanField.
     """
-    def __init__(self, attrs=None):
+    def __init__(self, attrs=None, outer_attrs=None):
         choices = ((u'1', ugettext('Unknown')), (u'2', ugettext('Yes')), (u'3', ugettext('No')))
-        super(NullBooleanSelect, self).__init__(attrs, choices)
+        super(NullBooleanSelect, self).__init__(attrs, outer_attrs, choices)
 
     def render(self, name, value, attrs=None, choices=()):
         try:
@@ -490,9 +496,9 @@
 
     You'll probably want to use this class with MultiValueField.
     """
-    def __init__(self, widgets, attrs=None):
+    def __init__(self, widgets, attrs=None, outer_attrs=None):
         self.widgets = [isinstance(w, type) and w() or w for w in widgets]
-        super(MultiWidget, self).__init__(attrs)
+        super(MultiWidget, self).__init__(attrs, outer_attrs)
 
     def render(self, name, value, attrs=None):
         # value is a list of values, each corresponding to a widget
@@ -552,9 +558,9 @@
     """
     A Widget that splits datetime input into two <input type="text"> boxes.
     """
-    def __init__(self, attrs=None):
+    def __init__(self, attrs=None, outer_attrs=None):
         widgets = (TextInput(attrs=attrs), TextInput(attrs=attrs))
-        super(SplitDateTimeWidget, self).__init__(widgets, attrs)
+        super(SplitDateTimeWidget, self).__init__(widgets, attrs, outer_attrs)
 
     def decompress(self, value):
         if value:

