[Zope3-checkins] CVS: Zope3/src/zope/schema - __init__.py:1.2 _bootstrapfields.py:1.2 _field.py:1.2 _schema.py:1.2 errornames.py:1.2 fieldproperty.py:1.2 interfaces.py:1.2 readme.stx:1.2

Jim Fulton jim@zope.com
Wed, 25 Dec 2002 09:15:51 -0500


Update of /cvs-repository/Zope3/src/zope/schema
In directory cvs.zope.org:/tmp/cvs-serv20790/src/zope/schema

Added Files:
	__init__.py _bootstrapfields.py _field.py _schema.py 
	errornames.py fieldproperty.py interfaces.py readme.stx 
Log Message:
Grand renaming:

- Renamed most files (especially python modules) to lower case.

- Moved views and interfaces into separate hierarchies within each
  project, where each top-level directory under the zope package
  is a separate project.

- Moved everything to src from lib/python.

  lib/python will eventually go away. I need access to the cvs
  repository to make this happen, however.

There are probably some bits that are broken. All tests pass
and zope runs, but I haven't tried everything. There are a number
of cleanups I'll work on tomorrow.



=== Zope3/src/zope/schema/__init__.py 1.1 => 1.2 ===
--- /dev/null	Wed Dec 25 09:15:51 2002
+++ Zope3/src/zope/schema/__init__.py	Wed Dec 25 09:15:20 2002
@@ -0,0 +1,24 @@
+##############################################################################
+#
+# 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.
+#
+##############################################################################
+"""Schema package constructor
+
+$Id$
+"""
+
+from zope.schema._field import Field, Container, Iterable, Orderable, MinMaxLen, ValueSet
+from zope.schema._field import Sequence
+from zope.schema._field import Bytes, BytesLine, Text, TextLine, Bool, Int, Float
+from zope.schema._field import Tuple, List, Dict, Datetime
+from zope.schema._schema import validateMapping, validateMappingAll,\
+     getFields, getFieldsInOrder


