[Zope3-checkins] CVS: Zope3/lib/python/Zope/Schema - FieldProperty.py:1.4 IField.py:1.10 _Field.py:1.5 __init__.py:1.4 _bootstrapFields.py:1.3

Jim Fulton jim@zope.com
Mon, 11 Nov 2002 15:24:36 -0500


Update of /cvs-repository/Zope3/lib/python/Zope/Schema
In directory cvs.zope.org:/tmp/cvs-serv15816

Modified Files:
	FieldProperty.py IField.py _Field.py __init__.py 
	_bootstrapFields.py 
Log Message:
Added TextLine and Line field types to model the common case of
single-line text.

Added a binding protocol to support fields whos validation or
properties depend on specific instances implementing the fields.
See the doc string for IField.


=== Zope3/lib/python/Zope/Schema/FieldProperty.py 1.3 => 1.4 ===
--- Zope3/lib/python/Zope/Schema/FieldProperty.py:1.3	Wed Sep 11 18:06:41 2002
+++ Zope3/lib/python/Zope/Schema/FieldProperty.py	Mon Nov 11 15:24:35 2002
@@ -43,14 +43,16 @@
 
         value = inst.__dict__.get(self.__name, _marker)
         if value is _marker:
-            value = getattr(self.__field, 'default', _marker)
+            field = self.__field.bind(inst)
+            value = getattr(field, 'default', _marker)
             if value is _marker:
                 raise AttributeError, self.__name
 
         return value
 
     def __set__(self, inst, value):
-        self.__field.validate(value)
+        field = self.__field.bind(inst)
+        field.validate(value)
         inst.__dict__[self.__name] = value
 
     def __getattr__(self, name):


=== Zope3/lib/python/Zope/Schema/IField.py 1.9 => 1.10 ===
--- Zope3/lib/python/Zope/Schema/IField.py:1.9	Thu Oct 10 18:17:57 2002
+++ Zope3/lib/python/Zope/Schema/IField.py	Mon Nov 11 15:24:35 2002
@@ -17,15 +17,66 @@
 """
 from Interface import Interface
 
-from _bootstrapFields import Field, Text, Bool, Int, Container, Iteratable
+from _bootstrapFields \
+     import Field, Text, TextLine, Bool, Int, Container, Iteratable
 
 class IField(Interface):
     u"""Fields
 
