[Zope3-checkins] CVS: Zope3/doc/zcml - meta.stx:1.11
Jim Fulton
jim@zope.com
Wed, 30 Jul 2003 10:34:46 -0400
Update of /cvs-repository/Zope3/doc/zcml
In directory cvs.zope.org:/tmp/cvs-serv939/doc/zcml
Modified Files:
meta.stx
Log Message:
Updated to point to src/zope/configuration/README.txt and
src/zope/configuration/tests/test_simple.py.
=== Zope3/doc/zcml/meta.stx 1.10 => 1.11 ===
--- Zope3/doc/zcml/meta.stx:1.10 Mon Jul 14 09:31:29 2003
+++ Zope3/doc/zcml/meta.stx Wed Jul 30 10:34:41 2003
@@ -5,363 +5,8 @@
zcml configuration directives to define the configuration directives
that will actually be used to configure the system.
- The Meta Configuration Directives
+ An overview of the meta-configuration system can be found in
+ src/zope/configuration/README.txt.
- The metaconfiguration directives are 'directives', 'directive', and
- 'subdirective'. We'll use a shortened version of the
- meta-configuration for the Zope StartUp system to explain the
- concepts (note that this may not bear any real relationship
- to the actual StartUp system configuration)::
-
- <ZopeConfigure xmlns="http://namespaces.zope.org/zope">
- <directives namespace="http://namespaces.zope.org/startup">
- <directive
- name="registerRequestFactory"
- handler="Zope.StartUp.metaConfigure.registerRequestFactory"
- description="Register a factory component that will produce
- request objects when requested by the publisher."
- >
- <attribute
- name="name"
- description="the name used to refer to this factory" />
- <attribute
- name="publication"
- description="resolvable name of the publication component" />
- <attribute
- name="request"
- required="yes"
- description="resolvable name of the component that implements
- the factory" />
- </directive>
- <directive
- name="defineSite"
- handler="Zope.Startup.metaConfigure.defineSite">
- description="Declare a particular server instance"
- >
- <attribute
- name="name"
- description="text label that identifies the server" />
- <attribute
- name="threads"
- description="the number of threads that will be used to service
- requests to this server" />
- <subdirective
- name="useFileStorage"
- description="Indicate this server should use a FileStorage" />
- >
- <attribute
- name="file"
- description="the name of the file to use for the FileStorage" />
- </subdirective>
- <subdirective
- name="useMappingStorage"
- description="Indicate that this server should use a MappingStorage" />
- </directive>
- </directives>
- </ZopeConfigure>
-
- The ZopeConfigure is the standard boilerplate. Inside this we
- have a 'directives' directive. This directive basically gives
- us a place to declare the namespace the contained directives
- will be in so we don't have to repeat it on each directive
- declaration. The namespace attribute gives the XML namespace
- for the directives.
-
- The first example directive directive defines a directive that
- has no subdirectives. The 'name' attribute gives the name of
- the directive. So in this case we are defining the
- 'registerRequestFactory' directive.
-
- The 'description' attribute allows us to provide explanatory
- text about the directive. This text will be extracted by tools
- for display to users of the configuration system. By convention
- this text may use any in-line StructuredText markup.
-
- Combining the namespace with the name, we are defining a directive
- that would look something like this when used::
-
- <ZopeConfigure
- xmlns="http://namespaces.zope.org/zope"
- xmlns:startup="http://namespaces.zope.org/startup"
- >
- <startup:registerRequestFactory ....>
- </ZopeConfigure>
-
- Inside the directive directive we have 'attribute' subdirectives.
- These allow us to name and describe the allowed attributes
- of the directive we are defining. This allows us to fill in
- the "..." in the example above: the various attribute subdirectives
- let us know that the registerRequestFactory directive can have
- attributes named 'name', 'publication', or 'request'. It also
- tells us that 'publication' is optional, and describes what the
- attributes are for. (Of course, in real code the descriptions
- would be more meaningful).
-
- The valid values for 'required' are 'yes' and 'no'. Yes means
- the attribute must be present or an error will be raised (this
- is done by the python code that implements the directive).
- A value of 'no' means that the attribute can always be omitted.
- If this attribute is not specified when configuring a directive,
- then either the attribute is optional or it may be part of a
- set of attributes one of which must be specified. However,
- all this is a documentation convention; how the directive
- actually works is determined by the python code that implements it.
-
- So, the directive we are defining can look something like this
- when used::
-
- <ZopeConfigure
- xmlns="http://namespaces.zope.org/zope"
- xmlns:startup="http://namespaces.zope.org/startup"
- >
- <startup:registerRequestFactory
- name="VFSRequestFactory"
- publication="Zope.App.Publication.VFS.Publication.VFSPublication"
- request="Zope.Publisher.VFS.VFSRequest." />
- </ZopeConfigure>
-
- The 'handler' attribute is what defines which python code will
- handle the directive. It must be a resolvable name following
- the standard zcml rules.
-
- The second directive example shows how to define a directive
- that can take subdirectives. Subdirectives are only meaningful
- when they appear inside their enclosing directive, and in fact
- each directive is effectively its own namespace from this point
- of view.
-
- There is no need to specify a handler method explicitly for a
- subdirective. By default, the configuration system will look
- for a method with the same name as the subdirective on an object
- specified by the handler for the enclosing directive. If this
- is not correct, and the handler method is named something else,
- you can use the 'handler_method' attribute to specify the correct
- name to be looked up.
-
- Note that subdirectives may have subdirectives.
-
- So, given the zcml above, we have defined a directive and
- subdirective that would look something like this when used::
-
- <ZopeConfigure
- xmlns="http://namespaces.zope.org/zope"
- xmlns:startup="http://namespaces.zope.org/startup"
- >
- <startup:defineSite name="Zope 3 Default" threads="4">
- <startup:useFileStorage file="Data.fs">
- </startup:defineSite>
- </ZopeConfigure>
-
-
- How Configuration Directives Become Actions
-
- When the configuration system processes configuration directives,
- it calls the handlers for each directive or subdirective it
- encounters. But the handler method is *not* responsible for
- taking whatever action it is that the directive is supposed to
- accomplish. Instead, the handler is responsible for the
- generation of a list of "actions" accompanied by "discriminators".
- The configuration system uses the discriminators to resolve
- conflicts between directives. (Recall that in case of conflict,
- an action returned by an included file will be overridden, while
- conflicts generated within the same file will be treated as
- errors.)
-
- An "action" is a python tuple with the following elements
- (IEmptyDirective is the canonical source for this definition)::
-
- - the discriminator
-
- - a callable object
-
- - an argument tuple
-
- - an optional keyword argument dictionary
-
- Once conflict resolution has been done, the configuration system
- processes the actions one by one by calling each callable and
- passing it the provided argument tuple and keyword dictionary.
-
- Thus, to implement directives, we implement handlers that manage
- the generation of action tuples encoding the calls to the methods
- that will actually perform the configuration actions.
-
- To aid python code in generating correctly formed Actions, there
- is an Action function defined in Zope.Configuration.Action. It
- takes four keyword arguments, 'discriminator', 'callable', 'args',
- and 'kw', which have the obvious meanings, and returns a properly
- arranged tuple.
-
-
- Implementing Directives with No Subdirectives
-
- The simplest type of directive (or subdirective) to implement
- is one that has no subdirectives. And the only difference
- between the two is where the handler method is actually located.
- In both cases, the handler must conform to the IEmptyDirective
- interface. That interface mandates that when called, the handler
- will return a list of actions. So, our registerRequestFactory
- directive above might have implementation code that looks
- something like this::
-
- from Zope.Configuration.Action import Action
- from Zope.StartUp.RequestFactory import RequestFactory
- def registerRequestFactory(_context, name, publication, request):
- publication = _context.resolve(publication)
- request = _context.resolve(request)
- return [
- Action(
- discriminator = ('startup:registerRequestFactory', name),
- callable = RequestFactoryRegistry.registerRequestFactory
- args = (name, publication, request),
- )
- ]
- registerRequestFactory.__implements__ = IEmptyDirective
-
- The first argument passed to the handler is an "execution
- context". The most important method it provides is 'resolve',
- which will take a dotted string and resolve it into a python
- object using the standard zcml rules. This method uses it to
- turn the values of the directive's publication and request
- attributes into python objects. The callable for the action
- is obtained from one of the other modules in the package. It's
- the code that will do the actual setup work this directive is
- intended to accomplish. The values of the 'name', 'publication',
- and 'request' attributes, in resolved form, are included in the
- Action to be passed to it as arguments.
-
- Note that we include the name of the directive, including the
- conventional short form of its namespace name, as part of a
- tuple to be used as the discriminator. This gives us reasonable
- assurance that this discriminator will match only other instances
- of registerRequestFactory directives that the configuration
- user might be specifying as overrides. The discriminator is
- arbitrary, but the programmer needs to take care that it is
- exactly as unique as it needs to be within the set of all actions
- the configuration system may need to deal with.
-
- Also note that by using positional arguments only we have made
- all the attributes required. If any are omitted from the
- directive, python will complain about missing arguments. Making
- an argument a keyword argument makes the correspondingly named
- attribute optional.
-
- Implementing Directives that have Subdirectives
-
- The handler for a directive that has subdirectives must conform
- to the INonEmptyDirective Interface. This means that when
- called it must return an object that implements the
- ISubdirectiveHandler Interface.
-
- When a subdirective is defined, a handler method for the
- subdirective is either specified or defaults to the name of the
- subdirective. This handler will be looked up on the
- ISubdirectiveHandler that the INonEmptyDirective returns.
-
- When a directive with subdirectives is processed, first the
- INonEmptyDirective is called to get the ISubdirectiveHandler.
- Then each subdirective is processed by calling the appropriate
- method on the ISubdirectiveHandler. Finally, the ISubdirectiveHandler
- itself is called, to give it an opportunity to specify
- directive-level actions (it does not have to, it can return an
- empty action list).
-
- The easiest way to implement an INonEmptyDirective is to create
- a class with the subdirective methods and a '__call__' method
- on it. 'Calling' the class (that is, instantiating an instance)
- then results in an object that implements ISubdirectiveHandler.
-
- For example, 'Zope.Startup.metaConfigure.defineSite' might be
- defined as follows::
-
- from Zope.StartUp.SiteDefinition import SiteDefinition
- class defineSite:
-
- classProvides(INonEmptyDirective)
- implements(ISubdirectiveHandler)
-
- def __init__(self, _context, name="default" threads=4):
- SiteDefinition.registerSite(name)
- self._name = name
- self._threads = int(threads)
-
- def useFileStorage(self, _context, file=DEFAULT_STORAGE_FILE):
- return [
- Action(
- discriminator=('startup:defineSite','storage',name),
- callable=SiteDefinition.useFileStorage,
- args=(self._name,file,)
- )
- ]
-
- def useMappingStorage(self, _context):
- return [
- Action(
- discriminator=('startup:defineSite','storage',name),
- callable=SiteDefinition.useMappingStorage,
- args=(self._name,)
- )
- ]
-
- def __call__(self):
- return [
- Action(
- discriminator=('startup:defineSite','threads',name),
- callable=SiteDefinition.setThreads,
- args=self._name,self._threads)
- )
- ]
-
- The two implements equates document the fact that the class
- implements INonEmptyDirective, while the objects returned by
- it implement ISubdirectiveHandler.
-
- In the init, we save the attribute information we've been passed
- so we can use it both in processing the subdirectives and in
- the actions returned by the final call. We also call the object
- that manages the actual configuration to tell it to initialize
- a configuration for the site that has been named. (Note: an
- alternative implementation would be to make the ISubdireciveHandler
- a singleton, in which case it could store the configuration
- information directly on itself). This actual configuration
- action done at parse time is both necessary and safe. Necessary
- because the subdirective actions need to be able to alter the
- configuration of the named site, so it needs to be set up. Safe,
- because no matter which action directives ultimately win out
- during conflict resolution, we know that *some* configuration
- is going to be needed for this named site.
-
- The two subdirective methods use the same discriminator, and
- the name of the site being defined is included in the discriminator.
- This means that a given site can only use one or the other
- storage. If both subdirectives are specified for the same site
- in the same configuration file, we'll get a configuration error.
- On the other hand, if one storage is specified in a given
- configuration file, but the configuration file that includes
- it (or some file above it) specifies a different storage, then
- the conflict resolution will allow the upper level file to
- override the lower.
-
- The action method returned by the storage directives will
- actually set the configuration to the specified storage type.
- Only the storage setting action that has made it through conflict
- resolution will get executed.
-
- The '__call__' method of the ISubdirectiveHandler returns an
- action that will tell the configuration object to set the thread
- count for the site being defined. Again, if a site definition
- in an included file specifies a thread count, it will get
- overridden by a site definition in an upper level file because
- of the discriminator conflict resolution.
-
- Overriding Directive Definitions
-
- Although it is not something that is useful in most normal
- projects, it is possible to override the definition of a
- directive. Unlike with configuration actions, where directives
- in included files are overridden by directives in the including
- files, metaconfiguration directives take effect as soon as they
- are encountered. When you re-specify the definition of a directive,
- you affect only the attributes of that directive. Any defined
- attributes or subdirectives remain defined unless they, too,
- are overridden. Currently it is not possible to delete a directive.
+ Instructions for creating simple configuration directives can be
+ found in src/zope/configuration/tests/test_simple.py.