[Zope3-checkins] CVS: Zope3/src/zope/schema - __init__.py:1.1.2.1 _bootstrapfields.py:1.1.2.1 _field.py:1.1.2.1 _schema.py:1.1.2.1 errornames.py:1.1.2.1 fieldproperty.py:1.1.2.1 interfaces.py:1.1.2.1 readme.stx:1.1.2.1
Jim Fulton
jim@zope.com
Mon, 23 Dec 2002 14:33:15 -0500
Update of /cvs-repository/Zope3/src/zope/schema
In directory cvs.zope.org:/tmp/cvs-serv19908/zope/schema
Added Files:
Tag: NameGeddon-branch
__init__.py _bootstrapfields.py _field.py _schema.py
errornames.py fieldproperty.py interfaces.py readme.stx
Log Message:
Initial renaming before debugging
=== Added File Zope3/src/zope/schema/__init__.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.
#
##############################################################################
"""Schema package constructor
$Id: __init__.py,v 1.1.2.1 2002/12/23 19:33:12 jim Exp $
"""
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
=== Added File Zope3/src/zope/schema/_bootstrapfields.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.
#
##############################################################################
"""
$Id: _bootstrapfields.py,v 1.1.2.1 2002/12/23 19:33:12 jim Exp $
"""
__metaclass__ = type
from zope.interface.element import Attribute
from zope.interface.implements import visitImplements
from zope.schema.interfaces import StopValidation, ValidationError
import zope.schema.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
=== Added File Zope3/src/zope/schema/_field.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.
#
##############################################################################
"""
$Id: _field.py,v 1.1.2.1 2002/12/23 19:33:12 jim Exp $
"""
__metaclass__ = type
from zope.interface.implements import implements
from zope.schema.interfaces import ValidationError
import zope.schema.errornames
import zope.schema.interfaces
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.IField['title'])
Field.description = FieldProperty(IField.IField['description'])
Field.required = FieldProperty(IField.IField['required'])
Field.readonly = FieldProperty(IField.IField['readonly'])
# Default is already taken care of
implements(Field, IField.IField)
implements(Container, IField.IContainer)
implements(Iterable, IField.IIterable)
implements(Orderable, IField.IOrderable)
MinMaxLen.min_length = FieldProperty(IField.IMinMaxLen['min_length'])
MinMaxLen.max_length = FieldProperty(IField.IMinMaxLen['max_length'])
implements(MinMaxLen, IField.IMinMaxLen)
implements(ValueSet, IField.IValueSet)
implements(Text, IField.IText)
implements(TextLine, IField.ITextLine)
implements(Bool, IField.IBool)
implements(Int, IField.IInt)
class Bytes(MinMaxLen, ValueSet):
__doc__ = IField.IBytes.__doc__
__implements__ = IField.IBytes
_type = str
class BytesLine(Bytes):
"""A Text field with no newlines."""
__implements__ = IField.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__ = IField.IFloat.__doc__
__implements__ = IField.IFloat
_type = float
class Datetime(ValueSet, Orderable):
__doc__ = IField.IDatetime.__doc__
__implements__ = IField.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__ = IField.ISequence.__doc__
value_types = FieldProperty(IField.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(ErrorNames.WrongContainedType, errors)
finally:
errors = None
class Tuple(Sequence):
"""A field representing a Tuple."""
__implements__ = IField.ITuple
_type = tuple
class List(Sequence):
"""A field representing a List."""
__implements__ = IField.IList
_type = list
class Dict(MinMaxLen, Iterable):
"""A field representing a Dict."""
__implements__ = IField.IDict
_type = dict
key_types = FieldProperty(IField.IDict['key_types'])
value_types = FieldProperty(IField.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(ErrorNames.WrongContainedType, errors)
finally:
errors = None
=== Added File Zope3/src/zope/schema/_schema.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.
#
##############################################################################
"""
$Id: _schema.py,v 1.1.2.1 2002/12/23 19:33:13 jim Exp $
"""
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
=== Added File Zope3/src/zope/schema/errornames.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.
#
##############################################################################
"""Common Schema Error Names
$Id: errornames.py,v 1.1.2.1 2002/12/23 19:33:13 jim Exp $
"""
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'
=== Added File Zope3/src/zope/schema/fieldproperty.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.
#
##############################################################################
"""
$Id: fieldproperty.py,v 1.1.2.1 2002/12/23 19:33:13 jim Exp $
"""
__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__
=== Added File Zope3/src/zope/schema/interfaces.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: interfaces.py,v 1.1.2.1 2002/12/23 19:33:13 jim Exp $
"""
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: interfaces.py,v 1.1.2.1 2002/12/23 19:33:13 jim Exp $
"""
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,
)
=== Added File Zope3/src/zope/schema/readme.stx ===
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