=== Zope3/src/zope/schema/_bootstrapfields.py 1.1 => 1.2 ===
--- /dev/null	Wed Dec 25 09:15:51 2002
+++ Zope3/src/zope/schema/_bootstrapfields.py	Wed Dec 25 09:15:20 2002
@@ -0,0 +1,274 @@
+##############################################################################
+#
+# 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.
+#
+##############################################################################
+"""
+$Id$
+"""
+__metaclass__ = type
+
+from zope.interface import Attribute
+from zope.interface.implements import visitImplements
+
+from zope.schema.interfaces import StopValidation, ValidationError
+from zope.schema import errornames
+from zope.schema._schema import getFields
+
+class ValidatedProperty:
+
+    def __init__(self, name, check = None):
+        self.__info  = name, check
+
+    def __set__(self, inst, value):
+        name, check = self.__info
+        if value is not None:
+            if check is not None:
+                check(inst, value)
+            else:
+                inst.validate(value)
+        inst.__dict__[name] = value
+
+class Field(Attribute):
+
+    # Type restrictions, if any
+    _type = None
+    order = 0
+    context = None
+
+    constraint = None
+    default = ValidatedProperty('default')
+
+    def __init__(self, __name__='', __doc__='',
+                 title=u'', description=u'',
+                 required=True, readonly=False, constraint=None, default=None,
+                 ):
+        """Pass in field values as keyword parameters."""
+
+
+        if not __doc__:
+            if title:
+                if description:
+                    __doc__ =  "%s\n\n%s" % (title, description)
+                else:
+                    __doc__ = title
+            elif description:
+                __doc__ = description
+
+        super(Field, self).__init__(__name__, __doc__)
+        self.title = title
+        self.description = description
+        self.required = required
+        self.readonly = readonly
+        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:
+            if self.required:
+                raise ValidationError(errornames.RequiredMissing)
+        else:
+            try:
+                self._validate(value)
+            except StopValidation:
+                pass
+
+    def __eq__(self, other):
+        # should be the same type
+        if type(self) != type(other):
+            return False
+        # should have the same properties
+        names = {} # used as set of property names, ignoring values
+        visitImplements(self.__implements__, self,
+                        lambda interface: names.update(getFields(interface)))
+        # order will be different always, don't compare it
+        if 'order' in names:
+            del names['order']
+        for name in names:
+            if getattr(self, name) != getattr(other, name):
+                return False
+        return True
+
+    def __ne__(self, other):
+        return not self.__eq__(other)
+
+    def _validate(self, value):
+
+        if self._type is not None and not isinstance(value, self._type):
+            raise ValidationError(errornames.WrongType, value, self._type)
+
+        if self.constraint is not None and not self.constraint(value):
+            raise ValidationError(errornames.ConstraintNotSatisfied, value)
+
+
+class Container(Field):
+
+    def _validate(self, value):
+        super(Container, self)._validate(value)
+
+        if not hasattr(value, '__contains__'):
+            try:
+                iter(value)
+            except:
+                raise ValidationError(errornames.NotAContainer, value)
+
+
+class Iterable(Container):
+
+    def _validate(self, value):
+        super(Iterable, self)._validate(value)
+
+        # See if we can get an iterator for it
+        try:
+            iter(value)
+        except:
+            raise ValidationError(errornames.NotAnIterator, value)
+
+
+class Orderable(Field):
+    """Values of ordered fields can be sorted
+
+    They can be restricted to a range of values.
+    """
+
+    min = ValidatedProperty('min')
+    max = ValidatedProperty('max')
+
+    def __init__(self, min=None, max=None, default=None, **kw):
+
+        # Set min and max to None so that we can validate if
+        # one of the super methods invoke validation.
+        self.min = None
+        self.max = None
+
+        super(Orderable, self).__init__(**kw)
+
+        # Now really set min and max
+        self.min = min
+        self.max = max
+
+        # We've taken over setting default so it can be limited by min
+        # and max.
+        self.default = default
+
+
+    def _validate(self, value):
+        super(Orderable, self)._validate(value)
+
+        if self.min is not None and value < self.min:
+            raise ValidationError(errornames.TooSmall, value, self.min)
+
+        if self.max is not None and value > self.max:
+            raise ValidationError(errornames.TooBig, value, self.max)
+
+_int_types = int, long
+class MinMaxLen(Field):
+    min_length = 0
+    max_length = None
+
+    def __init__(self, min_length=0, max_length=None, **kw):
+        self.min_length = min_length
+        self.max_length = max_length
+        super(MinMaxLen, self).__init__(**kw)
+
+    def _validate(self, value):
+        super(MinMaxLen, self)._validate(value)
+
+        if self.min_length is not None and len(value) < self.min_length:
+            raise ValidationError(errornames.TooShort, value, self.min_length)
+
+        if self.max_length is not None and len(value) > self.max_length:
+            raise ValidationError(errornames.TooLong, value, self.max_length)
+
+
+class ValueSet(Field):
+
+    def allowed_values(self, values):
+        # Reset current value so it doesn't hose validation
+        if not values:
+            return
+
+        old_allowed = getattr(self, 'allowed_values', None)
+        self.allowed_values = None
+
+        for value in values:
+
+            try:
+                self.validate(value)
+            except:
+                # restore the old value
+                self.allowed_values = old_allowed
+                raise
+
+    allowed_values = ValidatedProperty('allowed_values', allowed_values)
+
+    def __init__(self, allowed_values=None, default=None, **kw):
+
+        # Set allowed_values to None so that we can validate if
+        # one of the super methods invoke validation.
+        self.__dict__['allowed_values'] = None
+        super(ValueSet, self).__init__(**kw)
+        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.
+        self.default = default
+
+    def _validate(self, value):
+        super(ValueSet, self)._validate(value)
+
+        if self.allowed_values:
+            if not value in self.allowed_values:
+                raise ValidationError(errornames.InvalidValue, value,
+                                      self.allowed_values)
+
+
+class Text(MinMaxLen, ValueSet):
+    """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."""
+
+    try:
+        if type(True) is int:
+            # If we have True and it's an int, then pretend we're 2.2.0.
+            raise NameError("True")
+    except NameError:
+        # Pre booleans
+        _type = int
+    else:
+        _type = bool
+
+class Int(ValueSet, Orderable):
+    """A field representing a Integer."""
+    _type = int, long


