[Zope3-checkins] CVS: Zope3/lib/python/Zope/App/Forms - Exceptions.py:1.1 Converter.py:1.6 IConverter.py:1.2 IWidget.py:1.2 Widget.py:1.3

Jim Fulton jim@zope.com
Sat, 7 Sep 2002 12:19:18 -0400


Update of /cvs-repository/Zope3/lib/python/Zope/App/Forms
In directory cvs.zope.org:/tmp/cvs-serv19433/lib/python/Zope/App/Forms

Modified Files:
	Converter.py IConverter.py IWidget.py Widget.py 
Added Files:
	Exceptions.py 
Log Message:
More cleanup/refactoring of Schemas and forms. There's more to come,
but I'm checkpointing here.

I:

- Added schema field properties. These are like standard Python
  properies except that they are derived from Schema fields.

- Decomposed Str fields into Bytes fields and Text fields.
  Bytes fields contain 8-bit data and are stored as python strings.
  Text fields contain written human discourse, and are stored as
  unicode. It is invalid to store Python strings in Text fields or
  unicode in Bytes fields.

- Moved converters from schemas to forms, where they are used.

- Widgets are now responsible for:

  - Getting raw data from the request

  - Converting raw data to application data

  - Validating converted data against schema fields

- Began defining an error framework for errors in forms.

- Simplified FormViews to reflect new widget responsibilities.

- Added Bytes, Int and Float widgets and changed some application and
  test code to use them.




=== Added File Zope3/lib/python/Zope/App/Forms/Exceptions.py ===
##############################################################################
#
# Copyright (c) 2002 Zope Corporation and Contributors.
# All Rights Reserved.
# 
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE.
# 
##############################################################################
"""Validation Exceptions

$Id: Exceptions.py,v 1.1 2002/09/07 16:18:48 jim Exp $
"""


class WidgetInputError(Exception):
    """There were one or more user input errors 
    """

    def __init__(self, widget_name, widget_title, errors):
        self.widget_name = widget_name
        self.widget_title = widget_title
        self.errors = errors
    
    
class ConversionError(WidgetInputError):
    """If some conversion fails, this exception is raised.
    """

    def __init__(self, error_name, original_exception=None):
        Exception.__init__(self)
        self.error_name = error_name
        self.original_exception = original_exception



=== Zope3/lib/python/Zope/App/Forms/Converter.py 1.5 => 1.6 ===
--- Zope3/lib/python/Zope/App/Forms/Converter.py:1.5	Thu Sep  5 14:55:01 2002
+++ Zope3/lib/python/Zope/App/Forms/Converter.py	Sat Sep  7 12:18:48 2002
@@ -14,12 +14,72 @@
 """
 $Id$
 """
-from types import TupleType, ListType
-ListTypes = (TupleType, ListType)
-from Zope.Schema.IConverter import IConverter
-from Zope.Schema.Converter import NullConverter
-from Zope.Schema.IField import IField
 
