[Checkins] SVN: zope.schema/branches/tseaver-test_cleanup/ Move doctests to regular Sphinx documentation.
Tres Seaver
cvs-admin at zope.org
Fri Apr 20 19:20:54 UTC 2012
Log message for revision 125216:
Move doctests to regular Sphinx documentation.
You can still test the snippets by running 'make doctest' in the docs subdir.
Changed:
U zope.schema/branches/tseaver-test_cleanup/README.txt
A zope.schema/branches/tseaver-test_cleanup/docs/fields.rst
D zope.schema/branches/tseaver-test_cleanup/docs/index.rst
A zope.schema/branches/tseaver-test_cleanup/docs/index.rst
A zope.schema/branches/tseaver-test_cleanup/docs/narr.rst
A zope.schema/branches/tseaver-test_cleanup/docs/sources.rst
A zope.schema/branches/tseaver-test_cleanup/docs/validation.rst
U zope.schema/branches/tseaver-test_cleanup/setup.py
D zope.schema/branches/tseaver-test_cleanup/src/zope/schema/README.txt
D zope.schema/branches/tseaver-test_cleanup/src/zope/schema/fields.txt
D zope.schema/branches/tseaver-test_cleanup/src/zope/schema/index.txt
D zope.schema/branches/tseaver-test_cleanup/src/zope/schema/sources.txt
D zope.schema/branches/tseaver-test_cleanup/src/zope/schema/tests/test_docs.py
D zope.schema/branches/tseaver-test_cleanup/src/zope/schema/validation.txt
-=-
Modified: zope.schema/branches/tseaver-test_cleanup/README.txt
===================================================================
--- zope.schema/branches/tseaver-test_cleanup/README.txt 2012-04-20 19:20:46 UTC (rev 125215)
+++ zope.schema/branches/tseaver-test_cleanup/README.txt 2012-04-20 19:20:50 UTC (rev 125216)
@@ -11,4 +11,4 @@
specify characteristics such as its value being read-only or not
required.
-See 'src/zope/schema/README.txt' for more information.
+See 'docs/index.rst' for more information.
Copied: zope.schema/branches/tseaver-test_cleanup/docs/fields.rst (from rev 125215, zope.schema/branches/tseaver-test_cleanup/src/zope/schema/fields.txt)
===================================================================
--- zope.schema/branches/tseaver-test_cleanup/docs/fields.rst (rev 0)
+++ zope.schema/branches/tseaver-test_cleanup/docs/fields.rst 2012-04-20 19:20:50 UTC (rev 125216)
@@ -0,0 +1,171 @@
+======
+Fields
+======
+
+This document highlights unusual and subtle aspects of various fields and
+field classes, and is not intended to be a general introduction to schema
+fields. Please see README.txt for a more general introduction.
+
+While many field types, such as Int, TextLine, Text, and Bool are relatively
+straightforward, a few have some subtlety. We will explore the general
+class of collections and discuss how to create a custom creation field; discuss
+Choice fields, vocabularies, and their use with collections; and close with a
+look at the standard zope.app approach to using these fields to find views
+("widgets").
+
+Collections
+-----------
+
+Normal fields typically describe the API of the attribute -- does it behave as a
+Python Int, or a Float, or a Bool -- and various constraints to the model, such
+as a maximum or minimum value. Collection fields have additional requirements
+because they contain other types, which may also be described and constrained.
+
+For instance, imagine a list that contains non-negative floats and enforces
+uniqueness. In a schema, this might be written as follows:
+
+.. doctest::
+
+ >>> from zope.interface import Interface
+ >>> from zope.schema import List, Float
+ >>> from zope.schema._compat import u
+ >>> class IInventoryItem(Interface):
+ ... pricePoints = List(
+ ... title=u("Price Points"),
+ ... unique=True,
+ ... value_type=Float(title=u("Price"), min=0.0)
+ ... )
+
+This indicates several things.
+
+- pricePoints is an attribute of objects that implement IInventoryItem.
+- The contents of pricePoints can be accessed and manipulated via a Python list
+ API.
+- Each member of pricePoints must be a non-negative float.
+- Members cannot be duplicated within pricePoints: each must be must be unique.
+- The attribute and its contents have descriptive titles. Typically these
+ would be message ids.
+
+This declaration creates a field that implements a number of interfaces, among
+them these:
+
+.. doctest::
+
+ >>> from zope.schema.interfaces import IList, ISequence, ICollection
+ >>> IList.providedBy(IInventoryItem['pricePoints'])
+ True
+ >>> ISequence.providedBy(IInventoryItem['pricePoints'])
+ True
+ >>> ICollection.providedBy(IInventoryItem['pricePoints'])
+ True
+
+Creating a custom collection field
+----------------------------------
+
+Ideally, custom collection fields have interfaces that inherit appropriately
+from either zope.schema.interfaces.ISequence or
+zope.schema.interfaces.IUnorderedCollection. Most collection fields should be
+able to subclass zope.schema._field.AbstractCollection to get the necessary
+behavior. Notice the behavior of the Set field in zope.schema._field: this
+would also be necessary to implement a Bag.
+
+Choices and Vocabularies
+------------------------
+
+Choice fields are the schema way of spelling enumerated fields and more. By
+providing a dynamically generated vocabulary, the choices available to a
+choice field can be contextually calculated.
+
+Simple choices do not have to explicitly use vocabularies:
+
+.. doctest::
+
+ >>> from zope.schema import Choice
+ >>> f = Choice((640, 1028, 1600))
+ >>> f.validate(640)
+ >>> f.validate(960)
+ Traceback (most recent call last):
+ ...
+ ConstraintNotSatisfied: 960
+ >>> f.validate('bing')
+ Traceback (most recent call last):
+ ...
+ ConstraintNotSatisfied: bing
+
+More complex choices will want to use registered vocabularies. Vocabularies
+have a simple interface, as defined in
+zope.schema.interfaces.IBaseVocabulary. A vocabulary must minimally be able
+to determine whether it contains a value, to create a term object for a value,
+and to return a query interface (or None) to find items in itself. Term
+objects are an abstraction that wraps a vocabulary value.
+
+The Zope application server typically needs a fuller interface that provides
+"tokens" on its terms: ASCII values that have a one-to-one relationship to the
+values when the vocabulary is asked to "getTermByToken". If a vocabulary is
+small, it can also support the IIterableVocabulary interface.
+
+If a vocabulary has been registered, then the choice merely needs to pass the
+vocabulary identifier to the "vocabulary" argument of the choice during
+instantiation.
+
+A start to a vocabulary implementation that may do all you need for many simple
+tasks may be found in zope.schema.vocabulary.SimpleVocabulary. Because
+registered vocabularies are simply callables passed a context, many
+registered vocabularies can simply be functions that rely on SimpleVocabulary:
+
+.. doctest::
+
+ >>> from zope.schema.vocabulary import SimpleVocabulary
+ >>> def myDynamicVocabulary(context):
+ ... v = dynamic_context_calculation_that_returns_an_iterable(context)
+ ... return SimpleVocabulary.fromValues(v)
+ ...
+
+The vocabulary interface is simple enough that writing a custom vocabulary is
+not too difficult itself.
+
+See zope.schema.vocabulary.TreeVocabulary for another
+IBaseVocabulary supporting vocabulary that provides a nested, tree-like
+structure.
+
+Choices and Collections
+-----------------------
+
+Choices are a field type and can be used as a value_type for collections. Just
+as a collection of an "Int" value_type constrains members to integers, so a
+choice-based value type constrains members to choices within the Choice's
+vocabulary. Typically in the Zope application server widgets are found not
+only for the collection and the choice field but also for the vocabulary on
+which the choice is based.
+
+Using Choice and Collection Fields within a Widget Framework
+------------------------------------------------------------
+
+While fields support several use cases, including code documentation and data
+description and even casting, a significant use case influencing their design is
+to support form generation -- generating widgets for a field. Choice and
+collection fields are expected to be used within widget frameworks. The
+zope.app approach typically (but configurably) uses multiple dispatches to
+find widgets on the basis of various aspects of the fields.
+
+Widgets for all fields are found by looking up a browser view of the field
+providing an input or display widget view. Typically there is only a single
+"widget" registered for Choice fields. When it is looked up, it performs
+another dispatch -- another lookup -- for a widget registered for both the field
+and the vocabulary. This widget typically has enough information to render
+without a third dispatch.
+
+Collection fields may fire several dispatches. The first is the usual lookup
+by field. A single "widget" should be registered for ICollection, which does
+a second lookup by field and value_type constraint, if any, or, theoretically,
+if value_type is None, renders some absolutely generic collection widget that
+allows input of any value imaginable: a check-in of such a widget would be
+unexpected. This second lookup may find a widget that knows how to render,
+and stop. However, the value_type may be a choice, which will usually fire a
+third dispatch: a search for a browser widget for the collection field, the
+value_type field, and the vocabulary. Further lookups may even be configured
+on the basis of uniqueness and other constraints.
+
+This level of indirection may be unnecessary for some applications, and can be
+disabled with simple ZCML changes within `zope.app`.
+
Deleted: zope.schema/branches/tseaver-test_cleanup/docs/index.rst
===================================================================
--- zope.schema/branches/tseaver-test_cleanup/docs/index.rst 2012-04-20 19:20:46 UTC (rev 125215)
+++ zope.schema/branches/tseaver-test_cleanup/docs/index.rst 2012-04-20 19:20:50 UTC (rev 125216)
@@ -1,22 +0,0 @@
-.. zope.schema documentation master file, created by
- sphinx-quickstart on Fri Apr 20 15:00:47 2012.
- You can adapt this file completely to your liking, but it should at least
- contain the root `toctree` directive.
-
-Welcome to zope.schema's documentation!
-=======================================
-
-Contents:
-
-.. toctree::
- :maxdepth: 2
-
-
-
-Indices and tables
-==================
-
-* :ref:`genindex`
-* :ref:`modindex`
-* :ref:`search`
-
Copied: zope.schema/branches/tseaver-test_cleanup/docs/index.rst (from rev 125215, zope.schema/branches/tseaver-test_cleanup/src/zope/schema/index.txt)
===================================================================
--- zope.schema/branches/tseaver-test_cleanup/docs/index.rst (rev 0)
+++ zope.schema/branches/tseaver-test_cleanup/docs/index.rst 2012-04-20 19:20:50 UTC (rev 125216)
@@ -0,0 +1,19 @@
+Welcome to zope.schema's documentation!
+=======================================
+
+Contents:
+
+.. toctree::
+ :maxdepth: 2
+
+ narr
+ fields
+ sources
+ validation
+
+Indices and tables
+==================
+
+* :ref:`genindex`
+* :ref:`modindex`
+* :ref:`search`
Copied: zope.schema/branches/tseaver-test_cleanup/docs/narr.rst (from rev 125215, zope.schema/branches/tseaver-test_cleanup/src/zope/schema/README.txt)
===================================================================
--- zope.schema/branches/tseaver-test_cleanup/docs/narr.rst (rev 0)
+++ zope.schema/branches/tseaver-test_cleanup/docs/narr.rst 2012-04-20 19:20:50 UTC (rev 125216)
@@ -0,0 +1,298 @@
+==============
+Zope 3 Schemas
+==============
+
+Introduction
+------------
+
+*This package is intended to be independently reusable in any Python
+project. It is maintained by the* `Zope Toolkit project <http://docs.zope.org/zopetoolkit/>`_.
+
+Schemas extend the notion of interfaces to detailed descriptions of Attributes
+(but not 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 at least 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 being
+read-only or not required.
+
+Zope 3 schemas were born when Jim Fulton and Martijn Faassen thought
+about Formulator for Zope 3 and ``PropertySets`` while at the `Zope 3
+sprint`_ at the Zope BBQ in Berlin. They realized that if you strip
+all view logic from forms then you have something similar to interfaces. And
+thus schemas were born.
+
+.. _Zope 3 sprint: http://dev.zope.org/Zope3/ZopeBBQ2002Sprint
+
+.. contents::
+
+Simple Usage
+------------
+
+Let's have a look at a simple example. First we write an interface as usual,
+but instead of describing the attributes of the interface with ``Attribute``
+instances, we now use schema fields:
+
+.. doctest::
+
+ >>> import zope.interface
+ >>> import zope.schema
+ >>> from zope.schema._compat import u, b
+
+ >>> class IBookmark(zope.interface.Interface):
+ ... title = zope.schema.TextLine(
+ ... title=u('Title'),
+ ... description=u('The title of the bookmark'),
+ ... required=True)
+ ...
+ ... url = zope.schema.URI(
+ ... title=u('Bookmark URL'),
+ ... description=u('URL of the Bookmark'),
+ ... required=True)
+ ...
+
+Now we create a class that implements this interface and create an instance of
+it:
+
+.. doctest::
+
+ >>> @zope.interface.implementer(IBookmark)
+ ... class Bookmark(object):
+ ...
+ ... title = None
+ ... url = None
+
+ >>> bm = Bookmark()
+
+We would now like to only add validated values to the class. This can be done
+by first validating and then setting the value on the object. The first step
+is to define some data:
+
+.. doctest::
+
+ >>> title = u('Zope 3 Website')
+ >>> url = b('http://dev.zope.org/Zope3')
+
+Now we, get the fields from the interface:
+
+.. doctest::
+
+ >>> title_field = IBookmark.get('title')
+ >>> url_field = IBookmark.get('url')
+
+Next we have to bind these fields to the context, so that instance-specific
+information can be used for validation:
+
+.. doctest::
+
+ >>> title_bound = title_field.bind(bm)
+ >>> url_bound = url_field.bind(bm)
+
+Now that the fields are bound, we can finally validate the data:
+
+.. doctest::
+
+ >>> title_bound.validate(title)
+ >>> url_bound.validate(url)
+
+If the validation is successful, ``None`` is returned. If a validation error
+occurs a ``ValidationError`` will be raised; for example:
+
+.. doctest::
+
+ >>> url_bound.validate(u('http://zope.org/foo'))
+ Traceback (most recent call last):
+ ...
+ WrongType: (u'http://zope.org/foo', <type 'str'>, 'url')
+
+ >>> url_bound.validate(b('foo.bar'))
+ Traceback (most recent call last):
+ ...
+ InvalidURI: foo.bar
+
+Now that the data has been successfully validated, we can set it on the
+object:
+
+.. doctest::
+
+ >>> title_bound.set(bm, title)
+ >>> url_bound.set(bm, url)
+
+That's it. You still might think this is a lot of work to validate and set a
+value for an object. Note, however, that it is very easy to write helper
+functions that automate these tasks. If correctly designed, you will never
+have to worry explicitly about validation again, since the system takes care
+of it automatically.
+
+
+What is a schema, how does it compare to an interface?
+------------------------------------------------------
+
+A schema is an extended interface which defines fields. You can validate that
+the 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
+(respectively 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:
+
+.. doctest::
+
+ >>> import re
+
+ >>> class IContact(zope.interface.Interface):
+ ... """Provides access to basic contact information."""
+ ...
+ ... first = zope.schema.TextLine(title=u("First name"))
+ ...
+ ... last = zope.schema.TextLine(title=u("Last name"))
+ ...
+ ... email = zope.schema.TextLine(title=u("Electronic mail address"))
+ ...
+ ... address = zope.schema.Text(title=u("Postal address"))
+ ...
+ ... postalCode = zope.schema.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 adheres to the ``IContact`` schema:
+
+.. doctest::
+
+ >>> class Contact(object):
+ ... zope.interface.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:
+
+.. doctest::
+
+ >>> someone = Contact(u('Tim'), u('Roberts'), u('tim at roberts'), u(''),
+ ... u('12032-3492'))
+
+ >>> for field in zope.schema.getFields(IContact).values():
+ ... bound = field.bind(someone)
+ ... bound.validate(bound.get(someone))
+
+
+Data Modeling Concepts
+-----------------------
+
+The ``zope.schema`` package provides a core set of field types,
+including single- and multi-line text fields, binary data fields,
+integers, floating-point numbers, and date/time values.
+
+Selection issues; field type can specify:
+
+- "Raw" data value
+
+ Simple values not constrained by a selection list.
+
+- Value from enumeration (options provided by schema)
+
+ This models a single selection from a list of possible values
+ specified by the schema. The selection list is expected to be the
+ same for all values of the type. Changes to the list are driven by
+ schema evolution.
+
+ This is done by mixing-in the ``IEnumerated`` interface into the field
+ type, and the Enumerated mix-in for the implementation (or emulating
+ it in a concrete class).
+
+- Value from selection list (options provided by an object)
+
+ This models a single selection from a list of possible values
+ specified by a source outside the schema. The selection list
+ depends entirely on the source of the list, and may vary over time
+ and from object to object. Changes to the list are not related to
+ the schema, but changing how the list is determined is based on
+ schema evolution.
+
+ There is not currently a spelling of this, but it could be
+ facilitated using alternate mix-ins similar to IEnumerated and
+ Enumerated.
+
+- Whether or not the field is read-only
+
+ If a field value is read-only, it cannot be changed once the object is
+ created.
+
+- Whether or not the field is required
+
+ If a field is designated as required, assigned field values must always
+ be non-missing. See the next section for a description of missing values.
+
+- A value designated as ``missing``
+
+ Missing values, when assigned to an object, indicate that there is 'no
+ data' for that field. Missing values are analogous to null values in
+ relational databases. For example, a boolean value can be True, False, or
+ missing, in which case its value is unknown.
+
+ While Python's None is the most likely value to signify 'missing', some
+ fields may use different values. For example, it is common for text fields
+ to use the empty string ('') to signify that a value is missing. Numeric
+ fields may use 0 or -1 instead of None as their missing value.
+
+ A field that is 'required' signifies that missing values are invalid and
+ should not be assigned.
+
+- A default value
+
+ Default field values are assigned to objects when they are first created. A
+ default factory can be specified to dynamically compute default values.
+
+
+Fields and Widgets
+------------------
+
+Widgets are components that display field values and, in the case of
+writable fields, allow the user to edit those values.
+
+Widgets:
+
+- Display current field values, either in a read-only format, or in a
+ format that lets the user change the field value.
+
+- Update their corresponding field values based on values provided by users.
+
+- Manage the relationships between their representation of a field value
+ and the object's field value. For example, a widget responsible for
+ editing a number will likely represent that number internally as a string.
+ For this reason, widgets must be able to convert between the two value
+ formats. In the case of the number-editing widget, string values typed
+ by the user need to be converted to numbers such as int or float.
+
+- Support the ability to assign a missing value to a field. For example,
+ a widget may present a ``None`` option for selection that, when selected,
+ indicates that the object should be updated with the field's ``missing``
+ value.
+
+
+
+References
+----------
+
+- Use case list, http://dev.zope.org/Zope3/Zope3SchemasUseCases
+
+- Documented interfaces, zope/schema/interfaces.py
+
+- Jim Fulton's Programmers Tutorial; in CVS:
+ Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter2
Copied: zope.schema/branches/tseaver-test_cleanup/docs/sources.rst (from rev 125215, zope.schema/branches/tseaver-test_cleanup/src/zope/schema/sources.txt)
===================================================================
--- zope.schema/branches/tseaver-test_cleanup/docs/sources.rst (rev 0)
+++ zope.schema/branches/tseaver-test_cleanup/docs/sources.rst 2012-04-20 19:20:50 UTC (rev 125216)
@@ -0,0 +1,107 @@
+=======
+Sources
+=======
+
+Concepts
+--------
+
+Sources are designed with three concepts:
+
+- The source itself - an iterable
+
+ This can return any kind of object it wants. It doesn't have to care
+ for browser representation, encoding, ...
+
+- A way to map a value from the iterable to something that can be used
+ for form *values* - this is called a token. A token is commonly a
+ (unique) 7bit representation of the value.
+
+- A way to map a value to something that can be displayed to the user -
+ this is called a title
+
+The last two elements are dispatched using a so called `term`. The
+ITitledTokenizedTerm interface contains a triple of (value, token, title).
+
+Additionally there are some lookup functions to perform the mapping
+between values and terms and tokens and terms.
+
+Sources that require context use a special factory: a context source
+binder that is called with the context and instanciates the source when
+it is actually used.
+
+Sources in Fields
+-----------------
+
+A choice field can be constructed with a source or source name. When a source
+is used, it will be used as the source for valid values.
+
+Create a source for all odd numbers.
+
+.. doctest::
+
+ >>> from zope import interface
+ >>> from zope.schema.interfaces import ISource, IContextSourceBinder
+ >>> @interface.implementer(ISource)
+ ... class MySource(object):
+ ... divisor = 2
+ ... def __contains__(self, value):
+ ... return bool(value % self.divisor)
+ >>> my_source = MySource()
+ >>> 1 in my_source
+ True
+ >>> 2 in my_source
+ False
+
+ >>> from zope.schema import Choice
+ >>> choice = Choice(__name__='number', source=my_source)
+ >>> bound = choice.bind(object())
+ >>> bound.vocabulary
+ <...MySource...>
+
+If a IContextSourceBinder is passed as the `source` argument to Choice, it's
+`bind` method will be called with the context as its only argument. The
+result must implement ISource and will be used as the source.
+
+.. doctest::
+
+ >>> _my_binder_called = []
+ >>> def my_binder(context):
+ ... _my_binder_called.append(context)
+ ... source = MySource()
+ ... source.divisor = context.divisor
+ ... return source
+ >>> interface.directlyProvides(my_binder, IContextSourceBinder)
+
+ >>> class Context(object):
+ ... divisor = 3
+
+ >>> choice = Choice(__name__='number', source=my_binder)
+ >>> bound = choice.bind(Context())
+ >>> len(_my_binder_called)
+ 1
+ >>> bound.vocabulary
+ <...MySource...>
+ >>> bound.vocabulary.divisor
+ 3
+
+When using IContextSourceBinder together with default value, it's
+impossible to validate it on field initialization. Let's check if
+initalization doesn't fail in that case.
+
+.. doctest::
+
+ >>> choice = Choice(__name__='number', source=my_binder, default=2)
+
+ >>> del _my_binder_called[:]
+ >>> bound = choice.bind(Context())
+ >>> len(_my_binder_called)
+ 1
+
+ >>> bound.validate(bound.default)
+ >>> bound.validate(3)
+ Traceback (most recent call last):
+ ...
+ ConstraintNotSatisfied: 3
+
+It's developer's responsibility to provide a default value that fits the
+constraints when using context-based sources.
Copied: zope.schema/branches/tseaver-test_cleanup/docs/validation.rst (from rev 125215, zope.schema/branches/tseaver-test_cleanup/src/zope/schema/validation.txt)
===================================================================
--- zope.schema/branches/tseaver-test_cleanup/docs/validation.rst (rev 0)
+++ zope.schema/branches/tseaver-test_cleanup/docs/validation.rst 2012-04-20 19:20:50 UTC (rev 125216)
@@ -0,0 +1,217 @@
+=================
+Schema Validation
+=================
+
+There are two helper methods to verify schemas and interfaces:
+
+getValidationErrors
+ first validates via the zope.schema field validators. If that succeeds the
+ invariants are checked.
+getSchemaValidationErrors
+ *only* validateds via the zope.schema field validators. The invariants are
+ *not* checked.
+
+
+Create an interface to validate against:
+
+.. doctest::
+
+ >>> import zope.interface
+ >>> import zope.schema
+ >>> _a_greater_b_called = []
+ >>> class ITwoInts(zope.interface.Interface):
+ ... a = zope.schema.Int(max=10)
+ ... b = zope.schema.Int(min=5)
+ ...
+ ... @zope.interface.invariant
+ ... def a_greater_b(obj):
+ ... _a_greater_b_called.append(obj)
+ ... if obj.a <= obj.b:
+ ... raise zope.interface.Invalid("%s<=%s" % (obj.a, obj.b))
+ ...
+
+Create a silly model:
+
+.. doctest::
+
+ >>> class TwoInts(object):
+ ... pass
+
+Create an instance of TwoInts but do not set attributes. We get two errors:
+
+.. doctest::
+
+ >>> ti = TwoInts()
+ >>> r = zope.schema.getValidationErrors(ITwoInts, ti)
+ >>> r.sort()
+ >>> len(r)
+ 2
+ >>> r[0][0]
+ 'a'
+ >>> r[0][1].__class__.__name__
+ 'SchemaNotFullyImplemented'
+ >>> r[0][1].args[0].args
+ ("'TwoInts' object has no attribute 'a'",)
+ >>> r[1][0]
+ 'b'
+ >>> r[1][1].__class__.__name__
+ 'SchemaNotFullyImplemented'
+ >>> r[1][1].args[0].args
+ ("'TwoInts' object has no attribute 'b'",)
+
+The `getSchemaValidationErrors` function returns the same result:
+
+.. doctest::
+
+ >>> r = zope.schema.getSchemaValidationErrors(ITwoInts, ti)
+ >>> r.sort()
+ >>> len(r)
+ 2
+ >>> r[0][0]
+ 'a'
+ >>> r[0][1].__class__.__name__
+ 'SchemaNotFullyImplemented'
+ >>> r[0][1].args[0].args
+ ("'TwoInts' object has no attribute 'a'",)
+ >>> r[1][0]
+ 'b'
+ >>> r[1][1].__class__.__name__
+ 'SchemaNotFullyImplemented'
+ >>> r[1][1].args[0].args
+ ("'TwoInts' object has no attribute 'b'",)
+
+Note that see no error from the invariant because the invariants are not
+vaildated if there are other schema errors.
+
+When we set a valid value for `a` we still get the same error for `b`:
+
+.. doctest::
+
+ >>> ti.a = 11
+ >>> errors = zope.schema.getValidationErrors(ITwoInts, ti)
+ >>> errors.sort()
+ >>> len(errors)
+ 2
+ >>> errors[0][0]
+ 'a'
+ >>> errors[0][1].doc()
+ u'Value is too big'
+ >>> errors[0][1].__class__.__name__
+ 'TooBig'
+ >>> errors[0][1].args
+ (11, 10)
+ >>> errors[1][0]
+ 'b'
+ >>> errors[1][1].__class__.__name__
+ 'SchemaNotFullyImplemented'
+ >>> errors[1][1].args[0].args
+ ("'TwoInts' object has no attribute 'b'",)
+
+
+After setting a valid value for `a` there is only the error for the missing `b`
+left:
+
+.. doctest::
+
+ >>> ti.a = 8
+ >>> r = zope.schema.getValidationErrors(ITwoInts, ti)
+ >>> r
+ [('b', SchemaNotFullyImplemented(...AttributeError...))]
+ >>> r[0][1].args[0].args
+ ("'TwoInts' object has no attribute 'b'",)
+
+
+After setting valid value for `b` the schema is valid so the invariants are
+checked. As `b>a` the invariant fails:
+
+.. doctest::
+
+ >>> ti.b = 10
+ >>> errors = zope.schema.getValidationErrors(ITwoInts, ti)
+ >>> len(errors)
+ 1
+ >>> errors[0][0] is None
+ True
+ >>> errors[0][1].__class__.__name__
+ 'Invalid'
+ >>> len(_a_greater_b_called)
+ 1
+
+
+When using `getSchemaValidationErrors` we do not get an error any more:
+
+.. doctest::
+
+ >>> zope.schema.getSchemaValidationErrors(ITwoInts, ti)
+ []
+
+
+Set `b=5` so everything is fine:
+
+.. doctest::
+
+ >>> ti.b = 5
+ >>> del _a_greater_b_called[:]
+ >>> zope.schema.getValidationErrors(ITwoInts, ti)
+ []
+ >>> len(_a_greater_b_called)
+ 1
+
+
+Compare ValidationError
+-----------------------
+
+There was an issue with compare validation error with somthing else then an
+exceptions. Let's test if we can compare ValidationErrors with different things
+
+.. doctest::
+
+ >>> from zope.schema._bootstrapinterfaces import ValidationError
+ >>> v1 = ValidationError('one')
+ >>> v2 = ValidationError('one')
+ >>> v3 = ValidationError('another one')
+
+A ValidationError with the same arguments compares:
+
+.. doctest::
+
+ >>> v1 == v2
+ True
+
+but not with an error with different arguments:
+
+.. doctest::
+
+ >>> v1 == v3
+ False
+
+We can also compare validation erros with other things then errors. This
+was running into an AttributeError in previous versions of zope.schema. e.g.
+AttributeError: 'NoneType' object has no attribute 'args'
+
+.. doctest::
+
+ >>> v1 == None
+ False
+ >>> v1 == object()
+ False
+ >>> v1 == False
+ False
+ >>> v1 == True
+ False
+ >>> v1 == 0
+ False
+ >>> v1 == 1
+ False
+ >>> v1 == int
+ False
+
+If we compare a ValidationError with another validation error based class,
+we will get the following result:
+
+.. doctest::
+
+ >>> from zope.schema._bootstrapinterfaces import RequiredMissing
+ >>> r1 = RequiredMissing('one')
+ >>> v1 == r1
+ True
Modified: zope.schema/branches/tseaver-test_cleanup/setup.py
===================================================================
--- zope.schema/branches/tseaver-test_cleanup/setup.py 2012-04-20 19:20:46 UTC (rev 125215)
+++ zope.schema/branches/tseaver-test_cleanup/setup.py 2012-04-20 19:20:50 UTC (rev 125216)
@@ -79,15 +79,7 @@
description='zope.interface extension for defining data schemas',
author='Zope Foundation and Contributors',
author_email='zope-dev at zope.org',
- long_description=(read('src', 'zope', 'schema', 'README.txt')
- + '\n\n' +
- read('src', 'zope', 'schema', 'fields.txt')
- + '\n\n' +
- read('src', 'zope', 'schema', 'sources.txt')
- + '\n\n' +
- read('src', 'zope', 'schema', 'validation.txt')
- + '\n\n' +
- read('CHANGES.txt')),
+ long_description=(read('README.txt') + '\n\n' + read('CHANGES.txt')),
packages=find_packages('src'),
package_dir = {'': 'src'},
namespace_packages=['zope',],
Deleted: zope.schema/branches/tseaver-test_cleanup/src/zope/schema/README.txt
===================================================================
--- zope.schema/branches/tseaver-test_cleanup/src/zope/schema/README.txt 2012-04-20 19:20:46 UTC (rev 125215)
+++ zope.schema/branches/tseaver-test_cleanup/src/zope/schema/README.txt 2012-04-20 19:20:50 UTC (rev 125216)
@@ -1,276 +0,0 @@
-==============
-Zope 3 Schemas
-==============
-
-Introduction
-------------
-
-*This package is intended to be independently reusable in any Python
-project. It is maintained by the* `Zope Toolkit project <http://docs.zope.org/zopetoolkit/>`_.
-
-Schemas extend the notion of interfaces to detailed descriptions of Attributes
-(but not 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 at least 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 being
-read-only or not required.
-
-Zope 3 schemas were born when Jim Fulton and Martijn Faassen thought
-about Formulator for Zope 3 and ``PropertySets`` while at the `Zope 3
-sprint`_ at the Zope BBQ in Berlin. They realized that if you strip
-all view logic from forms then you have something similar to interfaces. And
-thus schemas were born.
-
-.. _Zope 3 sprint: http://dev.zope.org/Zope3/ZopeBBQ2002Sprint
-
-.. contents::
-
-Simple Usage
-------------
-
-Let's have a look at a simple example. First we write an interface as usual,
-but instead of describing the attributes of the interface with ``Attribute``
-instances, we now use schema fields:
-
- >>> import zope.interface
- >>> import zope.schema
- >>> from zope.schema._compat import u, b
-
- >>> class IBookmark(zope.interface.Interface):
- ... title = zope.schema.TextLine(
- ... title=u('Title'),
- ... description=u('The title of the bookmark'),
- ... required=True)
- ...
- ... url = zope.schema.URI(
- ... title=u('Bookmark URL'),
- ... description=u('URL of the Bookmark'),
- ... required=True)
- ...
-
-Now we create a class that implements this interface and create an instance of
-it:
-
- >>> @zope.interface.implementer(IBookmark)
- ... class Bookmark(object):
- ...
- ... title = None
- ... url = None
-
- >>> bm = Bookmark()
-
-We would now like to only add validated values to the class. This can be done
-by first validating and then setting the value on the object. The first step
-is to define some data:
-
- >>> title = u('Zope 3 Website')
- >>> url = b('http://dev.zope.org/Zope3')
-
-Now we, get the fields from the interface:
-
- >>> title_field = IBookmark.get('title')
- >>> url_field = IBookmark.get('url')
-
-Next we have to bind these fields to the context, so that instance-specific
-information can be used for validation:
-
- >>> title_bound = title_field.bind(bm)
- >>> url_bound = url_field.bind(bm)
-
-Now that the fields are bound, we can finally validate the data:
-
- >>> title_bound.validate(title)
- >>> url_bound.validate(url)
-
-If the validation is successful, ``None`` is returned. If a validation error
-occurs a ``ValidationError`` will be raised; for example:
-
- >>> url_bound.validate(u('http://zope.org/foo'))
- Traceback (most recent call last):
- ...
- WrongType: (u'http://zope.org/foo', <type 'str'>, 'url')
-
- >>> url_bound.validate(b('foo.bar'))
- Traceback (most recent call last):
- ...
- InvalidURI: foo.bar
-
-Now that the data has been successfully validated, we can set it on the
-object:
-
- >>> title_bound.set(bm, title)
- >>> url_bound.set(bm, url)
-
-That's it. You still might think this is a lot of work to validate and set a
-value for an object. Note, however, that it is very easy to write helper
-functions that automate these tasks. If correctly designed, you will never
-have to worry explicitly about validation again, since the system takes care
-of it automatically.
-
-
-What is a schema, how does it compare to an interface?
-------------------------------------------------------
-
-A schema is an extended interface which defines fields. You can validate that
-the 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
-(respectively 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:
-
- >>> import re
-
- >>> class IContact(zope.interface.Interface):
- ... """Provides access to basic contact information."""
- ...
- ... first = zope.schema.TextLine(title=u("First name"))
- ...
- ... last = zope.schema.TextLine(title=u("Last name"))
- ...
- ... email = zope.schema.TextLine(title=u("Electronic mail address"))
- ...
- ... address = zope.schema.Text(title=u("Postal address"))
- ...
- ... postalCode = zope.schema.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 adheres to the ``IContact`` schema:
-
- >>> class Contact(object):
- ... zope.interface.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:
-
- >>> someone = Contact(u('Tim'), u('Roberts'), u('tim at roberts'), u(''),
- ... u('12032-3492'))
-
- >>> for field in zope.schema.getFields(IContact).values():
- ... bound = field.bind(someone)
- ... bound.validate(bound.get(someone))
-
-
-Data Modeling Concepts
------------------------
-
-The ``zope.schema`` package provides a core set of field types,
-including single- and multi-line text fields, binary data fields,
-integers, floating-point numbers, and date/time values.
-
-Selection issues; field type can specify:
-
-- "Raw" data value
-
- Simple values not constrained by a selection list.
-
-- Value from enumeration (options provided by schema)
-
- This models a single selection from a list of possible values
- specified by the schema. The selection list is expected to be the
- same for all values of the type. Changes to the list are driven by
- schema evolution.
-
- This is done by mixing-in the ``IEnumerated`` interface into the field
- type, and the Enumerated mix-in for the implementation (or emulating
- it in a concrete class).
-
-- Value from selection list (options provided by an object)
-
- This models a single selection from a list of possible values
- specified by a source outside the schema. The selection list
- depends entirely on the source of the list, and may vary over time
- and from object to object. Changes to the list are not related to
- the schema, but changing how the list is determined is based on
- schema evolution.
-
- There is not currently a spelling of this, but it could be
- facilitated using alternate mix-ins similar to IEnumerated and
- Enumerated.
-
-- Whether or not the field is read-only
-
- If a field value is read-only, it cannot be changed once the object is
- created.
-
-- Whether or not the field is required
-
- If a field is designated as required, assigned field values must always
- be non-missing. See the next section for a description of missing values.
-
-- A value designated as ``missing``
-
- Missing values, when assigned to an object, indicate that there is 'no
- data' for that field. Missing values are analogous to null values in
- relational databases. For example, a boolean value can be True, False, or
- missing, in which case its value is unknown.
-
- While Python's None is the most likely value to signify 'missing', some
- fields may use different values. For example, it is common for text fields
- to use the empty string ('') to signify that a value is missing. Numeric
- fields may use 0 or -1 instead of None as their missing value.
-
- A field that is 'required' signifies that missing values are invalid and
- should not be assigned.
-
-- A default value
-
- Default field values are assigned to objects when they are first created. A
- default factory can be specified to dynamically compute default values.
-
-
-Fields and Widgets
-------------------
-
-Widgets are components that display field values and, in the case of
-writable fields, allow the user to edit those values.
-
-Widgets:
-
-- Display current field values, either in a read-only format, or in a
- format that lets the user change the field value.
-
-- Update their corresponding field values based on values provided by users.
-
-- Manage the relationships between their representation of a field value
- and the object's field value. For example, a widget responsible for
- editing a number will likely represent that number internally as a string.
- For this reason, widgets must be able to convert between the two value
- formats. In the case of the number-editing widget, string values typed
- by the user need to be converted to numbers such as int or float.
-
-- Support the ability to assign a missing value to a field. For example,
- a widget may present a ``None`` option for selection that, when selected,
- indicates that the object should be updated with the field's ``missing``
- value.
-
-
-
-References
-----------
-
-- Use case list, http://dev.zope.org/Zope3/Zope3SchemasUseCases
-
-- Documented interfaces, zope/schema/interfaces.py
-
-- Jim Fulton's Programmers Tutorial; in CVS:
- Docs/ZopeComponentArchitecture/PythonProgrammerTutorial/Chapter2
Deleted: zope.schema/branches/tseaver-test_cleanup/src/zope/schema/fields.txt
===================================================================
--- zope.schema/branches/tseaver-test_cleanup/src/zope/schema/fields.txt 2012-04-20 19:20:46 UTC (rev 125215)
+++ zope.schema/branches/tseaver-test_cleanup/src/zope/schema/fields.txt 2012-04-20 19:20:50 UTC (rev 125216)
@@ -1,163 +0,0 @@
-======
-Fields
-======
-
-This document highlights unusual and subtle aspects of various fields and
-field classes, and is not intended to be a general introduction to schema
-fields. Please see README.txt for a more general introduction.
-
-While many field types, such as Int, TextLine, Text, and Bool are relatively
-straightforward, a few have some subtlety. We will explore the general
-class of collections and discuss how to create a custom creation field; discuss
-Choice fields, vocabularies, and their use with collections; and close with a
-look at the standard zope.app approach to using these fields to find views
-("widgets").
-
-Collections
------------
-
-Normal fields typically describe the API of the attribute -- does it behave as a
-Python Int, or a Float, or a Bool -- and various constraints to the model, such
-as a maximum or minimum value. Collection fields have additional requirements
-because they contain other types, which may also be described and constrained.
-
-For instance, imagine a list that contains non-negative floats and enforces
-uniqueness. In a schema, this might be written as follows:
-
- >>> from zope.interface import Interface
- >>> from zope.schema import List, Float
- >>> from zope.schema._compat import u
- >>> class IInventoryItem(Interface):
- ... pricePoints = List(
- ... title=u("Price Points"),
- ... unique=True,
- ... value_type=Float(title=u("Price"), min=0.0)
- ... )
-
-This indicates several things.
-
-- pricePoints is an attribute of objects that implement IInventoryItem.
-- The contents of pricePoints can be accessed and manipulated via a Python list
- API.
-- Each member of pricePoints must be a non-negative float.
-- Members cannot be duplicated within pricePoints: each must be must be unique.
-- The attribute and its contents have descriptive titles. Typically these
- would be message ids.
-
-This declaration creates a field that implements a number of interfaces, among
-them these:
-
- >>> from zope.schema.interfaces import IList, ISequence, ICollection
- >>> IList.providedBy(IInventoryItem['pricePoints'])
- True
- >>> ISequence.providedBy(IInventoryItem['pricePoints'])
- True
- >>> ICollection.providedBy(IInventoryItem['pricePoints'])
- True
-
-Creating a custom collection field
-----------------------------------
-
-Ideally, custom collection fields have interfaces that inherit appropriately
-from either zope.schema.interfaces.ISequence or
-zope.schema.interfaces.IUnorderedCollection. Most collection fields should be
-able to subclass zope.schema._field.AbstractCollection to get the necessary
-behavior. Notice the behavior of the Set field in zope.schema._field: this
-would also be necessary to implement a Bag.
-
-Choices and Vocabularies
-------------------------
-
-Choice fields are the schema way of spelling enumerated fields and more. By
-providing a dynamically generated vocabulary, the choices available to a
-choice field can be contextually calculated.
-
-Simple choices do not have to explicitly use vocabularies:
-
- >>> from zope.schema import Choice
- >>> f = Choice((640, 1028, 1600))
- >>> f.validate(640)
- >>> f.validate(960)
- Traceback (most recent call last):
- ...
- ConstraintNotSatisfied: 960
- >>> f.validate('bing')
- Traceback (most recent call last):
- ...
- ConstraintNotSatisfied: bing
-
-More complex choices will want to use registered vocabularies. Vocabularies
-have a simple interface, as defined in
-zope.schema.interfaces.IBaseVocabulary. A vocabulary must minimally be able
-to determine whether it contains a value, to create a term object for a value,
-and to return a query interface (or None) to find items in itself. Term
-objects are an abstraction that wraps a vocabulary value.
-
-The Zope application server typically needs a fuller interface that provides
-"tokens" on its terms: ASCII values that have a one-to-one relationship to the
-values when the vocabulary is asked to "getTermByToken". If a vocabulary is
-small, it can also support the IIterableVocabulary interface.
-
-If a vocabulary has been registered, then the choice merely needs to pass the
-vocabulary identifier to the "vocabulary" argument of the choice during
-instantiation.
-
-A start to a vocabulary implementation that may do all you need for many simple
-tasks may be found in zope.schema.vocabulary.SimpleVocabulary. Because
-registered vocabularies are simply callables passed a context, many
-registered vocabularies can simply be functions that rely on SimpleVocabulary:
-
- >>> from zope.schema.vocabulary import SimpleVocabulary
- >>> def myDynamicVocabulary(context):
- ... v = dynamic_context_calculation_that_returns_an_iterable(context)
- ... return SimpleVocabulary.fromValues(v)
- ...
-
-The vocabulary interface is simple enough that writing a custom vocabulary is
-not too difficult itself.
-
-See zope.schema.vocabulary.TreeVocabulary for another
-IBaseVocabulary supporting vocabulary that provides a nested, tree-like
-structure.
-
-Choices and Collections
------------------------
-
-Choices are a field type and can be used as a value_type for collections. Just
-as a collection of an "Int" value_type constrains members to integers, so a
-choice-based value type constrains members to choices within the Choice's
-vocabulary. Typically in the Zope application server widgets are found not
-only for the collection and the choice field but also for the vocabulary on
-which the choice is based.
-
-Using Choice and Collection Fields within a Widget Framework
-------------------------------------------------------------
-
-While fields support several use cases, including code documentation and data
-description and even casting, a significant use case influencing their design is
-to support form generation -- generating widgets for a field. Choice and
-collection fields are expected to be used within widget frameworks. The
-zope.app approach typically (but configurably) uses multiple dispatches to
-find widgets on the basis of various aspects of the fields.
-
-Widgets for all fields are found by looking up a browser view of the field
-providing an input or display widget view. Typically there is only a single
-"widget" registered for Choice fields. When it is looked up, it performs
-another dispatch -- another lookup -- for a widget registered for both the field
-and the vocabulary. This widget typically has enough information to render
-without a third dispatch.
-
-Collection fields may fire several dispatches. The first is the usual lookup
-by field. A single "widget" should be registered for ICollection, which does
-a second lookup by field and value_type constraint, if any, or, theoretically,
-if value_type is None, renders some absolutely generic collection widget that
-allows input of any value imaginable: a check-in of such a widget would be
-unexpected. This second lookup may find a widget that knows how to render,
-and stop. However, the value_type may be a choice, which will usually fire a
-third dispatch: a search for a browser widget for the collection field, the
-value_type field, and the vocabulary. Further lookups may even be configured
-on the basis of uniqueness and other constraints.
-
-This level of indirection may be unnecessary for some applications, and can be
-disabled with simple ZCML changes within `zope.app`.
-
Deleted: zope.schema/branches/tseaver-test_cleanup/src/zope/schema/index.txt
===================================================================
--- zope.schema/branches/tseaver-test_cleanup/src/zope/schema/index.txt 2012-04-20 19:20:46 UTC (rev 125215)
+++ zope.schema/branches/tseaver-test_cleanup/src/zope/schema/index.txt 2012-04-20 19:20:50 UTC (rev 125216)
@@ -1,19 +0,0 @@
-Welcome to zope.schema's documentation!
-=======================================
-
-Contents:
-
-.. toctree::
- :maxdepth: 2
-
- README
- fields
- sources
- validation
-
-Indices and tables
-==================
-
-* :ref:`genindex`
-* :ref:`modindex`
-* :ref:`search`
Deleted: zope.schema/branches/tseaver-test_cleanup/src/zope/schema/sources.txt
===================================================================
--- zope.schema/branches/tseaver-test_cleanup/src/zope/schema/sources.txt 2012-04-20 19:20:46 UTC (rev 125215)
+++ zope.schema/branches/tseaver-test_cleanup/src/zope/schema/sources.txt 2012-04-20 19:20:50 UTC (rev 125216)
@@ -1,101 +0,0 @@
-=======
-Sources
-=======
-
-Concepts
---------
-
-Sources are designed with three concepts:
-
-- The source itself - an iterable
-
- This can return any kind of object it wants. It doesn't have to care
- for browser representation, encoding, ...
-
-- A way to map a value from the iterable to something that can be used
- for form *values* - this is called a token. A token is commonly a
- (unique) 7bit representation of the value.
-
-- A way to map a value to something that can be displayed to the user -
- this is called a title
-
-The last two elements are dispatched using a so called `term`. The
-ITitledTokenizedTerm interface contains a triple of (value, token, title).
-
-Additionally there are some lookup functions to perform the mapping
-between values and terms and tokens and terms.
-
-Sources that require context use a special factory: a context source
-binder that is called with the context and instanciates the source when
-it is actually used.
-
-Sources in Fields
------------------
-
-A choice field can be constructed with a source or source name. When a source
-is used, it will be used as the source for valid values.
-
-Create a source for all odd numbers.
-
- >>> from zope import interface
- >>> from zope.schema.interfaces import ISource, IContextSourceBinder
- >>> @interface.implementer(ISource)
- ... class MySource(object):
- ... divisor = 2
- ... def __contains__(self, value):
- ... return bool(value % self.divisor)
- >>> my_source = MySource()
- >>> 1 in my_source
- True
- >>> 2 in my_source
- False
-
- >>> from zope.schema import Choice
- >>> choice = Choice(__name__='number', source=my_source)
- >>> bound = choice.bind(object())
- >>> bound.vocabulary
- <...MySource...>
-
-If a IContextSourceBinder is passed as the `source` argument to Choice, it's
-`bind` method will be called with the context as its only argument. The
-result must implement ISource and will be used as the source.
-
- >>> _my_binder_called = []
- >>> def my_binder(context):
- ... _my_binder_called.append(context)
- ... source = MySource()
- ... source.divisor = context.divisor
- ... return source
- >>> interface.directlyProvides(my_binder, IContextSourceBinder)
-
- >>> class Context(object):
- ... divisor = 3
-
- >>> choice = Choice(__name__='number', source=my_binder)
- >>> bound = choice.bind(Context())
- >>> len(_my_binder_called)
- 1
- >>> bound.vocabulary
- <...MySource...>
- >>> bound.vocabulary.divisor
- 3
-
-When using IContextSourceBinder together with default value, it's
-impossible to validate it on field initialization. Let's check if
-initalization doesn't fail in that case.
-
- >>> choice = Choice(__name__='number', source=my_binder, default=2)
-
- >>> del _my_binder_called[:]
- >>> bound = choice.bind(Context())
- >>> len(_my_binder_called)
- 1
-
- >>> bound.validate(bound.default)
- >>> bound.validate(3)
- Traceback (most recent call last):
- ...
- ConstraintNotSatisfied: 3
-
-It's developer's responsibility to provide a default value that fits the
-constraints when using context-based sources.
Deleted: zope.schema/branches/tseaver-test_cleanup/src/zope/schema/tests/test_docs.py
===================================================================
--- zope.schema/branches/tseaver-test_cleanup/src/zope/schema/tests/test_docs.py 2012-04-20 19:20:46 UTC (rev 125215)
+++ zope.schema/branches/tseaver-test_cleanup/src/zope/schema/tests/test_docs.py 2012-04-20 19:20:50 UTC (rev 125216)
@@ -1,38 +0,0 @@
-##############################################################################
-#
-# Copyright (c) 2004 Zope Foundation and Contributors.
-# All Rights Reserved.
-#
-# This software is subject to the provisions of the Zope Public License,
-# Version 2.1 (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.
-#
-##############################################################################
-"""Tests for the schema package's documentation files
-"""
-import unittest
-
-def test_suite():
- import doctest
- import re
-
- from zope.testing import renormalizing
- import zope.schema.tests
- checker = renormalizing.RENormalizing([
- (re.compile(r"\[\(None, Invalid\('8<=10',\)\)\]"),
- r"[(None, <zope.interface.exceptions.Invalid instance at 0x...>)]",)
- ])
- checker = checker + zope.schema.tests.py3_checker
- return unittest.TestSuite((
- doctest.DocFileSuite('../sources.txt',
- optionflags=doctest.ELLIPSIS,
- checker=zope.schema.tests.py3_checker),
- doctest.DocFileSuite('../fields.txt', checker=zope.schema.tests.py3_checker),
- doctest.DocFileSuite('../README.txt', checker=zope.schema.tests.py3_checker),
- doctest.DocFileSuite(
- '../validation.txt', checker=checker,
- optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS),
- ))
Deleted: zope.schema/branches/tseaver-test_cleanup/src/zope/schema/validation.txt
===================================================================
--- zope.schema/branches/tseaver-test_cleanup/src/zope/schema/validation.txt 2012-04-20 19:20:46 UTC (rev 125215)
+++ zope.schema/branches/tseaver-test_cleanup/src/zope/schema/validation.txt 2012-04-20 19:20:50 UTC (rev 125216)
@@ -1,169 +0,0 @@
-=================
-Schema Validation
-=================
-
-There are two helper methods to verify schemas and interfaces:
-
-getValidationErrors
- first validates via the zope.schema field validators. If that succeeds the
- invariants are checked.
-getSchemaValidationErrors
- *only* validateds via the zope.schema field validators. The invariants are
- *not* checked.
-
-
-Create an interface to validate against:
-
- >>> import zope.interface
- >>> import zope.schema
- >>> _a_greater_b_called = []
- >>> class ITwoInts(zope.interface.Interface):
- ... a = zope.schema.Int(max=10)
- ... b = zope.schema.Int(min=5)
- ...
- ... @zope.interface.invariant
- ... def a_greater_b(obj):
- ... _a_greater_b_called.append(obj)
- ... if obj.a <= obj.b:
- ... raise zope.interface.Invalid("%s<=%s" % (obj.a, obj.b))
- ...
-
-Create a silly model:
-
- >>> class TwoInts(object):
- ... pass
-
-Create an instance of TwoInts but do not set attributes. We get two errors:
-
- >>> ti = TwoInts()
- >>> r = zope.schema.getValidationErrors(ITwoInts, ti)
- >>> r.sort()
- >>> r
- [('a', SchemaNotFullyImplemented(...AttributeError...)),
- ('b', SchemaNotFullyImplemented(...AttributeError...))]
- >>> r[0][1].args[0].args
- ("'TwoInts' object has no attribute 'a'",)
- >>> r[1][1].args[0].args
- ("'TwoInts' object has no attribute 'b'",)
-
-The `getSchemaValidationErrors` function returns the same result:
-
- >>> r = zope.schema.getSchemaValidationErrors(ITwoInts, ti)
- >>> r.sort()
- >>> r
- [('a', SchemaNotFullyImplemented(...AttributeError...)),
- ('b', SchemaNotFullyImplemented(...AttributeError...))]
- >>> r[0][1].args[0].args
- ("'TwoInts' object has no attribute 'a'",)
- >>> r[1][1].args[0].args
- ("'TwoInts' object has no attribute 'b'",)
-
-Note that see no error from the invariant because the invariants are not
-vaildated if there are other schema errors.
-
-When we set a valid value for `a` we still get the same error for `b`:
-
- >>> ti.a = 11
- >>> errors = zope.schema.getValidationErrors(ITwoInts, ti)
- >>> errors.sort()
- >>> errors
- [('a', TooBig(11, 10)),
- ('b', SchemaNotFullyImplemented(...AttributeError...))]
- >>> errors[1][1].args[0].args
- ("'TwoInts' object has no attribute 'b'",)
-
- >>> errors[0][1].doc()
- u'Value is too big'
-
-
-After setting a valid value for `a` there is only the error for the missing `b`
-left:
-
- >>> ti.a = 8
- >>> r = zope.schema.getValidationErrors(ITwoInts, ti)
- >>> r
- [('b', SchemaNotFullyImplemented(...AttributeError...))]
- >>> r[0][1].args[0].args
- ("'TwoInts' object has no attribute 'b'",)
-
-
-After setting valid value for `b` the schema is valid so the invariants are
-checked. As `b>a` the invariant fails:
-
- >>> ti.b = 10
- >>> errors = zope.schema.getValidationErrors(ITwoInts, ti)
- >>> len(_a_greater_b_called)
- 1
- >>> errors
- [(None, <zope.interface.exceptions.Invalid instance at 0x...>)]
-
-
-When using `getSchemaValidationErrors` we do not get an error any more:
-
- >>> zope.schema.getSchemaValidationErrors(ITwoInts, ti)
- []
-
-
-Set `b=5` so everything is fine:
-
- >>> ti.b = 5
- >>> del _a_greater_b_called[:]
- >>> zope.schema.getValidationErrors(ITwoInts, ti)
- []
- >>> len(_a_greater_b_called)
- 1
-
-
-Compare ValidationError
------------------------
-
-There was an issue with compare validation error with somthing else then an
-exceptions. Let's test if we can compare ValidationErrors with different things
-
- >>> from zope.schema._bootstrapinterfaces import ValidationError
- >>> v1 = ValidationError('one')
- >>> v2 = ValidationError('one')
- >>> v3 = ValidationError('another one')
-
-A ValidationError with the same arguments compares:
-
- >>> v1 == v2
- True
-
-but not with an error with different arguments:
-
- >>> v1 == v3
- False
-
-We can also compare validation erros with other things then errors. This
-was running into an AttributeError in previous versions of zope.schema. e.g.
-AttributeError: 'NoneType' object has no attribute 'args'
-
- >>> v1 == None
- False
-
- >>> v1 == object()
- False
-
- >>> v1 == False
- False
-
- >>> v1 == True
- False
-
- >>> v1 == 0
- False
-
- >>> v1 == 1
- False
-
- >>> v1 == int
- False
-
-If we compare a ValidationError with another validation error based class,
-we will get the following result:
-
- >>> from zope.schema._bootstrapinterfaces import RequiredMissing
- >>> r1 = RequiredMissing('one')
- >>> v1 == r1
- True
More information about the checkins
mailing list