=== Zope3/src/zope/schema/_field.py 1.1 => 1.2 ===
--- /dev/null	Wed Dec 25 09:15:51 2002
+++ Zope3/src/zope/schema/_field.py	Wed Dec 25 09:15:20 2002
@@ -0,0 +1,162 @@
+##############################################################################
+#
+# 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.
+#
+##############################################################################
+"""
+$Id$
+"""
+__metaclass__ = type
+
+from zope.interface.implements import implements
+
+from zope.schema.interfaces import ValidationError
+from zope.schema.errornames import WrongContainedType
+
+from zope.schema.interfaces import IField, IContainer, IIterable, IOrderable
+from zope.schema.interfaces import IMinMaxLen, IValueSet, IText, ITextLine
+from zope.schema.interfaces import IBool, IInt, IBytes, IBytesLine, IFloat
+from zope.schema.interfaces import IDatetime, ISequence, ITuple, IList, IDict
+
+from zope.schema._bootstrapfields import Field, Container, Iterable, Orderable, MinMaxLen
+from zope.schema._bootstrapfields import ValueSet, Text, TextLine, Bool, Int
+from zope.schema.fieldproperty import FieldProperty
+from datetime import datetime
+
+# Fix up bootstrap field types
+Field.title       = FieldProperty(IField['title'])
+Field.description = FieldProperty(IField['description'])
+Field.required    = FieldProperty(IField['required'])
+Field.readonly    = FieldProperty(IField['readonly'])
+# Default is already taken care of
+implements(Field, IField)
+
+implements(Container, IContainer)
+implements(Iterable, IIterable)
+implements(Orderable, IOrderable)
+
+MinMaxLen.min_length = FieldProperty(IMinMaxLen['min_length'])
+MinMaxLen.max_length = FieldProperty(IMinMaxLen['max_length'])
+implements(MinMaxLen, IMinMaxLen)
+
+implements(ValueSet, IValueSet)
+
+implements(Text, IText)
+implements(TextLine, ITextLine)
+implements(Bool, IBool)
+implements(Int, IInt)
+
+class Bytes(MinMaxLen, ValueSet):
+    __doc__ = IBytes.__doc__
+    __implements__ = IBytes
+
+    _type = str
+
+class BytesLine(Bytes):
+    """A Text field with no newlines."""
+
+    __implements__ = IBytesLine
+
+    def constraint(self, value):
+        # XXX we should probably use a more general definition of newlines
+        return '\n' not in value
+
+
+class Float(ValueSet, Orderable):
+    __doc__ = IFloat.__doc__
+    __implements__ = IFloat
+    _type = float
+
+class Datetime(ValueSet, Orderable):
+    __doc__ = IDatetime.__doc__
+    __implements__ = IDatetime
+    _type = datetime
+
+def _validate_sequence(value_types, value, errors=None):
+    if errors is None:
+        errors = []
+
+    if value_types is None:
+        return errors
+
+    for item in value:
+        error = None
+        for t in value_types:
+            try:
+                t.validate(item)
+            except ValidationError, error:
+                pass
+            else:
+                # We validated, so clear error (if any) and done with
+                # this value
+                error = None
+                break
+
+        if error is not None:
+            errors.append(error)
+
+    return errors
+
+
+class Sequence(MinMaxLen, Iterable):
+    __doc__ = ISequence.__doc__
+    value_types = FieldProperty(ISequence['value_types'])
+
+    def __init__(self, value_types=None, **kw):
+        super(Sequence, self).__init__(**kw)
+        self.value_types = value_types
+
+    def _validate(self, value):
+        super(Sequence, self)._validate(value)
+        try:
+            errors = _validate_sequence(self.value_types, value)
+            if errors:
+                raise ValidationError(WrongContainedType, errors)
+
+        finally:
+            errors = None
+
+class Tuple(Sequence):
+    """A field representing a Tuple."""
+    __implements__ = ITuple
+    _type = tuple
+
+class List(Sequence):
+    """A field representing a List."""
+    __implements__ = IList
+    _type = list
+
+class Dict(MinMaxLen, Iterable):
+    """A field representing a Dict."""
+    __implements__ = IDict
+    _type = dict
+    key_types   = FieldProperty(IDict['key_types'])
+    value_types = FieldProperty(IDict['value_types'])
+
+    def __init__(self, key_types=None, value_types=None, **kw):
+        super(Dict, self).__init__(**kw)
+        self.key_types = key_types
+        self.value_types = value_types
+
+    def _validate(self, value):
+        super(Dict, self)._validate(value)
+        errors = []
+        try:
+            if self.value_types:
+                errors = _validate_sequence(self.value_types, value.values(),
+                                            errors)
+            errors = _validate_sequence(self.key_types, value, errors)
+
+            if errors:
+                raise ValidationError(WrongContainedType, errors)
+
+        finally:
+            errors = None