-    Fields are attribute specifications.
+    Fields are attribute specifications. They specify the allowed
+    values for object attributes, Field are typically defined in an
+    interface. 
+
+    XXX We need to think aboyt the following
+    
+    Note that many field need information about the object
+    implementing a field. For example, when validating a value to be
+    set as an object attribute, it may be necessary for the field to
+    introspect the object's state. This meanss that the field needs to
+    have access to the object when performing validation.
+
+    We haven't really decided on the best way to approach providing
+    access to objects in field methods and properties. We've thought
+    of three approaches:
+
+    1. Always pass the object:
+
+         field.validate(value, object)
+
+    2. Bind the field to the object with a context wrapper:
+
+         field = ContextWrapper(field, object)
+         field.validate(value)
+
+    3. Provide a specialized binding protocol:
+
+         bound = field(object_
+         bound.validate(value)
+
+    Options 2 and 3 allow us to use properties, but require an extra
+    binding step.
+    
+    Option 1 and 3 will require a significant refactoring.
+
+    Option 2 requires us to make field methods, or at least the
+    validate method into ContextMethods, which is a bit intrusive.
+
+    For now, we will use option 3.  
+
     """
 
-    title = Text(
+    def bind(object):
+        """Bind the field to an object
+
+        This is done by returning a copy of the field with a "context"
+        attribute set to the object.
+
+        Many fields don't need to be bound. Only fields that condition
+        validation or properties on an object containing the field
+        need to be bound.
+        """
+
+    title = TextLine(
         title=u"Title",
         description=u"A short summary or label",
         default=u"",
@@ -159,10 +210,16 @@
     u"""Describes the footprint of a Bool variable."""
 
 class IBytes(ISized, IEnumeratable, IIteratable):
-    u"""Describes the footprint of a Bytes variable."""
+    u"""Describes the footprint of a Bytes variable"""
+
+class ILine(IBytes):
+    u"""Describes the footprint of a Bytes variable withouit newlines"""
 
 class IText(ISized, IEnumeratable, IIteratable):
-    u"""Describes the footprint of a Str variable."""
+    u"""Describes the footprint of a Text variable."""
+
+class ITextLine(IText):
+    u"""Describes the footprint of a one-line Text variable."""
     
 class IInt(IEnumeratable, IOrderable):
     u"""Describes the footprint of an Int variable."""


=== Zope3/lib/python/Zope/Schema/_Field.py 1.4 => 1.5 ===
--- Zope3/lib/python/Zope/Schema/_Field.py:1.4	Fri Oct  4 14:24:55 2002
+++ Zope3/lib/python/Zope/Schema/_Field.py	Mon Nov 11 15:24:35 2002
@@ -24,7 +24,7 @@
 
 import IField
 from _bootstrapFields import Field, Container, Iteratable, Orderable, Sized
-from _bootstrapFields import Enumeratable, Text, Bool, Int
+from _bootstrapFields import Enumeratable, Text, TextLine, Bool, Int
 from FieldProperty import FieldProperty
 from datetime import datetime
 
@@ -47,6 +47,7 @@
 implements(Enumeratable, IField.IEnumeratable)
 
 implements(Text, IField.IText)
+implements(TextLine, IField.ITextLine)
 implements(Bool, IField.IBool)
 implements(Int, IField.IInt)
             
@@ -55,6 +56,16 @@
     __implements__ = IField.IBytes
     
     _type = str
+
+class Line(Bytes):
+    """A Text field with no newlines."""
+
+    __implements__ = IField.ILine
+
+    def constraint(self, value):
+        # XXX we should probably use a more general definition of newlines
+        return '\n' not in value
+    
 
 class Float(Enumeratable, Orderable):
     __doc__ = IField.IFloat.__doc__


=== Zope3/lib/python/Zope/Schema/__init__.py 1.3 => 1.4 ===
--- Zope3/lib/python/Zope/Schema/__init__.py:1.3	Fri Oct  4 14:24:55 2002
+++ Zope3/lib/python/Zope/Schema/__init__.py	Mon Nov 11 15:24:35 2002
@@ -18,5 +18,6 @@
 
 from _Field import Field, Container, Iteratable, Orderable, Sized, Enumeratable
 from _Field import Sequence
-from _Field import Bytes, Text, Bool, Int, Float, Tuple, List, Dict, Datetime
+from _Field import Bytes, Line, Text, TextLine, Bool, Int, Float
+from _Field import Tuple, List, Dict, Datetime
 from _Schema import validateMapping, validateMappingAll, getFields


=== Zope3/lib/python/Zope/Schema/_bootstrapFields.py 1.2 => 1.3 ===
--- Zope3/lib/python/Zope/Schema/_bootstrapFields.py:1.2	Wed Sep 18 11:05:51 2002
+++ Zope3/lib/python/Zope/Schema/_bootstrapFields.py	Mon Nov 11 15:24:35 2002
@@ -64,13 +64,19 @@
         self.description = description
         self.required = required
         self.readonly = readonly
-        self.constraint = constraint
+        if constraint is not None:
+            self.constraint = constraint
         self.default = default
 
         # Keep track of the order of field definition
         Field.order += 1
         self.order = Field.order
 
+    def bind(self, object):
+        clone = self.__class__.__new__(self.__class__)
+        clone.__dict__.update(self.__dict__)
+        clone.context = object
+        return clone
         
     def validate(self, value):
         if value is None:
@@ -196,9 +202,10 @@
 
         # Set allowed_values to None so that we can validate if
         # one of the super methods invoke validation.
-        self.allowed_values = None
+        self.__dict__['allowed_values'] = None
         super(Enumeratable, self).__init__(**kw)
-        self.allowed_values = allowed_values
+        if allowed_values is not None:
+            self.allowed_values = allowed_values
 
         # We've taken over setting default so it can be limited by min
         # and max.
@@ -209,12 +216,22 @@
 
         if self.allowed_values:
             if not value in self.allowed_values:
-                raise ValidationError(ErrorNames.InvalidValue)
+                raise ValidationError(ErrorNames.InvalidValue, value,
+                                      self.allowed_values)
 
 
 class Text(Sized, Enumeratable):
-    """A field representing a Str."""
+    """A field containing text used for human discourse."""
     _type = unicode
+    
+
+class TextLine(Text):
+    """A Text field with no newlines."""
+
+    def constraint(self, value):
+        # XXX we should probably use a more general definition of newlines
+        return '\n' not in value
+    
 
 class Bool(Field):
     """A field representing a Bool."""