[Checkins] SVN: zope.configuration/branches/tseaver-test_cleanup/ Move extended examples to narrative docs.

Tres Seaver cvs-admin at zope.org
Tue May 8 01:19:16 UTC 2012


Log message for revision 125722:
  Move extended examples to narrative docs.

Changed:
  U   zope.configuration/branches/tseaver-test_cleanup/docs/narr.rst
  A   zope.configuration/branches/tseaver-test_cleanup/src/zope/configuration/tests/nested.py
  U   zope.configuration/branches/tseaver-test_cleanup/src/zope/configuration/tests/schema.zcml
  A   zope.configuration/branches/tseaver-test_cleanup/src/zope/configuration/tests/simple.py
  U   zope.configuration/branches/tseaver-test_cleanup/src/zope/configuration/tests/simple.zcml
  D   zope.configuration/branches/tseaver-test_cleanup/src/zope/configuration/tests/test_nested.py
  D   zope.configuration/branches/tseaver-test_cleanup/src/zope/configuration/tests/test_simple.py

-=-
Modified: zope.configuration/branches/tseaver-test_cleanup/docs/narr.rst
===================================================================
--- zope.configuration/branches/tseaver-test_cleanup/docs/narr.rst	2012-05-08 01:19:07 UTC (rev 125721)
+++ zope.configuration/branches/tseaver-test_cleanup/docs/narr.rst	2012-05-08 01:19:12 UTC (rev 125722)
@@ -41,7 +41,7 @@
   are typically functions that take a context and zero or more
   keyword arguments and return a sequence of configuration actions.
 
-  To learn how to create simple directives, see `tests/test_simple.py`.
+  To learn how to create simple directives, see `tests/simple.py`.
 
 
 - Grouping directives collect information to be used by nested
@@ -55,7 +55,7 @@
   Other directives can be nested in grouping directives.
 
   To learn how to implement nested directives, look at the
-  documentation in `tests/test_nested.py`.
+  documentation in the "Creating Nested Directives" section below.
 
 - Complex directives are directives that have subdirectives.  
   Subdirectives have handlers that are simply methods of complex
@@ -77,7 +77,7 @@
 .. todo::
    Flesh out narrative docs.
     
-Using the configuration machinery programattically
+Using the configuration machinery programatically
 ==================================================
 
 An extended example:
@@ -749,3 +749,376 @@
 
    logger.setLevel(oldlevel)
    logger.removeHandler(handler)