=== Zope3/src/zope/schema/_schema.py 1.1 => 1.2 ===
--- /dev/null	Wed Dec 25 09:15:51 2002
+++ Zope3/src/zope/schema/_schema.py	Wed Dec 25 09:15:20 2002
@@ -0,0 +1,67 @@
+##############################################################################
+#
+# 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.
+#
+##############################################################################
+"""
+$Id$
+"""
+from zope.interface import Interface
+from zope.schema.interfaces import ValidationError, ValidationErrorsAll
+
+def getFields(schema):
+    """Get all fields on a schema.
+    """
+    from zope.schema.interfaces import IField
+    fields = {}
+    for name in schema.names(1):
+        attr = schema.getDescriptionFor(name)
+        if IField.isImplementedBy(attr):
+            fields[name] = attr
+    return fields
+
+def getFieldsInOrder(schema,
+                     _fieldsorter=lambda x, y: cmp(x[1].order, y[1].order)):
+    """Get a list of (name, value) tuples in native schema order.
+    """
+    fields = getFields(schema).items()
+    fields.sort(_fieldsorter)
+    return fields
+
+# validate functions either return silently, or raise a ValidationError
+# or in case of the validate*All functions, a ValidationErrosAll exception.
+# this should somehow be stated in an interface.
+
+def validateMapping(schema, values):
+    """Pass in field values in mapping and validate whether they
+    conform to schema. Stop at first error.
+    """
+    from zope.schema.interfaces import IField
+    for name in schema.names(1):
+        attr = schema.getDescriptionFor(name)
+        if IField.isImplementedBy(attr):
+            attr.validate(values.get(name))
+
+def validateMappingAll(schema, values):
+    """Pass in field values in mapping and validate whether they
+    conform to schema.
+    """
+    errors = []
+    from zope.schema.interfaces import IField
+    for name in schema.names(1):
+        attr = schema.getDescriptionFor(name)
+        if IField.isImplementedBy(attr):
+            try:
+                attr.validate(values.get(name))
+            except ValidationError, e:
+                errors.append((name, e))
+    if errors:
+        raise ValidationErrorsAll, errors