+from IConverter import IConverter
+from Exceptions import ConversionError
+
+class NullConverter(object):
+    """A converter that really doesn't do a thing.
+    """
+    __implements__ = IConverter
+
+    def convert(self, value):
+        return value
+
+class CombinedConverter(object):
+    """A converter that chains several converters to each other.
+    """
+    __implements__ = IConverter
+
+    def __init__(self, converters):
+        self._converters = converters
+    
+    def convert(self, value):
+        for converter in self._converters:
+            value = converter.convert(value)
+        return value
+
+class FunctionConverter(object):
+    """Use a Python function to convert.
+    Turns *any* Python exception into a conversion error.
+    XXX Is this good/useful?
+    """
+    __implements__ = IConverter    
+
+    def convert(self, value):
+        try:
+            return self._function(value)
+        except Exception, e:
+            raise ConversionError('Conversion error', e)
+        
+def _functionConverterFactory(klass_name, function):
+    """Create a derived class of FunctionConvert which uses function.
+    """
+    klass = type(klass_name, (FunctionConverter,), {})
+    klass._function = function
+    return klass
+
+StrToIntConverter= _functionConverterFactory('StrToIntConverter', int)
+IntToStrConverter = _functionConverterFactory('IntToStrConverter', str)
+
+StrToFloatConverter = _functionConverterFactory('StrToFloatConverter', float)
+FloatToStrConverter = _functionConverterFactory('FloatToStrConverter', str)
+
+
+class FileToStrConverter(object):
+    __implements__ = IConverter    
+
+    def convert(self, value):
+        try:
+            value = value.read()
+        except Exception, e:
+            raise ConversionError('Value is not a file object', e)
+        else:
+            if len(value):
+                return value
+            else:
+                return None
+            
 
 class RawToHomogeneousListConverter(NullConverter):
     """Converts a list of raw values to a list of values with a specific
@@ -35,19 +95,6 @@
         return result
     
 
-class FieldToFieldConverter(NullConverter):
-    """ """
-    __convert_from__ = IField
-    __convert_to__ = IField
-
-    def convert(self, value):
-        'See Zope.App.Forms.IConverter.IConverter'
-        assert isinstance(value, self.__convert_from__.type), 'Wrong Type'
-        value = self.__convert_to__.type(value)
-        assert isinstance(value, self.__convert_to__.type), 'Wrong Type'
-        return value
-
-
 class NoneToEmptyListConverter(NullConverter):
     """Converts None object to an empty list."""
 
@@ -57,12 +104,13 @@
         else:
             return value
 
-
 class ValueToSingleItemListConverter(NullConverter):
     """Converts a single value to a list with the value being the only
     element."""
 
-    def convert(self, value):
+    def convert(self, value,
+                ListTypes = (tuple, list)
+                ):
         if not isinstance(value, ListTypes):
             return [value]
         else:


=== Zope3/lib/python/Zope/App/Forms/IConverter.py 1.1 => 1.2 ===
--- Zope3/lib/python/Zope/App/Forms/IConverter.py:1.1	Sun Jul 14 09:32:53 2002
+++ Zope3/lib/python/Zope/App/Forms/IConverter.py	Sat Sep  7 12:18:48 2002
@@ -14,18 +14,14 @@
 """
 $Id$
 """
-
 from Interface import Interface
-from Interface.Attribute import Attribute
-
 
 class IConverter(Interface):
-    """Converts from one type of Field type to another one. Most common will
-    be to convert from String to another type, such as Integer."""
-
-    __convert_to__ = Attribute('The field type this converter converts to.')
-    __convert_from__ = Attribute('The field type this converter accepts '
-                                 'for conversion.')
+    """A converter can convert a value from one type to another."""
 
     def convert(value):
-        """This method converts from __convert_from__ to __convert_to__."""
+        """Call an IConverter with a value, and it will try to convert to
+        another value and return the result. If conversion cannot take
+        place, the convertor will raise a ConversionError.
+        """
+    


=== Zope3/lib/python/Zope/App/Forms/IWidget.py 1.1 => 1.2 ===
--- Zope3/lib/python/Zope/App/Forms/IWidget.py:1.1	Sun Jul 14 09:32:53 2002
+++ Zope3/lib/python/Zope/App/Forms/IWidget.py	Sat Sep  7 12:18:48 2002
@@ -14,10 +14,10 @@
 """
 $Id$
 """
-from Zope.ComponentArchitecture.IContextDependent import IContextDependent
+from Zope.ComponentArchitecture.IView import IView
 from Interface.Attribute import Attribute
 
-class IWidget(IContextDependent):
+class IWidget(IView):
     """Generically describes the behavior of a widget.
 
     The widget defines a list of propertyNames, which describes
@@ -32,3 +32,23 @@
 
     def getValue(name):
         """Look up a Widget setting (value) by name."""
+
+    def getData():
+        """Return converted and validated widget data.
+
+        A WidgetInputError is returned in the case of one or more
+        errors encountered, inputing, convrting, or validating the data.
+        """
+
+    def getName():
+        """Return the unique name for the widget.
+
+        This must be unique within a set of widgets.
+        """
+
+    def getTitle():
+        """Return the widget title.
+
+        This is the text that will be used to label the widget.
+        """
+


=== Zope3/lib/python/Zope/App/Forms/Widget.py 1.2 => 1.3 ===
--- Zope3/lib/python/Zope/App/Forms/Widget.py:1.2	Tue Jul 16 19:42:58 2002
+++ Zope3/lib/python/Zope/App/Forms/Widget.py	Sat Sep  7 12:18:48 2002
@@ -15,12 +15,18 @@
 $Id$
 """
 from IWidget import IWidget
+from Zope.Schema.Exceptions import ValidationError
+from Zope.App.Forms.Exceptions import WidgetInputError
 
 class Widget(object):
     """I do not know what will be in this class, but it provides an extra
     layer."""
     __implements__ = IWidget
 
+    def __init__(self, context, request):
+        self.context = context
+        self.request = request
+
     # See Zope.App.Forms.IWidget.IWidget
     propertyNames = []
 
@@ -29,6 +35,21 @@
         if name in self.propertyNames:
             return getattr(self, name, None)
 
+    def getName(self):
+        return self.context.getName()
+
+    def getTitle(self):
+        return self.context.title
+
+    def getData(self):
+        raw = self._getRawData()
+        value = self._convert(raw)
+        try:
+            self.context.validate(value)
+        except ValidationError, v:
+            raise WidgetInputError(self.getName(), self.getTitle(), v)
+
+        return value
 
 class CustomWidget(object):
     """Custom Widget."""
@@ -44,3 +65,13 @@
             setattr(instance, item[0], item[1])
         return instance
                   
+class Customizer:
+    """Objects for making shorthands for creating CustomWidgets
+    """
+    
+    def __init__(self, widget_class):
+        self.__widget_class = widget_class
+
+    def __call__(self, **kw):
+        # XXX should have error checking here!
+        return CustomWidgets(self.__widget_class, **kw)