+
+
+Creating simple directives
+==========================
+
+A simple directive is a directive that doesn't contain other
+directives. It can be implemented via a fairly simple function.
+To implement a simple directive, you need to do 3 things:
+
+- You need to create a schema to describe the directive parameters,
+
+- You need to write a directive handler, and
+
+- You need to register the directive.
+
+In this example, we'll implement a contrived example that records
+information about files in a file registry. The file registry is just
+the list, ``file_registry``.
+
+.. doctest::
+
+   >>> from zope.configuration.tests.simple import file_registry
+
+Our registry will contain tuples with:
+
+  - file path
+
+  - file title
+
+  - description
+
+  - Information about where the file was defined
+
+Our schema is defined in ``zope.configuration.tests.simple.IRegisterFile``
+(q.v).
+
+.. doctest::
+
+   >>> from zope.configuration.tests.simple import IRegisterFile
+
+Our schema lists the ``path`` and ``title`` attributes.  We'll get the
+description and other information for free, as we'll see later.  The
+title is not required, and may be omitted.
+
+The job of a configuration handler is to compute one or more
+configuration actions.  Configuration actions are defered function
+calls. The handler doesn't perform the actions. It just computes
+actions, which may be performed later if they are not overridden by
+other directives.
+
+Our handler is given in the function,
+``zope.configuration.tests.simple.registerFile``.
+
+.. doctest::
+
+   >>> from zope.configuration.tests.simple import registerFile
+
+
+It takes a context, a path and a title. All directive handlers take the
+directive context as the first argument.  A directive context, at a minimim,
+implements, ``zope.configuration.IConfigurationContext``. 
+(Specialized contexts can implement more specific interfaces. We'll say more
+about that when we talk about grouping directives.)  The title argument
+must have a default value, because we indicated that the title was not
+required in the schema. (Alternatively, we could have made the title
+required, but provided a default value in the schema.
+
+In the first line of function ``registerFile``, we get the context information
+object. This object contains information about the configuration
+directive, such as the file and location within the file of the
+directive.
+
+The context information object also has a text attribute that contains
+the textual data contained by the configuration directive. (This is
+the concatenation of all of the xml text nodes directly contained by
+the directive.)  We use this for our description in the second line
+of the handler.
+
+The last thing the handler does is to compute an action by calling the
+action method of the context.  It passes the action method 3 keyword
+arguments:
+
+- discriminator
+
+  The discriminator is used to identify the action to be performed so
+  that duplicate actions can be detected.  Two actions are duplicated,
+  and this conflict, if they have the same discriminator values and
+  the values are not ``None``.  Conflicting actions can be resolved if
+  one of the conflicting actions is from a configuration file that
+  directly or indirectly includes the files containing the other
+  conflicting actions.
+
+  In function ``registerFile``, we a tuple with the string
+  ``'RegisterFile'`` and the path to be registered.
+
+- callable
+
+  The callable is the object to be called to perform the action.
+
+- args
+
+  The args argument contains positinal arguments to be passed to the
+  callable. In function ``registerFile``, we pass a tuple containing a
+  ``FileInfo`` object.
+
+  (Note that there's nothing special about the FileInfo class. It has
+   nothing to do with creating simple directives. It's just used in
+   this example to organize the application data.)
+
+
+The final step in implementing the simple directive is to register
+it. We do that with the zcml ``meta:directive`` directive.  This is
+given in the file simple.zcml.  Here we specify the name, namespace,
+schema, and handler for the directive.  We also provide a
+documentation for the directive as text between the start and end
+tags.
+
+The file simple.zcml also includes some directives that use the new
+directive to register some files.
+
+Now let's try it all out:
+
+.. doctest::
+
+   >>> from zope.configuration import tests
+   >>> from zope.configuration.xmlconfig import file
+   >>> context = file("simple.zcml", tests)
+
+Now we should see some file information in the registry:
+
+.. doctest::
+
+   >>> from zope.configuration.tests.test_xmlconfig import clean_text_w_paths
+   >>> from zope.configuration.tests.test_xmlconfig import clean_path
+   >>> print clean_path(file_registry[0].path)
+   tests/simple.py
+   >>> print file_registry[0].title
+   How to create a simple directive
+   >>> print file_registry[0].description
+   Describes how to implement a simple directive
+   >>> print clean_text_w_paths(file_registry[0].info)
+   File "tests/simple.zcml", line 19.2-24.2
+       <files:register
+           path="simple.py"
+           title="How to create a simple directive"
+           >
+         Describes how to implement a simple directive
+       </files:register>
+   >>> print clean_path(file_registry[1].path)
+   tests/simple.zcml
+   >>> print file_registry[1].title
+   <BLANKLINE>
+   >>> desc = file_registry[1].description
+   >>> print '\n'.join([l.rstrip()
+   ...                  for l in desc.strip().splitlines()
+   ...                    if l.rstrip()])
+   Shows the ZCML directives needed to register a simple directive.
+       Also show some usage examples,
+   >>> print clean_text_w_paths(file_registry[1].info)
+   File "tests/simple.zcml", line 26.2-30.2
+       <files:register path="simple.zcml">
+         Shows the ZCML directives needed to register a simple directive.
+         Also show some usage examples,
+       </files:register>
+   >>> print clean_path(file_registry[2].path)
+   tests/__init__.py
+   >>> print file_registry[2].title
+   Make this a package
+   >>> print file_registry[2].description
+   <BLANKLINE>
+   >>> print clean_text_w_paths(file_registry[2].info)
+   File "tests/simple.zcml", line 32.2-32.67
+       <files:register path="__init__.py" title="Make this a package" />
+
+Clean up after ourselves:
+
+.. doctest::
+
+   >>> del file_registry[:]
+
+
+
+Creating nested directives
+==========================
+
+When using ZCML, you sometimes nest ZCML directives. This is typically
+done either to:
+
+- Avoid repetative input.  Information shared among multiple
+  directives is provided in a surrounding directive.
+
+- Put together information that is too complex or structured to express
+  with a single set of directive parameters.
+
+Grouping directives are used to handle both of these cases.  See the
+documentation in :mod:`zope.configure.zopeconfigure`. This file describes the
+implementation of the zope ``configure`` directive, which groups
+directives that use a common package or internationalization domain.
+You should also have read the section on "Creating simple directives."
+
+This file shows you how to handle the second case above. In this case,
+we have grouping directives that are meant to collaborate with
+specific contained directives.  To do this, you have the grouping
+directives declare a more specific (or alternate) interface to
+``IConfigurationContext``. Directives designed to work with those
+grouping directives are registered for the new interface.
+
+Let's look at example. Suppose we wanted to be able to define schema
+using ZCML.  We'd use a grouping directive to specify schemas and
+contained directives to specify fields within the schema.  We'll use a
+schema registry to hold the defined schemas::
+
+.. doctest::
+
+   >>> from zope.configuration.tests.nested import schema_registry
+
+A schema has a name, an id, some documentation, and some fields.
+We'll provide the name and the id as parameters. We'll define fields
+as subdirectives and documentation as text contained in the schema
+directive.  The schema directive uses the schema, ``ISchemaInfo`` for
+it's parameters.
+
+.. doctest::
+
+   >>> from zope.configuration.tests.nested import ISchemaInfo
+
+We also define the schema, ISchema, that specifies an attribute that
+nested field directives will use to store the fields they define.
+
+.. doctest::
+
+   >>> from zope.configuration.tests.nested import ISchema
+
+The class, ``Schema``, provides the handler for the schema directive. (If
+you haven't read the documentation in ``zopeconfigure.py``, you need
+to do so now.)  The constructor saves its arguments as attributes
+and initializes its ``fields`` attribute:
+
+.. doctest::
+
+   >>> from zope.configuration.tests.nested import Schema
+
+The ``after`` method of the ``Schema`` class creates a schema and
+computes an action to register the schema in the schema registry.  The
+discriminator prevents two schema directives from registering the same
+schema.
+
+It's important to note that when we call the ``action`` method on
+``self``, rather than on ``self.context``.  This is because, in a
+grouping directive handler, the handler instance is itself a context.
+When we call the ``action`` method, the method stores additional meta
+data associated with the context it was called on. This meta data
+includes an include path, used when resolving conflicting actions,
+and an object that contains information about the XML source used
+to invole the directive. If we called the action method on
+``self.context``, the wrong meta data would be associated with the
+configuration action.
+
+The file ``schema.zcml`` contains the meta-configuration directive
+that defines the schema directive.
+
+To define fields, we'll create directives to define the fields.
+Let's start with a ``text`` field.  ``ITextField`` defines the schema for
+text field parameters. It extends ``IFieldInfo``, which defines data
+common to all fields.  We also define a simple handler method,
+textField, that takes a context and keyword arguments. (For
+information on writing simple directives, see ``test_simple.py``.)
+We've abstracted most of the logic into the function ``field``.
+
+The ``field`` function computes a field instance using the
+constructor, and the keyword arguments passed to it.  It also uses the
+context information object to get the text content of the directive,
+which it uses for the field description.
+
+After computing the field instance, it gets the ``Schema`` instance,
+which is the context of the context passed to the function. The
+function checks to see if there is already a field with that name. If
+there is, it raises an error. Otherwise, it saves the field. 
+
+We also define an ``IIntInfo`` schema and ``intField`` handler
+function to support defining integer fields. 
+
+We register the ``text`` and ``int`` directives in ``schema.zcml``.
+These are like the simple directive definition we saw in
+``test_simple.py`` with an important exception.  We provide a
+``usedIn`` parameter to say that these directives can *only* ne used
+in a ``ISchema`` context. In other words, these can only be used
+inside of ``schema`` directives.
+
+The ``schema.zcml`` file also contains some sample ``schema``
+directives.  We can execute the file:
+
+.. doctest::
+
+   >>> from zope.configuration import tests
+   >>> from zope.configuration.xmlconfig import file
+   >>> context = file("schema.zcml", tests)
+
+And verify that the schema registery has the schemas we expect:
+
+.. doctest::
+
+   >>> pprint(sorted(schema_registry))
+   ['zope.configuration.tests.nested.I1',
+    'zope.configuration.tests.nested.I2']
+
+   >>> def sorted(x):
+   ...     r = list(x)
+   ...     r.sort()
+   ...     return r
+
+   >>> i1 = schema_registry['zope.configuration.tests.nested.I1']
+   >>> sorted(i1)
+   ['a', 'b']
+   >>> i1['a'].__class__.__name__
+   'Text'
+   >>> i1['a'].description.strip()
+   u'A\n\n          Blah blah'
+   >>> i1['a'].min_length
+   1
+   >>> i1['b'].__class__.__name__
+   'Int'
+   >>> i1['b'].description.strip()
+   u'B\n\n          Not feeling very creative'
+   >>> i1['b'].min
+   1
+   >>> i1['b'].max
+   10
+
+   >>> i2 = schema_registry['zope.configuration.tests.nested.I2']
+   >>> sorted(i2)
+   ['x', 'y']
+
+
+Now let's look at some error situations. For example, let's see what
+happens if we use a field directive outside of a schema dorective.
+(Note that we used the context we created above, so we don't have to
+redefine our directives:
+
+.. doctest::
+
+   >>> from zope.configuration.xmlconfig import string
+   >>> from zope.configuration.xmlconfig import ZopeXMLConfigurationError
+   >>> try:
+   ...    v = string(
+   ...      '<text xmlns="http://sample.namespaces.zope.org/schema" name="x" />',
+   ...      context)
+   ... except ZopeXMLConfigurationError, v:
+   ...   pass
+   >>> print v
+   File "<string>", line 1.0
+       ConfigurationError: The directive (u'http://sample.namespaces.zope.org/schema', u'text') cannot be used in this context
+
+Let's see what happens if we declare duplicate fields:
+
+.. doctest::
+
+   >>> try:
+   ...    v = string(
+   ...      '''
+   ...      <schema name="I3" id="zope.configuration.tests.nested.I3"
+   ...              xmlns="http://sample.namespaces.zope.org/schema">
+   ...        <text name="x" />
+   ...        <text name="x" />
+   ...      </schema>
+   ...      ''',
+   ...      context)
+   ... except ZopeXMLConfigurationError, v:
+   ...   pass
+   >>> print v
+   File "<string>", line 5.7-5.24
+       ValueError: ('Duplicate field', 'x')
+

Copied: zope.configuration/branches/tseaver-test_cleanup/src/zope/configuration/tests/nested.py (from rev 125721, zope.configuration/branches/tseaver-test_cleanup/src/zope/configuration/tests/test_nested.py)
===================================================================
--- zope.configuration/branches/tseaver-test_cleanup/src/zope/configuration/tests/nested.py	                        (rev 0)
+++ zope.configuration/branches/tseaver-test_cleanup/src/zope/configuration/tests/nested.py	2012-05-08 01:19:12 UTC (rev 125722)
@@ -0,0 +1,149 @@
+##############################################################################
+#
+# Copyright (c) 2003 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.
+#
+##############################################################################
+""" Utilities for the 'nested directive' section in the narrative docs.
+"""
+
+from zope.interface import Attribute
+from zope.interface import Interface
+from zope.interface import implementer
+from zope.schema import BytesLine
+from zope.schema import Id
+from zope.schema import Int
+from zope.schema import Text
+from zope.schema import TextLine
+from zope.configuration.config import GroupingContextDecorator
+from zope.configuration.config import IConfigurationContext
+from zope.configuration.fields import Bool
+from zope.configuration._compat import u
+
+
+schema_registry = {}
+
+class ISchemaInfo(Interface):
+    """Parameter schema for the schema directive
+    """
+
+    name = TextLine(
+        title=u("The schema name"),
+        description=u("This is a descriptive name for the schema."),
+        )
+
+    id = Id(title=u("The unique id for the schema"))
+
+class ISchema(Interface):
+    """Interface that distinguishes the schema directive
+    """
+
+    fields = Attribute("Dictionary of field definitions")
+    
+
+ at implementer(IConfigurationContext, ISchema)
+class Schema(GroupingContextDecorator):
+    """Handle schema directives
+    """
+
+
+    def __init__(self, context, name, id):
+        self.context, self.name, self.id = context, name, id
+        self.fields = {}
+
+    def after(self):
+        schema = Interface.__class__(
+            self.name,
+            (Interface, ),
+            self.fields
+            )
+        schema.__doc__ = self.info.text.strip()
+        self.action(
+            discriminator=('schema', self.id),
+            callable=schema_registry.__setitem__,
+            args=(self.id, schema),
+            )
+        
+
+class IFieldInfo(Interface):
+
+    name = BytesLine(
+        title=u("The field name"),
+        )
+
+    title = TextLine(
+        title=u("Title"),
+        description=u("A short summary or label"),
+        default=u(""),
+        required=False,
+        )
+
+    required = Bool(
+        title=u("Required"),
+        description=u("Determines whether a value is required."),
+        default=True)
+
+    readonly = Bool(
+        title=u("Read Only"),
+        description=u("Can the value be modified?"),
+        required=False,
+        default=False)
+
+class ITextInfo(IFieldInfo):
+
+    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)
+
+def field(context, constructor, name, **kw):
+
+    # Compute the field
+    field = constructor(description=context.info.text.strip(), **kw)
+
+    # Save it in the schema's field dictionary
+    schema = context.context
+    if name in schema.fields:
+        raise ValueError("Duplicate field", name)
+    schema.fields[name] = field
+
+    
+def textField(context, **kw):
+    field(context, Text, **kw)
+
+class IIntInfo(IFieldInfo):
+
+    min = Int(
+        title=u("Start of the range"),
+        required=False,
+        default=None
+        )
+
+    max = Int(
+        title=u("End of the range (excluding the value itself)"),
+        required=False,
+        default=None
+        )
+    
+def intField(context, **kw):
+    field(context, Int, **kw)

Modified: zope.configuration/branches/tseaver-test_cleanup/src/zope/configuration/tests/schema.zcml
===================================================================
--- zope.configuration/branches/tseaver-test_cleanup/src/zope/configuration/tests/schema.zcml	2012-05-08 01:19:07 UTC (rev 125721)
+++ zope.configuration/branches/tseaver-test_cleanup/src/zope/configuration/tests/schema.zcml	2012-05-08 01:19:12 UTC (rev 125722)
@@ -6,8 +6,8 @@
   <meta:groupingDirective
       name="schema"
       namespace="http://sample.namespaces.zope.org/schema"
-      schema=".test_nested.ISchemaInfo"
-      handler=".test_nested.Schema"
+      schema=".nested.ISchemaInfo"
+      handler=".nested.Schema"
       >
 
       Define a schema
@@ -21,9 +21,9 @@
   <meta:directive
       name="text"
       namespace="http://sample.namespaces.zope.org/schema"
-      usedIn=".test_nested.ISchema"
-      schema=".test_nested.ITextInfo"
-      handler=".test_nested.textField"
+      usedIn=".nested.ISchema"
+      schema=".nested.ITextInfo"
+      handler=".nested.textField"
       >
       
       Define a text field
@@ -32,15 +32,15 @@
   <meta:directive
       name="int"
       namespace="http://sample.namespaces.zope.org/schema"
-      usedIn=".test_nested.ISchema"
-      schema=".test_nested.IIntInfo"
-      handler=".test_nested.intField"
+      usedIn=".nested.ISchema"
+      schema=".nested.IIntInfo"
+      handler=".nested.intField"
       >
       
       Define an integer field
   </meta:directive>
 
-  <schema:schema name="I1" id="zope.configuration.tests.test_nested.I1">
+  <schema:schema name="I1" id="zope.configuration.tests.nested.I1">
       Sample interface I1
   
       <schema:text name="a" min_length="1">
@@ -56,7 +56,7 @@
       </schema:int>
   </schema:schema>
 
-  <schema:schema name="I2" id="zope.configuration.tests.test_nested.I2">
+  <schema:schema name="I2" id="zope.configuration.tests.nested.I2">
       Sample interface I2
   
       <schema:text name="x">X</schema:text>

Copied: zope.configuration/branches/tseaver-test_cleanup/src/zope/configuration/tests/simple.py (from rev 125721, zope.configuration/branches/tseaver-test_cleanup/src/zope/configuration/tests/test_simple.py)
===================================================================
--- zope.configuration/branches/tseaver-test_cleanup/src/zope/configuration/tests/simple.py	                        (rev 0)
+++ zope.configuration/branches/tseaver-test_cleanup/src/zope/configuration/tests/simple.py	2012-05-08 01:19:12 UTC (rev 125722)
@@ -0,0 +1,50 @@
+##############################################################################
+#
+# Copyright (c) 2003 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.
+#
+##############################################################################
+""" Utilities for the 'simple directive' section in the narrative docs.
+"""
+
+from zope.interface import Interface
+from zope.schema import Text
+
+from zope.configuration.fields import Path
+from zope.configuration._compat import u
+
+class IRegisterFile(Interface):
+
+    path = Path(
+        title=u("File path"),
+        description=u("This is the path name of the file to be registered."),
+        )
+
+    title = Text(
+        title=u("Short summary of the file"),
+        description=u("This will be used in file listings"),
+        required = False
+        )
+
+class FileInfo(object):
+
+    def __init__(self, path, title, description, info):
+        (self.path, self.title, self.description, self.info
+         ) = path, title, description, info
+
+file_registry = []
+
+def registerFile(context, path, title=u("")):
+    info = context.info
+    description = info.text.strip()
+    context.action(discriminator=('RegisterFile', path),
+                   callable=file_registry.append,
+                   args=(FileInfo(path, title, description, info),)
+                   )

Modified: zope.configuration/branches/tseaver-test_cleanup/src/zope/configuration/tests/simple.zcml
===================================================================
--- zope.configuration/branches/tseaver-test_cleanup/src/zope/configuration/tests/simple.zcml	2012-05-08 01:19:07 UTC (rev 125721)
+++ zope.configuration/branches/tseaver-test_cleanup/src/zope/configuration/tests/simple.zcml	2012-05-08 01:19:12 UTC (rev 125722)
@@ -6,8 +6,8 @@
   <meta:directive
       name="register"
       namespace="http://sample.namespaces.zope.org/files"
-      schema=".test_simple.IRegisterFile"
-      handler=".test_simple.registerFile"
+      schema=".simple.IRegisterFile"
+      handler=".simple.registerFile"
       >
 
       Register a file with the file registry
@@ -17,7 +17,7 @@
   </meta:directive>
 
   <files:register 
-      path="test_simple.py"
+      path="simple.py"
       title="How to create a simple directive"
       >
     Describes how to implement a simple directive

Deleted: zope.configuration/branches/tseaver-test_cleanup/src/zope/configuration/tests/test_nested.py
===================================================================
--- zope.configuration/branches/tseaver-test_cleanup/src/zope/configuration/tests/test_nested.py	2012-05-08 01:19:07 UTC (rev 125721)
+++ zope.configuration/branches/tseaver-test_cleanup/src/zope/configuration/tests/test_nested.py	2012-05-08 01:19:12 UTC (rev 125722)
@@ -1,319 +0,0 @@
-##############################################################################
-#
-# Copyright (c) 2003 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.
-#
-##############################################################################
-r"""Creating nested directives
-
-When using ZCML, you sometimes nest ZCML directives. This is typically
-done either to:
-
-- Avoid repetative input.  Information shared among multiple
-  directives is provided in a surrounding directive.
-
-- Put together information that is too complex or structured to express
-  with a single set of directive parameters.
-
-Grouping directives are used to handle both of these cases.  See the
-documentation in ``../zopeconfigure.py``. This file describes the
-implementation of the zope ``configure`` directive, which groups
-directives that use a common package or internationalization domain.
-The documentation in ``../zopeconfigure.py`` provides background for
-the documentation here.  You should also have read the documentation
-in ``test_simple.py``, which documents how to create simple
-directives.
-
-This file shows you how to handle the second case above. In this case,
-we have grouping directives that are meant to collaborate with
-specific contained directives.  To do this, you have the grouping
-directives declare a more specific (or alternate) interface to
-``IConfigurationContext``. Directives designed to work with those
-grouping directives are registered for the new interface.
-
-Let's look at example. Suppose we wanted to be able to define schema
-using ZCML.  We'd use a grouping directive to specify schemas and
-contained directives to specify fields within the schema.  We'll use a
-schema registry to hold the defined schemas.
-
-A schema has a name, an id, some documentation, and some fields.
-We'll provide the name and the id as parameters. We'll define fields
-as subdirectives and documentation as text contained in the schema
-directive.  The schema directive uses the schema, ``ISchemaInfo`` for
-it's parameters.
-
-We also define the schema, ISchema, that specifies an attribute that
-nested field directives will use to store the fields they define.
-
-The class, ``Schema``, provides the handler for the schema directive. (If
-you haven't read the documentation in ``zopeconfigure.py``, you need
-to do so now.)  The constructor saves its arguments as attributes
-and initializes its ``fields`` attribute.
-
-The ``after`` method of the ``Schema`` class creates a schema and
-computes an action to register the schema in the schema registry.  The
-discriminator prevents two schema directives from registering the same
-schema.
-
-It's important to note that when we call the ``action`` method on
-``self``, rather than on ``self.context``.  This is because, in a
-grouping directive handler, the handler instance is itself a context.
-When we call the ``action`` method, the method stores additional meta
-data associated with the context it was called on. This meta data
-includes an include path, used when resolving conflicting actions,
-and an object that contains information about the XML source used
-to invole the directive. If we called the action method on
-``self.context``, the wrong meta data would be associated with the
-configuration action.
-
-The file ``schema.zcml`` contains the meta-configuration directive
-that defines the schema directive.
-
-To define fields, we'll create directives to define the fields.
-Let's start with a ``text`` field.  ``ITextField`` defines the schema for
-text field parameters. It extends ``IFieldInfo``, which defines data
-common to all fields.  We also define a simple handler method,
-textField, that takes a context and keyword arguments. (For
-information on writing simple directives, see ``test_simple.py``.)
-We've abstracted most of the logic into the function ``field``.
-
-The ``field`` function computes a field instance using the
-constructor, and the keyword arguments passed to it.  It also uses the
-context information object to get the text content of the directive,
-which it uses for the field description.
-
-After computing the field instance, it gets the ``Schema`` instance,
-which is the context of the context passed to the function. The
-function checks to see if there is already a field with that name. If
-there is, it raises an error. Otherwise, it saves the field. 
-
-We also define an ``IIntInfo`` schema and ``intField`` handler
-function to support defining integer fields. 
-
-We register the ``text`` and ``int`` directives in ``schema.zcml``.
-These are like the simple directive definition we saw in
-``test_simple.py`` with an important exception.  We provide a
-``usedIn`` parameter to say that these directives can *only* ne used
-in a ``ISchema`` context. In other words, these can only be used
-inside of ``schema`` directives.
-
-The ``schema.zcml`` file also contains some sample ``schema``
-directives.  We can execute the file:
-
->>> from zope.configuration import tests
->>> context = xmlconfig.file("schema.zcml", tests)
-
-And verify that the schema registery has the schemas we expect:
-
->>> from pprint import PrettyPrinter
->>> pprint=PrettyPrinter(width=70).pprint
->>> pprint(list(schema_registry))
-['zope.configuration.tests.test_nested.I1',
- 'zope.configuration.tests.test_nested.I2']
-
->>> def sorted(x):
-...     r = list(x)
-...     r.sort()
-...     return r
-
->>> i1 = schema_registry['zope.configuration.tests.test_nested.I1']
->>> sorted(i1)
-['a', 'b']
->>> i1['a'].__class__.__name__
-'Text'
->>> i1['a'].description.strip()
-u'A\n\n          Blah blah'
->>> i1['a'].min_length
-1
->>> i1['b'].__class__.__name__
-'Int'
->>> i1['b'].description.strip()
-u'B\n\n          Not feeling very creative'
->>> i1['b'].min
-1
->>> i1['b'].max
-10
-
->>> i2 = schema_registry['zope.configuration.tests.test_nested.I2']
->>> sorted(i2)
-['x', 'y']
-
-
-Now let's look at some error situations. For example, let's see what
-happens if we use a field directive outside of a schema dorective.
-(Note that we used the context we created above, so we don't have to
-redefine our directives:
-
->>> try:
-...    v = xmlconfig.string(
-...      '<text xmlns="http://sample.namespaces.zope.org/schema" name="x" />',
-...      context)
-... except xmlconfig.ZopeXMLConfigurationError, v:
-...   pass
->>> print v
-File "<string>", line 1.0
-    ConfigurationError: The directive """ \
-        """(u'http://sample.namespaces.zope.org/schema', u'text') """ \
-        """cannot be used in this context
-
-Let's see what happens if we declare duplicate fields:
-
->>> try:
-...    v = xmlconfig.string(
-...      '''
-...      <schema name="I3" id="zope.configuration.tests.test_nested.I3"
-...              xmlns="http://sample.namespaces.zope.org/schema">
-...        <text name="x" />
-...        <text name="x" />
-...      </schema>
-...      ''',
-...      context)
-... except xmlconfig.ZopeXMLConfigurationError, v:
-...   pass
->>> print v
-File "<string>", line 5.7-5.24
-    ValueError: ('Duplicate field', 'x')
-"""
-
-import unittest
-from doctest import DocTestSuite
-from zope import interface, schema
-from zope.configuration import config, xmlconfig, fields
-
-
-schema_registry = {}
-
-class ISchemaInfo(interface.Interface):
-    """Parameter schema for the schema directive
-    """
-
-    name = schema.TextLine(
-        title=u"The schema name",
-        description=u"This is a descriptive name for the schema."
-        )
-
-    id = schema.Id(
-        title=u"The unique id for the schema"
-        )
-
-class ISchema(interface.Interface):
-    """Interface that distinguishes the schema directive
-    """
-
-    fields = interface.Attribute("Dictionary of field definitions"
-        )
-    
-class Schema(config.GroupingContextDecorator):
-    """Handle schema directives
-    """
-
-    interface.implements(config.IConfigurationContext, ISchema)
-
-    def __init__(self, context, name, id):
-        self.context, self.name, self.id = context, name, id
-        self.fields = {}
-
-    def after(self):
-        schema = interface.Interface.__class__(
-            self.name,
-            (interface.Interface, ),
-            self.fields
-            )
-        schema.__doc__ = self.info.text.strip()
-        self.action(
-            discriminator=('schema', self.id),
-            callable=schema_registry.__setitem__,
-            args=(self.id, schema),
-            )
-        
-
-class IFieldInfo(interface.Interface):
-
-    name = schema.BytesLine(
-        title=u"The field name"
-        )
-
-    title = schema.TextLine(
-        title=u"Title",
-        description=u"A short summary or label",
-        default=u"",
-        required=False,
-        )
-
-    required = fields.Bool(
-        title=u"Required",
-        description=u"Determines whether a value is required.",
-        default=True)
-
-    readonly = fields.Bool(
-        title=u"Read Only",
-        description=u"Can the value be modified?",
-        required=False,
-        default=False)
-
-class ITextInfo(IFieldInfo):
-
-    min_length = schema.Int(
-        title=u"Minimum length",
-        description=u"Value after whitespace processing cannot have less than "
-                    u"min_length characters. If min_length is None, there is "
-                    u"no minimum.",
-        required=False,
-        min=0, # needs to be a positive number
-        default=0)
-
-    max_length = schema.Int(
-        title=u"Maximum length",
-        description=u"Value after whitespace processing cannot have greater "
-                    u"or equal than max_length characters. If max_length is "
-                    u"None, there is no maximum.",
-        required=False,
-        min=0, # needs to be a positive number
-        default=None)
-
-def field(context, constructor, name, **kw):
-
-    # Compute the field
-    field = constructor(description=context.info.text.strip(), **kw)
-
-    # Save it in the schema's field dictionary
-    schema = context.context
-    if name in schema.fields:
-        raise ValueError("Duplicate field", name)
-    schema.fields[name] = field
-
-    
-def textField(context, **kw):
-    field(context, schema.Text, **kw)
-
-class IIntInfo(IFieldInfo):
-
-    min = schema.Int(
-        title=u"Start of the range",
-        required=False,
-        default=None
-        )
-
-    max = schema.Int(
-        title=u"End of the range (excluding the value itself)",
-        required=False,
-        default=None
-        )
-    
-def intField(context, **kw):
-    field(context, schema.Int, **kw)
-    
-
-def test_suite():
-    return unittest.TestSuite((
-        DocTestSuite(),
-        ))
-
-if __name__ == '__main__': unittest.main()

Deleted: zope.configuration/branches/tseaver-test_cleanup/src/zope/configuration/tests/test_simple.py
===================================================================
--- zope.configuration/branches/tseaver-test_cleanup/src/zope/configuration/tests/test_simple.py	2012-05-08 01:19:07 UTC (rev 125721)
+++ zope.configuration/branches/tseaver-test_cleanup/src/zope/configuration/tests/test_simple.py	2012-05-08 01:19:12 UTC (rev 125722)
@@ -1,208 +0,0 @@
-##############################################################################
-#
-# Copyright (c) 2003 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.
-#
-##############################################################################
-r"""How to write a simple directive
-
-This module documents how to write a simple directive.
-
-A simple directive is a directive that doesn't contain other
-directives. It can be implemented via a fairly simple function.
-To implement a simple directive, you need to do 3 things:
-
-- You need to create a schema to describe the directive parameters,
-
-- You need to write a directive handler, and
-
-- You need to register the directive.
-
-In this module, we'll implement a contrived example that records
-information about files in a file registry. The file registry is just
-the list, ``file_registry``. Our registry will contain tuples
-with:
-
-  - file path
-
-  - file title
-
-  - description
-
-  - Information about where the file was defined
-
-Our schema is defined in IRegisterFile (see below). Our schema lists
-the path and title.  We'll get the description and other information
-for free, as we'll see later.  The title is not required, and may be
-ommmitted.
-
-The job of a configuration handler is to compute one or more
-configuration actions.  Configuration actions are defered function
-calls. The handler doesn't perform the actions. It just computes
-actions, which may be performed later if they are not overridden by
-other directives.
-
-Out handler is given in the function, ``registerFile``. It takes a context,
-a path and a title. All directive handlers take the directive context
-as the first argument.  A directive context, at a minimim, implements,
-``zope.configuration.IConfigurationContext``.  (Specialized contexts
-can implement more specific interfaces. We'll say more about that when
-we talk about grouping directives.)  The title argument
-must have a default value, because we indicated that the title was not
-required in the schema. (Alternatively, we could have made the title
-required, but provided a default value in the schema.
-
-In the first line of function `registerFile`, we get the context information
-object. This object contains information about the configuration
-directive, such as the file and location within the file of the
-directive.
-
-The context information object also has a text attribute that contains
-the textual data contained by the configuration directive. (This is
-the concatenation of all of the xml text nodes directly contained by
-the directive.)  We use this for our description in the second line
-of the handler.
-
-The last thing the handler does is to compute an action by calling the
-action method of the context.  It passes the action method 3 keyword
-arguments:
-
-- discriminator
-
-  The discriminator is used to identify the action to be performed so
-  that duplicate actions can be detected.  Two actions are duplicated,
-  and this conflict, if they have the same discriminator values and
-  the values are not ``None``.  Conflicting actions can be resolved if
-  one of the conflicting actions is from a configuration file that
-  directly or indirectly includes the files containing the other
-  conflicting actions.
-
-  In function ``registerFile``, we a tuple with the string
-  ``'RegisterFile'`` and the path to be registered.
-
-- callable
-
-  The callable is the object to be called to perform the action.
-
-- args
-
-  The args argument contains positinal arguments to be passed to the
-  callable. In function ``registerFile``, we pass a tuple containing a
-  ``FileInfo`` object.
-
-  (Note that there's nothing special about the FileInfo class. It has
-   nothing to do with creating simple directives. It's just used in
-   this example to organize the application data.)
-
-
-The final step in implementing the simple directive is to register
-it. We do that with the zcml ``meta:directive`` directive.  This is
-given in the file simple.zcml.  Here we specify the name, namespace,
-schema, and handler for the directive.  We also provide a
-documentation for the directive as text between the start and end
-tags.
-
-The file simple.zcml also includes some directives that use the new
-directive to register some files.
-
-Now let's try it all out:
-
->>> from zope.configuration import tests
->>> context = xmlconfig.file("simple.zcml", tests)
-
-Now we should see some file information in the  registry:
-
->>> from zope.configuration.tests.test_xmlconfig import clean_text_w_paths
->>> from zope.configuration.tests.test_xmlconfig import clean_path
->>> for i in file_registry:
-...   print "path:", clean_path(i.path)
-...   print "title:", i.title
-...   print "description:", '\n'.join(
-...               [l.rstrip()
-...                for l in i.description.strip().split('\n')
-...                if l.rstrip()])
-...   print "info:"
-...   print clean_text_w_paths(i.info)
-path: tests/test_simple.py
-title: How to create a simple directive
-description: Describes how to implement a simple directive
-info:
-File "tests/simple.zcml", line 19.2-24.2
-    <files:register
-        path="test_simple.py"
-        title="How to create a simple directive"
-        >
-      Describes how to implement a simple directive
-    </files:register>
-path: tests/simple.zcml
-title: 
-description: Shows the ZCML directives needed to register a simple directive.
-    Also show some usage examples,
-info:
-File "tests/simple.zcml", line 26.2-30.2
-    <files:register path="simple.zcml">
-      Shows the ZCML directives needed to register a simple directive.
-      Also show some usage examples,
-    </files:register>
-path: tests/__init__.py
-title: Make this a package
-description: 
-info:
-File "tests/simple.zcml", line 32.2-32.67
-    <files:register path="__init__.py" title="Make this a package" />
-
-
-We'll clean up after ourselves:
-
->>> del file_registry[:]
-"""
-
-file_registry = []
-
-
-import unittest
-from doctest import DocTestSuite
-from zope import interface
-from zope import schema
-from zope.configuration import fields, xmlconfig
-
-class IRegisterFile(interface.Interface):
-
-    path = fields.Path(
-        title=u"File path",
-        description=u"This is the path name of the file to be registered."
-        )
-
-    title = schema.Text(
-        title=u"Short summary of the file",
-        description=u"This will be used in file listings",
-        required = False
-        )
-
-class FileInfo(object):
-
-    def __init__(self, path, title, description, info):
-        (self.path, self.title, self.description, self.info
-         ) = path, title, description, info
-
-def registerFile(context, path, title=u""):
-    info = context.info
-    description = info.text.strip()
-    context.action(discriminator=('RegisterFile', path),
-                   callable=file_registry.append,
-                   args=(FileInfo(path, title, description, info),)
-                   )
-
-def test_suite():
-    return unittest.TestSuite((
-        DocTestSuite(),
-        ))
-
-if __name__ == '__main__': unittest.main()



More information about the checkins mailing list