=== Zope3/src/zope/schema/errornames.py 1.1 => 1.2 ===
--- /dev/null	Wed Dec 25 09:15:51 2002
+++ Zope3/src/zope/schema/errornames.py	Wed Dec 25 09:15:20 2002
@@ -0,0 +1,42 @@
+##############################################################################
+#
+# 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.
+#
+##############################################################################
+"""Common Schema Error Names
+
+$Id$
+"""
+WrongType = u"WrongType"
+
+RequiredMissing = u'RequiredMissing'
+
+RequiredEmptyStr = u'RequiredEmptyString'
+
+TooBig = u'TooBig'
+
+TooSmall = u'TooSmall'
+
+TooLong = u'TooLong'
+
+TooShort = u'TooShort'
+
+InvalidValue = u'InvalidValue'
+
+TooManyDecimals = u'TooManyDecimals'
+
+WrongContainedType = u"WrongContainedType"
+
+ConstraintNotSatisfied = u'ConstraintNotSatisfied'
+
+NotAContainer = u'NotAContainer'
+
+NotAnIterator = u'NotAnIterator'


=== Zope3/src/zope/schema/fieldproperty.py 1.1 => 1.2 ===
--- /dev/null	Wed Dec 25 09:15:51 2002
+++ Zope3/src/zope/schema/fieldproperty.py	Wed Dec 25 09:15:20 2002
@@ -0,0 +1,61 @@
+##############################################################################
+#
+# 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.
+#
+##############################################################################
+"""
+$Id$
+"""
+
+__metaclass__ = type
+
+_marker = object()
+
+class FieldProperty:
+    """Computed attributes based on schema fields
+
+    Field properties provide default values, data validation and error messages
+    based on data found in field meta-data.
+
+    Note that FieldProperties cannot be used with slots. They can only
+    be used for attributes stored in instance dictionaries.
+
+    """
+
+    def __init__(self, field, name=None):
+        if name is None:
+            name = field.__name__
+
+        self.__field = field
+        self.__name = name
+
+    def __get__(self, inst, klass):
+        if inst is None:
+            return self
+
+        value = inst.__dict__.get(self.__name, _marker)
+        if value is _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):
+        field = self.__field.bind(inst)
+        field.validate(value)
+        inst.__dict__[self.__name] = value
+
+    def __getattr__(self, name):
+        return getattr(self.__field, name)
+
+__doc__ = FieldProperty.__doc__ + __doc__


