summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJason Madden <jason+github@nextthought.com>2018-09-28 06:46:24 -0500
committerGitHub <noreply@github.com>2018-09-28 06:46:24 -0500
commit7c98fbbc5b1b864ce76ef235c8152054eaf6b96f (patch)
treec310c6b4fd73737ff127f479ee7725d142656ceb
parentee3c202b62ca09fda23a5d02ab7ae8118b8f6635 (diff)
parente313ece59efb2bc4c5928377376b170ff4ba3f94 (diff)
downloadzope-configuration-7c98fbbc5b1b864ce76ef235c8152054eaf6b96f.tar.gz
Merge pull request #44 from zopefoundation/issue40
Unify docs back to .py files
-rw-r--r--docs/api/config.rst1037
-rw-r--r--docs/api/docutils.rst29
-rw-r--r--docs/api/exceptions.rst9
-rw-r--r--docs/api/fields.rst352
-rw-r--r--docs/api/interfaces.rst19
-rw-r--r--docs/api/name.rst13
-rw-r--r--docs/api/xmlconfig.rst214
-rw-r--r--docs/api/zopeconfigure.rst5
-rw-r--r--docs/conf.py7
-rw-r--r--src/zope/configuration/config.py899
-rw-r--r--src/zope/configuration/docutils.py34
-rw-r--r--src/zope/configuration/fields.py365
-rw-r--r--src/zope/configuration/interfaces.py29
-rw-r--r--src/zope/configuration/name.py6
-rw-r--r--src/zope/configuration/tests/test_docs.py33
-rw-r--r--src/zope/configuration/xmlconfig.py172
-rw-r--r--src/zope/configuration/zopeconfigure.py6
17 files changed, 1486 insertions, 1743 deletions
diff --git a/docs/api/config.rst b/docs/api/config.rst
index c1a5662..df1d205 100644
--- a/docs/api/config.rst
+++ b/docs/api/config.rst
@@ -1,1034 +1,5 @@
-:mod:`zope.configuration.config`
-================================
+===========================
+ zope.configuration.config
+===========================
-.. module:: zope.configuration.config
-
-.. autoclass:: ConfigurationContext
-
- .. automethod:: resolve
-
- Examples:
-
- .. doctest::
-
- >>> from zope.configuration.config import ConfigurationContext
- >>> from zope.configuration.config import ConfigurationError
- >>> c = ConfigurationContext()
- >>> import zope, zope.interface
- >>> c.resolve('zope') is zope
- True
- >>> c.resolve('zope.interface') is zope.interface
- True
- >>> c.resolve('zope.configuration.eek') #doctest: +NORMALIZE_WHITESPACE
- Traceback (most recent call last):
- ...
- ConfigurationError:
- ImportError: Module zope.configuration has no global eek
-
- >>> c.resolve('.config.ConfigurationContext')
- Traceback (most recent call last):
- ...
- AttributeError: 'ConfigurationContext' object has no attribute 'package'
- >>> import zope.configuration
- >>> c.package = zope.configuration
- >>> c.resolve('.') is zope.configuration
- True
- >>> c.resolve('.config.ConfigurationContext') is ConfigurationContext
- True
- >>> c.resolve('..interface') is zope.interface
- True
- >>> c.resolve('str') is str
- True
-
- .. automethod:: path
-
- Examples:
-
- .. doctest::
-
- >>> import os
- >>> from zope.configuration.config import ConfigurationContext
- >>> c = ConfigurationContext()
- >>> c.path("/x/y/z") == os.path.normpath("/x/y/z")
- True
- >>> c.path("y/z")
- Traceback (most recent call last):
- ...
- AttributeError: 'ConfigurationContext' object has no attribute 'package'
- >>> import zope.configuration
- >>> c.package = zope.configuration
- >>> import os
- >>> d = os.path.dirname(zope.configuration.__file__)
- >>> c.path("y/z") == d + os.path.normpath("/y/z")
- True
- >>> c.path("y/./z") == d + os.path.normpath("/y/z")
- True
- >>> c.path("y/../z") == d + os.path.normpath("/z")
- True
-
- .. automethod:: checkDuplicate
-
- Examples:
-
- .. doctest::
-
- >>> from zope.configuration.config import ConfigurationContext
- >>> from zope.configuration.config import ConfigurationError
- >>> c = ConfigurationContext()
- >>> c.checkDuplicate('/foo.zcml')
- >>> try:
- ... c.checkDuplicate('/foo.zcml')
- ... except ConfigurationError as e:
- ... # On Linux the exact msg has /foo, on Windows \foo.
- ... str(e).endswith("foo.zcml' included more than once")
- True
-
- You may use different ways to refer to the same file:
-
- .. doctest::
-
- >>> import zope.configuration
- >>> c.package = zope.configuration
- >>> import os
- >>> d = os.path.dirname(zope.configuration.__file__)
- >>> c.checkDuplicate('bar.zcml')
- >>> try:
- ... c.checkDuplicate(d + os.path.normpath('/bar.zcml'))
- ... except ConfigurationError as e:
- ... str(e).endswith("bar.zcml' included more than once")
- ...
- True
-
- .. automethod:: processFile
-
- Examples:
-
- .. doctest::
-
- >>> from zope.configuration.config import ConfigurationContext
- >>> c = ConfigurationContext()
- >>> c.processFile('/foo.zcml')
- True
- >>> c.processFile('/foo.zcml')
- False
-
- You may use different ways to refer to the same file:
-
- .. doctest::
-
- >>> import zope.configuration
- >>> c.package = zope.configuration
- >>> import os
- >>> d = os.path.dirname(zope.configuration.__file__)
- >>> c.processFile('bar.zcml')
- True
- >>> c.processFile('bar.zcml')
- False
-
- .. automethod:: action
-
- Examples:
-
- .. doctest::
-
- >>> from zope.configuration.config import ConfigurationContext
- >>> c = ConfigurationContext()
-
- Normally, the context gets actions from subclasses. We'll provide
- an actions attribute ourselves:
-
- .. doctest::
-
- >>> c.actions = []
-
- We'll use a test callable that has a convenient string representation
-
- .. doctest::
-
- >>> from zope.configuration.tests.directives import f
- >>> c.action(1, f, (1, ), {'x': 1})
- >>> from pprint import PrettyPrinter
- >>> pprint=PrettyPrinter(width=60).pprint
- >>> pprint(c.actions)
- [{'args': (1,),
- 'callable': f,
- 'discriminator': 1,
- 'includepath': (),
- 'info': '',
- 'kw': {'x': 1},
- 'order': 0}]
-
- >>> c.action(None)
- >>> pprint(c.actions)
- [{'args': (1,),
- 'callable': f,
- 'discriminator': 1,
- 'includepath': (),
- 'info': '',
- 'kw': {'x': 1},
- 'order': 0},
- {'args': (),
- 'callable': None,
- 'discriminator': None,
- 'includepath': (),
- 'info': '',
- 'kw': {},
- 'order': 0}]
-
- Now set the include path and info:
-
- .. doctest::
-
- >>> c.includepath = ('foo.zcml',)
- >>> c.info = "?"
- >>> c.action(None)
- >>> pprint(c.actions[-1])
- {'args': (),
- 'callable': None,
- 'discriminator': None,
- 'includepath': ('foo.zcml',),
- 'info': '?',
- 'kw': {},
- 'order': 0}
-
- We can add an order argument to crudely control the order
- of execution:
-
- .. doctest::
-
- >>> c.action(None, order=99999)
- >>> pprint(c.actions[-1])
- {'args': (),
- 'callable': None,
- 'discriminator': None,
- 'includepath': ('foo.zcml',),
- 'info': '?',
- 'kw': {},
- 'order': 99999}
-
- We can also pass an includepath argument, which will be used as the the
- includepath for the action. (if includepath is None, self.includepath
- will be used):
-
- .. doctest::
-
- >>> c.action(None, includepath=('abc',))
- >>> pprint(c.actions[-1])
- {'args': (),
- 'callable': None,
- 'discriminator': None,
- 'includepath': ('abc',),
- 'info': '?',
- 'kw': {},
- 'order': 0}
-
- We can also pass an info argument, which will be used as the the
- source line info for the action. (if info is None, self.info will be
- used):
-
- .. doctest::
-
- >>> c.action(None, info='abc')
- >>> pprint(c.actions[-1])
- {'args': (),
- 'callable': None,
- 'discriminator': None,
- 'includepath': ('foo.zcml',),
- 'info': 'abc',
- 'kw': {},
- 'order': 0}
-
- .. automethod:: hasFeature
-
- Examples:
-
- .. doctest::
-
- >>> from zope.configuration.config import ConfigurationContext
- >>> c = ConfigurationContext()
- >>> c.hasFeature('onlinehelp')
- False
-
- You can declare that a feature is provided
-
- .. doctest::
-
- >>> c.provideFeature('onlinehelp')
-
- and it becomes available
-
- .. doctest::
-
- >>> c.hasFeature('onlinehelp')
- True
-
- .. automethod:: provideFeature
-
-.. autoclass:: ConfigurationAdapterRegistry
- :members:
- :member-order: bysource
-
- Examples:
-
- .. doctest::
-
- >>> from zope.configuration.interfaces import IConfigurationContext
- >>> from zope.configuration.config import ConfigurationAdapterRegistry
- >>> from zope.configuration.config import ConfigurationError
- >>> from zope.configuration.config import ConfigurationMachine
- >>> r = ConfigurationAdapterRegistry()
- >>> c = ConfigurationMachine()
- >>> r.factory(c, ('http://www.zope.com','xxx'))
- Traceback (most recent call last):
- ...
- ConfigurationError: ('Unknown directive', 'http://www.zope.com', 'xxx')
- >>> def f():
- ... pass
-
- >>> r.register(IConfigurationContext, ('http://www.zope.com', 'xxx'), f)
- >>> r.factory(c, ('http://www.zope.com','xxx')) is f
- True
- >>> r.factory(c, ('http://www.zope.com','yyy')) is f
- Traceback (most recent call last):
- ...
- ConfigurationError: ('Unknown directive', 'http://www.zope.com', 'yyy')
- >>> r.register(IConfigurationContext, 'yyy', f)
- >>> r.factory(c, ('http://www.zope.com','yyy')) is f
- True
-
- Test the documentation feature:
-
- .. doctest::
-
- >>> from zope.configuration.config import IFullInfo
- >>> r._docRegistry
- []
- >>> r.document(('ns', 'dir'), IFullInfo, IConfigurationContext, None,
- ... 'inf', None)
- >>> r._docRegistry[0][0] == ('ns', 'dir')
- True
- >>> r._docRegistry[0][1] is IFullInfo
- True
- >>> r._docRegistry[0][2] is IConfigurationContext
- True
- >>> r._docRegistry[0][3] is None
- True
- >>> r._docRegistry[0][4] == 'inf'
- True
- >>> r._docRegistry[0][5] is None
- True
- >>> r.document('all-dir', None, None, None, None)
- >>> r._docRegistry[1][0]
- ('', 'all-dir')
-
-.. autoclass:: ConfigurationMachine
-
- Example:
-
- .. doctest::
-
- >>> from zope.configuration.config import ConfigurationMachine
- >>> machine = ConfigurationMachine()
- >>> ns = "http://www.zope.org/testing"
-
- Register a directive:
-
- .. doctest::
-
- >>> from zope.configuration.config import metans
- >>> machine((metans, "directive"),
- ... namespace=ns, name="simple",
- ... schema="zope.configuration.tests.directives.ISimple",
- ... handler="zope.configuration.tests.directives.simple")
-
- and try it out:
-
- .. doctest::
-
- >>> machine((ns, "simple"), a=u"aa", c=u"cc")
- >>> from pprint import PrettyPrinter
- >>> pprint = PrettyPrinter(width=60).pprint
- >>> pprint(machine.actions)
- [{'args': ('aa', 'xxx', 'cc'),
- 'callable': f,
- 'discriminator': ('simple', 'aa', 'xxx', 'cc'),
- 'includepath': (),
- 'info': None,
- 'kw': {},
- 'order': 0}]
-
- .. automethod:: begin
-
- .. automethod:: end
-
- .. automethod:: __call__
-
- .. automethod:: getInfo
-
- .. automethod:: setInfo
-
- .. automethod:: execute_actions
-
- For example:
-
- .. doctest::
-
- >>> from zope.configuration.config import ConfigurationMachine
- >>> output = []
- >>> def f(*a, **k): #* syntax highlighting
- ... output.append(('f', a, k))
- >>> context = ConfigurationMachine()
- >>> context.actions = [
- ... (1, f, (1,)),
- ... (1, f, (11,), {}, ('x', )),
- ... (2, f, (2,)),
- ... ]
- >>> context.execute_actions()
- >>> output
- [('f', (1,), {}), ('f', (2,), {})]
-
- If the action raises an error, we convert it to a
- ConfigurationExecutionError.
-
- .. doctest::
-
- >>> from zope.configuration.config import ConfigurationExecutionError
- >>> output = []
- >>> def bad():
- ... bad.xxx
- >>> context.actions = [
- ... (1, f, (1,)),
- ... (1, f, (11,), {}, ('x', )),
- ... (2, f, (2,)),
- ... (3, bad, (), {}, (), 'oops')
- ... ]
- >>> try:
- ... v = context.execute_actions()
- ... except ConfigurationExecutionError as e:
- ... v = e
- >>> lines = str(v).splitlines()
- >>> 'AttributeError' in lines[0]
- True
- >>> lines[0].endswith("'function' object has no attribute 'xxx'")
- True
- >>> lines[1:]
- [' in:', ' oops']
-
- Note that actions executed before the error still have an effect:
-
- .. doctest::
-
- >>> output
- [('f', (1,), {}), ('f', (2,), {})]
-
-.. autoclass:: ConfigurationExecutionError
-
-.. autointerface:: IStackItem
- :members:
- :member-order: bysource
-
-.. autoclass:: SimpleStackItem
- :members:
- :member-order: bysource
-
-.. autoclass:: RootStackItem
- :members:
- :member-order: bysource
-
-.. autoclass:: GroupingStackItem
- :members:
- :member-order: bysource
-
- To see how this works, let's look at an example:
-
- We need a context. We'll just use a configuration machine
-
- .. doctest::
-
- >>> from zope.configuration.config import GroupingStackItem
- >>> from zope.configuration.config import ConfigurationMachine
- >>> context = ConfigurationMachine()
-
- We need a callable to use in configuration actions. We'll use a
- convenient one from the tests:
-
- .. doctest::
-
- >>> from zope.configuration.tests.directives import f
-
- We need a handler for the grouping directive. This is a class
- that implements a context decorator. The decorator must also
- provide ``before`` and ``after`` methods that are called before
- and after any contained directives are processed. We'll typically
- subclass ``GroupingContextDecorator``, which provides context
- decoration, and default ``before`` and ``after`` methods.
-
- .. doctest::
-
- >>> from zope.configuration.config import GroupingContextDecorator
- >>> class SampleGrouping(GroupingContextDecorator):
- ... def before(self):
- ... self.action(('before', self.x, self.y), f)
- ... def after(self):
- ... self.action(('after'), f)
-
- We'll use our decorator to decorate our initial context, providing
- keyword arguments x and y:
-
- .. doctest::
-
- >>> dec = SampleGrouping(context, x=1, y=2)
-
- Note that the keyword arguments are made attributes of the
- decorator.
-
- Now we'll create the stack item.
-
- .. doctest::
-
- >>> item = GroupingStackItem(dec)
-
- We still haven't called the before action yet, which we can verify
- by looking at the context actions:
-
- .. doctest::
-
- >>> context.actions
- []
-
- Subdirectives will get looked up as adapters of the context.
-
- We'll create a simple handler:
-
- .. doctest::
-
- >>> def simple(context, data, info):
- ... context.action(("simple", context.x, context.y, data), f)
- ... return info
-
- and register it with the context:
-
- .. doctest::
-
- >>> from zope.configuration.interfaces import IConfigurationContext
- >>> from zope.configuration.config import testns
- >>> context.register(IConfigurationContext, (testns, 'simple'), simple)
-
- This handler isn't really a propert handler, because it doesn't
- return a new context. It will do for this example.
-
- Now we'll call the contained method on the stack item:
-
- .. doctest::
-
- >>> item.contained((testns, 'simple'), {'z': 'zope'}, "someinfo")
- 'someinfo'
-
- We can verify thet the simple method was called by looking at the
- context actions. Note that the before method was called before
- handling the contained directive.
-
- .. doctest::
-
- >>> from pprint import PrettyPrinter
- >>> pprint = PrettyPrinter(width=60).pprint
-
- >>> pprint(context.actions)
- [{'args': (),
- 'callable': f,
- 'discriminator': ('before', 1, 2),
- 'includepath': (),
- 'info': '',
- 'kw': {},
- 'order': 0},
- {'args': (),
- 'callable': f,
- 'discriminator': ('simple', 1, 2, {'z': 'zope'}),
- 'includepath': (),
- 'info': '',
- 'kw': {},
- 'order': 0}]
-
- Finally, we call finish, which calls the decorator after method:
-
- .. doctest::
-
- >>> item.finish()
-
- >>> pprint(context.actions)
- [{'args': (),
- 'callable': f,
- 'discriminator': ('before', 1, 2),
- 'includepath': (),
- 'info': '',
- 'kw': {},
- 'order': 0},
- {'args': (),
- 'callable': f,
- 'discriminator': ('simple', 1, 2, {'z': 'zope'}),
- 'includepath': (),
- 'info': '',
- 'kw': {},
- 'order': 0},
- {'args': (),
- 'callable': f,
- 'discriminator': 'after',
- 'includepath': (),
- 'info': '',
- 'kw': {},
- 'order': 0}]
-
- If there were no nested directives:
-
- .. doctest::
-
- >>> context = ConfigurationMachine()
- >>> dec = SampleGrouping(context, x=1, y=2)
- >>> item = GroupingStackItem(dec)
- >>> item.finish()
-
- Then before will be when we call finish:
-
- .. doctest::
-
- >>> pprint(context.actions)
- [{'args': (),
- 'callable': f,
- 'discriminator': ('before', 1, 2),
- 'includepath': (),
- 'info': '',
- 'kw': {},
- 'order': 0},
- {'args': (),
- 'callable': f,
- 'discriminator': 'after',
- 'includepath': (),
- 'info': '',
- 'kw': {},
- 'order': 0}]
-
-.. autoclass:: ComplexStackItem
- :members:
- :member-order: bysource
-
- To see how this works, let's look at an example:
-
- We need a context. We'll just use a configuration machine
-
- .. doctest::
-
- >>> from zope.configuration.config import ConfigurationMachine
- >>> context = ConfigurationMachine()
-
- We need a callable to use in configuration actions. We'll use a
- convenient one from the tests:
-
- .. doctest::
-
- >>> from zope.configuration.tests.directives import f
-
- We need a handler for the complex directive. This is a class
- with a method for each subdirective:
-
- .. doctest::
-
- >>> class Handler(object):
- ... def __init__(self, context, x, y):
- ... self.context, self.x, self.y = context, x, y
- ... context.action('init', f)
- ... def sub(self, context, a, b):
- ... context.action(('sub', a, b), f)
- ... def __call__(self):
- ... self.context.action(('call', self.x, self.y), f)
-
- We need a complex directive definition:
-
- .. doctest::
-
- >>> from zope.interface import Interface
- >>> from zope.schema import TextLine
- >>> from zope.configuration.config import ComplexDirectiveDefinition
- >>> class Ixy(Interface):
- ... x = TextLine()
- ... y = TextLine()
- >>> definition = ComplexDirectiveDefinition(
- ... context, name="test", schema=Ixy,
- ... handler=Handler)
- >>> class Iab(Interface):
- ... a = TextLine()
- ... b = TextLine()
- >>> definition['sub'] = Iab, ''
-
- OK, now that we have the context, handler and definition, we're
- ready to use a stack item.
-
- .. doctest::
-
- >>> from zope.configuration.config import ComplexStackItem
- >>> item = ComplexStackItem(definition, context, {'x': u'xv', 'y': u'yv'},
- ... 'foo')
-
- When we created the definition, the handler (factory) was called.
-
- .. doctest::
-
- >>> from pprint import PrettyPrinter
- >>> pprint = PrettyPrinter(width=60).pprint
- >>> pprint(context.actions)
- [{'args': (),
- 'callable': f,
- 'discriminator': 'init',
- 'includepath': (),
- 'info': 'foo',
- 'kw': {},
- 'order': 0}]
-
- If a subdirective is provided, the ``contained`` method of the stack item
- is called. It will lookup the subdirective schema and call the
- corresponding method on the handler instance:
-
- .. doctest::
-
- >>> simple = item.contained(('somenamespace', 'sub'),
- ... {'a': u'av', 'b': u'bv'}, 'baz')
- >>> simple.finish()
-
- Note that the name passed to ``contained`` is a 2-part name, consisting of
- a namespace and a name within the namespace.
-
- .. doctest::
-
- >>> pprint(context.actions)
- [{'args': (),
- 'callable': f,
- 'discriminator': 'init',
- 'includepath': (),
- 'info': 'foo',
- 'kw': {},
- 'order': 0},
- {'args': (),
- 'callable': f,
- 'discriminator': ('sub', 'av', 'bv'),
- 'includepath': (),
- 'info': 'baz',
- 'kw': {},
- 'order': 0}]
-
- The new stack item returned by contained is one that doesn't allow
- any more subdirectives,
-
- When all of the subdirectives have been provided, the ``finish``
- method is called:
-
- .. doctest::
-
- >>> item.finish()
-
- The stack item will call the handler if it is callable.
-
- .. doctest::
-
- >>> pprint(context.actions)
- [{'args': (),
- 'callable': f,
- 'discriminator': 'init',
- 'includepath': (),
- 'info': 'foo',
- 'kw': {},
- 'order': 0},
- {'args': (),
- 'callable': f,
- 'discriminator': ('sub', 'av', 'bv'),
- 'includepath': (),
- 'info': 'baz',
- 'kw': {},
- 'order': 0},
- {'args': (),
- 'callable': f,
- 'discriminator': ('call', 'xv', 'yv'),
- 'includepath': (),
- 'info': 'foo',
- 'kw': {},
- 'order': 0}]
-
-.. autoclass:: GroupingContextDecorator
- :members:
- :member-order: bysource
-
-.. autoclass:: DirectiveSchema
- :members:
- :member-order: bysource
-
-.. autointerface:: IDirectivesInfo
- :members:
- :member-order: bysource
-
-.. autointerface:: IDirectivesContext
- :members:
- :member-order: bysource
-
-.. autoclass:: DirectivesHandler
- :members:
- :member-order: bysource
-
-.. autointerface:: IDirectiveInfo
- :members:
- :member-order: bysource
-
-.. autointerface:: IFullInfo
- :members:
- :member-order: bysource
-
-.. autointerface:: IStandaloneDirectiveInfo
- :members:
- :member-order: bysource
-
-.. autofunction:: defineSimpleDirective
-
- Example:
-
- .. doctest::
-
- >>> from zope.configuration.config import ConfigurationMachine
- >>> context = ConfigurationMachine()
- >>> from zope.interface import Interface
- >>> from zope.schema import TextLine
- >>> from zope.configuration.tests.directives import f
- >>> class Ixy(Interface):
- ... x = TextLine()
- ... y = TextLine()
- >>> def s(context, x, y):
- ... context.action(('s', x, y), f)
-
- >>> from zope.configuration.config import defineSimpleDirective
- >>> defineSimpleDirective(context, 's', Ixy, s, testns)
-
- >>> context((testns, "s"), x=u"vx", y=u"vy")
- >>> from pprint import PrettyPrinter
- >>> pprint = PrettyPrinter(width=60).pprint
- >>> pprint(context.actions)
- [{'args': (),
- 'callable': f,
- 'discriminator': ('s', 'vx', 'vy'),
- 'includepath': (),
- 'info': None,
- 'kw': {},
- 'order': 0}]
-
- >>> context(('http://www.zope.com/t1', "s"), x=u"vx", y=u"vy")
- Traceback (most recent call last):
- ...
- ConfigurationError: ('Unknown directive', 'http://www.zope.com/t1', 's')
-
- >>> context = ConfigurationMachine()
- >>> defineSimpleDirective(context, 's', Ixy, s, "*")
-
- >>> context(('http://www.zope.com/t1', "s"), x=u"vx", y=u"vy")
- >>> pprint(context.actions)
- [{'args': (),
- 'callable': f,
- 'discriminator': ('s', 'vx', 'vy'),
- 'includepath': (),
- 'info': None,
- 'kw': {},
- 'order': 0}]
-
-.. autofunction:: defineGroupingDirective
-
- Example:
-
- .. doctest::
-
- >>> from zope.configuration.config import ConfigurationMachine
- >>> context = ConfigurationMachine()
- >>> from zope.interface import Interface
- >>> from zope.schema import TextLine
- >>> from zope.configuration.tests.directives import f
- >>> class Ixy(Interface):
- ... x = TextLine()
- ... y = TextLine()
-
- We won't bother creating a special grouping directive class. We'll
- just use :class:`GroupingContextDecorator`, which simply sets up a
- grouping context that has extra attributes defined by a schema:
-
- .. doctest::
-
- >>> from zope.configuration.config import defineGroupingDirective
- >>> from zope.configuration.config import GroupingContextDecorator
- >>> defineGroupingDirective(context, 'g', Ixy,
- ... GroupingContextDecorator, testns)
-
- >>> context.begin((testns, "g"), x=u"vx", y=u"vy")
- >>> context.stack[-1].context.x
- 'vx'
- >>> context.stack[-1].context.y
- 'vy'
-
- >>> context(('http://www.zope.com/t1', "g"), x=u"vx", y=u"vy")
- Traceback (most recent call last):
- ...
- ConfigurationError: ('Unknown directive', 'http://www.zope.com/t1', 'g')
-
- >>> context = ConfigurationMachine()
- >>> defineGroupingDirective(context, 'g', Ixy,
- ... GroupingContextDecorator, "*")
-
- >>> context.begin(('http://www.zope.com/t1', "g"), x=u"vx", y=u"vy")
- >>> context.stack[-1].context.x
- 'vx'
- >>> context.stack[-1].context.y
- 'vy'
-
-.. autointerface:: IComplexDirectiveContext
- :members:
- :member-order: bysource
-
-.. autoclass:: ComplexDirectiveDefinition
- :members:
- :member-order: bysource
-
-.. autofunction:: subdirective
-
-.. autointerface:: IProvidesDirectiveInfo
- :members:
- :member-order: bysource
-
-.. autofunction:: provides
-
- Example:
-
- .. doctest::
-
- >>> from zope.configuration.config import ConfigurationContext
- >>> from zope.configuration.config import provides
- >>> c = ConfigurationContext()
- >>> provides(c, 'apidoc')
- >>> c.hasFeature('apidoc')
- True
-
- Spaces are not allowed in feature names (this is reserved for providing
- many features with a single directive in the futute).
-
- .. doctest::
-
- >>> provides(c, 'apidoc onlinehelp')
- Traceback (most recent call last):
- ...
- ValueError: Only one feature name allowed
-
- >>> c.hasFeature('apidoc onlinehelp')
- False
-
-.. autofunction:: toargs
-
- Example:
-
- .. doctest::
-
- >>> from zope.configuration.config import toargs
- >>> from zope.schema import BytesLine
- >>> from zope.schema import Float
- >>> from zope.schema import Int
- >>> from zope.schema import TextLine
- >>> from zope.schema import URI
- >>> class schema(Interface):
- ... in_ = Int(constraint=lambda v: v > 0)
- ... f = Float()
- ... n = TextLine(min_length=1, default=u"rob")
- ... x = BytesLine(required=False)
- ... u = URI()
-
- >>> context = ConfigurationMachine()
- >>> from pprint import PrettyPrinter
- >>> pprint=PrettyPrinter(width=50).pprint
-
- >>> pprint(toargs(context, schema,
- ... {'in': u'1', 'f': u'1.2', 'n': u'bob', 'x': u'x.y.z',
- ... 'u': u'http://www.zope.org' }))
- {'f': 1.2,
- 'in_': 1,
- 'n': 'bob',
- 'u': 'http://www.zope.org',
- 'x': b'x.y.z'}
-
- If we have extra data, we'll get an error:
-
- .. doctest::
-
- >>> toargs(context, schema,
- ... {'in': u'1', 'f': u'1.2', 'n': u'bob', 'x': u'x.y.z',
- ... 'u': u'http://www.zope.org', 'a': u'1'})
- Traceback (most recent call last):
- ...
- ConfigurationError: ('Unrecognized parameters:', 'a')
-
- Unless we set a tagged value to say that extra arguments are ok:
-
- .. doctest::
-
- >>> schema.setTaggedValue('keyword_arguments', True)
-
- >>> pprint(toargs(context, schema,
- ... {'in': u'1', 'f': u'1.2', 'n': u'bob', 'x': u'x.y.z',
- ... 'u': u'http://www.zope.org', 'a': u'1'}))
- {'a': '1',
- 'f': 1.2,
- 'in_': 1,
- 'n': 'bob',
- 'u': 'http://www.zope.org',
- 'x': b'x.y.z'}
-
- If we omit required data we get an error telling us what was omitted:
-
- .. doctest::
-
- >>> pprint(toargs(context, schema,
- ... {'in': u'1', 'f': u'1.2', 'n': u'bob', 'x': u'x.y.z'}))
- Traceback (most recent call last):
- ...
- ConfigurationError: ('Missing parameter:', 'u')
-
- Although we can omit not-required data:
-
- .. doctest::
-
- >>> pprint(toargs(context, schema,
- ... {'in': u'1', 'f': u'1.2', 'n': u'bob',
- ... 'u': u'http://www.zope.org', 'a': u'1'}))
- {'a': '1',
- 'f': 1.2,
- 'in_': 1,
- 'n': 'bob',
- 'u': 'http://www.zope.org'}
-
- And we can omit required fields if they have valid defaults
- (defaults that are valid values):
-
- .. doctest::
-
- >>> pprint(toargs(context, schema,
- ... {'in': u'1', 'f': u'1.2',
- ... 'u': u'http://www.zope.org', 'a': u'1'}))
- {'a': '1',
- 'f': 1.2,
- 'in_': 1,
- 'n': 'rob',
- 'u': 'http://www.zope.org'}
-
- We also get an error if any data was invalid:
-
- .. doctest::
-
- >>> pprint(toargs(context, schema,
- ... {'in': u'0', 'f': u'1.2', 'n': u'bob', 'x': u'x.y.z',
- ... 'u': u'http://www.zope.org', 'a': u'1'}))
- Traceback (most recent call last):
- ...
- ConfigurationError: ('Invalid value for', 'in', '0')
-
-.. autofunction:: expand_action
-
-.. autofunction:: resolveConflicts
-
-.. autoclass:: ConfigurationConflictError
+.. automodule:: zope.configuration.config
diff --git a/docs/api/docutils.rst b/docs/api/docutils.rst
index b75830c..b556c6f 100644
--- a/docs/api/docutils.rst
+++ b/docs/api/docutils.rst
@@ -1,26 +1,5 @@
-:mod:`zope.configuration.docutils`
-==================================
+=============================
+ zope.configuration.docutils
+=============================
-.. module:: zope.configuration.docutils
-
-.. autofunction:: wrap
-
- Examples:
-
- .. doctest::
-
- >>> from zope.configuration.docutils import wrap
- >>> print(wrap('foo bar')[:-2])
- foo bar
- >>> print(wrap('foo bar', indent=2)[:-2])
- foo bar
- >>> print(wrap('foo bar, more foo bar', 10)[:-2])
- foo bar,
- more foo
- bar
- >>> print(wrap('foo bar, more foo bar', 10, 2)[:-2])
- foo bar,
- more foo
- bar
-
-.. autofunction:: makeDocStructures
+.. automodule:: zope.configuration.docutils
diff --git a/docs/api/exceptions.rst b/docs/api/exceptions.rst
index 73fb127..dd324db 100644
--- a/docs/api/exceptions.rst
+++ b/docs/api/exceptions.rst
@@ -1,6 +1,5 @@
-:mod:`zope.configuration.exceptions`
-====================================
+===============================
+ zope.configuration.exceptions
+===============================
-.. module:: zope.configuration.exceptions
-
-.. autoclass:: ConfigurationError
+.. automodule:: zope.configuration.exceptions
diff --git a/docs/api/fields.rst b/docs/api/fields.rst
index c44dcf1..b640a30 100644
--- a/docs/api/fields.rst
+++ b/docs/api/fields.rst
@@ -1,349 +1,5 @@
-:mod:`zope.configuration.fields`
-================================
+===========================
+ zope.configuration.fields
+===========================
-.. module:: zope.configuration.fields
-
-.. autoclass:: PythonIdentifier
- :members:
- :member-order: bysource
-
- Let's look at an example:
-
- .. doctest::
-
- >>> from zope.configuration.fields import PythonIdentifier
- >>> class FauxContext(object):
- ... pass
- >>> context = FauxContext()
- >>> field = PythonIdentifier().bind(context)
-
- Let's test the fromUnicode method:
-
- .. doctest::
-
- >>> field.fromUnicode(u'foo')
- 'foo'
- >>> field.fromUnicode(u'foo3')
- 'foo3'
- >>> field.fromUnicode(u'_foo3')
- '_foo3'
-
- Now let's see whether validation works alright
-
- .. doctest::
-
- >>> for value in (u'foo', u'foo3', u'foo_', u'_foo3', u'foo_3', u'foo3_'):
- ... _ = field.fromUnicode(value)
- >>> from zope.schema import ValidationError
- >>> for value in (u'3foo', u'foo:', u'\\', u''):
- ... try:
- ... field.fromUnicode(value)
- ... except ValidationError:
- ... print('Validation Error ' + repr(value))
- Validation Error '3foo'
- Validation Error 'foo:'
- Validation Error '\\'
- Validation Error ''
-
-.. autoclass:: GlobalObject
- :members:
- :member-order: bysource
-
- Let's look at an example:
-
- .. doctest::
-
- >>> d = {'x': 1, 'y': 42, 'z': 'zope'}
- >>> class fakeresolver(dict):
- ... def resolve(self, n):
- ... return self[n]
- >>> fake = fakeresolver(d)
-
- >>> from zope.schema import Int
- >>> from zope.configuration.fields import GlobalObject
- >>> g = GlobalObject(value_type=Int())
- >>> gg = g.bind(fake)
- >>> gg.fromUnicode("x")
- 1
- >>> gg.fromUnicode(" x \n ")
- 1
- >>> gg.fromUnicode("y")
- 42
- >>> gg.fromUnicode("z")
- Traceback (most recent call last):
- ...
- WrongType: ('zope', (<type 'int'>, <type 'long'>), '')
-
- >>> g = GlobalObject(constraint=lambda x: x%2 == 0)
- >>> gg = g.bind(fake)
- >>> gg.fromUnicode("x")
- Traceback (most recent call last):
- ...
- ConstraintNotSatisfied: 1
- >>> gg.fromUnicode("y")
- 42
- >>> g = GlobalObject()
- >>> gg = g.bind(fake)
- >>> print(gg.fromUnicode('*'))
- None
-
-.. autoclass:: GlobalInterface
- :members:
- :member-order: bysource
-
- Example:
-
- First, we need to set up a stub name resolver:
-
- .. doctest::
-
- >>> from zope.interface import Interface
- >>> class IFoo(Interface):
- ... pass
- >>> class Foo(object):
- ... pass
- >>> d = {'Foo': Foo, 'IFoo': IFoo}
- >>> class fakeresolver(dict):
- ... def resolve(self, n):
- ... return self[n]
- >>> fake = fakeresolver(d)
-
- Now verify constraints are checked correctly:
-
- .. doctest::
-
- >>> from zope.configuration.fields import GlobalInterface
- >>> g = GlobalInterface()
- >>> gg = g.bind(fake)
- >>> gg.fromUnicode('IFoo') is IFoo
- True
- >>> gg.fromUnicode(' IFoo ') is IFoo
- True
- >>> gg.fromUnicode('Foo')
- Traceback (most recent call last):
- ...
- NotAnInterface: (<class 'Foo'>, ...
-
-.. autoclass:: Tokens
- :members:
- :member-order: bysource
-
- Consider GlobalObject tokens:
-
- First, we need to set up a stub name resolver:
-
- .. doctest::
-
- >>> d = {'x': 1, 'y': 42, 'z': 'zope', 'x.y.x': 'foo'}
- >>> class fakeresolver(dict):
- ... def resolve(self, n):
- ... return self[n]
- >>> fake = fakeresolver(d)
-
- >>> from zope.configuration.fields import Tokens
- >>> from zope.configuration.fields import GlobalObject
- >>> g = Tokens(value_type=GlobalObject())
- >>> gg = g.bind(fake)
- >>> gg.fromUnicode(" \n x y z \n")
- [1, 42, 'zope']
-
- >>> from zope.schema import Int
- >>> g = Tokens(value_type=
- ... GlobalObject(value_type=
- ... Int(constraint=lambda x: x%2 == 0)))
- >>> gg = g.bind(fake)
- >>> gg.fromUnicode("x y")
- Traceback (most recent call last):
- ...
- InvalidToken: 1 in x y
-
- >>> gg.fromUnicode("z y")
- Traceback (most recent call last):
- ...
- InvalidToken: ('zope', (<type 'int'>, <type 'long'>), '') in z y
- >>> gg.fromUnicode("y y")
- [42, 42]
-
-.. autoclass:: Path
- :members:
- :member-order: bysource
-
- Let's look at an example:
-
- First, we need a "context" for the field that has a path
- function for converting relative path to an absolute path.
-
- We'll be careful to do this in an os-independent fashion.
-
- .. doctest::
-
- >>> from zope.configuration.fields import Path
- >>> class FauxContext(object):
- ... def path(self, p):
- ... return os.path.join(os.sep, 'faux', 'context', p)
- >>> context = FauxContext()
- >>> field = Path().bind(context)
-
- Lets try an absolute path first:
-
- .. doctest::
-
- >>> import os
- >>> p = os.path.join(os.sep, u'a', u'b')
- >>> n = field.fromUnicode(p)
- >>> n.split(os.sep)
- ['', 'a', 'b']
-
- This should also work with extra spaces around the path:
-
- .. doctest::
-
- >>> p = " \n %s \n\n " % p
- >>> n = field.fromUnicode(p)
- >>> n.split(os.sep)
- ['', 'a', 'b']
-
- Environment variables are expanded:
-
- .. doctest::
-
- >>> os.environ['path-test'] = '42'
- >>> with_env = os.path.join(os.sep, u'a', u'${path-test}')
- >>> n = field.fromUnicode(with_env)
- >>> n.split(os.sep)
- ['', 'a', '42']
-
- Now try a relative path:
-
- .. doctest::
-
- >>> p = os.path.join(u'a', u'b')
- >>> n = field.fromUnicode(p)
- >>> n.split(os.sep)
- ['', 'faux', 'context', 'a', 'b']
-
- The current user is expanded (these are implicitly relative paths):
-
- .. doctest::
-
- >>> old_home = os.environ.get('HOME')
- >>> os.environ['HOME'] = os.path.join(os.sep, 'HOME')
- >>> n = field.fromUnicode('~')
- >>> n.split(os.sep)
- ['', 'HOME']
- >>> if old_home:
- ... os.environ['HOME'] = old_home
- ... else:
- ... del os.environ['HOME']
-
-
-.. autoclass:: Bool
- :members:
- :member-order: bysource
-
- .. doctest::
-
- >>> from zope.configuration.fields import Bool
- >>> Bool().fromUnicode(u"yes")
- True
- >>> Bool().fromUnicode(u"y")
- True
- >>> Bool().fromUnicode(u"true")
- True
- >>> Bool().fromUnicode(u"no")
- False
-
-.. autoclass:: MessageID
- :members:
- :member-order: bysource
-
- .. doctest::
-
- >>> from zope.configuration.fields import MessageID
- >>> class Info(object):
- ... file = 'file location'
- ... line = 8
- >>> class FauxContext(object):
- ... i18n_strings = {}
- ... info = Info()
- >>> context = FauxContext()
- >>> field = MessageID().bind(context)
-
- There is a fallback domain when no domain has been specified.
-
- Exchange the warn function so we can make test whether the warning
- has been issued
-
- .. doctest::
-
- >>> warned = None
- >>> def fakewarn(*args, **kw): #* syntax highlighting
- ... global warned
- ... warned = args
-
- >>> import warnings
- >>> realwarn = warnings.warn
- >>> warnings.warn = fakewarn
-
- >>> i = field.fromUnicode(u"Hello world!")
- >>> i
- 'Hello world!'
- >>> i.domain
- 'untranslated'
- >>> warned
- ("You did not specify an i18n translation domain for the '' field in file location",)
-
- >>> warnings.warn = realwarn
-
- With the domain specified:
-
- .. doctest::
-
- >>> context.i18n_strings = {}
- >>> context.i18n_domain = 'testing'
-
- We can get a message id:
-
- .. doctest::
-
- >>> i = field.fromUnicode(u"Hello world!")
- >>> i
- 'Hello world!'
- >>> i.domain
- 'testing'
-
- In addition, the string has been registered with the context:
-
- .. doctest::
-
- >>> context.i18n_strings
- {'testing': {'Hello world!': [('file location', 8)]}}
-
- >>> i = field.fromUnicode(u"Foo Bar")
- >>> i = field.fromUnicode(u"Hello world!")
- >>> from pprint import PrettyPrinter
- >>> pprint=PrettyPrinter(width=70).pprint
- >>> pprint(context.i18n_strings)
- {'testing': {'Foo Bar': [('file location', 8)],
- 'Hello world!': [('file location', 8),
- ('file location', 8)]}}
-
- >>> from zope.i18nmessageid import Message
- >>> isinstance(list(context.i18n_strings['testing'].keys())[0], Message)
- True
-
- Explicit Message IDs
-
- .. doctest::
-
- >>> i = field.fromUnicode(u'[View-Permission] View')
- >>> i
- 'View-Permission'
- >>> i.default
- 'View'
-
- >>> i = field.fromUnicode(u'[] [Some] text')
- >>> i
- '[Some] text'
- >>> i.default is None
- True
+.. automodule:: zope.configuration.fields
diff --git a/docs/api/interfaces.rst b/docs/api/interfaces.rst
index 6c76434..a3a2624 100644
--- a/docs/api/interfaces.rst
+++ b/docs/api/interfaces.rst
@@ -1,16 +1,5 @@
-:mod:`zope.configuration.interfaces`
-====================================
+======================================
+ zope.configuration.interfaces
+======================================
-.. module:: zope.configuration.interfaces
-
-.. autoclass:: InvalidToken
- :members:
- :member-order: bysource
-
-.. autointerface:: IConfigurationContext
- :members:
- :member-order: bysource
-
-.. autointerface:: IGroupingContext
- :members:
- :member-order: bysource
+.. automodule:: zope.configuration.interfaces
diff --git a/docs/api/name.rst b/docs/api/name.rst
index 69a3f56..4471308 100644
--- a/docs/api/name.rst
+++ b/docs/api/name.rst
@@ -1,10 +1,5 @@
-:mod:`zope.configuration.name`
-==============================
+=========================
+ zope.configuration.name
+=========================
-.. module:: zope.configuration.name
-
-.. autofunction:: resolve
-
-.. autofunction:: getNormalizedName
-
-.. autofunction:: path
+.. automodule:: zope.configuration.name
diff --git a/docs/api/xmlconfig.rst b/docs/api/xmlconfig.rst
index 63b94f5..a76b9c8 100644
--- a/docs/api/xmlconfig.rst
+++ b/docs/api/xmlconfig.rst
@@ -1,211 +1,5 @@
-:mod:`zope.configuration.xmlconfig`
-===================================
+==============================
+ zope.configuration.xmlconfig
+==============================
-.. module:: zope.configuration.xmlconfig
-
-.. autoclass:: ZopeXMLConfigurationError
-
- Example
-
- .. doctest::
-
- >>> from zope.configuration.xmlconfig import ZopeXMLConfigurationError
- >>> v = ZopeXMLConfigurationError("blah", AttributeError, "xxx")
- >>> print(v)
- 'blah'
- AttributeError: xxx
-
-.. autoclass:: ZopeSAXParseException
-
- Example
-
- .. doctest::
-
- >>> from zope.configuration.xmlconfig import ZopeSAXParseException
- >>> v = ZopeSAXParseException("foo.xml:12:3:Not well formed")
- >>> print(v)
- File "foo.xml", line 12.3, Not well formed
-
-.. autoclass:: ParserInfo
- :members:
- :member-order: bysource
-
- Example
-
- .. doctest::
-
- >>> from zope.configuration.xmlconfig import ParserInfo
- >>> info = ParserInfo('tests//sample.zcml', 1, 0)
- >>> info
- File "tests//sample.zcml", line 1.0
-
- >>> print(info)
- File "tests//sample.zcml", line 1.0
-
- >>> info.characters("blah\\n")
- >>> info.characters("blah")
- >>> info.text
- 'blah\\nblah'
-
- >>> info.end(7, 0)
- >>> info
- File "tests//sample.zcml", line 1.0-7.0
-
- >>> print(info)
- File "tests//sample.zcml", line 1.0-7.0
- <configure xmlns='http://namespaces.zope.org/zope'>
- <!-- zope.configure -->
- <directives namespace="http://namespaces.zope.org/zope">
- <directive name="hook" attributes="name implementation module"
- handler="zope.configuration.metaconfigure.hook" />
- </directives>
- </configure>
-
-.. autoclass:: ConfigurationHandler
-
- .. automethod:: evaluateCondition
-
- The ``have`` and ``not-have`` verbs each take one argument: the name
- of a feature:
-
- .. doctest::
-
- >>> from zope.configuration.config import ConfigurationContext
- >>> from zope.configuration.xmlconfig import ConfigurationHandler
- >>> context = ConfigurationContext()
- >>> context.provideFeature('apidoc')
- >>> c = ConfigurationHandler(context, testing=True)
- >>> c.evaluateCondition("have apidoc")
- True
- >>> c.evaluateCondition("not-have apidoc")
- False
- >>> c.evaluateCondition("have onlinehelp")
- False
- >>> c.evaluateCondition("not-have onlinehelp")
- True
-
- Ill-formed expressions raise an error:
-
- .. doctest::
-
- >>> c.evaluateCondition("want apidoc")
- Traceback (most recent call last):
- ...
- ValueError: Invalid ZCML condition: 'want apidoc'
-
- >>> c.evaluateCondition("have x y")
- Traceback (most recent call last):
- ...
- ValueError: Only one feature allowed: 'have x y'
-
- >>> c.evaluateCondition("have")
- Traceback (most recent call last):
- ...
- ValueError: Feature name missing: 'have'
-
-
- The ``installed`` and ``not-installed`` verbs each take one argument:
- the dotted name of a pacakge.
-
- If the pacakge is found, in other words, can be imported,
- then the condition will return true / false:
-
- .. doctest::
-
- >>> context = ConfigurationContext()
- >>> c = ConfigurationHandler(context, testing=True)
- >>> c.evaluateCondition('installed zope.interface')
- True
- >>> c.evaluateCondition('not-installed zope.interface')
- False
- >>> c.evaluateCondition('installed zope.foo')
- False
- >>> c.evaluateCondition('not-installed zope.foo')
- True
-
- Ill-formed expressions raise an error:
-
- .. doctest::
-
- >>> c.evaluateCondition("installed foo bar")
- Traceback (most recent call last):
- ...
- ValueError: Only one package allowed: 'installed foo bar'
-
- >>> c.evaluateCondition("installed")
- Traceback (most recent call last):
- ...
- ValueError: Package name missing: 'installed'
-
-.. autofunction:: processxmlfile
-
-.. autofunction:: openInOrPlain
-
- For example, the tests/samplepackage dirextory has files:
-
- - configure.zcml
- - configure.zcml.in
- - foo.zcml.in
-
- If we open configure.zcml, we'll get that file:
-
- .. doctest::
-
- >>> import os
- >>> from zope.configuration.xmlconfig import __file__
- >>> from zope.configuration.xmlconfig import openInOrPlain
- >>> here = os.path.dirname(__file__)
- >>> path = os.path.join(here, 'tests', 'samplepackage', 'configure.zcml')
- >>> f = openInOrPlain(path)
- >>> f.name[-14:]
- 'configure.zcml'
- >>> f.close()
-
- But if we open foo.zcml, we'll get foo.zcml.in, since there isn't a
- foo.zcml:
-
- .. doctest::
-
- >>> path = os.path.join(here, 'tests', 'samplepackage', 'foo.zcml')
- >>> f = openInOrPlain(path)
- >>> f.name[-11:]
- 'foo.zcml.in'
- >>> f.close()
-
- Make sure other IOErrors are re-raised. We need to do this in a
- try-except block because different errors are raised on Windows and
- on Linux.
-
- .. doctest::
-
- >>> try:
- ... f = openInOrPlain('.')
- ... except IOError:
- ... print("passed")
- ... else:
- ... print("failed")
- passed
-
-.. autointerface:: IInclude
- :members:
- :member-order: bysource
-
-.. autofunction:: include
-
-.. autofunction:: exclude
-
-.. autofunction:: includeOverrides
-
-.. autofunction:: registerCommonDirectives
-
-.. autofunction:: file
-
-.. autofunction:: string
-
-.. autoclass:: XMLConfig
- :members:
- :member-order: bysource
-
-.. autofunction:: xmlconfig
-
-.. autofunction:: testxmlconfig
+.. automodule:: zope.configuration.xmlconfig
diff --git a/docs/api/zopeconfigure.rst b/docs/api/zopeconfigure.rst
index a84937e..8570e35 100644
--- a/docs/api/zopeconfigure.rst
+++ b/docs/api/zopeconfigure.rst
@@ -1,4 +1,5 @@
-:mod:`zope.configuration.zopeconfigure`
-=======================================
+==================================
+ zope.configuration.zopeconfigure
+==================================
.. automodule:: zope.configuration.zopeconfigure
diff --git a/docs/conf.py b/docs/conf.py
index 9fffd20..ac65d4b 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -257,6 +257,7 @@ intersphinx_mapping = {
'https://zopeschema.readthedocs.io/en/latest': None,
}
-autodoc_default_flags = ['members', 'show-inheritance']
-autoclass_content = 'both'
-autodoc_member_order = 'bysource'
+autodoc_default_flags = [
+ 'members',
+ 'show-inheritance',
+]
diff --git a/src/zope/configuration/config.py b/src/zope/configuration/config.py
index a768021..e7ba289 100644
--- a/src/zope/configuration/config.py
+++ b/src/zope/configuration/config.py
@@ -125,7 +125,39 @@ class ConfigurationContext(object):
self._features = set()
def resolve(self, dottedname):
- """Resolve a dotted name to an object.
+ """
+ Resolve a dotted name to an object.
+
+ Examples:
+
+ >>> from zope.configuration.config import ConfigurationContext
+ >>> from zope.configuration.config import ConfigurationError
+ >>> c = ConfigurationContext()
+ >>> import zope, zope.interface
+ >>> c.resolve('zope') is zope
+ True
+ >>> c.resolve('zope.interface') is zope.interface
+ True
+ >>> c.resolve('zope.configuration.eek') #doctest: +NORMALIZE_WHITESPACE
+ Traceback (most recent call last):
+ ...
+ ConfigurationError:
+ ImportError: Module zope.configuration has no global eek
+
+ >>> c.resolve('.config.ConfigurationContext')
+ Traceback (most recent call last):
+ ...
+ AttributeError: 'ConfigurationContext' object has no attribute 'package'
+ >>> import zope.configuration
+ >>> c.package = zope.configuration
+ >>> c.resolve('.') is zope.configuration
+ True
+ >>> c.resolve('.config.ConfigurationContext') is ConfigurationContext
+ True
+ >>> c.resolve('..interface') is zope.interface
+ True
+ >>> c.resolve('str') is str
+ True
"""
name = dottedname.strip()
@@ -213,7 +245,31 @@ class ConfigurationContext(object):
"ImportError: Module %s has no global %s" % (mname, oname))
def path(self, filename):
- """ Compute package-relative paths.
+ """
+ Compute package-relative paths.
+
+ Examples:
+
+ >>> import os
+ >>> from zope.configuration.config import ConfigurationContext
+ >>> c = ConfigurationContext()
+ >>> c.path("/x/y/z") == os.path.normpath("/x/y/z")
+ True
+ >>> c.path("y/z")
+ Traceback (most recent call last):
+ ...
+ AttributeError: 'ConfigurationContext' object has no attribute 'package'
+ >>> import zope.configuration
+ >>> c.package = zope.configuration
+ >>> import os
+ >>> d = os.path.dirname(zope.configuration.__file__)
+ >>> c.path("y/z") == d + os.path.normpath("/y/z")
+ True
+ >>> c.path("y/./z") == d + os.path.normpath("/y/z")
+ True
+ >>> c.path("y/../z") == d + os.path.normpath("/z")
+ True
+
"""
filename, needs_processing = PathProcessor.expand(filename)
@@ -239,10 +295,39 @@ class ConfigurationContext(object):
return os.path.normpath(os.path.join(basepath, filename))
def checkDuplicate(self, filename):
- """Check for duplicate imports of the same file.
+ """
+ Check for duplicate imports of the same file.
+
+ Raises an exception if this file had been processed before.
+ This is better than an unlimited number of conflict errors.
+
+ Examples:
+
+ >>> from zope.configuration.config import ConfigurationContext
+ >>> from zope.configuration.config import ConfigurationError
+ >>> c = ConfigurationContext()
+ >>> c.checkDuplicate('/foo.zcml')
+ >>> try:
+ ... c.checkDuplicate('/foo.zcml')
+ ... except ConfigurationError as e:
+ ... # On Linux the exact msg has /foo, on Windows \\foo.
+ ... str(e).endswith("foo.zcml' included more than once")
+ True
+
+ You may use different ways to refer to the same file:
+
+ >>> import zope.configuration
+ >>> c.package = zope.configuration
+ >>> import os
+ >>> d = os.path.dirname(zope.configuration.__file__)
+ >>> c.checkDuplicate('bar.zcml')
+ >>> try:
+ ... c.checkDuplicate(d + os.path.normpath('/bar.zcml'))
+ ... except ConfigurationError as e:
+ ... str(e).endswith("bar.zcml' included more than once")
+ ...
+ True
- Raises an exception if this file had been processed before. This
- is better than an unlimited number of conflict errors.
"""
path = self.path(filename)
if path in self._seen_files:
@@ -250,13 +335,34 @@ class ConfigurationContext(object):
self._seen_files.add(path)
def processFile(self, filename):
- """Check whether a file needs to be processed.
+ """
+ Check whether a file needs to be processed.
Return True if processing is needed and False otherwise. If
the file needs to be processed, it will be marked as
processed, assuming that the caller will procces the file if
it needs to be procssed.
- """ #' <-- bow to font-lock
+
+ Examples:
+
+ >>> from zope.configuration.config import ConfigurationContext
+ >>> c = ConfigurationContext()
+ >>> c.processFile('/foo.zcml')
+ True
+ >>> c.processFile('/foo.zcml')
+ False
+
+ You may use different ways to refer to the same file:
+
+ >>> import zope.configuration
+ >>> c.package = zope.configuration
+ >>> import os
+ >>> d = os.path.dirname(zope.configuration.__file__)
+ >>> c.processFile('bar.zcml')
+ True
+ >>> c.processFile(os.path.join(d, 'bar.zcml'))
+ False
+ """
path = self.path(filename)
if path in self._seen_files:
return False
@@ -265,13 +371,114 @@ class ConfigurationContext(object):
def action(self, discriminator, callable=None, args=(), kw=None, order=0,
includepath=None, info=None, **extra):
- """Add an action with the given discriminator, callable and arguments.
-
- For testing purposes, the callable and arguments may be omitted.
- In that case, a default noop callable is used.
+ """
+ Add an action with the given discriminator, callable and
+ arguments.
+
+ For testing purposes, the callable and arguments may be
+ omitted. In that case, a default noop callable is used.
+
+ The discriminator must be given, but it can be None, to
+ indicate that the action never conflicts.
+
+
+ Examples:
+
+ >>> from zope.configuration.config import ConfigurationContext
+ >>> c = ConfigurationContext()
+
+ Normally, the context gets actions from subclasses. We'll provide
+ an actions attribute ourselves:
+
+ >>> c.actions = []
+
+ We'll use a test callable that has a convenient string representation
+
+ >>> from zope.configuration.tests.directives import f
+ >>> c.action(1, f, (1, ), {'x': 1})
+ >>> from pprint import PrettyPrinter
+ >>> pprint = PrettyPrinter(width=60).pprint
+ >>> pprint(c.actions)
+ [{'args': (1,),
+ 'callable': f,
+ 'discriminator': 1,
+ 'includepath': (),
+ 'info': '',
+ 'kw': {'x': 1},
+ 'order': 0}]
+
+ >>> c.action(None)
+ >>> pprint(c.actions)
+ [{'args': (1,),
+ 'callable': f,
+ 'discriminator': 1,
+ 'includepath': (),
+ 'info': '',
+ 'kw': {'x': 1},
+ 'order': 0},
+ {'args': (),
+ 'callable': None,
+ 'discriminator': None,
+ 'includepath': (),
+ 'info': '',
+ 'kw': {},
+ 'order': 0}]
+
+ Now set the include path and info:
+
+ >>> c.includepath = ('foo.zcml',)
+ >>> c.info = "?"
+ >>> c.action(None)
+ >>> pprint(c.actions[-1])
+ {'args': (),
+ 'callable': None,
+ 'discriminator': None,
+ 'includepath': ('foo.zcml',),
+ 'info': '?',
+ 'kw': {},
+ 'order': 0}
+
+ We can add an order argument to crudely control the order
+ of execution:
+
+ >>> c.action(None, order=99999)
+ >>> pprint(c.actions[-1])
+ {'args': (),
+ 'callable': None,
+ 'discriminator': None,
+ 'includepath': ('foo.zcml',),
+ 'info': '?',
+ 'kw': {},
+ 'order': 99999}
+
+ We can also pass an includepath argument, which will be used as the the
+ includepath for the action. (if includepath is None, self.includepath
+ will be used):
+
+ >>> c.action(None, includepath=('abc',))
+ >>> pprint(c.actions[-1])
+ {'args': (),
+ 'callable': None,
+ 'discriminator': None,
+ 'includepath': ('abc',),
+ 'info': '?',
+ 'kw': {},
+ 'order': 0}
+
+ We can also pass an info argument, which will be used as the the
+ source line info for the action. (if info is None, self.info will be
+ used):
+
+ >>> c.action(None, info='abc')
+ >>> pprint(c.actions[-1])
+ {'args': (),
+ 'callable': None,
+ 'discriminator': None,
+ 'includepath': ('foo.zcml',),
+ 'info': 'abc',
+ 'kw': {},
+ 'order': 0}
- The discriminator must be given, but it can be None, to indicate that
- the action never conflicts.
"""
if kw is None:
kw = {}
@@ -299,14 +506,32 @@ class ConfigurationContext(object):
self.actions.append(action)
def hasFeature(self, feature):
- """Check whether a named feature has been provided.
+ """
+ Check whether a named feature has been provided.
+
+ Initially no features are provided.
+
+ Examples:
+
+ >>> from zope.configuration.config import ConfigurationContext
+ >>> c = ConfigurationContext()
+ >>> c.hasFeature('onlinehelp')
+ False
- Initially no features are provided
+ You can declare that a feature is provided
+
+ >>> c.provideFeature('onlinehelp')
+
+ and it becomes available
+
+ >>> c.hasFeature('onlinehelp')
+ True
"""
return feature in self._features
def provideFeature(self, feature):
- """Declare thata named feature has been provided.
+ """
+ Declare that a named feature has been provided.
See :meth:`hasFeature` for examples.
"""
@@ -314,9 +539,58 @@ class ConfigurationContext(object):
class ConfigurationAdapterRegistry(object):
- """Simple adapter registry that manages directives as adapters
"""
-
+ Simple adapter registry that manages directives as adapters.
+
+ Examples:
+
+ >>> from zope.configuration.interfaces import IConfigurationContext
+ >>> from zope.configuration.config import ConfigurationAdapterRegistry
+ >>> from zope.configuration.config import ConfigurationError
+ >>> from zope.configuration.config import ConfigurationMachine
+ >>> r = ConfigurationAdapterRegistry()
+ >>> c = ConfigurationMachine()
+ >>> r.factory(c, ('http://www.zope.com', 'xxx'))
+ Traceback (most recent call last):
+ ...
+ ConfigurationError: ('Unknown directive', 'http://www.zope.com', 'xxx')
+ >>> def f():
+ ... pass
+
+ >>> r.register(IConfigurationContext, ('http://www.zope.com', 'xxx'), f)
+ >>> r.factory(c, ('http://www.zope.com', 'xxx')) is f
+ True
+ >>> r.factory(c, ('http://www.zope.com', 'yyy')) is f
+ Traceback (most recent call last):
+ ...
+ ConfigurationError: ('Unknown directive', 'http://www.zope.com', 'yyy')
+ >>> r.register(IConfigurationContext, 'yyy', f)
+ >>> r.factory(c, ('http://www.zope.com', 'yyy')) is f
+ True
+
+ Test the documentation feature:
+
+ >>> from zope.configuration.config import IFullInfo
+ >>> r._docRegistry
+ []
+ >>> r.document(('ns', 'dir'), IFullInfo, IConfigurationContext, None,
+ ... 'inf', None)
+ >>> r._docRegistry[0][0] == ('ns', 'dir')
+ True
+ >>> r._docRegistry[0][1] is IFullInfo
+ True
+ >>> r._docRegistry[0][2] is IConfigurationContext
+ True
+ >>> r._docRegistry[0][3] is None
+ True
+ >>> r._docRegistry[0][4] == 'inf'
+ True
+ >>> r._docRegistry[0][5] is None
+ True
+ >>> r.document('all-dir', None, None, None, None)
+ >>> r._docRegistry[1][0]
+ ('', 'all-dir')
+ """
def __init__(self):
super(ConfigurationAdapterRegistry, self).__init__()
@@ -355,7 +629,36 @@ class ConfigurationAdapterRegistry(object):
@implementer(IConfigurationContext)
class ConfigurationMachine(ConfigurationAdapterRegistry, ConfigurationContext):
- """Configuration machine
+ """
+ Configuration machine.
+
+ Example:
+
+ >>> from zope.configuration.config import ConfigurationMachine
+ >>> machine = ConfigurationMachine()
+ >>> ns = "http://www.zope.org/testing"
+
+ Register a directive:
+
+ >>> from zope.configuration.config import metans
+ >>> machine((metans, "directive"),
+ ... namespace=ns, name="simple",
+ ... schema="zope.configuration.tests.directives.ISimple",
+ ... handler="zope.configuration.tests.directives.simple")
+
+ and try it out:
+
+ >>> machine((ns, "simple"), a=u"aa", c=u"cc")
+ >>> from pprint import PrettyPrinter
+ >>> pprint = PrettyPrinter(width=60).pprint
+ >>> pprint(machine.actions)
+ [{'args': ('aa', 'xxx', 'cc'),
+ 'callable': f,
+ 'discriminator': ('simple', 'aa', 'xxx', 'cc'),
+ 'includepath': (),
+ 'info': None,
+ 'kw': {},
+ 'order': 0}]
"""
package = None
basepath = None
@@ -402,9 +705,56 @@ class ConfigurationMachine(ConfigurationAdapterRegistry, ConfigurationContext):
self.stack[-1].context.info = info
def execute_actions(self, clear=True, testing=False):
- """Execute the configuration actions.
+ """
+ Execute the configuration actions.
This calls the action callables after resolving conflicts.
+
+ For example:
+
+ >>> from zope.configuration.config import ConfigurationMachine
+ >>> output = []
+ >>> def f(*a, **k):
+ ... output.append(('f', a, k))
+ >>> context = ConfigurationMachine()
+ >>> context.actions = [
+ ... (1, f, (1,)),
+ ... (1, f, (11,), {}, ('x', )),
+ ... (2, f, (2,)),
+ ... ]
+ >>> context.execute_actions()
+ >>> output
+ [('f', (1,), {}), ('f', (2,), {})]
+
+ If the action raises an error, we convert it to a
+ ConfigurationExecutionError.
+
+ >>> from zope.configuration.config import ConfigurationExecutionError
+ >>> output = []
+ >>> def bad():
+ ... bad.xxx
+ >>> context.actions = [
+ ... (1, f, (1,)),
+ ... (1, f, (11,), {}, ('x', )),
+ ... (2, f, (2,)),
+ ... (3, bad, (), {}, (), 'oops')
+ ... ]
+ >>> try:
+ ... v = context.execute_actions()
+ ... except ConfigurationExecutionError as e:
+ ... v = e
+ >>> lines = str(v).splitlines()
+ >>> 'AttributeError' in lines[0]
+ True
+ >>> lines[0].endswith("'function' object has no attribute 'xxx'")
+ True
+ >>> lines[1:]
+ [' in:', ' oops']
+
+ Note that actions executed before the error still have an effect:
+
+ >>> output
+ [('f', (1,), {}), ('f', (2,), {})]
"""
pass_through_exceptions = self.pass_through_exceptions
if testing:
@@ -434,7 +784,8 @@ class ConfigurationMachine(ConfigurationAdapterRegistry, ConfigurationContext):
del self.actions[:]
class ConfigurationExecutionError(ConfigurationError):
- """An error occurred during execution of a configuration action
+ """
+ An error occurred during execution of a configuration action
"""
def __init__(self, etype, evalue, info):
self.etype, self.evalue, self.info = etype, evalue, info
@@ -446,7 +797,8 @@ class ConfigurationExecutionError(ConfigurationError):
# Stack items
class IStackItem(Interface):
- """Configuration machine stack items
+ """
+ Configuration machine stack items
Stack items are created when a directive is being processed.
@@ -471,7 +823,8 @@ class IStackItem(Interface):
@implementer(IStackItem)
class SimpleStackItem(object):
- """Simple stack item
+ """
+ Simple stack item
A simple stack item can't have anything added after it. It can
only be removed. It is used for simple directives and
@@ -508,6 +861,9 @@ class SimpleStackItem(object):
@implementer(IStackItem)
class RootStackItem(object):
+ """
+ A root stack item.
+ """
def __init__(self, context):
self.context = context
@@ -529,15 +885,158 @@ class RootStackItem(object):
@implementer(IStackItem)
class GroupingStackItem(RootStackItem):
- """Stack item for a grouping directive
+ """
+ Stack item for a grouping directive
A grouping stack item is in the stack when a grouping directive is
- being processed. Grouping directives group other directives.
+ being processed. Grouping directives group other directives.
Often, they just manage common data, but they may also take
actions, either before or after contained directives are executed.
A grouping stack item is created with a grouping directive
definition, a configuration context, and directive data.
+
+ To see how this works, let's look at an example:
+
+ We need a context. We'll just use a configuration machine
+
+ >>> from zope.configuration.config import GroupingStackItem
+ >>> from zope.configuration.config import ConfigurationMachine
+ >>> context = ConfigurationMachine()
+
+ We need a callable to use in configuration actions. We'll use a
+ convenient one from the tests:
+
+ >>> from zope.configuration.tests.directives import f
+
+ We need a handler for the grouping directive. This is a class that
+ implements a context decorator. The decorator must also provide
+ ``before`` and ``after`` methods that are called before and after
+ any contained directives are processed. We'll typically subclass
+ ``GroupingContextDecorator``, which provides context decoration,
+ and default ``before`` and ``after`` methods.
+
+ >>> from zope.configuration.config import GroupingContextDecorator
+ >>> class SampleGrouping(GroupingContextDecorator):
+ ... def before(self):
+ ... self.action(('before', self.x, self.y), f)
+ ... def after(self):
+ ... self.action(('after'), f)
+
+ We'll use our decorator to decorate our initial context, providing
+ keyword arguments x and y:
+
+ >>> dec = SampleGrouping(context, x=1, y=2)
+
+ Note that the keyword arguments are made attributes of the
+ decorator.
+
+ Now we'll create the stack item.
+
+ >>> item = GroupingStackItem(dec)
+
+ We still haven't called the before action yet, which we can verify
+ by looking at the context actions:
+
+ >>> context.actions
+ []
+
+ Subdirectives will get looked up as adapters of the context.
+
+ We'll create a simple handler:
+
+ >>> def simple(context, data, info):
+ ... context.action(("simple", context.x, context.y, data), f)
+ ... return info
+
+ and register it with the context:
+
+ >>> from zope.configuration.interfaces import IConfigurationContext
+ >>> from zope.configuration.config import testns
+ >>> context.register(IConfigurationContext, (testns, 'simple'), simple)
+
+ This handler isn't really a propert handler, because it doesn't
+ return a new context. It will do for this example.
+
+ Now we'll call the contained method on the stack item:
+
+ >>> item.contained((testns, 'simple'), {'z': 'zope'}, "someinfo")
+ 'someinfo'
+
+ We can verify thet the simple method was called by looking at the
+ context actions. Note that the before method was called before
+ handling the contained directive.
+
+ >>> from pprint import PrettyPrinter
+ >>> pprint = PrettyPrinter(width=60).pprint
+
+ >>> pprint(context.actions)
+ [{'args': (),
+ 'callable': f,
+ 'discriminator': ('before', 1, 2),
+ 'includepath': (),
+ 'info': '',
+ 'kw': {},
+ 'order': 0},
+ {'args': (),
+ 'callable': f,
+ 'discriminator': ('simple', 1, 2, {'z': 'zope'}),
+ 'includepath': (),
+ 'info': '',
+ 'kw': {},
+ 'order': 0}]
+
+ Finally, we call finish, which calls the decorator after method:
+
+ >>> item.finish()
+
+ >>> pprint(context.actions)
+ [{'args': (),
+ 'callable': f,
+ 'discriminator': ('before', 1, 2),
+ 'includepath': (),
+ 'info': '',
+ 'kw': {},
+ 'order': 0},
+ {'args': (),
+ 'callable': f,
+ 'discriminator': ('simple', 1, 2, {'z': 'zope'}),
+ 'includepath': (),
+ 'info': '',
+ 'kw': {},
+ 'order': 0},
+ {'args': (),
+ 'callable': f,
+ 'discriminator': 'after',
+ 'includepath': (),
+ 'info': '',
+ 'kw': {},
+ 'order': 0}]
+
+ If there were no nested directives:
+
+ >>> context = ConfigurationMachine()
+ >>> dec = SampleGrouping(context, x=1, y=2)
+ >>> item = GroupingStackItem(dec)
+ >>> item.finish()
+
+ Then before will be when we call finish:
+
+ >>> pprint(context.actions)
+ [{'args': (),
+ 'callable': f,
+ 'discriminator': ('before', 1, 2),
+ 'includepath': (),
+ 'info': '',
+ 'kw': {},
+ 'order': 0},
+ {'args': (),
+ 'callable': f,
+ 'discriminator': 'after',
+ 'includepath': (),
+ 'info': '',
+ 'kw': {},
+ 'order': 0}]
"""
def __init__(self, context):
@@ -571,14 +1070,135 @@ def noop():
@implementer(IStackItem)
class ComplexStackItem(object):
- """Complex stack item
+ """
+ Complex stack item
A complex stack item is in the stack when a complex directive is
- being processed. It only allows subdirectives to be used.
+ being processed. It only allows subdirectives to be used.
A complex stack item is created with a complex directive
definition (IComplexDirectiveContext), a configuration context,
and directive data.
+
+ To see how this works, let's look at an example:
+
+ We need a context. We'll just use a configuration machine
+
+ >>> from zope.configuration.config import ConfigurationMachine
+ >>> context = ConfigurationMachine()
+
+ We need a callable to use in configuration actions. We'll use a
+ convenient one from the tests:
+
+ >>> from zope.configuration.tests.directives import f
+
+ We need a handler for the complex directive. This is a class with
+ a method for each subdirective:
+
+ >>> class Handler(object):
+ ... def __init__(self, context, x, y):
+ ... self.context, self.x, self.y = context, x, y
+ ... context.action('init', f)
+ ... def sub(self, context, a, b):
+ ... context.action(('sub', a, b), f)
+ ... def __call__(self):
+ ... self.context.action(('call', self.x, self.y), f)
+
+ We need a complex directive definition:
+
+ >>> from zope.interface import Interface
+ >>> from zope.schema import TextLine
+ >>> from zope.configuration.config import ComplexDirectiveDefinition
+ >>> class Ixy(Interface):
+ ... x = TextLine()
+ ... y = TextLine()
+ >>> definition = ComplexDirectiveDefinition(
+ ... context, name="test", schema=Ixy,
+ ... handler=Handler)
+ >>> class Iab(Interface):
+ ... a = TextLine()
+ ... b = TextLine()
+ >>> definition['sub'] = Iab, ''
+
+ OK, now that we have the context, handler and definition, we're
+ ready to use a stack item.
+
+ >>> from zope.configuration.config import ComplexStackItem
+ >>> item = ComplexStackItem(definition, context, {'x': u'xv', 'y': u'yv'},
+ ... 'foo')
+
+ When we created the definition, the handler (factory) was called.
+
+ >>> from pprint import PrettyPrinter
+ >>> pprint = PrettyPrinter(width=60).pprint
+ >>> pprint(context.actions)
+ [{'args': (),
+ 'callable': f,
+ 'discriminator': 'init',
+ 'includepath': (),
+ 'info': 'foo',
+ 'kw': {},
+ 'order': 0}]
+
+ If a subdirective is provided, the ``contained`` method of the
+ stack item is called. It will lookup the subdirective schema and
+ call the corresponding method on the handler instance:
+
+ >>> simple = item.contained(('somenamespace', 'sub'),
+ ... {'a': u'av', 'b': u'bv'}, 'baz')
+ >>> simple.finish()
+
+ Note that the name passed to ``contained`` is a 2-part name,
+ consisting of a namespace and a name within the namespace.
+
+ >>> pprint(context.actions)
+ [{'args': (),
+ 'callable': f,
+ 'discriminator': 'init',
+ 'includepath': (),
+ 'info': 'foo',
+ 'kw': {},
+ 'order': 0},
+ {'args': (),
+ 'callable': f,
+ 'discriminator': ('sub', 'av', 'bv'),
+ 'includepath': (),
+ 'info': 'baz',
+ 'kw': {},
+ 'order': 0}]
+
+ The new stack item returned by contained is one that doesn't allow
+ any more subdirectives,
+
+ When all of the subdirectives have been provided, the ``finish``
+ method is called:
+
+ >>> item.finish()
+
+ The stack item will call the handler if it is callable.
+
+ >>> pprint(context.actions)
+ [{'args': (),
+ 'callable': f,
+ 'discriminator': 'init',
+ 'includepath': (),
+ 'info': 'foo',
+ 'kw': {},
+ 'order': 0},
+ {'args': (),
+ 'callable': f,
+ 'discriminator': ('sub', 'av', 'bv'),
+ 'includepath': (),
+ 'info': 'baz',
+ 'kw': {},
+ 'order': 0},
+ {'args': (),
+ 'callable': f,
+ 'discriminator': ('call', 'xv', 'yv'),
+ 'includepath': (),
+ 'info': 'foo',
+ 'kw': {},
+ 'order': 0}]
"""
def __init__(self, meta, context, data, info):
newcontext = GroupingContextDecorator(context)
@@ -724,12 +1344,61 @@ class IStandaloneDirectiveInfo(IDirectivesInfo, IFullInfo):
def defineSimpleDirective(context, name, schema, handler,
namespace='', usedIn=IConfigurationContext):
- """Define a simple directive
+ """
+ Define a simple directive
Define and register a factory that invokes the simple directive
- and returns a new stack item, which is always the same simple stack item.
-
- If the namespace is '*', the directive is registered for all namespaces.
+ and returns a new stack item, which is always the same simple
+ stack item.
+
+ If the namespace is '*', the directive is registered for all
+ namespaces.
+
+ Example:
+
+ >>> from zope.configuration.config import ConfigurationMachine
+ >>> context = ConfigurationMachine()
+ >>> from zope.interface import Interface
+ >>> from zope.schema import TextLine
+ >>> from zope.configuration.tests.directives import f
+ >>> class Ixy(Interface):
+ ... x = TextLine()
+ ... y = TextLine()
+ >>> def s(context, x, y):
+ ... context.action(('s', x, y), f)
+
+ >>> from zope.configuration.config import defineSimpleDirective
+ >>> defineSimpleDirective(context, 's', Ixy, s, testns)
+
+ >>> context((testns, "s"), x=u"vx", y=u"vy")
+ >>> from pprint import PrettyPrinter
+ >>> pprint = PrettyPrinter(width=60).pprint
+ >>> pprint(context.actions)
+ [{'args': (),
+ 'callable': f,
+ 'discriminator': ('s', 'vx', 'vy'),
+ 'includepath': (),
+ 'info': None,
+ 'kw': {},
+ 'order': 0}]
+
+ >>> context(('http://www.zope.com/t1', "s"), x=u"vx", y=u"vy")
+ Traceback (most recent call last):
+ ...
+ ConfigurationError: ('Unknown directive', 'http://www.zope.com/t1', 's')
+
+ >>> context = ConfigurationMachine()
+ >>> defineSimpleDirective(context, 's', Ixy, s, "*")
+
+ >>> context(('http://www.zope.com/t1', "s"), x=u"vx", y=u"vy")
+ >>> pprint(context.actions)
+ [{'args': (),
+ 'callable': f,
+ 'discriminator': ('s', 'vx', 'vy'),
+ 'includepath': (),
+ 'info': None,
+ 'kw': {},
+ 'order': 0}]
"""
namespace = namespace or context.namespace
if namespace != '*':
@@ -744,11 +1413,54 @@ def defineSimpleDirective(context, name, schema, handler,
def defineGroupingDirective(context, name, schema, handler,
namespace='', usedIn=IConfigurationContext):
- """Define a grouping directive
+ """
+ Define a grouping directive
Define and register a factory that sets up a grouping directive.
- If the namespace is '*', the directive is registered for all namespaces.
+ If the namespace is '*', the directive is registered for all
+ namespaces.
+
+ Example:
+
+ >>> from zope.configuration.config import ConfigurationMachine
+ >>> context = ConfigurationMachine()
+ >>> from zope.interface import Interface
+ >>> from zope.schema import TextLine
+ >>> from zope.configuration.tests.directives import f
+ >>> class Ixy(Interface):
+ ... x = TextLine()
+ ... y = TextLine()
+
+ We won't bother creating a special grouping directive class. We'll
+ just use :class:`GroupingContextDecorator`, which simply sets up a
+ grouping context that has extra attributes defined by a schema:
+
+ >>> from zope.configuration.config import defineGroupingDirective
+ >>> from zope.configuration.config import GroupingContextDecorator
+ >>> defineGroupingDirective(context, 'g', Ixy,
+ ... GroupingContextDecorator, testns)
+
+ >>> context.begin((testns, "g"), x=u"vx", y=u"vy")
+ >>> context.stack[-1].context.x
+ 'vx'
+ >>> context.stack[-1].context.y
+ 'vy'
+
+ >>> context(('http://www.zope.com/t1', "g"), x=u"vx", y=u"vy")
+ Traceback (most recent call last):
+ ...
+ ConfigurationError: ('Unknown directive', 'http://www.zope.com/t1', 'g')
+
+ >>> context = ConfigurationMachine()
+ >>> defineGroupingDirective(context, 'g', Ixy,
+ ... GroupingContextDecorator, "*")
+
+ >>> context.begin(('http://www.zope.com/t1', "g"), x=u"vx", y=u"vy")
+ >>> context.stack[-1].context.x
+ 'vx'
+ >>> context.stack[-1].context.y
+ 'vy'
"""
namespace = namespace or context.namespace
if namespace != '*':
@@ -806,7 +1518,28 @@ class IProvidesDirectiveInfo(Interface):
)
def provides(context, feature):
- """Declare that a feature is provided in context.
+ """
+ Declare that a feature is provided in context.
+
+ Example:
+
+ >>> from zope.configuration.config import ConfigurationContext
+ >>> from zope.configuration.config import provides
+ >>> c = ConfigurationContext()
+ >>> provides(c, 'apidoc')
+ >>> c.hasFeature('apidoc')
+ True
+
+ Spaces are not allowed in feature names (this is reserved for
+ providing many features with a single directive in the future).
+
+ >>> provides(c, 'apidoc onlinehelp')
+ Traceback (most recent call last):
+ ...
+ ValueError: Only one feature name allowed
+
+ >>> c.hasFeature('apidoc onlinehelp')
+ False
"""
if len(feature.split()) > 1:
raise ValueError("Only one feature name allowed")
@@ -817,7 +1550,8 @@ def provides(context, feature):
# Argument conversion
def toargs(context, schema, data):
- """Marshal data to an argument dictionary using a schema
+ """
+ Marshal data to an argument dictionary using a schema
Names that are python keywords have an underscore added as a
suffix in the schema and in the argument list, but are used
@@ -828,6 +1562,98 @@ def toargs(context, schema, data):
All of the items in the data must have corresponding fields in the
schema unless the schema has a true tagged value named
'keyword_arguments'.
+
+ Example:
+
+ >>> from zope.configuration.config import toargs
+ >>> from zope.schema import BytesLine
+ >>> from zope.schema import Float
+ >>> from zope.schema import Int
+ >>> from zope.schema import TextLine
+ >>> from zope.schema import URI
+ >>> class schema(Interface):
+ ... in_ = Int(constraint=lambda v: v > 0)
+ ... f = Float()
+ ... n = TextLine(min_length=1, default=u"rob")
+ ... x = BytesLine(required=False)
+ ... u = URI()
+
+ >>> context = ConfigurationMachine()
+ >>> from pprint import PrettyPrinter
+ >>> pprint = PrettyPrinter(width=50).pprint
+
+ >>> pprint(toargs(context, schema,
+ ... {'in': u'1', 'f': u'1.2', 'n': u'bob', 'x': u'x.y.z',
+ ... 'u': u'http://www.zope.org' }))
+ {'f': 1.2,
+ 'in_': 1,
+ 'n': 'bob',
+ 'u': 'http://www.zope.org',
+ 'x': b'x.y.z'}
+
+ If we have extra data, we'll get an error:
+
+ >>> toargs(context, schema,
+ ... {'in': u'1', 'f': u'1.2', 'n': u'bob', 'x': u'x.y.z',
+ ... 'u': u'http://www.zope.org', 'a': u'1'})
+ Traceback (most recent call last):
+ ...
+ ConfigurationError: ('Unrecognized parameters:', 'a')
+
+ Unless we set a tagged value to say that extra arguments are ok:
+
+ >>> schema.setTaggedValue('keyword_arguments', True)
+
+ >>> pprint(toargs(context, schema,
+ ... {'in': u'1', 'f': u'1.2', 'n': u'bob', 'x': u'x.y.z',
+ ... 'u': u'http://www.zope.org', 'a': u'1'}))
+ {'a': '1',
+ 'f': 1.2,
+ 'in_': 1,
+ 'n': 'bob',
+ 'u': 'http://www.zope.org',
+ 'x': b'x.y.z'}
+
+ If we omit required data we get an error telling us what was
+ omitted:
+
+ >>> pprint(toargs(context, schema,
+ ... {'in': u'1', 'f': u'1.2', 'n': u'bob', 'x': u'x.y.z'}))
+ Traceback (most recent call last):
+ ...
+ ConfigurationError: ('Missing parameter:', 'u')
+
+ Although we can omit not-required data:
+
+ >>> pprint(toargs(context, schema,
+ ... {'in': u'1', 'f': u'1.2', 'n': u'bob',
+ ... 'u': u'http://www.zope.org', 'a': u'1'}))
+ {'a': '1',
+ 'f': 1.2,
+ 'in_': 1,
+ 'n': 'bob',
+ 'u': 'http://www.zope.org'}
+
+ And we can omit required fields if they have valid defaults
+ (defaults that are valid values):
+
+ >>> pprint(toargs(context, schema,
+ ... {'in': u'1', 'f': u'1.2',
+ ... 'u': u'http://www.zope.org', 'a': u'1'}))
+ {'a': '1',
+ 'f': 1.2,
+ 'in_': 1,
+ 'n': 'rob',
+ 'u': 'http://www.zope.org'}
+
+ We also get an error if any data was invalid:
+
+ >>> pprint(toargs(context, schema,
+ ... {'in': u'0', 'f': u'1.2', 'n': u'bob', 'x': u'x.y.z',
+ ... 'u': u'http://www.zope.org', 'a': u'1'}))
+ Traceback (most recent call last):
+ ...
+ ConfigurationError: ('Invalid value for', 'in', '0')
"""
data = dict(data)
args = {}
@@ -892,7 +1718,8 @@ def expand_action(discriminator, callable=None, args=(), kw=None,
return action
def resolveConflicts(actions):
- """Resolve conflicting actions
+ """
+ Resolve conflicting actions.
Given an actions list, identify and try to resolve conflicting actions.
Actions conflict if they have the same non-None discriminator.
diff --git a/src/zope/configuration/docutils.py b/src/zope/configuration/docutils.py
index c2386c2..37b0060 100644
--- a/src/zope/configuration/docutils.py
+++ b/src/zope/configuration/docutils.py
@@ -26,7 +26,24 @@ para_sep = re.compile('\n{2,}')
whitespace = re.compile('[ \t\n\r]+')
def wrap(text, width=78, indent=0):
- """Makes sure that we keep a line length of a certain width.
+ """
+ Makes sure that we keep a line length of a certain width.
+
+ Examples:
+
+ >>> from zope.configuration.docutils import wrap
+ >>> print(wrap('foo bar')[:-2])
+ foo bar
+ >>> print(wrap('foo bar', indent=2)[:-2])
+ foo bar
+ >>> print(wrap('foo bar, more foo bar', 10)[:-2])
+ foo bar,
+ more foo
+ bar
+ >>> print(wrap('foo bar, more foo bar', 10, 2)[:-2])
+ foo bar,
+ more foo
+ bar
"""
paras = para_sep.split(text.strip())
@@ -54,15 +71,18 @@ def wrap(text, width=78, indent=0):
def makeDocStructures(context):
- """Creates two structures that provide a friendly format for
+ """
+ makeDocStructures(context) -> namespaces, subdirs
+
+ Creates two structures that provide a friendly format for
documentation.
- 'namespaces' is a dictionary that maps namespaces to a directives
- dictionary with the key being the name of the directive and the value is a
- tuple: (schema, handler, info).
+ *namespaces* is a dictionary that maps namespaces to a directives
+ dictionary with the key being the name of the directive and the
+ value is a tuple: (schema, handler, info).
- 'subdirs' maps a (namespace, name) pair to a list of subdirectives that
- have the form (namespace, name, schema, info).
+ *subdirs* maps a (namespace, name) pair to a list of subdirectives
+ that have the form (namespace, name, schema, info).
"""
namespaces = {}
subdirs = {}
diff --git a/src/zope/configuration/fields.py b/src/zope/configuration/fields.py
index 668c4a3..c4af0ad 100644
--- a/src/zope/configuration/fields.py
+++ b/src/zope/configuration/fields.py
@@ -33,20 +33,53 @@ from zope.configuration.exceptions import ConfigurationError
from zope.configuration.interfaces import InvalidToken
__all__ = [
- 'PythonIdentifier',
+ 'Bool',
'GlobalObject',
'GlobalInterface',
- 'Tokens',
- 'Path',
- 'Bool',
'MessageID',
+ 'Path',
+ 'PythonIdentifier',
+ 'Tokens',
]
class PythonIdentifier(schema_PythonIdentifier):
- """
+ r"""
This class is like `zope.schema.PythonIdentifier`.
+
+ Let's look at an example:
+
+ >>> from zope.configuration.fields import PythonIdentifier
+ >>> class FauxContext(object):
+ ... pass
+ >>> context = FauxContext()
+ >>> field = PythonIdentifier().bind(context)
+
+ Let's test the fromUnicode method:
+
+ >>> field.fromUnicode(u'foo')
+ 'foo'
+ >>> field.fromUnicode(u'foo3')
+ 'foo3'
+ >>> field.fromUnicode(u'_foo3')
+ '_foo3'
+
+ Now let's see whether validation works alright
+
+ >>> for value in (u'foo', u'foo3', u'foo_', u'_foo3', u'foo_3', u'foo3_'):
+ ... _ = field.fromUnicode(value)
+ >>> from zope.schema import ValidationError
+ >>> for value in (u'3foo', u'foo:', u'\\', u''):
+ ... try:
+ ... field.fromUnicode(value)
+ ... except ValidationError:
+ ... print('Validation Error ' + repr(value))
+ Validation Error '3foo'
+ Validation Error 'foo:'
+ Validation Error '\\'
+ Validation Error ''
+
.. versionchanged:: 4.2.0
Extend `zope.schema.PythonIdentifier`, which implies that `fromUnicode`
validates the strings.
@@ -79,6 +112,43 @@ class GlobalObject(Field):
self.value_type.validate(value)
def fromUnicode(self, value):
+ r"""
+ Find and return the module global at the path *value*.
+
+ >>> d = {'x': 1, 'y': 42, 'z': 'zope'}
+ >>> class fakeresolver(dict):
+ ... def resolve(self, n):
+ ... return self[n]
+ >>> fake = fakeresolver(d)
+
+ >>> from zope.schema import Int
+ >>> from zope.configuration.fields import GlobalObject
+ >>> g = GlobalObject(value_type=Int())
+ >>> gg = g.bind(fake)
+ >>> gg.fromUnicode("x")
+ 1
+ >>> gg.fromUnicode(" x \n ")
+ 1
+ >>> gg.fromUnicode("y")
+ 42
+ >>> gg.fromUnicode("z")
+ Traceback (most recent call last):
+ ...
+ WrongType: ('zope', (<type 'int'>, <type 'long'>), '')
+
+ >>> g = GlobalObject(constraint=lambda x: x%2 == 0)
+ >>> gg = g.bind(fake)
+ >>> gg.fromUnicode("x")
+ Traceback (most recent call last):
+ ...
+ ConstraintNotSatisfied: 1
+ >>> gg.fromUnicode("y")
+ 42
+ >>> g = GlobalObject()
+ >>> gg = g.bind(fake)
+ >>> print(gg.fromUnicode('*'))
+ None
+ """
name = str(value.strip())
# special case, mostly for interfaces
@@ -108,7 +178,38 @@ class GlobalObject(Field):
@implementer(IFromUnicode)
class GlobalInterface(GlobalObject):
- """An interface that can be accessed from a module.
+ """
+ An interface that can be accessed from a module.
+
+ Example:
+
+ First, we need to set up a stub name resolver:
+
+ >>> from zope.interface import Interface
+ >>> class IFoo(Interface):
+ ... pass
+ >>> class Foo(object):
+ ... pass
+ >>> d = {'Foo': Foo, 'IFoo': IFoo}
+ >>> class fakeresolver(dict):
+ ... def resolve(self, n):
+ ... return self[n]
+ >>> fake = fakeresolver(d)
+
+ Now verify constraints are checked correctly:
+
+ >>> from zope.configuration.fields import GlobalInterface
+ >>> g = GlobalInterface()
+ >>> gg = g.bind(fake)
+ >>> gg.fromUnicode('IFoo') is IFoo
+ True
+ >>> gg.fromUnicode(' IFoo ') is IFoo
+ True
+ >>> gg.fromUnicode('Foo')
+ Traceback (most recent call last):
+ ...
+ NotAnInterface: (<class 'Foo'>, ...
+
"""
def __init__(self, **kw):
super(GlobalInterface, self).__init__(InterfaceField(), **kw)
@@ -116,18 +217,57 @@ class GlobalInterface(GlobalObject):
@implementer(IFromUnicode)
class Tokens(List):
- """A list that can be read from a space-separated string.
"""
- def fromUnicode(self, u):
- u = u.strip()
- if u:
+ A list that can be read from a space-separated string.
+ """
+
+ def fromUnicode(self, value):
+ r"""
+ Split the input string and convert it to *value_type*.
+
+ Consider GlobalObject tokens:
+
+ First, we need to set up a stub name resolver:
+
+ >>> d = {'x': 1, 'y': 42, 'z': 'zope', 'x.y.x': 'foo'}
+ >>> class fakeresolver(dict):
+ ... def resolve(self, n):
+ ... return self[n]
+ >>> fake = fakeresolver(d)
+
+ >>> from zope.configuration.fields import Tokens
+ >>> from zope.configuration.fields import GlobalObject
+ >>> g = Tokens(value_type=GlobalObject())
+ >>> gg = g.bind(fake)
+ >>> gg.fromUnicode(" \n x y z \n")
+ [1, 42, 'zope']
+
+ >>> from zope.schema import Int
+ >>> g = Tokens(value_type=
+ ... GlobalObject(value_type=
+ ... Int(constraint=lambda x: x%2 == 0)))
+ >>> gg = g.bind(fake)
+ >>> gg.fromUnicode("x y")
+ Traceback (most recent call last):
+ ...
+ InvalidToken: 1 in x y
+
+ >>> gg.fromUnicode("z y")
+ Traceback (most recent call last):
+ ...
+ InvalidToken: ('zope', (<type 'int'>, <type 'long'>), '') in z y
+ >>> gg.fromUnicode("y y")
+ [42, 42]
+ """
+ value = value.strip()
+ if value:
vt = self.value_type.bind(self.context)
values = []
- for s in u.split():
+ for s in value.split():
try:
v = vt.fromUnicode(s)
- except ValidationError as v:
- raise InvalidToken("%s in %s" % (v, u)).with_field_and_value(self, s)
+ except ValidationError as ex:
+ raise InvalidToken("%s in %r" % (ex, value)).with_field_and_value(self, s)
else:
values.append(v)
else:
@@ -159,15 +299,76 @@ class PathProcessor(object):
@implementer(IFromUnicode)
class Path(Text):
- """A file path name, which may be input as a relative path
+ """
+ A file path name, which may be input as a relative path
Input paths are converted to absolute paths and normalized.
-
- .. versionchanged:: 4.2.0
- Start expanding home directories and environment variables.
"""
def fromUnicode(self, value):
+ r"""
+ Convert the input path to a normalized, absolute path.
+
+ Let's look at an example:
+
+ First, we need a "context" for the field that has a path
+ function for converting relative path to an absolute path.
+
+ We'll be careful to do this in an operating system independent fashion.
+
+ >>> from zope.configuration.fields import Path
+ >>> class FauxContext(object):
+ ... def path(self, p):
+ ... return os.path.join(os.sep, 'faux', 'context', p)
+ >>> context = FauxContext()
+ >>> field = Path().bind(context)
+
+ Lets try an absolute path first:
+
+ >>> import os
+ >>> p = os.path.join(os.sep, u'a', u'b')
+ >>> n = field.fromUnicode(p)
+ >>> n.split(os.sep)
+ ['', 'a', 'b']
+
+ This should also work with extra spaces around the path:
+
+ >>> p = " \n %s \n\n " % p
+ >>> n = field.fromUnicode(p)
+ >>> n.split(os.sep)
+ ['', 'a', 'b']
+
+ Environment variables are expanded:
+
+ >>> os.environ['path-test'] = '42'
+ >>> with_env = os.path.join(os.sep, u'a', u'${path-test}')
+ >>> n = field.fromUnicode(with_env)
+ >>> n.split(os.sep)
+ ['', 'a', '42']
+
+ Now try a relative path:
+
+ >>> p = os.path.join(u'a', u'b')
+ >>> n = field.fromUnicode(p)
+ >>> n.split(os.sep)
+ ['', 'faux', 'context', 'a', 'b']
+
+ The current user is expanded (these are implicitly relative paths):
+
+ >>> old_home = os.environ.get('HOME')
+ >>> os.environ['HOME'] = os.path.join(os.sep, 'HOME')
+ >>> n = field.fromUnicode('~')
+ >>> n.split(os.sep)
+ ['', 'HOME']
+ >>> if old_home:
+ ... os.environ['HOME'] = old_home
+ ... else:
+ ... del os.environ['HOME']
+
+
+ .. versionchanged:: 4.2.0
+ Start expanding home directories and environment variables.
+ """
filename, needs_processing = PathProcessor.expand(value)
if needs_processing:
filename = self.context.path(filename)
@@ -177,12 +378,46 @@ class Path(Text):
@implementer(IFromUnicode)
class Bool(schema_Bool):
- """A boolean value
+ """
+ A boolean value.
Values may be input (in upper or lower case) as any of:
- yes, no, y, n, true, false, t, or f.
+
+ - yes / no
+ - y / n
+ - true / false
+ - t / f
+
+ .. caution::
+
+ Do not confuse this with :class:`zope.schema.Bool`.
+ That class will only parse ``"True"`` and ``"true"`` as
+ `True` values. Any other value will silently be accepted as
+ `False`. This class raises a validation error for unrecognized
+ input.
+
"""
+
def fromUnicode(self, value):
+ """
+ Convert the input string to a boolean.
+
+ Example:
+
+ >>> from zope.configuration.fields import Bool
+ >>> Bool().fromUnicode(u"yes")
+ True
+ >>> Bool().fromUnicode(u"y")
+ True
+ >>> Bool().fromUnicode(u"true")
+ True
+ >>> Bool().fromUnicode(u"no")
+ False
+ >>> Bool().fromUnicode(u"surprise")
+ Traceback (most recent call last):
+ ...
+ zope.schema._bootstrapinterfaces.InvalidValue
+ """
value = value.lower()
if value in ('1', 'true', 'yes', 't', 'y'):
return True
@@ -195,22 +430,106 @@ class Bool(schema_Bool):
@implementer(IFromUnicode)
class MessageID(Text):
- """Text string that should be translated.
+ """
+ Text string that should be translated.
- When a string is converted to a message ID, it is also
- recorded in the context.
+ When a string is converted to a message ID, it is also recorded in
+ the context.
"""
__factories = {}
def fromUnicode(self, u):
+ """
+ Translate a string to a MessageID.
+
+ >>> from zope.configuration.fields import MessageID
+ >>> class Info(object):
+ ... file = 'file location'
+ ... line = 8
+ >>> class FauxContext(object):
+ ... i18n_strings = {}
+ ... info = Info()
+ >>> context = FauxContext()
+ >>> field = MessageID().bind(context)
+
+ There is a fallback domain when no domain has been specified.
+
+ Exchange the warn function so we can make test whether the warning
+ has been issued
+
+ >>> warned = None
+ >>> def fakewarn(*args, **kw):
+ ... global warned
+ ... warned = args
+
+ >>> import warnings
+ >>> realwarn = warnings.warn
+ >>> warnings.warn = fakewarn
+
+ >>> i = field.fromUnicode(u"Hello world!")
+ >>> i
+ 'Hello world!'
+ >>> i.domain
+ 'untranslated'
+ >>> warned
+ ("You did not specify an i18n translation domain for the '' field in file location",)
+
+ >>> warnings.warn = realwarn
+
+ With the domain specified:
+
+ >>> context.i18n_strings = {}
+ >>> context.i18n_domain = 'testing'
+
+ We can get a message id:
+
+ >>> i = field.fromUnicode(u"Hello world!")
+ >>> i
+ 'Hello world!'
+ >>> i.domain
+ 'testing'
+
+ In addition, the string has been registered with the context:
+
+ >>> context.i18n_strings
+ {'testing': {'Hello world!': [('file location', 8)]}}
+
+ >>> i = field.fromUnicode(u"Foo Bar")
+ >>> i = field.fromUnicode(u"Hello world!")
+ >>> from pprint import PrettyPrinter
+ >>> pprint=PrettyPrinter(width=70).pprint
+ >>> pprint(context.i18n_strings)
+ {'testing': {'Foo Bar': [('file location', 8)],
+ 'Hello world!': [('file location', 8),
+ ('file location', 8)]}}
+
+ >>> from zope.i18nmessageid import Message
+ >>> isinstance(list(context.i18n_strings['testing'].keys())[0], Message)
+ True
+
+ Explicit Message IDs
+
+ >>> i = field.fromUnicode(u'[View-Permission] View')
+ >>> i
+ 'View-Permission'
+ >>> i.default
+ 'View'
+
+ >>> i = field.fromUnicode(u'[] [Some] text')
+ >>> i
+ '[Some] text'
+ >>> i.default is None
+ True
+
+ """
context = self.context
domain = getattr(context, 'i18n_domain', '')
if not domain:
domain = 'untranslated'
warnings.warn(
"You did not specify an i18n translation domain for the "\
- "'%s' field in %s" % (self.getName(), context.info.file )
+ "'%s' field in %s" % (self.getName(), context.info.file)
)
if not isinstance(domain, str):
# IZopeConfigure specifies i18n_domain as a BytesLine, but that's
diff --git a/src/zope/configuration/interfaces.py b/src/zope/configuration/interfaces.py
index 45e5ac6..ee66358 100644
--- a/src/zope/configuration/interfaces.py
+++ b/src/zope/configuration/interfaces.py
@@ -98,16 +98,25 @@ class IConfigurationContext(Interface):
"""Record a configuration action
The job of most directives is to compute actions for later
- processing. The action method is used to record those
- actions. The discriminator is used to to find actions that
- conflict. Actions conflict if they have the same
- discriminator. The exception to this is the special case of
- the discriminator with the value None. An actions with a
- discriminator of None never conflicts with other actions. This
- is possible to add an order argument to crudely control the
- order of execution. 'info' is optional source line information,
- 'includepath' is None (the default) or a tuple of include paths for
- this action.
+ processing. The action method is used to record those actions.
+
+ :param callable: The object to call to implement the action.
+ :param tuple args: Arguments to pass to *callable*
+ :param dict kw: Keyword arguments to pass to *callable*
+
+ :param object discriminator: Used to to find actions that conflict.
+ Actions conflict if they have equal discriminators. The
+ exception to this is the special case of the discriminator
+ with the value `None`. An action with a discriminator of `None`
+ never conflicts with other actions.
+
+ :keyword int order: This is possible to add an order argument to crudely control
+ the order of execution.
+
+ :keyword str info: Optional source line information
+
+ :keyword includepath: is None (the default) or a tuple of
+ include paths for this action.
"""
def provideFeature(name):
diff --git a/src/zope/configuration/name.py b/src/zope/configuration/name.py
index a162ea1..97b7d76 100644
--- a/src/zope/configuration/name.py
+++ b/src/zope/configuration/name.py
@@ -11,7 +11,11 @@
# FOR A PARTICULAR PURPOSE.
#
##############################################################################
-"""Provide configuration object name resolution
+"""
+Provide configuration object name resolution.
+
+.. note:: This module is no longer used by `zope.configuration` and
+ may be deprecated soon. Its functions are not documented.
"""
import os
diff --git a/src/zope/configuration/tests/test_docs.py b/src/zope/configuration/tests/test_docs.py
index 915d2f0..3452e3b 100644
--- a/src/zope/configuration/tests/test_docs.py
+++ b/src/zope/configuration/tests/test_docs.py
@@ -20,6 +20,7 @@ from __future__ import division
from __future__ import print_function
import re
+import sys
import os.path
import unittest
import doctest
@@ -41,6 +42,11 @@ checker = renormalizing.RENormalizing([
(re.compile('b(".*?")'), r"\1"),
])
+optionflags = (
+ doctest.NORMALIZE_WHITESPACE
+ | doctest.ELLIPSIS
+ | doctest.IGNORE_EXCEPTION_DETAIL
+)
def test_suite():
here = os.path.dirname(os.path.abspath(__file__))
@@ -64,15 +70,22 @@ def test_suite():
'xmlconfig.rst',
)
+ # Plain doctest suites
+ api_to_test = (
+ 'config',
+ 'docutils',
+ 'fields',
+ 'interfaces',
+ 'name',
+ 'xmlconfig',
+ 'zopeconfigure',
+ )
+
paths = [os.path.join(docs, f) for f in doc_files_to_test]
paths += [os.path.join(api_docs, f) for f in api_files_to_test]
m = manuel.ignore.Manuel()
- m += manuel.doctest.Manuel(checker=checker, optionflags=(
- doctest.NORMALIZE_WHITESPACE
- | doctest.ELLIPSIS
- | doctest.IGNORE_EXCEPTION_DETAIL
- ))
+ m += manuel.doctest.Manuel(checker=checker, optionflags=optionflags)
m += manuel.codeblock.Manuel()
m += manuel.capture.Manuel()
@@ -84,4 +97,14 @@ def test_suite():
)
)
+ for mod_name in api_to_test:
+ mod_name = 'zope.configuration.' + mod_name
+ suite.addTest(
+ doctest.DocTestSuite(
+ mod_name,
+ checker=checker,
+ optionflags=optionflags
+ )
+ )
+
return suite
diff --git a/src/zope/configuration/xmlconfig.py b/src/zope/configuration/xmlconfig.py
index 18a2bbe..e979e13 100644
--- a/src/zope/configuration/xmlconfig.py
+++ b/src/zope/configuration/xmlconfig.py
@@ -71,10 +71,19 @@ ZCML_CONDITION = (ZCML_NAMESPACE, u"condition")
class ZopeXMLConfigurationError(ConfigurationError):
- """Zope XML Configuration error
+ """
+ Zope XML Configuration error
+
+ These errors are wrappers for other errors. They include
+ configuration info and the wrapped error type and value.
+
+ Example
- These errors are wrappers for other errors. They include configuration
- info and the wrapped error type and value.
+ >>> from zope.configuration.xmlconfig import ZopeXMLConfigurationError
+ >>> v = ZopeXMLConfigurationError("blah", AttributeError, "xxx")
+ >>> print(v)
+ 'blah'
+ AttributeError: xxx
"""
def __init__(self, info, etype, evalue):
self.info, self.etype, self.evalue = info, etype, evalue
@@ -86,7 +95,15 @@ class ZopeXMLConfigurationError(ConfigurationError):
repr(self.info), self.etype.__name__, self.evalue)
class ZopeSAXParseException(ConfigurationError):
- """Sax Parser errors, reformatted in an emacs friendly way
+ """
+ Sax Parser errors, reformatted in an emacs friendly way.
+
+ Example
+
+ >>> from zope.configuration.xmlconfig import ZopeSAXParseException
+ >>> v = ZopeSAXParseException("foo.xml:12:3:Not well formed")
+ >>> print(v)
+ File "foo.xml", line 12.3, Not well formed
"""
def __init__(self, v):
self._v = v
@@ -100,10 +117,40 @@ class ZopeSAXParseException(ConfigurationError):
return str(v)
class ParserInfo(object):
- """Information about a directive based on parser data
+ r"""
+ Information about a directive based on parser data
This includes the directive location, as well as text data
contained in the directive.
+
+ Example
+
+ >>> from zope.configuration.xmlconfig import ParserInfo
+ >>> info = ParserInfo('tests//sample.zcml', 1, 0)
+ >>> info
+ File "tests//sample.zcml", line 1.0
+
+ >>> print(info)
+ File "tests//sample.zcml", line 1.0
+
+ >>> info.characters("blah\n")
+ >>> info.characters("blah")
+ >>> info.text
+ 'blah\nblah'
+
+ >>> info.end(7, 0)
+ >>> info
+ File "tests//sample.zcml", line 1.0-7.0
+
+ >>> print(info)
+ File "tests//sample.zcml", line 1.0-7.0
+ <configure xmlns='http://namespaces.zope.org/zope'>
+ <!-- zope.configure -->
+ <directives namespace="http://namespaces.zope.org/zope">
+ <directive name="hook" attributes="name implementation module"
+ handler="zope.configuration.metaconfigure.hook" />
+ </directives>
+ </configure>
"""
text = u''
@@ -173,7 +220,8 @@ class ParserInfo(object):
class ConfigurationHandler(ContentHandler):
- """Interface to the xml parser
+ """
+ Interface to the XML parser
Translate parser events into calls into the configuration system.
"""
@@ -231,12 +279,76 @@ class ConfigurationHandler(ContentHandler):
self.context.setInfo(info)
def evaluateCondition(self, expression):
- """Evaluate a ZCML condition.
+ """
+ Evaluate a ZCML condition.
``expression`` is a string of the form "verb arguments".
Currently the supported verbs are ``have``, ``not-have``,
``installed`` and ``not-installed``.
+
+ The ``have`` and ``not-have`` verbs each take one argument:
+ the name of a feature:
+
+ >>> from zope.configuration.config import ConfigurationContext
+ >>> from zope.configuration.xmlconfig import ConfigurationHandler
+ >>> context = ConfigurationContext()
+ >>> context.provideFeature('apidoc')
+ >>> c = ConfigurationHandler(context, testing=True)
+ >>> c.evaluateCondition("have apidoc")
+ True
+ >>> c.evaluateCondition("not-have apidoc")
+ False
+ >>> c.evaluateCondition("have onlinehelp")
+ False
+ >>> c.evaluateCondition("not-have onlinehelp")
+ True
+
+ Ill-formed expressions raise an error:
+
+ >>> c.evaluateCondition("want apidoc")
+ Traceback (most recent call last):
+ ...
+ ValueError: Invalid ZCML condition: 'want apidoc'
+
+ >>> c.evaluateCondition("have x y")
+ Traceback (most recent call last):
+ ...
+ ValueError: Only one feature allowed: 'have x y'
+
+ >>> c.evaluateCondition("have")
+ Traceback (most recent call last):
+ ...
+ ValueError: Feature name missing: 'have'
+
+ The ``installed`` and ``not-installed`` verbs each take one
+ argument: the dotted name of a pacakge.
+
+ If the pacakge is found, in other words, can be imported, then
+ the condition will return true / false:
+
+ >>> context = ConfigurationContext()
+ >>> c = ConfigurationHandler(context, testing=True)
+ >>> c.evaluateCondition('installed zope.interface')
+ True
+ >>> c.evaluateCondition('not-installed zope.interface')
+ False
+ >>> c.evaluateCondition('installed zope.foo')
+ False
+ >>> c.evaluateCondition('not-installed zope.foo')
+ True
+
+ Ill-formed expressions raise an error:
+
+ >>> c.evaluateCondition("installed foo bar")
+ Traceback (most recent call last):
+ ...
+ ValueError: Only one package allowed: 'installed foo bar'
+
+ >>> c.evaluateCondition("installed")
+ Traceback (most recent call last):
+ ...
+ ValueError: Package name missing: 'installed'
"""
arguments = expression.split(None)
verb = arguments.pop(0)
@@ -313,11 +425,53 @@ def processxmlfile(file, context, testing=False):
def openInOrPlain(filename):
- """Open a file, falling back to filename.in.
+ """
+ Open a file, falling back to filename.in.
If the requested file does not exist and filename.in does, fall
- back to filename.in. If opening the original filename fails for
+ back to filename.in. If opening the original filename fails for
any other reason, allow the failure to propogate.
+
+ For example, the tests/samplepackage dirextory has files:
+
+ - configure.zcml
+
+ - configure.zcml.in
+
+ - foo.zcml.in
+
+ If we open configure.zcml, we'll get that file:
+
+ >>> import os
+ >>> from zope.configuration.xmlconfig import __file__
+ >>> from zope.configuration.xmlconfig import openInOrPlain
+ >>> here = os.path.dirname(__file__)
+ >>> path = os.path.join(here, 'tests', 'samplepackage', 'configure.zcml')
+ >>> f = openInOrPlain(path)
+ >>> f.name[-14:]
+ 'configure.zcml'
+ >>> f.close()
+
+ But if we open foo.zcml, we'll get foo.zcml.in, since there isn't
+ a foo.zcml:
+
+ >>> path = os.path.join(here, 'tests', 'samplepackage', 'foo.zcml')
+ >>> f = openInOrPlain(path)
+ >>> f.name[-11:]
+ 'foo.zcml.in'
+ >>> f.close()
+
+ Make sure other IOErrors are re-raised. We need to do this in a
+ try-except block because different errors are raised on Windows
+ and on Linux.
+
+ >>> try:
+ ... f = openInOrPlain('.')
+ ... except IOError:
+ ... print("passed")
+ ... else:
+ ... print("failed")
+ passed
"""
try:
return open(filename)
diff --git a/src/zope/configuration/zopeconfigure.py b/src/zope/configuration/zopeconfigure.py
index 0f8077d..266fd46 100644
--- a/src/zope/configuration/zopeconfigure.py
+++ b/src/zope/configuration/zopeconfigure.py
@@ -123,7 +123,7 @@ class IZopeConfigure(Interface):
information collected is used by subdirectives.
It may seem that this directive can only be used once per file, but it can
- be applied whereever it is convenient.
+ be applied wherever it is convenient.
"""
package = GlobalObject(
@@ -149,7 +149,9 @@ class IZopeConfigure(Interface):
class ZopeConfigure(GroupingContextDecorator):
- __doc__ = __doc__
+ """
+ The implementation of `IZopeConfigure`.
+ """
def __init__(self, context, **kw):
super(ZopeConfigure, self).__init__(context, **kw)