=== Zope3/src/zope/schema/interfaces.py 1.1 => 1.2 ===
--- /dev/null	Wed Dec 25 09:15:51 2002
+++ Zope3/src/zope/schema/interfaces.py	Wed Dec 25 09:15:20 2002
@@ -0,0 +1,363 @@
+##############################################################################
+#
+# 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$
+"""
+
+class StopValidation(Exception):
+    """This exception is raised, if the validation is done for sure early.
+    Note that this exception should be always caught, since it is just a
+    way for the validator to save time."""
+    pass
+
+
+class ValidationError(Exception):
+    """If some check during the Validation process fails, this exception is
+    raised."""
+
+    def __cmp__(self, other):
+        return cmp(self.args, other.args)
+
+    def __repr__(self):
+        return ' '.join(map(str, self.args))
+
+# XXX YAGNI, this is doomed. ;)
+
+class ErrorContainer(Exception):
+    """ """
+
+    def __init__(self, errors):
+        Exception.__init__(self)
+        self.errors = errors
+
+    def __len__(self):
+        return len(self.errors)
+
+    def __getitem__(self, key):
+        return self.errors[key]
+
+    def __iter__(self):
+        return iter(self.errors)
+
+
+class ValidationErrorsAll(ErrorContainer):
+    """This is a collection error that contains all exceptions that occured
+    during the validation process."""
+
+
+class ConversionErrorsAll(ErrorContainer):
+    """This is a collection error that contains all exceptions that occured
+    during the conversion process."""
+
+
+
+"""These are the interfaces for the common fields.
+
+$Id$
+"""
+from zope.interface import Interface
+
+from zope.schema._bootstrapfields \
+     import Field, Text, TextLine, Bool, Int, Container, Iterable
+
+class IField(Interface):
+    """Basic Schema Field Interface.
+
+    Fields are used for Interface specifications.  They at least provide
+    a title, description and a default value.  You can also
+    specify if they are required and/or readonly.
+
+    The Field Interface is also used for validation and specifying
+    constraints.
+
+    We want to make it possible for a IField to not only work
+    on its value but also on the object this value is bound to.
+    This enables a Field implementation to perform validation
+    against an object which also marks a certain place.
+
+    Note that many fields need information about the object
+    containing 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 means 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.bind(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.
+
+    """
+
+    def bind(object):
+        """return a copy of this field which is bound to an object.
+
+        The copy of the Field will have the 'context' attribute set
+        to 'object'.  This way a Field can implement more complex
+        checks involving the object and its location.
+
+        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"",
+        required=False,
+        )
+
+    description = Text(
+        title=u"Description",
+        description=u"A description of the field",
+        default=u"",
+        required=False,
+        )
+
+    required = Bool(
+        title=u"Required",
+        description=(
+        u"tells whether a field requires its value to exist."),
+        default=True)
+
+    readonly = Bool(
+        title=u"Read Only",
+        description=u"Read-only.", # XXX what is this?
+        required=False,
+        default=False)
+
+    default = Field(
+        title=u"default field value if no value is present",
+        description=u"""The field default value may be None or a legal
+                        field value"""
+        )
+
+    def constraint(value):
+        u"""check a customized contraint on the value.
+
+        You can implement this method with your Field to
+        require a certain constraint.  This relaxes the need
+        to inherit/subclass a Field you to add a simple contraint.
+        Returns true if the given value is within the Field's contraint.
+        """
+
+    def validate(value):
+        u"""Validate that the given value is a valid field value.
+
+        Returns nothing but raises an error if the value is invalid.
+        It checks everything specific to a Field and also checks
+        with the additional constraint.
+        """
+
+    order = Int(
+        title=u"Field Order",
+        description=u"""\
+        The order attribute can be used to determine the order in
+        which fields in a schema were defined. If one field is created
+        after another (in the same thread), its order will be
+        greater.
+
+        (Fields in separate threads could have the same order.)
+        """,
+        required=True,
+        readonly=True,
+        )
+
+
+class IIterable(IField):
+    u"""Fields with a value that can be iterated over.
+
+    The value needs to follow the python __iter__ protocol.
+    """
+
+class IContainer(IField):
+    u"""Fields whose value allows an 'x in value' check.
+
+    The Value needs to have a conventional __contains__ method.
+    """
+
+class IOrderable(IField):
+    u"""a Field requiring its value to be orderable.
+
+    The value needs to have a conventional __cmp__ method.
+    """
+
+class ILen(IField):
+    u"""a Field requiring its value to have a length.
+
+    The value needs to have a conventional __len__ method.
+    """
+
+class IMinMax(IOrderable):
+    u"""a Field requiring its value to be between min and max.
+
+    This also means that the value needs to support the IOrderable interface.
+    """
+
+    min = Field(
+        title=u"Start of the range",
+        required=False,
+        default=None
+        )
+
+    max = Field(
+        title=u"End of the range (excluding the value itself)",
+        required=False,
+        default=None
+        )
+
+
+class IMinMaxLen(ILen):
+    u"""a Field requiring the length of its value to be within a range"""
+
+    min_length = Int(
+        title=u"Minimum length",
+        description=u"""\
+        Value after whitespace processing cannot have less than
+        min_length characters. If min_length is None, there is
+        no minimum.
+        """,
+        required=False,
+        min=0, # needs to be a positive number
+        default=0)
+
+    max_length = Int(
+        title=u"Maximum length",
+        description=u"""\
+        Value after whitespace processing cannot have greater
+        or equal than max_length characters. If max_length is
+        None, there is no maximum.""",
+        required=False,
+        min=0, # needs to be a positive number
+        default=None)
+
+class IValueSet(IField):
+    u"""Field whose value is contained in a predefined set"""
+
+    allowed_values = Container(
+        title=u"Allowed Values",
+        description=u"""\
+        Only values specified here can be values of this field.
+        If the list is empty, then there are no further
+        restictions.""",
+        required=False)
+
+class IBool(IField):
+    u"""a Boolean Field."""
+
+class IBytes(IMinMaxLen, IValueSet, IIterable):
+    u"""a Field containing a byte string (like the python str).
+
+    The value might be contrained to be with length limits, or
+    be within a set of values.
+    """
+
+class IBytesLine(IBytes):
+    u"""a Field containing a byte string without newlines."""
+
+class IText(IMinMaxLen, IValueSet, IIterable):
+    u"""a Field containing a unicode string."""
+
+class ITextLine(IText):
+    u"""a Field containing a unicode string without newlines."""
+
+class IInt(IMinMax, IValueSet):
+    u"""a Field containing an Integer Value."""
+
+class IFloat(IMinMax, IValueSet):
+    u"""a Field containing a Float."""
+
+class IDatetime(IMinMax, IValueSet):
+    u"""a Field containing a DateTime."""
+
+def _fields(values):
+    for value in values:
+        if not IField.isImplementedBy(value):
+            return False
+    return True
+
+class ISequence(IMinMaxLen, IIterable):
+    u"""a Field containing a Sequence value.
+
+    The Value must be iterable and may have a min_length/max_length.
+    """
+
+    value_types = Iterable(
+        title=u"Value Types",
+        description=(
+        u"""\
+        If set to a non-empty value, field value items must conform to one
+        of the given types, which are expressed via fields.
+        """),
+        required=False,
+        constraint=_fields,
+        )
+
+class ITuple(ISequence):
+    u"""a Field containing a conventional tuple."""
+
+class IList(ISequence):
+    u"""a Field containing a conventional list."""
+
+class IDict(IMinMaxLen, IIterable):
+    u"""a Field containing a conventional dict.
+
+    the key_types and value_types field allow specification
+    of restrictions for the dict.
+    """
+
+    key_types = Iterable(
+        title=u"Value Types",
+        description=u"""\
+        If set to a non-empty value, field value keys must conform to one
+        of the given types, which are expressed via fields.
+        """,
+        constraint=_fields,
+        required=False,
+        )
+
+    value_types = Iterable(
+        title=u"Value Types",
+        description=(
+        u"""\
+        If set to a non-empty value, field value values must conform to one
+        of the given types, which are expressed via fields.
+        """),
+        constraint=_fields,
+        required=False,
+        )


=== Zope3/src/zope/schema/readme.stx 1.1 => 1.2 ===
--- /dev/null	Wed Dec 25 09:15:51 2002
+++ Zope3/src/zope/schema/readme.stx	Wed Dec 25 09:15:20 2002
@@ -0,0 +1,164 @@
+Zope 3 Schemas
+
+Table of contents
+
+     Short introduction
+
+     Long introduction
+
+     What is a schema, how does it compare to an interface and why
+     should you care?
+
+     What does a Schema look like 
+     
+     Where Schemas make sense: Providing views from Schemas 
+     
+     Issues to be solved 
+
+
+Introduction
+
+     Schemas extend the notion of interfaces to descriptions of
+     Attributes rather than methods.  Every Schema is an interface
+     and specifies the public fields of an object.  A "Field" roughly
+     corresponds to an attribute of a python object.  But a Field
+     provides space for a title and a description. It can also
+     constrain its value and provide a validation method.  Besides
+     you can optionally specify characteristics such as its value
+     beeing readonly or not required.
+
+     Zope 3 schemas were born when Jim Fulton and Martijn Faassen
+     thought about Formulator for Zope3 and PropertySets while at
+     the Zope3 sprint at the Zope BBQ in Berlin. They realized that
+     if you strip all view logic from forms than you have something
+     to interfaces.  And thus schemas were born.
+
+Dependencies
+
+    The Schema package only depends on the Interface package. 
+
+Simple Usage
+
+    $ python
+    >>> class Bookmark:
+    ...     def __init__(self, url):
+    ...         self.url = url
+
+    >>> from Zope.Schema import TextLine, validateMapping
+    >>> from Interface import Interface
+    >>> class IBookmark(Interface):
+    ...     url = TextLine(title=u'url of the bookmark')
+    ...
+
+    >>> obj = Bookmark(u'zope website', u'http://www.zope.org',
+    ...                    keywords=('web', 'python'))
+    >>> validateMapping(IBookmark, obj.__dict__)
+
+    the last statement validates that our object conforms 
+    to the IBookmark Schema. 
+
+What is a schema, how does it compare to an interface?
+
+    A schema is an extended interface which defines Fields.  
+    You can validate that attributes of an object conform to their Fields 
+    defined on the schema.  With plain interfaces you can only validate 
+    that Methods conform to their interface specification.  
+
+    So interfaces and schemas refer to different aspects of an object 
+    (resp. its code and state). 
+
+    A Schema starts out like an interface but defines certain
+    Fields to which an object's attributes must conform. Let's look at 
+    a stripped down example from the programmer's tutorial (chapter two)::
+
+    from Interface import Interface
+    from Zope.Schema import Interface, Text, TextLine
+
+    class IContact(Interface):
+        "Provides access to basic contact information."
+
+        first = TextLine(title=u"First name")
+        last = TextLine(title=u"Last name")
+        email = TextLine(title=u"Electronic mail address")
+        address = Text(title=u"Postal address")
+        postalCode = TextLine(title=u"Postal code",
+                       constraint = re.compile("\d{5,5}(-\d{4,4})?$").match)
+
+    'TextLine' is a field and expresses that an attribute is a single line
+    of unicode text.  'Text' expresses an arbitrary unicode ("text") object. 
+    The most interesting part is the last attribute specification. It constrains
+    the 'postalCode' attribute to only have values that are US postal codes.
+      
+    Now we want a class that aheres to the IContact Schema:
+
+    class Contact(Persistence.Persistent):
+        __implements__ = IContact
+
+        def __init__(self, first, last, email, address, pc):
+            self.first = first
+            self.last = last
+            self.email = email
+            self.address = address
+            self.postalCode = pc
+
+    Now you can see if an instance of 'Contact' actually implements 
+    the schema:
+
+    from Zope.App.Schema import validateMapping
+    someone = contact('Tim','Roberts', 'tim@roberts', '','')
+    validateMapping(IContact, someone.__dict__)
+
+     
+Issues to be solved
+
+    These issues were written up at Rotterdam Sprint (12/4/2002). 
+ 
+ I18n 
+ 
+    How i18n interferes with Schemas is not thought out. In an non-english
+    context we probably want to have titles and descriptions easily 
+    translatable.  The best idea so far is to use an attribute name 
+    together with its surrounding namespace (Interface-name etc.) 
+    as the canonical unique id used for looking up translations. 
+
+    Example:
+  
+    class book(Interface):
+        author = ITextLine()
+     
+    To get to the And in view, while the widget or widget's messages
+    are constructed:
+
+    TranslatorService.getMessage('book.author.title','DE_DE')
+
+ Integration with Interfaces
+
+    How closely are Interfaces and Schema related?  Should they be refactored
+    into one package?  Currently the Interface package is outside the Zope
+    namespace and Schema is at Zope.Schema. Should this stay this way? Are
+    Schemas Zope-Specific? 
+
+    Shouldn't the 'implements' function on the interface package be
+    named setImplements as opposed to getImplements? This way if you write
+    
+      setImplements(klass, interface) 
+    
+    it is obvious what you mean (as opposed to implements(klass, interface)
+    which could mean *asking* if the klass implements the interface.
+
+ Clarify and clean up use cases
+
+    Some use cases are not easy to understand.  A lot of them look like 
+    features rather than use cases.  The list of schema use cases 
+    needs to be cleaned up and be (sometimes) more detailed.  
+
+References
+
+ Use case list, 
+   http://dev.zope.org/Wikis/DevSite/Projects/ComponentArchitecture/Zope3SchemasUseCases
+
+ documented interfaces, Zope/Schema/IField.py 
+ Jim Fulton's Programmers Tutorial, 
+ in CVS Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter2
+
+