zope.configuration-3.7.4/.bzrignore0000644000000000000000000000010011356550612015473 0ustar 00000000000000./.installed.cfg ./bin ./develop-eggs ./eggs ./parts *.egg-info zope.configuration-3.7.4/bootstrap.py0000644000000000000000000000742211366603326016100 0ustar 00000000000000############################################################################## # # Copyright (c) 2006 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """Bootstrap a buildout-based project Simply run this script in a directory containing a buildout.cfg. The script accepts buildout command-line options, so you can use the -c option to specify an alternate configuration file. $Id: bootstrap.py 111664 2010-04-30 17:35:35Z hannosch $ """ import os, shutil, sys, tempfile, urllib2 from optparse import OptionParser tmpeggs = tempfile.mkdtemp() is_jython = sys.platform.startswith('java') # parsing arguments parser = OptionParser() parser.add_option("-v", "--version", dest="version", help="use a specific zc.buildout version") parser.add_option("-d", "--distribute", action="store_true", dest="distribute", default=False, help="Use Disribute rather than Setuptools.") parser.add_option("-c", None, action="store", dest="config_file", help=("Specify the path to the buildout configuration " "file to be used.")) options, args = parser.parse_args() # if -c was provided, we push it back into args for buildout' main function if options.config_file is not None: args += ['-c', options.config_file] if options.version is not None: VERSION = '==%s' % options.version else: VERSION = '' USE_DISTRIBUTE = options.distribute args = args + ['bootstrap'] to_reload = False try: import pkg_resources if not hasattr(pkg_resources, '_distribute'): to_reload = True raise ImportError except ImportError: ez = {} if USE_DISTRIBUTE: exec urllib2.urlopen('http://python-distribute.org/distribute_setup.py' ).read() in ez ez['use_setuptools'](to_dir=tmpeggs, download_delay=0, no_fake=True) else: exec urllib2.urlopen('http://peak.telecommunity.com/dist/ez_setup.py' ).read() in ez ez['use_setuptools'](to_dir=tmpeggs, download_delay=0) if to_reload: reload(pkg_resources) else: import pkg_resources if sys.platform == 'win32': def quote(c): if ' ' in c: return '"%s"' % c # work around spawn lamosity on windows else: return c else: def quote (c): return c cmd = 'from setuptools.command.easy_install import main; main()' ws = pkg_resources.working_set if USE_DISTRIBUTE: requirement = 'distribute' else: requirement = 'setuptools' if is_jython: import subprocess assert subprocess.Popen([sys.executable] + ['-c', quote(cmd), '-mqNxd', quote(tmpeggs), 'zc.buildout' + VERSION], env=dict(os.environ, PYTHONPATH= ws.find(pkg_resources.Requirement.parse(requirement)).location ), ).wait() == 0 else: assert os.spawnle( os.P_WAIT, sys.executable, quote (sys.executable), '-c', quote (cmd), '-mqNxd', quote (tmpeggs), 'zc.buildout' + VERSION, dict(os.environ, PYTHONPATH= ws.find(pkg_resources.Requirement.parse(requirement)).location ), ) == 0 ws.add_entry(tmpeggs) ws.require('zc.buildout' + VERSION) import zc.buildout.buildout zc.buildout.buildout.main(args) shutil.rmtree(tmpeggs) zope.configuration-3.7.4/buildout.cfg0000644000000000000000000000030211152260654016003 0ustar 00000000000000[buildout] develop = . parts = test python [test] recipe = zc.recipe.testrunner eggs = zope.configuration [test] [python] recipe = zc.recipe.egg eggs = zope.configuration interpreter = python zope.configuration-3.7.4/CHANGES.txt0000644000000000000000000000500511546070036015311 0ustar 00000000000000======= Changes ======= 3.7.4 (2011-04-03) ------------------ - Test fixes for Windows. 3.7.3 (2011-03-11) ------------------ - Correctly locate packages with a __path__ attribute but no __file__ attribute (such as namespace packages installed with setup.py install --single-version-externally-managed). - Allow "info" and "includepath" to be passed optionally to context.action. 3.7.2 (2010-04-30) ------------------ - Prefer the standard libraries doctest module over zope.testing.doctest. 3.7.1 (2010-01-05) ------------------ - Jython support: use ``__builtin__`` module import rather than assuming ``__builtins__`` is available. - Jython support: deal with the fact that the Jython SAX parser returns attribute sets that have an empty string indicating no namespace instead of ``None``. - Allow ``setup.py test`` to run at least a subset of the tests that would be run when using the zope testrunner: ``setup.py test`` runs 53 tests, while ``bin/test`` runs 156. 3.7.0 (2009-12-22) ------------------ - Adjust testing output to newer zope.schema. - Prefer zope.testing.doctest over doctestunit. 3.6.0 (2009-04-01) ------------------ - Removed dependency of `zope.deprecation` package. - Don't suppress deprecation warnings any more in 'zope.configuration' package level. This makes it more likely other packages will generate deprecation warnings now, which will allow us to remove more outdated ones. - Don't fail when zope.testing is not installed. - Added missing ``processFile`` method to ``IConfigurationContext``. It is already implemented in the mix-in class, ``zope.configuration.config.ConfigurationContext``, and used by implementations of ``include`` and ``exclude`` directives. 3.5.0 (2009-02-26) ------------------ - Added the ``exclude`` directive to standard directives. It was previously available via ``zc.configuration`` package and now it's merged into ``zope.configuration``. - Changed package's mailing list address to zope-dev at zope.org, change "cheeseshop" to "pypi" in the package's url. 3.4.1 (2008-12-11) ------------------ - Use built-in 'set' type, rather than importin the 'sets' module, which is deprecated in Python 2.6. - Added support to bootstrap on Jython. 3.4.0 (2007-10-02) ------------------ - Initial release as a standalone package. Before 3.4.0 ------------ This package was part of the Zope 3 distribution and did not have its own CHANGES.txt. For earlier changes please refer to either our subversion log or the CHANGES.txt of earlier Zope 3 releases. zope.configuration-3.7.4/COPYRIGHT.txt0000644000000000000000000000004011356550612015605 0ustar 00000000000000Zope Foundation and Contributorszope.configuration-3.7.4/LICENSE.txt0000644000000000000000000000402611356550612015327 0ustar 00000000000000Zope Public License (ZPL) Version 2.1 A copyright notice accompanies this license document that identifies the copyright holders. This license has been certified as open source. It has also been designated as GPL compatible by the Free Software Foundation (FSF). Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions in source code must retain the accompanying copyright notice, this list of conditions, and the following disclaimer. 2. Redistributions in binary form must reproduce the accompanying copyright notice, this list of conditions, and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Names of the copyright holders must not be used to endorse or promote products derived from this software without prior written permission from the copyright holders. 4. The right to distribute this software or to use it for any purpose does not give you the right to use Servicemarks (sm) or Trademarks (tm) of the copyright holders. Use of them is covered by separate agreement with the copyright holders. 5. If any files are modified, you must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. Disclaimer THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. zope.configuration-3.7.4/PKG-INFO0000644000000000000000000002022411546070066014600 0ustar 00000000000000Metadata-Version: 1.0 Name: zope.configuration Version: 3.7.4 Summary: Zope Configuration Markup Language (ZCML) Home-page: http://pypi.python.org/pypi/zope.configuration Author: Zope Foundation and Contributors Author-email: zope-dev@zope.org License: ZPL 2.1 Description: zope.configuration ================== Overview -------- The zope configuration system provides an extensible system for supporting various kinds of configurations. It is based on the idea of configuration directives. Users of the configuration system provide configuration directives in some language that express configuration choices. The intent is that the language be pluggable. An XML language is provided by default. Detailed Documentation ---------------------- ========================== Zope configuration system ========================== The zope configuration system provides an extensible system for supporting variouse kinds of configurations. It is based on the idea of configuration directives. Users of the configuration system provide configuration directives in some language that express configuration choices. The intent is that the language be pluggable. An XML language is provided by default. Configuration is performed in three stages. In the first stage, directives are processed to compute configuration actions. Configuration actions consist of: - A discriminator - A callable - Positional arguments - Keyword arguments The actions are essentially delayed function calls. Two or more actions conflict if they have the same discriminator. The configuration system has rules for resolving conflicts. If conflicts cannot be resolved, an error will result. Conflict resolution typically discards all but one of the conflicting actions, so that the remaining action of the originally-conflicting actions no longer conflicts. Non-conflicting actions are executed in the order that they were created by passing the positional and non-positional arguments to the action callable. The system is extensible. There is a meta-configuration language for defining configuration directives. A directive is defined by providing meta data about the directive and handler code to process the directive. There are four kinds of directives: - Simple directives compute configuration actions. Their handlers are typically functions that take a context and zero or more keyword arguments and return a sequence of configuration actions. To learn how to create simple directives, see `tests/test_simple.py`. - Grouping directives collect information to be used by nested directives. They are called with a context object which they adapt to some interface that extends IConfigurationContext. To learn how to create grouping directives, look at the documentation in zopeconfigure.py, which provides the implementation of the zope `configure` directive. Other directives can be nested in grouping directives. To learn how to implement nested directives, look at the documentation in `tests/test_nested.py`. - Complex directives are directives that have subdirectives. Subdirectives have handlers that are simply methods of complex directives. Complex diretives are handled by factories, typically classes, that create objects that have methods for handling subdirectives. These objects also have __call__ methods that are called when processing of subdirectives is finished. Complex directives only exist to support old directive handlers. They will probably be deprecated in the future. - Subdirectives are nested in complex directives. They are like simple directives except that they hane handlers that are complex directive methods. Subdirectives, like complex directives only exist to support old directive handlers. They will probably be deprecated in the future. ======= Changes ======= 3.7.4 (2011-04-03) ------------------ - Test fixes for Windows. 3.7.3 (2011-03-11) ------------------ - Correctly locate packages with a __path__ attribute but no __file__ attribute (such as namespace packages installed with setup.py install --single-version-externally-managed). - Allow "info" and "includepath" to be passed optionally to context.action. 3.7.2 (2010-04-30) ------------------ - Prefer the standard libraries doctest module over zope.testing.doctest. 3.7.1 (2010-01-05) ------------------ - Jython support: use ``__builtin__`` module import rather than assuming ``__builtins__`` is available. - Jython support: deal with the fact that the Jython SAX parser returns attribute sets that have an empty string indicating no namespace instead of ``None``. - Allow ``setup.py test`` to run at least a subset of the tests that would be run when using the zope testrunner: ``setup.py test`` runs 53 tests, while ``bin/test`` runs 156. 3.7.0 (2009-12-22) ------------------ - Adjust testing output to newer zope.schema. - Prefer zope.testing.doctest over doctestunit. 3.6.0 (2009-04-01) ------------------ - Removed dependency of `zope.deprecation` package. - Don't suppress deprecation warnings any more in 'zope.configuration' package level. This makes it more likely other packages will generate deprecation warnings now, which will allow us to remove more outdated ones. - Don't fail when zope.testing is not installed. - Added missing ``processFile`` method to ``IConfigurationContext``. It is already implemented in the mix-in class, ``zope.configuration.config.ConfigurationContext``, and used by implementations of ``include`` and ``exclude`` directives. 3.5.0 (2009-02-26) ------------------ - Added the ``exclude`` directive to standard directives. It was previously available via ``zc.configuration`` package and now it's merged into ``zope.configuration``. - Changed package's mailing list address to zope-dev at zope.org, change "cheeseshop" to "pypi" in the package's url. 3.4.1 (2008-12-11) ------------------ - Use built-in 'set' type, rather than importin the 'sets' module, which is deprecated in Python 2.6. - Added support to bootstrap on Jython. 3.4.0 (2007-10-02) ------------------ - Initial release as a standalone package. Before 3.4.0 ------------ This package was part of the Zope 3 distribution and did not have its own CHANGES.txt. For earlier changes please refer to either our subversion log or the CHANGES.txt of earlier Zope 3 releases. Keywords: zope configuration zcml Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable Classifier: Environment :: Web Environment Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: Zope Public License Classifier: Programming Language :: Python Classifier: Natural Language :: English Classifier: Operating System :: OS Independent Classifier: Topic :: Internet :: WWW/HTTP Classifier: Framework :: Zope3 zope.configuration-3.7.4/README.txt0000644000000000000000000000065311153664220015200 0ustar 00000000000000zope.configuration ================== Overview -------- The zope configuration system provides an extensible system for supporting various kinds of configurations. It is based on the idea of configuration directives. Users of the configuration system provide configuration directives in some language that express configuration choices. The intent is that the language be pluggable. An XML language is provided by default. zope.configuration-3.7.4/setup.cfg0000644000000000000000000000007311546070066015324 0ustar 00000000000000[egg_info] tag_build = tag_date = 0 tag_svn_revision = 0 zope.configuration-3.7.4/setup.py0000644000000000000000000000721411546070032015212 0ustar 00000000000000############################################################################## # # Copyright (c) 2006 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## # This package is developed by the Zope Toolkit project, documented here: # http://docs.zope.org/zopetoolkit # When developing and releasing this package, please follow the documented # Zope Toolkit policies as described by this documentation. ############################################################################## import os from setuptools import setup, find_packages def read(*rnames): return open(os.path.join(os.path.dirname(__file__), *rnames)).read() def _modname(path, base, name=''): if path == base: return name dirname, basename = os.path.split(path) return _modname(dirname, base, basename + '.' + name) def alltests(): import logging import pkg_resources import unittest class NullHandler(logging.Handler): level = 50 def emit(self, record): pass logging.getLogger().addHandler(NullHandler()) suite = unittest.TestSuite() base = pkg_resources.working_set.find( pkg_resources.Requirement.parse('zope.configuration')).location for dirpath, dirnames, filenames in os.walk(base): if os.path.basename(dirpath) == 'tests': for filename in filenames: if ( filename.endswith('.py') and filename.startswith('test') ): mod = __import__( _modname(dirpath, base, os.path.splitext(filename)[0]), {}, {}, ['*']) suite.addTest(mod.test_suite()) return suite setup(name='zope.configuration', version = '3.7.4', author='Zope Foundation and Contributors', author_email='zope-dev@zope.org', description='Zope Configuration Markup Language (ZCML)', long_description=( read('README.txt') + '\n\n' + 'Detailed Documentation\n' + '----------------------\n' + '\n\n' + read('src', 'zope', 'configuration', 'README.txt') + '\n\n' + read('CHANGES.txt') ), keywords = "zope configuration zcml", classifiers = [ 'Development Status :: 5 - Production/Stable', 'Environment :: Web Environment', 'Intended Audience :: Developers', 'License :: OSI Approved :: Zope Public License', 'Programming Language :: Python', 'Natural Language :: English', 'Operating System :: OS Independent', 'Topic :: Internet :: WWW/HTTP', 'Framework :: Zope3'], url='http://pypi.python.org/pypi/zope.configuration', license='ZPL 2.1', packages=find_packages('src'), package_dir={'': 'src'}, namespace_packages=['zope'], extras_require=dict( test=['zope.testing']), install_requires=['zope.i18nmessageid', 'zope.interface', 'zope.schema', 'setuptools', ], include_package_data=True, zip_safe=False, tests_require = 'zope.testing', test_suite='__main__.alltests', ) zope.configuration-3.7.4/src/zope/__init__.py0000644000000000000000000000007011155754000017346 0ustar 00000000000000__import__('pkg_resources').declare_namespace(__name__) zope.configuration-3.7.4/src/zope/configuration/__init__.py0000644000000000000000000000151311356545654022240 0ustar 00000000000000############################################################################## # # Copyright (c) 2001, 2002 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """Zope configuration support Software that wants to provide new config directives calls zope.configuration.meta.register. """ def namespace(suffix): return 'http://namespaces.zope.org/'+suffix zope.configuration-3.7.4/src/zope/configuration/config.py0000644000000000000000000014647711536465362021766 0ustar 00000000000000############################################################################## # # Copyright (c) 2003 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """Configuration processor See README.txt. """ __docformat__ = 'restructuredtext' import __builtin__ import os.path import sys import zope.schema from keyword import iskeyword from zope.configuration.exceptions import ConfigurationError from zope.configuration.interfaces import IConfigurationContext from zope.configuration.interfaces import IGroupingContext from zope.interface.adapter import AdapterRegistry from zope.interface import Interface, implements, providedBy from zope.configuration import fields zopens = 'http://namespaces.zope.org/zope' metans = 'http://namespaces.zope.org/meta' testns = 'http://namespaces.zope.org/test' _import_chickens = {}, {}, ("*",) # dead chickens needed by __import__ class ConfigurationContext(object): """Mix-in that implements IConfigurationContext Subclasses provide a ``package`` attribute and a ``basepath`` attribute. If the base path is not None, relative paths are converted to absolute paths using the the base path. If the package is not none, relative imports are performed relative to the package. In general, the basepath and package attributes should be consistent. When a package is provided, the base path should be set to the path of the package directory. Subclasses also provide an ``actions`` attribute, which is a list of actions, an ``includepath`` attribute, and an ``info`` attribute. The include path is appended to each action and is used when resolving conflicts among actions. Normally, only the a ConfigurationMachine provides the actions attribute. Decorators simply use the actions of the context they decorate. The ``includepath`` attribute is a tuple of names. Each name is typically the name of an included configuration file. The ``info`` attribute contains descriptive information helpful when reporting errors. If not set, it defaults to an empty string. The actions attribute is a sequence of tuples with items: - discriminator, a value that identifies the action. Two actions that have the same (non None) discriminator conflict. - an object that is called to execute the action, - positional arguments for the action - keyword arguments for the action - a tuple of include file names (defaults to ()) - an object that has descriptive information about the action (defaults to '') For brevity, trailing items after the callable in the tuples are ommitted if they are empty. """ def __init__(self): super(ConfigurationContext, self).__init__() self._seen_files = set() self._features = set() def resolve(self, dottedname): """Resolve a dotted name to an object Examples: >>> c = ConfigurationContext() >>> import zope, zope.interface >>> c.resolve('zope') is zope 1 >>> c.resolve('zope.interface') is zope.interface 1 >>> 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 1 >>> c.resolve('.config.ConfigurationContext') is ConfigurationContext 1 >>> c.resolve('..interface') is zope.interface 1 >>> c.resolve('unicode') """ name = dottedname.strip() if not name: raise ValueError("The given name is blank") if name == '.': return self.package names = name.split('.') if not names[-1]: raise ValueError( "Trailing dots are no longer supported in dotted names") if len(names) == 1: # Check for built-in objects marker = object() obj = getattr(__builtin__, names[0], marker) if obj is not marker: return obj if not names[0]: # Got a relative name. Convert it to abs using package info if self.package is None: raise ConfigurationError( "Can't use leading dots in dotted names, " "no package has been set.") pnames = self.package.__name__.split(".") pnames.append('') while names and not names[0]: try: names.pop(0) except IndexError: raise ConfigurationError("Invalid global name", name) try: pnames.pop() except IndexError: raise ConfigurationError("Invalid global name", name) names[0:0] = pnames # Now we should have an absolute dotted name # Split off object name: oname, mname = names[-1], '.'.join(names[:-1]) # Import the module if not mname: # Just got a single name. Must me a module mname = oname oname = '' try: mod = __import__(mname, *_import_chickens) except ImportError, v: if sys.exc_info()[2].tb_next is not None: # ImportError was caused deeper raise raise ConfigurationError( "ImportError: Couldn't import %s, %s" % (mname, v)) if not oname: # see not mname case above return mod try: obj = getattr(mod, oname) return obj except AttributeError: # No such name, maybe it's a module that we still need to import try: return __import__(mname+'.'+oname, *_import_chickens) except ImportError: if sys.exc_info()[2].tb_next is not None: # ImportError was caused deeper raise raise ConfigurationError( "ImportError: Module %s has no global %s" % (mname, oname)) def path(self, filename): """ Examples: >>> c = ConfigurationContext() >>> c.path("/x/y/z") == os.path.normpath("/x/y/z") 1 >>> 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") 1 >>> c.path("y/./z") == d + os.path.normpath("/y/z") 1 >>> c.path("y/../z") == d + os.path.normpath("/z") 1 """ filename = os.path.normpath(filename) if os.path.isabs(filename): return filename # Got a relative path, combine with base path. # If we have no basepath, compute the base path from the package # path. basepath = getattr(self, 'basepath', '') if not basepath: if self.package is None: basepath = os.getcwd() else: if hasattr(self.package, '__path__'): basepath = self.package.__path__[0] else: basepath = os.path.dirname(self.package.__file__) basepath = os.path.abspath(basepath) self.basepath = basepath return os.path.join(basepath, filename) def checkDuplicate(self, filename): """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. >>> c = ConfigurationContext() >>> c.checkDuplicate('/foo.zcml') >>> try: ... c.checkDuplicate('/foo.zcml') ... except ConfigurationError, 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, e: ... str(e).endswith("bar.zcml' included more than once") ... True """ #' <-- bow to font-lock path = self.path(filename) if path in self._seen_files: raise ConfigurationError('%r included more than once' % path) self._seen_files.add(path) def processFile(self, filename): """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. >>> 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('bar.zcml') False """ #' <-- bow to font-lock path = self.path(filename) if path in self._seen_files: return False self._seen_files.add(path) return True def action(self, discriminator, callable=None, args=(), kw={}, order=0, includepath=None, info=None): """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. Let's look at some examples: >>> 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}) >>> c.actions [(1, f, (1,), {'x': 1})] >>> c.action(None) >>> c.actions [(1, f, (1,), {'x': 1}), (None, None)] Now set the include path and info: >>> c.includepath = ('foo.zcml',) >>> c.info = "?" >>> c.action(None) >>> c.actions[-1] (None, None, (), {}, ('foo.zcml',), '?') We can add an order argument to crudely control the order of execution: >>> c.action(None, order=99999) >>> c.actions[-1] (None, None, (), {}, ('foo.zcml',), '?', 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',)) >>> c.actions[-1] (None, None, (), {}, ('abc',), '?') 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') >>> c.actions[-1] (None, None, (), {}, ('foo.zcml',), 'abc') """ if info is None: info = getattr(self, 'info', '') if includepath is None: includepath = getattr(self, 'includepath', ()) action = (discriminator, callable, args, kw, includepath, info, order) # remove trailing false items while (len(action) > 2) and not action[-1]: action = action[:-1] self.actions.append(action) def hasFeature(self, feature): """Check whether a named feature has been provided. Initially no features are provided >>> c = ConfigurationContext() >>> c.hasFeature('onlinehelp') False 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. See `hasFeature` for examples. """ self._features.add(feature) class ConfigurationAdapterRegistry(object): """Simple adapter registry that manages directives as adapters >>> 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') >>> from zope.configuration.interfaces import IConfigurationContext >>> def f(): ... pass >>> r.register(IConfigurationContext, ('http://www.zope.com', 'xxx'), f) >>> r.factory(c, ('http://www.zope.com','xxx')) is f 1 >>> 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 1 Test the documentation feature: >>> r._docRegistry [] >>> r.document(('ns', 'dir'), IFullInfo, IConfigurationContext, None, ... 'inf', None) >>> r._docRegistry[0][0] == ('ns', 'dir') 1 >>> r._docRegistry[0][1] is IFullInfo 1 >>> r._docRegistry[0][2] is IConfigurationContext 1 >>> r._docRegistry[0][3] is None 1 >>> r._docRegistry[0][4] == 'inf' 1 >>> r._docRegistry[0][5] is None 1 >>> r.document('all-dir', None, None, None, None) >>> r._docRegistry[1][0] ('', 'all-dir') """ def __init__(self): super(ConfigurationAdapterRegistry, self).__init__() self._registry = {} # Stores tuples of form: # (namespace, name), schema, usedIn, info, parent self._docRegistry = [] def register(self, interface, name, factory): r = self._registry.get(name) if r is None: r = AdapterRegistry() self._registry[name] = r r.register([interface], Interface, '', factory) def document(self, name, schema, usedIn, handler, info, parent=None): if isinstance(name, (str, unicode)): name = ('', name) self._docRegistry.append((name, schema, usedIn, handler, info, parent)) def factory(self, context, name): r = self._registry.get(name) if r is None: # Try namespace-independent name ns, n = name r = self._registry.get(n) if r is None: raise ConfigurationError("Unknown directive", ns, n) f = r.lookup1(providedBy(context), Interface) if f is None: raise ConfigurationError( "The directive %s cannot be used in this context" % (name, )) return f class ConfigurationMachine(ConfigurationAdapterRegistry, ConfigurationContext): """Configuration machine Example: >>> machine = ConfigurationMachine() >>> ns = "http://www.zope.org/testing" Register a directive: >>> 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") >>> machine.actions [(('simple', u'aa', u'xxx', 'cc'), f, (u'aa', u'xxx', 'cc'))] A more extensive example can be found in the unit tests. """ implements(IConfigurationContext) package = None basepath = None includepath = () info = '' def __init__(self): super(ConfigurationMachine, self).__init__() self.actions = [] self.stack = [RootStackItem(self)] self.i18n_strings = {} _bootstrap(self) def begin(self, __name, __data=None, __info=None, **kw): if __data: if kw: raise TypeError("Can't provide a mapping object and keyword " "arguments") else: __data = kw self.stack.append(self.stack[-1].contained(__name, __data, __info)) def end(self): self.stack.pop().finish() def __call__(self, __name, __info=None, **__kw): self.begin(__name, __kw, __info) self.end() def getInfo(self): return self.stack[-1].context.info def setInfo(self, info): self.stack[-1].context.info = info def execute_actions(self, clear=True, testing=False): """Execute the configuration actions This calls the action callables after resolving conflicts For example: >>> 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. >>> 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, v: ... pass >>> print v exceptions.AttributeError: 'function' object has no attribute 'xxx' in: oops Note that actions executed before the error still have an effect: >>> output [('f', (1,), {}), ('f', (2,), {})] """ try: for action in resolveConflicts(self.actions): (discriminator, callable, args, kw, includepath, info, order ) = expand_action(*action) if callable is None: continue try: callable(*args, **kw) except (KeyboardInterrupt, SystemExit): raise except: if testing: raise t, v, tb = sys.exc_info() raise ConfigurationExecutionError(t, v, info), None, tb finally: if clear: del self.actions[:] class ConfigurationExecutionError(ConfigurationError): """An error occurred during execution of a configuration action """ def __init__(self, etype, evalue, info): self.etype, self.evalue, self.info = etype, evalue, info def __str__(self): return "%s: %s\n in:\n %s" % (self.etype, self.evalue, self.info) ############################################################################## # Stack items class IStackItem(Interface): """Configuration machine stack items Stack items are created when a directive is being processed. A stack item is created for each directive use. """ def contained(name, data, info): """Begin processing a contained directive The data are a dictionary of attribute names mapped to unicode strings. The info argument is an object that can be converted to a string and that contains information about the directive. The begin method returns the next item to be placed on the stack. """ def finish(): """Finish processing a directive """ class SimpleStackItem(object): """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 subdirectives, which can't contain other directives. It also defers any computation until the end of the directive has been reached. """ implements(IStackItem) def __init__(self, context, handler, info, *argdata): newcontext = GroupingContextDecorator(context) newcontext.info = info self.context = newcontext self.handler = handler self.argdata = argdata def contained(self, name, data, info): raise ConfigurationError("Invalid directive %s" % str(name)) def finish(self): # We're going to use the context that was passed to us, which wasn't # created for the directive. We want to set it's info to the one # passed to us while we make the call, so we'll save the old one # and restore it. context = self.context args = toargs(context, *self.argdata) actions = self.handler(context, **args) if actions: # we allow the handler to return nothing for action in actions: context.action(*action) class RootStackItem(object): def __init__(self, context): self.context = context def contained(self, name, data, info): """Handle a contained directive We have to compute a new stack item by getting a named adapter for the current context object. """ factory = self.context.factory(self.context, name) if factory is None: raise ConfigurationError("Invalid directive", name) adapter = factory(self.context, data, info) return adapter def finish(self): pass class GroupingStackItem(RootStackItem): """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. 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 >>> 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. >>> 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: >>> 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) [(('before', 1, 2), f), (('simple', 1, 2, {'z': 'zope'}), f)] Finally, we call finish, which calls the decorator after method: >>> item.finish() >>> pprint(context.actions) [(('before', 1, 2), f), (('simple', 1, 2, {'z': 'zope'}), f), ('after', f)] 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) [(('before', 1, 2), f), ('after', f)] """ implements(IStackItem) def __init__(self, context): super(GroupingStackItem, self).__init__(context) def __callBefore(self): actions = self.context.before() if actions: for action in actions: self.context.action(*action) self.__callBefore = noop def contained(self, name, data, info): self.__callBefore() return RootStackItem.contained(self, name, data, info) def finish(self): self.__callBefore() actions = self.context.after() if actions: for action in actions: self.context.action(*action) def noop(): pass class ComplexStackItem(object): """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. 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 >>> 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: >>> class Ixy(Interface): ... x = zope.schema.TextLine() ... y = zope.schema.TextLine() >>> definition = ComplexDirectiveDefinition( ... context, name="test", schema=Ixy, ... handler=Handler) >>> class Iab(Interface): ... a = zope.schema.TextLine() ... b = zope.schema.TextLine() >>> definition['sub'] = Iab, '' OK, now that we have the context, handler and definition, we're ready to use a stack item. >>> item = ComplexStackItem(definition, context, {'x': u'xv', 'y': u'yv'}, ... 'foo') When we created the definition, the handler (factory) was called. >>> context.actions [('init', f, (), {}, (), 'foo')] 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. >>> from pprint import PrettyPrinter >>> pprint=PrettyPrinter(width=60).pprint >>> pprint(context.actions) [('init', f, (), {}, (), 'foo'), (('sub', u'av', u'bv'), f, (), {}, (), 'baz')] 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) [('init', f, (), {}, (), 'foo'), (('sub', u'av', u'bv'), f, (), {}, (), 'baz'), (('call', u'xv', u'yv'), f, (), {}, (), 'foo')] """ implements(IStackItem) def __init__(self, meta, context, data, info): newcontext = GroupingContextDecorator(context) newcontext.info = info self.context = newcontext self.meta = meta # Call the handler contructor args = toargs(newcontext, meta.schema, data) self.handler = self.meta.handler(newcontext, **args) def contained(self, name, data, info): """Handle a subdirective """ # Look up the subdirective meta data on our meta object ns, name = name schema = self.meta.get(name) if schema is None: raise ConfigurationError("Invalid directive", name) schema = schema[0] # strip off info handler = getattr(self.handler, name) return SimpleStackItem(self.context, handler, info, schema, data) def finish(self): # when we're done, we call the handler, which might return more actions # Need to save and restore old info try: actions = self.handler() except AttributeError, v: if v[0] == '__call__': return # noncallable raise except TypeError: return # non callable if actions: # we allow the handler to return nothing for action in actions: self.context.action(*action) ############################################################################## # Helper classes class GroupingContextDecorator(ConfigurationContext): """Helper mix-in class for building grouping directives See the discussion (and test) in GroupingStackItem. """ implements(IConfigurationContext, IGroupingContext) def __init__(self, context, **kw): self.context = context for name, v in kw.items(): setattr(self, name, v) def __getattr__(self, name, getattr=getattr, setattr=setattr): v = getattr(self.context, name) # cache result in self setattr(self, name, v) return v def before(self): pass def after(self): pass ############################################################################## # Directive-definition class DirectiveSchema(fields.GlobalInterface): """A field that contains a global variable value that must be a schema """ class IDirectivesInfo(Interface): """Schema for the ``directives`` directive """ namespace = zope.schema.URI( title=u"Namespace", description=u"The namespace in which directives' names will be defined", ) class IDirectivesContext(IDirectivesInfo, IConfigurationContext): pass class DirectivesHandler(GroupingContextDecorator): """Handler for the directives directive This is just a grouping directive that adds a namespace attribute to the normal directive context. """ implements(IDirectivesContext) class IDirectiveInfo(Interface): """Information common to all directive definitions have """ name = zope.schema.TextLine( title = u"Directive name", description = u"The name of the directive being defined", ) schema = DirectiveSchema( title = u"Directive handler", description = u"The dotted name of the directive handler", ) class IFullInfo(IDirectiveInfo): """Information that all top-level directives (not subdirectives) have """ handler = fields.GlobalObject( title = u"Directive handler", description = u"The dotted name of the directive handler", ) usedIn = fields.GlobalInterface( title = u"The directive types the directive can be used in", description = (u"The interface of the directives that can contain " u"the directive" ), default = IConfigurationContext, ) class IStandaloneDirectiveInfo(IDirectivesInfo, IFullInfo): """Info for full directives defined outside a directives directives """ def defineSimpleDirective(context, name, schema, handler, namespace='', usedIn=IConfigurationContext): """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. for example: >>> context = ConfigurationMachine() >>> from zope.configuration.tests.directives import f >>> class Ixy(Interface): ... x = zope.schema.TextLine() ... y = zope.schema.TextLine() >>> def s(context, x, y): ... context.action(('s', x, y), f) >>> defineSimpleDirective(context, 's', Ixy, s, testns) >>> context((testns, "s"), x=u"vx", y=u"vy") >>> context.actions [(('s', u'vx', u'vy'), f)] >>> 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") >>> context.actions [(('s', u'vx', u'vy'), f)] """ namespace = namespace or context.namespace if namespace != '*': name = namespace, name def factory(context, data, info): return SimpleStackItem(context, handler, info, schema, data) factory.schema = schema context.register(usedIn, name, factory) context.document(name, schema, usedIn, handler, context.info) def defineGroupingDirective(context, name, schema, handler, namespace='', usedIn=IConfigurationContext): """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. for example: >>> context = ConfigurationMachine() >>> from zope.configuration.tests.directives import f >>> class Ixy(Interface): ... x = zope.schema.TextLine() ... y = zope.schema.TextLine() We won't bother creating a special grouping directive class. We'll just use GroupingContextDecorator, which simply sets up a grouping context that has extra attributes defined by a schema: >>> defineGroupingDirective(context, 'g', Ixy, ... GroupingContextDecorator, testns) >>> context.begin((testns, "g"), x=u"vx", y=u"vy") >>> context.stack[-1].context.x u'vx' >>> context.stack[-1].context.y u'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 u'vx' >>> context.stack[-1].context.y u'vy' """ namespace = namespace or context.namespace if namespace != '*': name = namespace, name def factory(context, data, info): args = toargs(context, schema, data) newcontext = handler(context, **args) newcontext.info = info return GroupingStackItem(newcontext) factory.schema = schema context.register(usedIn, name, factory) context.document(name, schema, usedIn, handler, context.info) class IComplexDirectiveContext(IFullInfo, IConfigurationContext): pass class ComplexDirectiveDefinition(GroupingContextDecorator, dict): """Handler for defining complex directives See the description and tests for ComplexStackItem. """ implements(IComplexDirectiveContext) def before(self): def factory(context, data, info): return ComplexStackItem(self, context, data, info) factory.schema = self.schema self.register(self.usedIn, (self.namespace, self.name), factory) self.document((self.namespace, self.name), self.schema, self.usedIn, self.handler, self.info) def subdirective(context, name, schema): context.document((context.namespace, name), schema, context.usedIn, getattr(context.handler, name, context.handler), context.info, context.context) context.context[name] = schema, context.info ############################################################################## # Features class IProvidesDirectiveInfo(Interface): """Information for a directive""" feature = zope.schema.TextLine( title = u"Feature name", description = u"""The name of the feature being provided You can test available features with zcml:condition="have featurename". """, ) def provides(context, feature): """Declare that a feature is provided in context. >>> 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). >>> 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") context.provideFeature(feature) ############################################################################## # Argument conversion def toargs(context, schema, data): """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 without the underscore in the data. The fields in the schema must all implement IFromUnicode. 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'. Here's an example: >>> from zope import schema >>> class schema(Interface): ... in_ = zope.schema.Int(constraint=lambda v: v > 0) ... f = zope.schema.Float() ... n = zope.schema.TextLine(min_length=1, default=u"rob") ... x = zope.schema.BytesLine(required=False) ... u = zope.schema.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': u'bob', 'u': 'http://www.zope.org', 'x': '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': u'1', 'f': 1.2, 'in_': 1, 'n': u'bob', 'u': 'http://www.zope.org', 'x': 'x.y.z'} If we ommit 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': u'1', 'f': 1.2, 'in_': 1, 'n': u'bob', 'u': 'http://www.zope.org'} And we can ommit 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': u'1', 'f': 1.2, 'in_': 1, 'n': u'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 = {} for name, field in schema.namesAndDescriptions(True): field = field.bind(context) n = name if n.endswith('_') and iskeyword(n[:-1]): n = n[:-1] s = data.get(n, data) if s is not data: s = unicode(s) del data[n] try: args[str(name)] = field.fromUnicode(s) except zope.schema.ValidationError, v: raise ConfigurationError( "Invalid value for", n, str(v)), None, sys.exc_info()[2] elif field.required: # if the default is valid, we can use that: default = field.default try: field.validate(default) except zope.schema.ValidationError: raise ConfigurationError("Missing parameter:", n) args[str(name)] = default if data: # we had data left over try: keyword_arguments = schema.getTaggedValue('keyword_arguments') except KeyError: keyword_arguments = False if not keyword_arguments: raise ConfigurationError("Unrecognized parameters:", *data) for name in data: args[str(name)] = data[name] return args ############################################################################## # Conflict resolution def expand_action(discriminator, callable=None, args=(), kw={}, includepath=(), info='', order=0): return (discriminator, callable, args, kw, includepath, info, order) def resolveConflicts(actions): """Resolve conflicting actions Given an actions list, identify and try to resolve conflicting actions. Actions conflict if they have the same non-null discriminator. Conflicting actions can be resolved if the include path of one of the actions is a prefix of the includepaths of the other conflicting actions and is unequal to the include paths in the other conflicting actions. Here are some examples to illustrate how this works: >>> from zope.configuration.tests.directives import f >>> from pprint import PrettyPrinter >>> pprint=PrettyPrinter(width=60).pprint >>> pprint(resolveConflicts([ ... (None, f), ... (1, f, (1,), {}, (), 'first'), ... (1, f, (2,), {}, ('x',), 'second'), ... (1, f, (3,), {}, ('y',), 'third'), ... (4, f, (4,), {}, ('y',), 'should be last', 99999), ... (3, f, (3,), {}, ('y',)), ... (None, f, (5,), {}, ('y',)), ... ])) [(None, f), (1, f, (1,), {}, (), 'first'), (3, f, (3,), {}, ('y',)), (None, f, (5,), {}, ('y',)), (4, f, (4,), {}, ('y',), 'should be last')] >>> try: ... v = resolveConflicts([ ... (None, f), ... (1, f, (2,), {}, ('x',), 'eek'), ... (1, f, (3,), {}, ('y',), 'ack'), ... (4, f, (4,), {}, ('y',)), ... (3, f, (3,), {}, ('y',)), ... (None, f, (5,), {}, ('y',)), ... ]) ... except ConfigurationConflictError, v: ... pass >>> print v Conflicting configuration actions For: 1 eek ack """ # organize actions by discriminators unique = {} output = [] for i in range(len(actions)): (discriminator, callable, args, kw, includepath, info, order ) = expand_action(*(actions[i])) order = order or i if discriminator is None: # The discriminator is None, so this directive can # never conflict. We can add it directly to the # configuration actions. output.append( (order, discriminator, callable, args, kw, includepath, info) ) continue a = unique.setdefault(discriminator, []) a.append( (includepath, order, callable, args, kw, info) ) # Check for conflicts conflicts = {} for discriminator, dups in unique.items(): # We need to sort the actions by the paths so that the shortest # path with a given prefix comes first: dups.sort() (basepath, i, callable, args, kw, baseinfo) = dups[0] output.append( (i, discriminator, callable, args, kw, basepath, baseinfo) ) for includepath, i, callable, args, kw, info in dups[1:]: # Test whether path is a prefix of opath if (includepath[:len(basepath)] != basepath # not a prefix or (includepath == basepath) ): if discriminator not in conflicts: conflicts[discriminator] = [baseinfo] conflicts[discriminator].append(info) if conflicts: raise ConfigurationConflictError(conflicts) # Now put the output back in the original order, and return it: output.sort() r = [] for o in output: action = o[1:] while len(action) > 2 and not action[-1]: action = action[:-1] r.append(action) return r class ConfigurationConflictError(ConfigurationError): def __init__(self, conflicts): self._conflicts = conflicts def __str__(self): r = ["Conflicting configuration actions"] items = self._conflicts.items() items.sort() for discriminator, infos in items: r.append(" For: %s" % (discriminator, )) for info in infos: for line in unicode(info).rstrip().split(u'\n'): r.append(u" "+line) return "\n".join(r) ############################################################################## # Bootstap code def _bootstrap(context): # Set enough machinery to register other directives # Define the directive (simple directive) directive by calling it's # handler directly info = 'Manually registered in zope/configuration/config.py' context.info = info defineSimpleDirective( context, namespace=metans, name='directive', schema=IStandaloneDirectiveInfo, handler=defineSimpleDirective) context.info = '' # OK, now that we have that, we can use the machine to define the # other directives. This isn't the easiest way to proceed, but it lets # us eat our own dogfood. :) # Standalone groupingDirective context((metans, 'directive'), info, name='groupingDirective', namespace=metans, handler="zope.configuration.config.defineGroupingDirective", schema="zope.configuration.config.IStandaloneDirectiveInfo" ) # Now we can use the grouping directive to define the directives directive context((metans, 'groupingDirective'), info, name='directives', namespace=metans, handler="zope.configuration.config.DirectivesHandler", schema="zope.configuration.config.IDirectivesInfo" ) # directive and groupingDirective inside directives context((metans, 'directive'), info, name='directive', namespace=metans, usedIn="zope.configuration.config.IDirectivesContext", handler="zope.configuration.config.defineSimpleDirective", schema="zope.configuration.config.IFullInfo" ) context((metans, 'directive'), info, name='groupingDirective', namespace=metans, usedIn="zope.configuration.config.IDirectivesContext", handler="zope.configuration.config.defineGroupingDirective", schema="zope.configuration.config.IFullInfo" ) # Setup complex directive directive, both standalone, and in # directives directive context((metans, 'groupingDirective'), info, name='complexDirective', namespace=metans, handler="zope.configuration.config.ComplexDirectiveDefinition", schema="zope.configuration.config.IStandaloneDirectiveInfo" ) context((metans, 'groupingDirective'), info, name='complexDirective', namespace=metans, usedIn="zope.configuration.config.IDirectivesContext", handler="zope.configuration.config.ComplexDirectiveDefinition", schema="zope.configuration.config.IFullInfo" ) # Finally, setup subdirective directive context((metans, 'directive'), info, name='subdirective', namespace=metans, usedIn="zope.configuration.config.IComplexDirectiveContext", handler="zope.configuration.config.subdirective", schema="zope.configuration.config.IDirectiveInfo" ) # meta:provides context((metans, 'directive'), info, name='provides', namespace=metans, handler="zope.configuration.config.provides", schema="zope.configuration.config.IProvidesDirectiveInfo" ) zope.configuration-3.7.4/src/zope/configuration/docutils.py0000644000000000000000000000534311401620130022302 0ustar 00000000000000############################################################################## # # Copyright (c) 2004 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """Helper Utility to wrap a text to a set width of characters """ __docformat__ = 'restructuredtext' import re 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. Examples: >>> 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()) new_paras = [] for par in paras: words= filter(None, whitespace.split(par)) lines = [] line = [] length = indent for word in words: if length + len(word) <= width: line.append(word) length += len(word) + 1 else: lines.append(' '*indent + ' '.join(line)) line = [word] length = len(word) + 1 + indent lines.append(' '*indent + ' '.join(line)) new_paras.append('\n'.join(lines)) return '\n\n'.join(new_paras) + '\n\n' def makeDocStructures(context): """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). 'subdirs' maps a (namespace, name) pair to a list of subdirectives that have the form (namespace, name, schema, info). """ namespaces = {} subdirs = {} registry = context._docRegistry for (namespace, name), schema, usedIn, handler, info, parent in registry: if not parent: ns_entry = namespaces.setdefault(namespace, {}) ns_entry[name] = (schema, handler, info) else: sd_entry = subdirs.setdefault((parent.namespace, parent.name), []) sd_entry.append((namespace, name, schema, handler, info)) return namespaces, subdirs zope.configuration-3.7.4/src/zope/configuration/exceptions.py0000644000000000000000000000137411401620130022635 0ustar 00000000000000############################################################################## # # Copyright (c) 2002 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """Standard configuration errors """ class ConfigurationError(Exception): """There was an error in a configuration """ zope.configuration-3.7.4/src/zope/configuration/exclude.txt0000644000000000000000000000461011151505046022302 0ustar 00000000000000Filtering and Inhibiting Configuration ====================================== The ``exclude`` standard directive is provided for inhibiting unwanted configuration. It is used to exclude processing of configuration files. It is useful when including a configuration that includes some other configuration that you don't want. It must be used BEFORE including the files to be excluded. First, let's look at an example. The zope.configuration.tests.excludedemo package has a ZCML configuration that includes some other configuration files. We'll set a log handler so we can see what's going on: >>> import logging, sys >>> logger = logging.getLogger('config') >>> oldlevel = logger.level >>> logger.setLevel(logging.DEBUG) >>> handler = logging.StreamHandler(sys.stdout) >>> logger.addHandler(handler) Now, we'll include the zope.configuration.tests.excludedemo config: >>> from zope.configuration import xmlconfig >>> _ = xmlconfig.string('') include /zope.configuration/src/zope/configuration/tests/excludedemo/configure.zcml include /zope.configuration/src/zope/configuration/tests/excludedemo/sub/configure.zcml include /zope.configuration/src/zope/configuration/tests/excludedemo/spam.zcml Each run of the configuration machinery runs with fresh state, so rerunning gives the same thing: >>> _ = xmlconfig.string('') include /zope.configuration/src/zope/configuration/tests/excludedemo/configure.zcml include /zope.configuration/src/zope/configuration/tests/excludedemo/sub/configure.zcml include /zope.configuration/src/zope/configuration/tests/excludedemo/spam.zcml Now, we'll use the exclude directive to exclude the two files included by the configuration file in zope.configuration.tests.excludedemo: >>> _ = xmlconfig.string( ... ''' ... ... ... ... ... ... ''') include /zope.configuration/src/zope/configuration/tests/excludedemo/configure.zcml .. cleanup >>> logger.setLevel(oldlevel) >>> logger.removeHandler(handler) zope.configuration-3.7.4/src/zope/configuration/fields.py0000644000000000000000000002750411401620130021725 0ustar 00000000000000############################################################################## # # Copyright (c) 2003 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """Configuration-specific schema fields """ __docformat__ = 'restructuredtext' import os, re, warnings from zope import schema from zope.schema.interfaces import IFromUnicode from zope.schema.interfaces import ConstraintNotSatisfied from zope.configuration.exceptions import ConfigurationError from zope.interface import implements from zope.configuration.interfaces import InvalidToken PYIDENTIFIER_REGEX = u'\\A[a-zA-Z_]+[a-zA-Z0-9_]*\\Z' pyidentifierPattern = re.compile(PYIDENTIFIER_REGEX) class PythonIdentifier(schema.TextLine): r"""This field describes a python identifier, i.e. a variable name. Let's look at an example: >>> class FauxContext(object): ... pass >>> context = FauxContext() >>> field = PythonIdentifier().bind(context) Let's test the fromUnicode method: >>> field.fromUnicode(u'foo') u'foo' >>> field.fromUnicode(u'foo3') u'foo3' >>> field.fromUnicode(u'_foo3') u'_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._validate(value) >>> >>> from zope import schema >>> >>> for value in (u'3foo', u'foo:', u'\\', u''): ... try: ... field._validate(value) ... except schema.ValidationError: ... print 'Validation Error' Validation Error Validation Error Validation Error Validation Error """ implements(IFromUnicode) def fromUnicode(self, u): return u.strip() def _validate(self, value): super(PythonIdentifier, self)._validate(value) if pyidentifierPattern.match(value) is None: raise schema.ValidationError(value) class GlobalObject(schema.Field): """An object that can be accessed as a module global. Examples: First, we need to set up a stub name resolver: >>> d = {'x': 1, 'y': 42, 'z': 'zope'} >>> class fakeresolver(dict): ... def resolve(self, n): ... return self[n] >>> fake = fakeresolver(d) >>> g = GlobalObject(value_type=schema.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', (, ), '') >>> 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) >>> gg.fromUnicode('*') >>> """ implements(IFromUnicode) def __init__(self, value_type=None, **kw): self.value_type = value_type super(GlobalObject, self).__init__(**kw) def _validate(self, value): super(GlobalObject, self)._validate(value) if self.value_type is not None: self.value_type.validate(value) def fromUnicode(self, u): name = str(u.strip()) # special case, mostly for interfaces if name == '*': return None try: value = self.context.resolve(name) except ConfigurationError, v: raise schema.ValidationError(v) self.validate(value) return value class GlobalInterface(GlobalObject): """An interface that can be accessed from a module. First, we need to set up a stub name resolver: >>> class Foo(object): pass >>> from zope.interface import Interface >>> class IFoo(Interface): 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. >>> g = GlobalInterface() >>> gg = g.bind(fake) >>> gg.fromUnicode('IFoo') >>> gg.fromUnicode(' IFoo ') >>> gg.fromUnicode('Foo') Traceback (most recent call last): ... WrongType: ('An interface is required', , '') """ def __init__(self, **kw): super(GlobalInterface, self).__init__(schema.InterfaceField(), **kw) class Tokens(schema.List): """A list that can be read from a space-separated string Consider GlobalObject tokens: Examples: 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) >>> g = Tokens(value_type=GlobalObject()) >>> gg = g.bind(fake) >>> gg.fromUnicode(" \\n x y z \\n") [1, 42, 'zope'] >>> g = Tokens(value_type= ... GlobalObject(value_type= ... schema.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', (, ), '') in z y >>> gg.fromUnicode("y y") [42, 42] >>> """ implements(IFromUnicode) def fromUnicode(self, u): u = u.strip() if u: vt = self.value_type.bind(self.context) values = [] for s in u.split(): try: v = vt.fromUnicode(s) except schema.ValidationError, v: raise InvalidToken("%s in %s" % (v, u)) else: values.append(v) else: values = [] self.validate(values) return values class Path(schema.Text): r"""A file path name, which may be input as a relative path Input paths are converted to absolute paths and normalized. 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. >>> 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: >>> p = unicode(os.path.join(os.sep, 'a', 'b')) >>> n = field.fromUnicode(p) >>> n.split(os.sep) [u'', u'a', u'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) [u'', u'a', u'b'] Now try a relative path: >>> p = unicode(os.path.join('a', 'b')) >>> n = field.fromUnicode(p) >>> n.split(os.sep) [u'', u'faux', u'context', u'a', u'b'] """ implements(IFromUnicode) def fromUnicode(self, u): u = u.strip() if os.path.isabs(u): return os.path.normpath(u) return self.context.path(u) class Bool(schema.Bool): """A boolean value Values may be input (in upper or lower case) as any of: yes, no, y, n, true, false, t, or f. >>> Bool().fromUnicode(u"yes") 1 >>> Bool().fromUnicode(u"y") 1 >>> Bool().fromUnicode(u"true") 1 >>> Bool().fromUnicode(u"no") 0 """ implements(IFromUnicode) def fromUnicode(self, u): u = u.lower() if u in ('1', 'true', 'yes', 't', 'y'): return True if u in ('0', 'false', 'no', 'f', 'n'): return False raise schema.ValidationError class MessageID(schema.Text): """Text string that should be translated. When a string is converted to a message ID, it is also recorded in the context. >>> 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 u'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 u'Hello world!' >>> i.domain 'testing' In addition, the string has been registered with the context: >>> context.i18n_strings {'testing': {u'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': {u'Foo Bar': [('file location', 8)], u'Hello world!': [('file location', 8), ('file location', 8)]}} >>> from zope.i18nmessageid import Message >>> isinstance(context.i18n_strings['testing'].keys()[0], Message) 1 Explicit Message IDs >>> i = field.fromUnicode(u'[View-Permission] View') >>> i u'View-Permission' >>> i.default u'View' >>> i = field.fromUnicode(u'[] [Some] text') >>> i u'[Some] text' >>> i.default is None True """ implements(IFromUnicode) __factories = {} def fromUnicode(self, u): 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 ) ) v = super(MessageID, self).fromUnicode(u) # Check whether there is an explicit message is specified default = None if v.startswith('[]'): v = v[2:].lstrip() elif v.startswith('['): end = v.find(']') default = v[end+2:] v = v[1:end] # Convert to a message id, importing the factory, if necessary factory = self.__factories.get(domain) if factory is None: import zope.i18nmessageid factory = zope.i18nmessageid.MessageFactory(domain) self.__factories[domain] = factory msgid = factory(v, default) # Record the string we got for the domain i18n_strings = context.i18n_strings strings = i18n_strings.get(domain) if strings is None: strings = i18n_strings[domain] = {} locations = strings.setdefault(msgid, []) locations.append((context.info.file, context.info.line)) return msgid zope.configuration-3.7.4/src/zope/configuration/interfaces.py0000644000000000000000000001065711500462636022622 0ustar 00000000000000############################################################################## # # Copyright (c) 2001, 2002 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """Zope Configuration (ZCML) interfaces """ from zope.interface import Interface from zope.schema import BytesLine from zope.schema.interfaces import ValidationError class InvalidToken(ValidationError): """Invaid token in list.""" class IConfigurationContext(Interface): """Configuration Context The configuration context manages information about the state of the configuration system, such as the package containing the configuration file. More importantly, it provides methods for importing objects and opening files relative to the package. """ package = BytesLine( title=u"The current package name", description=u"""\ This is the name of the package containing the configuration file being executed. If the configuration file was not included by package, then this is None. """, required=False, ) def resolve(dottedname): """Resolve a dotted name to an object A dotted name is constructed by concatenating a dotted module name with a global name within the module using a dot. For example, the object named "spam" in the foo.bar module has a dotted name of foo.bar.spam. If the current package is a prefix of a dotted name, then the package name can be relaced with a leading dot, So, for example, if the configuration file is in the foo package, then the dotted name foo.bar.spam can be shortened to .bar.spam. If the current package is multiple levels deep, multiple leading dots can be used to refer to higher-level modules. For example, if the current package is x.y.z, the dotted object name ..foo refers to x.y.foo. """ def path(filename): """Compute a full file name for the given file If the filename is relative to the package, then the returned name will include the package path, otherwise, the original file name is returned. """ def checkDuplicate(filename): """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. """ def processFile(filename): """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. """ def action(self, discriminator, callable, args=(), kw={}, order=0, includepath=None, info=None): """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. """ def provideFeature(name): """Record that a named feature is available in this context.""" def hasFeature(name): """Check whether a named feature is available in this context.""" class IGroupingContext(Interface): def before(): """Do something before processing nested directives """ def after(): """Do something after processing nested directives """ zope.configuration-3.7.4/src/zope/configuration/name.py0000644000000000000000000000450011401620130021366 0ustar 00000000000000############################################################################## # # Copyright (c) 2001, 2002 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """Provide configuration object name resolution """ import os from types import ModuleType def resolve(name, package='zopeproducts', _silly=('__doc__',), _globals={}): name = name.strip() if name.startswith('.'): name=package+name if name.endswith('.') or name.endswith('+'): name = name[:-1] repeat = 1 else: repeat = 0 names=name.split('.') last=names[-1] mod='.'.join(names[:-1]) if not mod: return __import__(name, _globals, _globals, _silly) while 1: m=__import__(mod, _globals, _globals, _silly) try: a=getattr(m, last) except AttributeError: if not repeat: return __import__(name, _globals, _globals, _silly) else: if not repeat or (not isinstance(a, ModuleType)): return a mod += '.' + last def getNormalizedName(name, package): name=name.strip() if name.startswith('.'): name=package+name if name.endswith('.') or name.endswith('+'): name = name[:-1] repeat = 1 else: repeat = 0 name=name.split(".") while len(name)>1 and name[-1]==name[-2]: name.pop() repeat=1 name=".".join(name) if repeat: name+="+" return name def path(file='', package = 'zopeproducts', _silly=('__doc__',), _globals={}): try: package = __import__(package, _globals, _globals, _silly) except ImportError: if file and os.path.abspath(file) == file: # The package didn't matter return file raise path = os.path.dirname(package.__file__) if file: path = os.path.join(path, file) return path zope.configuration-3.7.4/src/zope/configuration/README.txt0000644000000000000000000000600410207444300021601 0ustar 00000000000000========================== Zope configuration system ========================== The zope configuration system provides an extensible system for supporting variouse kinds of configurations. It is based on the idea of configuration directives. Users of the configuration system provide configuration directives in some language that express configuration choices. The intent is that the language be pluggable. An XML language is provided by default. Configuration is performed in three stages. In the first stage, directives are processed to compute configuration actions. Configuration actions consist of: - A discriminator - A callable - Positional arguments - Keyword arguments The actions are essentially delayed function calls. Two or more actions conflict if they have the same discriminator. The configuration system has rules for resolving conflicts. If conflicts cannot be resolved, an error will result. Conflict resolution typically discards all but one of the conflicting actions, so that the remaining action of the originally-conflicting actions no longer conflicts. Non-conflicting actions are executed in the order that they were created by passing the positional and non-positional arguments to the action callable. The system is extensible. There is a meta-configuration language for defining configuration directives. A directive is defined by providing meta data about the directive and handler code to process the directive. There are four kinds of directives: - Simple directives compute configuration actions. Their handlers are typically functions that take a context and zero or more keyword arguments and return a sequence of configuration actions. To learn how to create simple directives, see `tests/test_simple.py`. - Grouping directives collect information to be used by nested directives. They are called with a context object which they adapt to some interface that extends IConfigurationContext. To learn how to create grouping directives, look at the documentation in zopeconfigure.py, which provides the implementation of the zope `configure` directive. Other directives can be nested in grouping directives. To learn how to implement nested directives, look at the documentation in `tests/test_nested.py`. - Complex directives are directives that have subdirectives. Subdirectives have handlers that are simply methods of complex directives. Complex diretives are handled by factories, typically classes, that create objects that have methods for handling subdirectives. These objects also have __call__ methods that are called when processing of subdirectives is finished. Complex directives only exist to support old directive handlers. They will probably be deprecated in the future. - Subdirectives are nested in complex directives. They are like simple directives except that they hane handlers that are complex directive methods. Subdirectives, like complex directives only exist to support old directive handlers. They will probably be deprecated in the future. zope.configuration-3.7.4/src/zope/configuration/stxdocs.py0000644000000000000000000001317711401620130022147 0ustar 00000000000000############################################################################## # # Copyright (c) 2004 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """STX Configuration Documentation Renderer Usage: stxdocs.py [options] Options: -h / --help Print this message and exit. -f Specifies the root ZCML meta directives file, relative to the current location. All included files will be considered as well -o Specifies a directory, relative to the current location in which the documentation is stored. Note that this tool will create sub-directories with files in them. """ import sys, os, getopt import zope.configuration from zope.schema import getFieldsInOrder from zope.configuration import config, xmlconfig from zope.configuration.docutils import wrap, makeDocStructures def usage(code, msg=''): # Python 2.1 required print >> sys.stderr, __doc__ if msg: print >> sys.stderr, msg sys.exit(code) def _directiveDocs(name, schema, handler, info, indent_offset=0): """Generate the documentation for one directive.""" # Write out the name of the directive text = ' '*indent_offset text += '%s\n\n' %name # Specify the file and location it has been declared if isinstance(info, xmlconfig.ParserInfo): # We do not want to specify the whole path; starting at the 'zope' # package is enough. base_dir = os.path.dirname(os.path.dirname( zope.configuration.__file__))[:-4] file = info.file.replace(base_dir, '') info_text = 'File %s, lines %i - %i.' %(file, info.line, info.eline) text += wrap(info_text, 78, indent_offset+2) elif isinstance(info, (str, unicode)) and info: text += wrap(info, 78, indent_offset+2) # Insert Handler information if handler is not None: handler_path = handler.__module__ + '.' + handler.__name__ text += wrap('Handler: %s' %handler_path, 78, indent_offset+2) # Use the schema documentation string as main documentation text for the # directive. text += wrap(schema.getDoc(), 78, indent_offset+2) text += ' '*indent_offset + ' Attributes\n\n' # Create directive attribute documentation for name, field in getFieldsInOrder(schema): name = name.strip('_') if field.required: opt = 'required' else: opt = 'optional, default=%s' %repr(field.default) text += ' '*indent_offset text += ' %s -- %s (%s)\n\n' %(name, field.__class__.__name__, opt) text += wrap(field.title, 78, indent_offset+6) text += wrap(field.description, 78, indent_offset+6) return text def _subDirectiveDocs(subdirs, namespace, name): """Appends a list of sub-directives and their full specification.""" if subdirs.has_key((namespace, name)): text = '\n Subdirectives\n\n' sub_dirs = [] # Simply walk through all sub-directives here. subs = subdirs[(namespace, name)] for sd_ns, sd_name, sd_schema, sd_handler, sd_info in subs: sub_dirs.append(_directiveDocs( sd_name, sd_schema, sd_handler, sd_info, 4)) return text + '\n\n'.join(sub_dirs) return '' def makedocs(target_dir, zcml_file): """Generate the documentation tree. All we need for this is a starting ZCML file and a directory in which to put the documentation. """ context = xmlconfig.file(zcml_file, execute=False) namespaces, subdirs = makeDocStructures(context) for namespace, directives in namespaces.items(): ns_dir = os.path.join(target_dir, namespace.split('/')[-1]) # Create a directory for the namespace, if necessary if not os.path.exists(ns_dir): os.mkdir(ns_dir) # Create a file for each directive for name, (schema, handler, info) in directives.items(): dir_file = os.path.join(ns_dir, name+'.stx') text = _directiveDocs(name, schema, handler, info) text += _subDirectiveDocs(subdirs, namespace, name) open(dir_file, 'w').write(text) def _makeabs(path): """Make an absolute path from the possibly relative path.""" if not path == os.path.abspath(path): cwd = os.getcwd() # This is for symlinks. if os.environ.has_key('PWD'): cwd = os.environ['PWD'] path = os.path.normpath(os.path.join(cwd, path)) return path def main(argv=sys.argv): try: opts, args = getopt.getopt( sys.argv[1:], 'h:f:o:', ['help']) except getopt.error, msg: usage(1, msg) zcml_file = None output_dir = None for opt, arg in opts: if opt in ('-h', '--help'): usage(0) elif opt in ('-o', ): output_dir = arg elif opt in ('-f', ): zcml_file = _makeabs(arg) if not os.path.exists(zcml_file): usage(1, 'The specified zcml file does not exist.') if zcml_file is None or output_dir is None: usage(0, "Both, the '-f' and '-o' option are required") # Generate the docs makedocs(output_dir, zcml_file) if __name__ == '__main__': main() zope.configuration-3.7.4/src/zope/configuration/xmlconfig.py0000644000000000000000000005471711401620130022453 0ustar 00000000000000############################################################################## # # Copyright (c) 2001, 2002, 2003 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """Support for the XML configuration file format Note, for a detailed description of the way that conflicting configuration actions are resolved, see the detailed example in test_includeOverrides in tests/test_xmlconfig.py """ __docformat__ = 'restructuredtext' import errno import os import sys import logging import zope.configuration.config as config from glob import glob from xml.sax import make_parser from xml.sax.xmlreader import InputSource from xml.sax.handler import ContentHandler, feature_namespaces from xml.sax import SAXParseException from zope import schema from zope.configuration.exceptions import ConfigurationError from zope.configuration.zopeconfigure import IZopeConfigure, ZopeConfigure from zope.interface import Interface logger = logging.getLogger("config") ZCML_NAMESPACE = "http://namespaces.zope.org/zcml" ZCML_CONDITION = (ZCML_NAMESPACE, u"condition") class ZopeXMLConfigurationError(ConfigurationError): """Zope XML Configuration error These errors are wrappers for other errors. The include configuration info and the wrapped error type and value: >>> 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 def __str__(self): # Only use the repr of the info. This is because we expect to # get a parse info and we only want the location information. return "%s\n %s: %s" % ( `self.info`, self.etype.__name__, self.evalue) class ZopeSAXParseException(ConfigurationError): """Sax Parser errors, reformatted in an emacs friendly way >>> 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 def __str__(self): v = self._v s = tuple(str(v).split(':')) if len(s) == 4: return 'File "%s", line %s.%s, %s' % s else: return str(v) class ParserInfo(object): """Information about a directive based on parser data This includes the directive location, as well as text data contained in the directive. >>> 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 u'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 """ text = u'' def __init__(self, file, line, column): self.file, self.line, self.column = file, line, column self.eline, self.ecolumn = line, column def end(self, line, column): self.eline, self.ecolumn = line, column def __repr__(self): if (self.line, self.column) == (self.eline, self.ecolumn): return 'File "%s", line %s.%s' % ( self.file, self.line, self.column) return 'File "%s", line %s.%s-%s.%s' % ( self.file, self.line, self.column, self.eline, self.ecolumn) def __str__(self): if (self.line, self.column) == (self.eline, self.ecolumn): return 'File "%s", line %s.%s' % ( self.file, self.line, self.column) file = self.file if file == 'tests//sample.zcml': # special case for testing file = os.path.join(os.path.dirname(__file__), 'tests', 'sample.zcml') try: f = open(file) except IOError: src = " Could not read source." else: lines = f.readlines()[self.line-1:self.eline] ecolumn = self.ecolumn if lines[-1][ecolumn:ecolumn+2] == '', ecolumn) if l >= 0: lines[-1] = lines[-1][:l+1] else: lines[-1] = lines[-1][:ecolumn+1] column = self.column if lines[0][:column].strip(): # Remove text before start if it's noy whitespace lines[0] = lines[0][self.column:] try: src = u''.join([u" "+l for l in lines]) except UnicodeDecodeError: # XXX: # I hope so most internation zcml will use UTF-8 as encoding # otherwise this code must be made more clever src = u''.join([u" "+l.decode('utf-8') for l in lines]) # unicode won't be printable, at least on my console src = src.encode('ascii','replace') return "%s\n%s" % (`self`, src) def characters(self, characters): self.text += characters class ConfigurationHandler(ContentHandler): """Interface to the xml parser Translate parser events into calls into the configuration system. """ def __init__(self, context, testing=0): self.context = context self.testing = testing self.ignore_depth = 0 def setDocumentLocator(self, locator): self.locator = locator def characters(self, text): self.context.getInfo().characters(text) def startElementNS(self, name, qname, attrs): if self.ignore_depth: self.ignore_depth += 1 return data = {} for (ns, aname), value in attrs.items(): # NB: even though on CPython, 'ns' will be ``None`` always, # do not change the below to "if ns is None" because Jython's # sax parser generates attrs that have empty strings for # the namepace instead of ``None``. if not ns: aname = str(aname) data[aname] = value if (ns, aname) == ZCML_CONDITION: # need to process the expression to determine if we # use this element and it's descendents use = self.evaluateCondition(value) if not use: self.ignore_depth = 1 return info = ParserInfo( self.locator.getSystemId(), self.locator.getLineNumber(), self.locator.getColumnNumber(), ) try: self.context.begin(name, data, info) except (KeyboardInterrupt, SystemExit): raise except: if self.testing: raise raise ZopeXMLConfigurationError(info, sys.exc_info()[0], sys.exc_info()[1]), None, sys.exc_info()[2] self.context.setInfo(info) def evaluateCondition(self, expression): """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' verb takes one argument: the name of a feature. >>> from zope.configuration.config import ConfigurationContext >>> 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' verb takes 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. >>> from zope.configuration.config import ConfigurationContext >>> 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) if verb in ('have', 'not-have'): if not arguments: raise ValueError("Feature name missing: %r" % expression) if len(arguments) > 1: raise ValueError("Only one feature allowed: %r" % expression) if verb == 'have': return self.context.hasFeature(arguments[0]) elif verb == 'not-have': return not self.context.hasFeature(arguments[0]) elif verb in ('installed', 'not-installed'): if not arguments: raise ValueError("Package name missing: %r" % expression) if len(arguments) > 1: raise ValueError("Only one package allowed: %r" % expression) try: __import__(arguments[0]) installed = True except ImportError: installed = False if verb == 'installed': return installed elif verb == 'not-installed': return not installed else: raise ValueError("Invalid ZCML condition: %r" % expression) def endElementNS(self, name, qname): # If ignore_depth is set, this element will be ignored, even # if this this decrements ignore_depth to 0. if self.ignore_depth: self.ignore_depth -= 1 return info = self.context.getInfo() info.end( self.locator.getLineNumber(), self.locator.getColumnNumber(), ) try: self.context.end() except (KeyboardInterrupt, SystemExit): raise except: if self.testing: raise raise ZopeXMLConfigurationError(info, sys.exc_info()[0], sys.exc_info()[1]), None, sys.exc_info()[2] def processxmlfile(file, context, testing=False): """Process a configuration file See examples in tests/text_xmlconfig.py """ src = InputSource(getattr(file, 'name', '')) src.setByteStream(file) parser = make_parser() parser.setContentHandler(ConfigurationHandler(context, testing=testing)) parser.setFeature(feature_namespaces, True) try: parser.parse(src) except SAXParseException: raise ZopeSAXParseException(sys.exc_info()[1]), None, sys.exc_info()[2] def openInOrPlain(filename): """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 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: >>> here = os.path.dirname(__file__) >>> path = os.path.join(here, 'tests', 'samplepackage', 'configure.zcml') >>> f = openInOrPlain(path) >>> f.name[-14:] 'configure.zcml' 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' 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: fp = open(filename) except IOError, (code, msg): if code == errno.ENOENT: fn = filename + ".in" if os.path.exists(fn): fp = open(fn) else: raise else: raise return fp class IInclude(Interface): """The ``include``, ``includeOverrides`` and ``exclude`` directives These directives allows you to include or preserve including of another ZCML file in the configuration. This enables you to write configuration files in each package and then link them together. """ file = schema.BytesLine( title=u"Configuration file name", description=u"The name of a configuration file to be included/excluded, " u"relative to the directive containing the " u"including configuration file.", required=False, ) files = schema.BytesLine( title=u"Configuration file name pattern", description=u""" The names of multiple configuration files to be included/excluded, expressed as a file-name pattern, relative to the directive containing the including or excluding configuration file. The pattern can include: - ``*`` matches 0 or more characters - ``?`` matches a single character - ``[]`` matches any character in seq - ``[!]`` matches any character not in seq The file names are included in sorted order, where sorting is without regard to case. """, required=False, ) package = config.fields.GlobalObject( title=u"Include or exclude package", description=u""" Include or exclude the named file (or configure.zcml) from the directory of this package. """, required=False, ) def include(_context, file=None, package=None, files=None): """Include a zcml file See examples in tests/text_xmlconfig.py """ if files: if file: raise ValueError("Must specify only one of file or files") elif not file: file = 'configure.zcml' # BBB 2006/12/19 -- to be removed after 12 months # This is a backward-compatibility support for old site.conf if package and (package.__name__ == 'zope.app'): try: import zope.app.zcmlfiles except ImportError: pass # maybe this is an old zope without zope.app.zcmlfiles else: dirpath, filename = os.path.split(file) # be careful, because zope.app is a namespace package # we can't assume that zcmlfiles is a subdirectory of the # zope.app package dirpath = os.path.dirname(zope.app.zcmlfiles.__file__) file = os.path.join(dirpath, filename) import warnings warnings.warn('In configuration file: %s ' 'replace: ' 'with: ' 'This will go away in Zope 3.6.' % os.path.abspath(file), DeprecationWarning, 2) # This is a tad tricky. We want to behave as a grouping directive. context = config.GroupingContextDecorator(_context) if package is not None: context.package = package context.basepath = None if files: paths = glob(context.path(files)) paths = zip([path.lower() for path in paths], paths) paths.sort() paths = [path for (l, path) in paths] else: paths = [context.path(file)] for path in paths: if context.processFile(path): f = openInOrPlain(path) logger.debug("include %s" % f.name) context.basepath = os.path.dirname(path) context.includepath = _context.includepath + (f.name, ) _context.stack.append(config.GroupingStackItem(context)) processxmlfile(f, context) f.close() assert _context.stack[-1].context is context _context.stack.pop() def exclude(_context, file=None, package=None, files=None): """Exclude a zcml file This directive should be used before any ZML that includes configuration you want to exclude. """ if files: if file: raise ValueError("Must specify only one of file or files") elif not file: file = 'configure.zcml' context = config.GroupingContextDecorator(_context) if package is not None: context.package = package context.basepath = None if files: paths = glob(context.path(files)) paths = zip([path.lower() for path in paths], paths) paths.sort() paths = [path for (l, path) in paths] else: paths = [context.path(file)] for path in paths: # processFile returns a boolean indicating if the file has been # processed or not, it *also* marks the file as having been processed, # here the side effect is used to keep the given file from being # processed in the future context.processFile(path) def includeOverrides(_context, file=None, package=None, files=None): """Include zcml file containing overrides The actions in the included file are added to the context as if they were in the including file directly. See the detailed example in test_includeOverrides in tests/text_xmlconfig.py """ # We need to remember how many actions we had before nactions = len(_context.actions) # We'll give the new actions this include path includepath = _context.includepath # Now we'll include the file. We'll munge the actions after include(_context, file, package, files) # Now we'll grab the new actions, resolve conflicts, # and munge the includepath: newactions = [] for action in config.resolveConflicts(_context.actions[nactions:]): (discriminator, callable, args, kw, oldincludepath, info, order ) = config.expand_action(*action) newactions.append( (discriminator, callable, args, kw, includepath, info, order) ) # and replace the new actions with the munched new actions: _context.actions[nactions:] = newactions def registerCommonDirectives(context): # We have to use the direct definition functions to define # a directive for all namespaces. config.defineSimpleDirective( context, "include", IInclude, include, namespace="*") config.defineSimpleDirective( context, "exclude", IInclude, exclude, namespace="*") config.defineSimpleDirective( context, "includeOverrides", IInclude, includeOverrides, namespace="*") config.defineGroupingDirective( context, name="configure", namespace="*", schema=IZopeConfigure, handler=ZopeConfigure, ) def file(name, package=None, context=None, execute=True): """Execute a zcml file """ if context is None: context = config.ConfigurationMachine() registerCommonDirectives(context) context.package = package include(context, name, package) if execute: context.execute_actions() return context def string(s, context=None, name="", execute=True): """Execute a zcml string """ from StringIO import StringIO if context is None: context = config.ConfigurationMachine() registerCommonDirectives(context) f = StringIO(s) f.name = name processxmlfile(f, context) if execute: context.execute_actions() return context ############################################################################## # Backward compatability, mainly for tests _context = None def _clearContext(): global _context _context = config.ConfigurationMachine() registerCommonDirectives(_context) def _getContext(): global _context if _context is None: _clearContext() try: from zope.testing.cleanup import addCleanUp except ImportError: pass else: addCleanUp(_clearContext) del addCleanUp return _context class XMLConfig(object): """Provide high-level handling of configuration files. See examples in tests/text_xmlconfig.py """ def __init__(self, file_name, module=None): context = _getContext() include(context, file_name, module) self.context = context def __call__(self): self.context.execute_actions() def xmlconfig(file, testing=False): context = _getContext() processxmlfile(file, context, testing=testing) context.execute_actions(testing=testing) def testxmlconfig(file, context=None): """xmlconfig that doesn't raise configuration errors This is useful for testing, as it doesn't mask exception types. """ context = _getContext() processxmlfile(file, context, testing=True) context.execute_actions(testing=True) zope.configuration-3.7.4/src/zope/configuration/zopeconfigure.py0000644000000000000000000001364411413705050023346 0ustar 00000000000000############################################################################## # # Copyright (c) 2001, 2002, 2003 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """Zope configure directive This file contains the implementation of the Zope configure directive. It is broken out in a separate file to provide an example of a grouping directive. The zope configuration directive is a pure grouping directive. It doesn't compute any actions on it's own. Instead, it allows a package to be specified, affecting the interpretation of relative dotted names and file paths. It also allows an i18n domain to be specified. The information collected is used by subdirectives. To define a grouping directive, we need to do three things: - Define a schema for the parameters passed to the directive - Define a handler class. - Register the class The parameter schema is given by IZopeConfigure. It specifies a package parameter and an i18n_domain parameter. The package parameter is specified as a ``GlobalObject``. This means it must be given as a dotted name that can be resolved through import. The i18n domain is just a plain (not unicode) string. The handler class has a constructor that takes a context to be adapted and zero or more arguments (depending on the paramter schema). The handler class must implement ``zope.configuration.interfaces.IGroupingContext``, which defines hooks ``before`` and ``after``, that are called with no arguments before and after nested directives are processed. If a grouping directive handler creates any actions, or does any computation, this is normally done in either the ``before`` or ``after`` hooks. Grouping handlers are normally decorators. The base class, ``zope.configuration.config.GroupingContextDecorator``, is normally used to define grouping directive handlers. It provides: - An implementation of IConfigurationContext, which grouping directive handlers should normally implement, - A default implementation of ``IGroupingContext`` that provides empty hooks. - Decorator support that uses a ``__getattr__`` method to delegate attribute accesses to adapted contexts, and - A constructor that sets the ``context`` attribute to the adapted context and assigns keyword arguments to attributes. The ``ZopeConfigure`` provides handling for the ``configure`` directive. It subclasses GroupingContextDecorator, and overrides the constructor to set the ``basepath`` attribute if a ``package`` argument is provided. Note that it delegates the job of assigning paramters to attribute to the ``GroupingContextDecorator`` constructor. The last step is to register the directive using the meta configuration directive. If we wanted to register the Zope ``configure`` directive for the ``zope`` namespace, we'd use a meta-configuration directive like:: Zope configure The ``configure`` node is normally used as the root node for a configuration file. It can also be used to specify a package or internationalization domain for a group of directives within a file by grouping those directives. We use the groupingDirective meta-directive to register a grouping directive. The parameters are self explanatory. The textual contents of the directive provide documentation text, excluding parameter documentation, which is provided by the schema. (The Zope ``configuration`` directive is actually registered using a lower-level Python API because it is registered for all namespaces, which isn't supported using the meta-configuration directives.) """ __docformat__ = 'restructuredtext' import os import zope.configuration.config as config from zope import schema from zope.interface import Interface class IZopeConfigure(Interface): """The ``zope:configure`` Directive The zope configuration directive is a pure grouping directive. It doesn't compute any actions on it's own. Instead, it allows a package to be specified, affecting the interpretation of relative dotted names and file paths. It also allows an i18n domain to be specified. The 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. """ package = config.fields.GlobalObject( title=u"Package", description=u"The package to be used for evaluating relative imports " u"and file names.", required=False) i18n_domain = schema.BytesLine( title=u"Internationalization domain", description=u"This is a name for the software project. It must be a " u"legal file-system name as it will be used to contruct " u"names for directories containing translation data. " u"\n" u"The domain defines a namespace for the message ids " u"used by a project.", required=False) class ZopeConfigure(config.GroupingContextDecorator): __doc__ = __doc__ def __init__(self, context, **kw): super(ZopeConfigure, self).__init__(context, **kw) if 'package' in kw: # if we have a package, we want to also define basepath # so we don't acquire one self.basepath = os.path.dirname(self.package.__file__) zope.configuration-3.7.4/src/zope/configuration/tests/__init__.py0000644000000000000000000000007510050206152023355 0ustar 00000000000000# # This file is necessary to make this directory a package. zope.configuration-3.7.4/src/zope/configuration/tests/bad.py0000644000000000000000000000011510050206152022337 0ustar 00000000000000# I'm bad. I want to be bad. Don't try to change me. import bad_to_the_bone zope.configuration-3.7.4/src/zope/configuration/tests/conditions.zcml0000644000000000000000000000377610206132044024320 0ustar 00000000000000 This registers a directive which creates registrations we can test. ZCML directives inside here should be included. This registration should be included. ZCML directives inside here should be ignored. This registration should be ignored. zope.configuration-3.7.4/src/zope/configuration/tests/directives.py0000644000000000000000000000456011401620130023757 0ustar 00000000000000############################################################################## # # Copyright (c) 2001, 2002 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """Test directives """ from zope.interface import Interface, implements from zope.schema import Text, BytesLine from zope.configuration.config import GroupingContextDecorator from zope.configuration.interfaces import IConfigurationContext from zope.configuration.fields import GlobalObject class F(object): def __repr__(self): return 'f' def __call__(self, *a, **k): pass f = F() class ISimple(Interface): a = Text() b = Text(required=False) c = BytesLine() def simple(context, a=None, c=None, b=u"xxx"): return [(('simple', a, b, c), f, (a, b, c))] def newsimple(context, a, c, b=u"xxx"): context.action(('newsimple', a, b, c), f, (a, b, c)) class IPackaged(Interface): package = GlobalObject() class IPackagedContext(IPackaged, IConfigurationContext): pass class Packaged(GroupingContextDecorator): implements(IPackagedContext) class IFactory(Interface): factory = GlobalObject() def factory(context, factory): context.action(('factory', 1,2), factory) class Complex(object): def __init__(self, context, a, c, b=u"xxx"): self.a, self.b, self.c = a, b, c context.action("Complex.__init__") def factory(self, context, factory): return [(('Complex.factory', 1,2), factory, (self.a, ))] def factory2(self, context, factory): return [(('Complex.factory', 1,2), factory, (self.a, ))] def __call__(self): return [(('Complex', 1,2), f, (self.b, self.c))] class Ik(Interface): for_ = BytesLine() class_ = BytesLine() x = BytesLine() def k(context, for_, class_, x): context.action(('k', for_), f, (for_, class_, x)) def kkw(context, for_, class_, x, **kw): context.action(('k', for_), f, (for_, class_, x, kw)) zope.configuration-3.7.4/src/zope/configuration/tests/sample.zcml0000644000000000000000000000044310050206152023413 0ustar 00000000000000 zope.configuration-3.7.4/src/zope/configuration/tests/schema.zcml0000644000000000000000000000313410050206152023372 0ustar 00000000000000 Define a schema Use field directives (e.g. text and int directives) to define the schema fields. Define a text field Define an integer field Sample interface I1 A Blah blah B Not feeling very creative Sample interface I2 X zope.configuration-3.7.4/src/zope/configuration/tests/simple.zcml0000644000000000000000000000160210050206152023421 0ustar 00000000000000 Register a file with the file registry Blah blah blah :) Describes how to implement a simple directive Shows the ZCML directives needed to register a simple directive. Also show some usage examples, zope.configuration-3.7.4/src/zope/configuration/tests/test_conditions.py0000644000000000000000000000671711366603366025062 0ustar 00000000000000############################################################################## # # Copyright (c) 2003 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## r'''How to conditionalize specific directives There is a "condition" attribute in the "http://namespaces.zope.org/zcml" namespace which is honored on all elements in ZCML. The value of the attribute is an expression which is used to determine if that element and its descendents are used. If the condition is true, processing continues normally, otherwise that element and its descendents are ignored. Currently the expression is always of the form "have featurename", and it checks for the presence of a . Our demonstration uses a trivial registry; each registration consists of a simple id inserted in the global `registry` in this module. We can checked that a registration was made by checking whether the id is present in `registry`. We start by loading the example ZCML file, *conditions.zcml*:: >>> import zope.configuration.tests >>> import zope.configuration.xmlconfig >>> context = zope.configuration.xmlconfig.file("conditions.zcml", ... zope.configuration.tests) To show that our sample directive works, we see that the unqualified registration was successful:: >>> "unqualified.registration" in registry True When the expression specified with ``zcml:condition`` evaluates to true, the element it is attached to and all contained elements (not otherwise conditioned) should be processed normally:: >>> "direct.true.condition" in registry True >>> "nested.true.condition" in registry True However, when the expression evaluates to false, the conditioned element and all contained elements should be ignored:: >>> "direct.false.condition" in registry False >>> "nested.false.condition" in registry False Conditions on container elements affect the conditions in nested elements in a reasonable way. If an "outer" condition is true, nested conditions are processed normally:: >>> "true.condition.nested.in.true" in registry True >>> "false.condition.nested.in.true" in registry False If the outer condition is false, inner conditions are not even evaluated, and the nested elements are ignored:: >>> "true.condition.nested.in.false" in registry False >>> "false.condition.nested.in.false" in registry False Now we need to clean up after ourselves:: >>> del registry[:] ''' __docformat__ = "reStructuredText" import doctest import zope.interface import zope.schema class IRegister(zope.interface.Interface): """Trivial sample registry.""" id = zope.schema.Id( title=u"Identifier", description=u"Some identifier that can be checked.", required=True, ) registry = [] def register(context, id): context.action(discriminator=('Register', id), callable=registry.append, args=(id,) ) def test_suite(): return doctest.DocTestSuite() zope.configuration-3.7.4/src/zope/configuration/tests/test_config.py0000644000000000000000000002304311536670432024142 0ustar 00000000000000############################################################################## # # Copyright (c) 2003 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """Test configuration machinery. """ import sys import unittest import re from doctest import DocTestSuite from zope.testing import renormalizing from zope.configuration.config import metans, ConfigurationMachine from zope.configuration import config def test_config_extended_example(): """Configuration machine Examples: >>> machine = ConfigurationMachine() >>> ns = "http://www.zope.org/testing" Register some test directives: Start with a grouping directive that sets a package: >>> machine((metans, "groupingDirective"), ... name="package", namespace=ns, ... schema="zope.configuration.tests.directives.IPackaged", ... handler="zope.configuration.tests.directives.Packaged", ... ) Now we can set the package: >>> machine.begin((ns, "package"), ... package="zope.configuration.tests.directives", ... ) Which makes it easier to define the other directives: First, define some simple directives: >>> machine((metans, "directive"), ... namespace=ns, name="simple", ... schema=".ISimple", handler=".simple") >>> machine((metans, "directive"), ... namespace=ns, name="newsimple", ... schema=".ISimple", handler=".newsimple") and try them out: >>> machine((ns, "simple"), "first", a=u"aa", c=u"cc") >>> machine((ns, "newsimple"), "second", a=u"naa", c=u"ncc", b=u"nbb") >>> from pprint import PrettyPrinter >>> pprint=PrettyPrinter(width=50).pprint >>> pprint(machine.actions) [(('simple', u'aa', u'xxx', 'cc'), f, (u'aa', u'xxx', 'cc'), {}, (), 'first'), (('newsimple', u'naa', u'nbb', 'ncc'), f, (u'naa', u'nbb', 'ncc'), {}, (), 'second')] Define and try a simple directive that uses a component: >>> machine((metans, "directive"), ... namespace=ns, name="factory", ... schema=".IFactory", handler=".factory") >>> machine((ns, "factory"), factory=u".f") >>> pprint(machine.actions[-1:]) [(('factory', 1, 2), f)] Define and try a complex directive: >>> machine.begin((metans, "complexDirective"), ... namespace=ns, name="testc", ... schema=".ISimple", handler=".Complex") >>> machine((metans, "subdirective"), ... name="factory", schema=".IFactory") >>> machine.end() >>> machine.begin((ns, "testc"), None, "third", a=u'ca', c='cc') >>> machine((ns, "factory"), "fourth", factory=".f") Note that we can't call a complex method unless there is a directive for it: >>> machine((ns, "factory2"), factory=".f") Traceback (most recent call last): ... ConfigurationError: ('Invalid directive', 'factory2') >>> machine.end() >>> pprint(machine.actions) [(('simple', u'aa', u'xxx', 'cc'), f, (u'aa', u'xxx', 'cc'), {}, (), 'first'), (('newsimple', u'naa', u'nbb', 'ncc'), f, (u'naa', u'nbb', 'ncc'), {}, (), 'second'), (('factory', 1, 2), f), ('Complex.__init__', None, (), {}, (), 'third'), (('Complex.factory', 1, 2), f, (u'ca',), {}, (), 'fourth'), (('Complex', 1, 2), f, (u'xxx', 'cc'), {}, (), 'third')] Done with the package >>> machine.end() Verify that we can use a simple directive outside of the package: >>> machine((ns, "simple"), a=u"oaa", c=u"occ", b=u"obb") But we can't use the factory directive, because it's only valid inside a package directive: >>> machine((ns, "factory"), factory=u".F") Traceback (most recent call last): ... ConfigurationError: ('Invalid value for', 'factory',""" \ """ "Can't use leading dots in dotted names, no package has been set.") >>> pprint(machine.actions) [(('simple', u'aa', u'xxx', 'cc'), f, (u'aa', u'xxx', 'cc'), {}, (), 'first'), (('newsimple', u'naa', u'nbb', 'ncc'), f, (u'naa', u'nbb', 'ncc'), {}, (), 'second'), (('factory', 1, 2), f), ('Complex.__init__', None, (), {}, (), 'third'), (('Complex.factory', 1, 2), f, (u'ca',), {}, (), 'fourth'), (('Complex', 1, 2), f, (u'xxx', 'cc'), {}, (), 'third'), (('simple', u'oaa', u'obb', 'occ'), f, (u'oaa', u'obb', 'occ'))] """ #' def test_keyword_handling(): """ >>> machine = ConfigurationMachine() >>> ns = "http://www.zope.org/testing" Register some test directives: Start with a grouping directive that sets a package: >>> machine((metans, "groupingDirective"), ... name="package", namespace=ns, ... schema="zope.configuration.tests.directives.IPackaged", ... handler="zope.configuration.tests.directives.Packaged", ... ) Now we can set the package: >>> machine.begin((ns, "package"), ... package="zope.configuration.tests.directives", ... ) Which makes it easier to define the other directives: >>> machine((metans, "directive"), ... namespace=ns, name="k", ... schema=".Ik", handler=".k") >>> machine((ns, "k"), "yee ha", **{"for": u"f", "class": u"c", "x": u"x"}) >>> machine.actions [(('k', 'f'), f, ('f', 'c', 'x'), {}, (), 'yee ha')] """ def test_basepath_absolute(): """Path must always return an absolute path. >>> import os >>> class stub: ... __file__ = os.path.join('relative', 'path') >>> c = config.ConfigurationContext() >>> c.package = stub() >>> os.path.isabs(c.path('y/z')) True """ def test_basepath_uses_dunder_path(): """Determine package path using __path__ if __file__ isn't available. (i.e. namespace package installed with --single-version-externally-managed) >>> import os >>> class stub: ... __path__ = [os.path.join('relative', 'path')] >>> c = config.ConfigurationContext() >>> c.package = stub() >>> os.path.isabs(c.path('y/z')) True """ def test_trailing_dot_in_resolve(): """Dotted names are no longer allowed to end in dots >>> c = config.ConfigurationContext() >>> c.resolve('zope.') Traceback (most recent call last): ... ValueError: Trailing dots are no longer supported in dotted names >>> c.resolve(' ') Traceback (most recent call last): ... ValueError: The given name is blank """ def test_bad_dotted_last_import(): """ >>> c = config.ConfigurationContext() Import error caused by a bad last component in the dotted name. >>> c.resolve('zope.configuration.tests.nosuch') Traceback (most recent call last): ... ConfigurationError: ImportError: Module zope.configuration.tests""" \ """ has no global nosuch """ def test_bad_dotted_import(): """ >>> c = config.ConfigurationContext() Import error caused by a totally wrong dotted name. >>> c.resolve('zope.configuration.nosuch.noreally') Traceback (most recent call last): ... ConfigurationError: ImportError: Couldn't import""" \ """ zope.configuration.nosuch, No module named nosuch """ def test_bad_sub_last_import(): """ >>> c = config.ConfigurationContext() Import error caused by a bad sub import inside the referenced dotted name. Here we keep the standard traceback. >>> c.resolve('zope.configuration.tests.victim') Traceback (most recent call last): ... File "...bad.py", line 3 in ? import bad_to_the_bone ImportError: No module named bad_to_the_bone Cleanup: >>> for name in ('zope.configuration.tests.victim', ... 'zope.configuration.tests.bad'): ... if name in sys.modules: ... del sys.modules[name] """ def test_bad_sub_import(): """ >>> c = config.ConfigurationContext() Import error caused by a bad sub import inside part of the referenced dotted name. Here we keep the standard traceback. >>> c.resolve('zope.configuration.tests.victim.nosuch') Traceback (most recent call last): ... File "...bad.py", line 3 in ? import bad_to_the_bone ImportError: No module named bad_to_the_bone Cleanup: >>> for name in ('zope.configuration.tests.victim', ... 'zope.configuration.tests.bad'): ... if name in sys.modules: ... del sys.modules[name] """ def test_suite(): checker = renormalizing.RENormalizing([ (re.compile(r":"), r'exceptions.\1Error:'), ]) return unittest.TestSuite(( DocTestSuite('zope.configuration.fields'), DocTestSuite('zope.configuration.config',checker=checker), DocTestSuite(), )) if __name__ == '__main__': unittest.main() zope.configuration-3.7.4/src/zope/configuration/tests/test_docutils.py0000644000000000000000000000160111401620130024474 0ustar 00000000000000############################################################################## # # Copyright (c) 2003 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """Doc Tests for for zope.configuration.docutils """ import unittest from doctest import DocTestSuite def test_suite(): return unittest.TestSuite(( DocTestSuite('zope.configuration.docutils'), )) if __name__ == '__main__': unittest.main() zope.configuration-3.7.4/src/zope/configuration/tests/test_nested.py0000644000000000000000000002511711401620130024140 0ustar 00000000000000############################################################################## # # Copyright (c) 2003 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## r"""Creating nested directives When using ZCML, you sometimes nest ZCML directives. This is typically done either to: - Avoid repetative input. Information shared among multiple directives is provided in a surrounding directive. - Put together information that is too complex or structured to express with a single set of directive parameters. Grouping directives are used to handle both of these cases. See the documentation in ``../zopeconfigure.py``. This file describes the implementation of the zope ``configure`` directive, which groups directives that use a common package or internationalization domain. The documentation in ``../zopeconfigure.py`` provides background for the documentation here. You should also have read the documentation in ``test_simple.py``, which documents how to create simple directives. This file shows you how to handle the second case above. In this case, we have grouping directives that are meant to collaborate with specific contained directives. To do this, you have the grouping directives declare a more specific (or alternate) interface to ``IConfigurationContext``. Directives designed to work with those grouping directives are registered for the new interface. Let's look at example. Suppose we wanted to be able to define schema using ZCML. We'd use a grouping directive to specify schemas and contained directives to specify fields within the schema. We'll use a schema registry to hold the defined schemas. A schema has a name, an id, some documentation, and some fields. We'll provide the name and the id as parameters. We'll define fields as subdirectives and documentation as text contained in the schema directive. The schema directive uses the schema, ``ISchemaInfo`` for it's parameters. We also define the schema, ISchema, that specifies an attribute that nested field directives will use to store the fields they define. The class, ``Schema``, provides the handler for the schema directive. (If you haven't read the documentation in ``zopeconfigure.py``, you need to do so now.) The constructor saves its arguments as attributes and initializes its ``fields`` attribute. The ``after`` method of the ``Schema`` class creates a schema and computes an action to register the schema in the schema registry. The discriminator prevents two schema directives from registering the same schema. It's important to note that when we call the ``action`` method on ``self``, rather than on ``self.context``. This is because, in a grouping directive handler, the handler instance is itself a context. When we call the ``action`` method, the method stores additional meta data associated with the context it was called on. This meta data includes an include path, used when resolving conflicting actions, and an object that contains information about the XML source used to invole the directive. If we called the action method on ``self.context``, the wrong meta data would be associated with the configuration action. The file ``schema.zcml`` contains the meta-configuration directive that defines the schema directive. To define fields, we'll create directives to define the fields. Let's start with a ``text`` field. ``ITextField`` defines the schema for text field parameters. It extends ``IFieldInfo``, which defines data common to all fields. We also define a simple handler method, textField, that takes a context and keyword arguments. (For information on writing simple directives, see ``test_simple.py``.) We've abstracted most of the logic into the function ``field``. The ``field`` function computes a field instance using the constructor, and the keyword arguments passed to it. It also uses the context information object to get the text content of the directive, which it uses for the field description. After computing the field instance, it gets the ``Schema`` instance, which is the context of the context passed to the function. The function checks to see if there is already a field with that name. If there is, it raises an error. Otherwise, it saves the field. We also define an ``IIntInfo`` schema and ``intField`` handler function to support defining integer fields. We register the ``text`` and ``int`` directives in ``schema.zcml``. These are like the simple directive definition we saw in ``test_simple.py`` with an important exception. We provide a ``usedIn`` parameter to say that these directives can *only* ne used in a ``ISchema`` context. In other words, these can only be used inside of ``schema`` directives. The ``schema.zcml`` file also contains some sample ``schema`` directives. We can execute the file: >>> from zope.configuration import tests >>> context = xmlconfig.file("schema.zcml", tests) And verify that the schema registery has the schemas we expect: >>> from pprint import PrettyPrinter >>> pprint=PrettyPrinter(width=70).pprint >>> pprint(list(schema_registry)) ['zope.configuration.tests.test_nested.I1', 'zope.configuration.tests.test_nested.I2'] >>> def sorted(x): ... r = list(x) ... r.sort() ... return r >>> i1 = schema_registry['zope.configuration.tests.test_nested.I1'] >>> sorted(i1) ['a', 'b'] >>> i1['a'].__class__.__name__ 'Text' >>> i1['a'].description.strip() u'A\n\n Blah blah' >>> i1['a'].min_length 1 >>> i1['b'].__class__.__name__ 'Int' >>> i1['b'].description.strip() u'B\n\n Not feeling very creative' >>> i1['b'].min 1 >>> i1['b'].max 10 >>> i2 = schema_registry['zope.configuration.tests.test_nested.I2'] >>> sorted(i2) ['x', 'y'] Now let's look at some error situations. For example, let's see what happens if we use a field directive outside of a schema dorective. (Note that we used the context we created above, so we don't have to redefine our directives: >>> try: ... v = xmlconfig.string( ... '', ... context) ... except xmlconfig.ZopeXMLConfigurationError, v: ... pass >>> print v File "", line 1.0 ConfigurationError: The directive """ \ """(u'http://sample.namespaces.zope.org/schema', u'text') """ \ """cannot be used in this context Let's see what happens if we declare duplicate fields: >>> try: ... v = xmlconfig.string( ... ''' ... ... ... ... ... ''', ... context) ... except xmlconfig.ZopeXMLConfigurationError, v: ... pass >>> print v File "", line 5.7-5.24 ValueError: ('Duplicate field', 'x') """ import unittest from doctest import DocTestSuite from zope import interface, schema from zope.configuration import config, xmlconfig, fields schema_registry = {} class ISchemaInfo(interface.Interface): """Parameter schema for the schema directive """ name = schema.TextLine( title=u"The schema name", description=u"This is a descriptive name for the schema." ) id = schema.Id( title=u"The unique id for the schema" ) class ISchema(interface.Interface): """Interface that distinguishes the schema directive """ fields = interface.Attribute("Dictionary of field definitions" ) class Schema(config.GroupingContextDecorator): """Handle schema directives """ interface.implements(config.IConfigurationContext, ISchema) def __init__(self, context, name, id): self.context, self.name, self.id = context, name, id self.fields = {} def after(self): schema = interface.Interface.__class__( self.name, (interface.Interface, ), self.fields ) schema.__doc__ = self.info.text.strip() self.action( discriminator=('schema', self.id), callable=schema_registry.__setitem__, args=(self.id, schema), ) class IFieldInfo(interface.Interface): name = schema.BytesLine( title=u"The field name" ) title = schema.TextLine( title=u"Title", description=u"A short summary or label", default=u"", required=False, ) required = fields.Bool( title=u"Required", description=u"Determines whether a value is required.", default=True) readonly = fields.Bool( title=u"Read Only", description=u"Can the value be modified?", required=False, default=False) class ITextInfo(IFieldInfo): min_length = schema.Int( title=u"Minimum length", description=u"Value after whitespace processing cannot have less than " u"min_length characters. If min_length is None, there is " u"no minimum.", required=False, min=0, # needs to be a positive number default=0) max_length = schema.Int( title=u"Maximum length", description=u"Value after whitespace processing cannot have greater " u"or equal than max_length characters. If max_length is " u"None, there is no maximum.", required=False, min=0, # needs to be a positive number default=None) def field(context, constructor, name, **kw): # Compute the field field = constructor(description=context.info.text.strip(), **kw) # Save it in the schema's field dictionary schema = context.context if name in schema.fields: raise ValueError("Duplicate field", name) schema.fields[name] = field def textField(context, **kw): field(context, schema.Text, **kw) class IIntInfo(IFieldInfo): min = schema.Int( title=u"Start of the range", required=False, default=None ) max = schema.Int( title=u"End of the range (excluding the value itself)", required=False, default=None ) def intField(context, **kw): field(context, schema.Int, **kw) def test_suite(): return unittest.TestSuite(( DocTestSuite(), )) if __name__ == '__main__': unittest.main() zope.configuration-3.7.4/src/zope/configuration/tests/test_simple.py0000644000000000000000000001623511401620130024150 0ustar 00000000000000############################################################################## # # Copyright (c) 2003 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## r"""How to write a simple directive This module documents how to write a simple directive. A simple directive is a directive that doesn't contain other directives. It can be implemented via a fairly simple function. To implement a simple directive, you need to do 3 things: - You need to create a schema to describe the directive parameters, - You need to write a directive handler, and - You need to register the directive. In this module, we'll implement a contrived example that records information about files in a file registry. The file registry is just the list, ``file_registry``. Our registry will contain tuples with: - file path - file title - description - Information about where the file was defined Our schema is defined in IRegisterFile (see below). Our schema lists the path and title. We'll get the description and other information for free, as we'll see later. The title is not required, and may be ommmitted. The job of a configuration handler is to compute one or more configuration actions. Configuration actions are defered function calls. The handler doesn't perform the actions. It just computes actions, which may be performed later if they are not overridden by other directives. Out handler is given in the function, ``registerFile``. It takes a context, a path and a title. All directive handlers take the directive context as the first argument. A directive context, at a minimim, implements, ``zope.configuration.IConfigurationContext``. (Specialized contexts can implement more specific interfaces. We'll say more about that when we talk about grouping directives.) The title argument must have a default value, because we indicated that the title was not required in the schema. (Alternatively, we could have made the title required, but provided a default value in the schema. In the first line of function `registerFile`, we get the context information object. This object contains information about the configuration directive, such as the file and location within the file of the directive. The context information object also has a text attribute that contains the textual data contained by the configuration directive. (This is the concatenation of all of the xml text nodes directly contained by the directive.) We use this for our description in the second line of the handler. The last thing the handler does is to compute an action by calling the action method of the context. It passes the action method 3 keyword arguments: - discriminator The discriminator is used to identify the action to be performed so that duplicate actions can be detected. Two actions are duplicated, and this conflict, if they have the same discriminator values and the values are not ``None``. Conflicting actions can be resolved if one of the conflicting actions is from a configuration file that directly or indirectly includes the files containing the other conflicting actions. In function ``registerFile``, we a tuple with the string ``'RegisterFile'`` and the path to be registered. - callable The callable is the object to be called to perform the action. - args The args argument contains positinal arguments to be passed to the callable. In function ``registerFile``, we pass a tuple containing a ``FileInfo`` object. (Note that there's nothing special about the FileInfo class. It has nothing to do with creating simple directives. It's just used in this example to organize the application data.) The final step in implementing the simple directive is to register it. We do that with the zcml ``meta:directive`` directive. This is given in the file simple.zcml. Here we specify the name, namespace, schema, and handler for the directive. We also provide a documentation for the directive as text between the start and end tags. The file simple.zcml also includes some directives that use the new directive to register some files. Now let's try it all out: >>> from zope.configuration import tests >>> context = xmlconfig.file("simple.zcml", tests) Now we should see some file information in the registry: >>> from zope.configuration.tests.test_xmlconfig import clean_text_w_paths >>> from zope.configuration.tests.test_xmlconfig import clean_path >>> for i in file_registry: ... print "path:", clean_path(i.path) ... print "title:", i.title ... print "description:", '\n'.join( ... [l.rstrip() ... for l in i.description.strip().split('\n') ... if l.rstrip()]) ... print "info:" ... print clean_text_w_paths(i.info) path: tests/test_simple.py title: How to create a simple directive description: Describes how to implement a simple directive info: File "tests/simple.zcml", line 19.2-24.2 Describes how to implement a simple directive path: tests/simple.zcml title: description: Shows the ZCML directives needed to register a simple directive. Also show some usage examples, info: File "tests/simple.zcml", line 26.2-30.2 Shows the ZCML directives needed to register a simple directive. Also show some usage examples, path: tests/__init__.py title: Make this a package description: info: File "tests/simple.zcml", line 32.2-32.67 We'll clean up after ourselves: >>> del file_registry[:] """ file_registry = [] import unittest from doctest import DocTestSuite from zope import interface from zope import schema from zope.configuration import fields, xmlconfig class IRegisterFile(interface.Interface): path = fields.Path( title=u"File path", description=u"This is the path name of the file to be registered." ) title = schema.Text( title=u"Short summary of the file", description=u"This will be used in file listings", required = False ) class FileInfo(object): def __init__(self, path, title, description, info): (self.path, self.title, self.description, self.info ) = path, title, description, info def registerFile(context, path, title=u""): info = context.info description = info.text.strip() context.action(discriminator=('RegisterFile', path), callable=file_registry.append, args=(FileInfo(path, title, description, info),) ) def test_suite(): return unittest.TestSuite(( DocTestSuite(), )) if __name__ == '__main__': unittest.main() zope.configuration-3.7.4/src/zope/configuration/tests/test_xmlconfig.py0000644000000000000000000004775211401620130024655 0ustar 00000000000000############################################################################## # # Copyright (c) 2003 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """Test XML configuration (ZCML) machinery. """ import unittest import os import re from doctest import DocTestSuite, DocFileSuite from zope.testing import renormalizing from zope.configuration import xmlconfig, config from zope.configuration.tests.samplepackage import foo from pprint import PrettyPrinter, pprint class FauxLocator(object): def __init__(self, file, line, column): self.file, self.line, self.column = file, line, column def getSystemId(self): return self.file def getLineNumber(self): return self.line def getColumnNumber(self): return self.column class FauxContext(object): def setInfo(self, info): self.info = info def getInfo(self): return self.info def begin(self, name, data, info): self.begin_args = name, data self.info = info def end(self): self.end_called = 1 def path(*p): return os.path.join(os.path.dirname(__file__), *p) def test_ConfigurationHandler_normal(): """ >>> context = FauxContext() >>> locator = FauxLocator('tests//sample.zcml', 1, 1) >>> handler = xmlconfig.ConfigurationHandler(context) >>> handler.setDocumentLocator(locator) >>> handler.startElementNS((u"ns", u"foo"), u"foo", ... {(u"xxx", u"splat"): u"splatv", ... (None, u"a"): u"avalue", ... (None, u"b"): u"bvalue", ... }) >>> context.info File "tests//sample.zcml", line 1.1 >>> from pprint import PrettyPrinter >>> pprint=PrettyPrinter(width=50).pprint >>> pprint(context.begin_args) ((u'ns', u'foo'), {'a': u'avalue', 'b': u'bvalue'}) >>> getattr(context, "end_called", 0) 0 >>> locator.line, locator.column = 7, 16 >>> handler.endElementNS((u"ns", u"foo"), u"foo") >>> context.info File "tests//sample.zcml", line 1.1-7.16 >>> context.end_called 1 """ def test_ConfigurationHandler_err_start(): """ >>> class FauxContext(FauxContext): ... def begin(self, *args): ... raise AttributeError("xxx") >>> context = FauxContext() >>> locator = FauxLocator('tests//sample.zcml', 1, 1) >>> handler = xmlconfig.ConfigurationHandler(context) >>> handler.setDocumentLocator(locator) >>> try: ... v = handler.startElementNS((u"ns", u"foo"), u"foo", ... {(u"xxx", u"splat"): u"splatv", ... (None, u"a"): u"avalue", ... (None, u"b"): u"bvalue", ... }) ... except xmlconfig.ZopeXMLConfigurationError, v: ... pass >>> print v File "tests//sample.zcml", line 1.1 AttributeError: xxx """ def test_ConfigurationHandler_err_end(): """ >>> class FauxContext(FauxContext): ... def end(self): ... raise AttributeError("xxx") >>> context = FauxContext() >>> locator = FauxLocator('tests//sample.zcml', 1, 1) >>> handler = xmlconfig.ConfigurationHandler(context) >>> handler.setDocumentLocator(locator) >>> handler.startElementNS((u"ns", u"foo"), u"foo", ... {(u"xxx", u"splat"): u"splatv", ... (None, u"a"): u"avalue", ... (None, u"b"): u"bvalue", ... }) >>> locator.line, locator.column = 7, 16 >>> try: ... v = handler.endElementNS((u"ns", u"foo"), u"foo") ... except xmlconfig.ZopeXMLConfigurationError, v: ... pass >>> print v File "tests//sample.zcml", line 1.1-7.16 AttributeError: xxx """ def clean_info_path(s): part1 = s[:6] part2 = s[6:s.find('"', 6)] part2 = part2[part2.rfind("tests"):] part2 = part2.replace(os.sep, '/') part3 = s[s.find('"', 6):].rstrip() return part1+part2+part3 def clean_path(s): s = s[s.rfind("tests"):] s = s.replace(os.sep, '/') return s def test_processxmlfile(): """ >>> file = open(path("samplepackage", "configure.zcml")) >>> context = config.ConfigurationMachine() >>> xmlconfig.registerCommonDirectives(context) >>> xmlconfig.processxmlfile(file, context) >>> foo.data [] >>> context.execute_actions() >>> data = foo.data.pop() >>> data.args (('x', 'blah'), ('y', 0)) >>> print clean_info_path(`data.info`) File "tests/samplepackage/configure.zcml", line 12.2-12.29 >>> print clean_info_path(str(data.info)) File "tests/samplepackage/configure.zcml", line 12.2-12.29 >>> data.package >>> data.basepath """ def test_file(): """ >>> file_name = path("samplepackage", "configure.zcml") >>> context = xmlconfig.file(file_name) >>> data = foo.data.pop() >>> data.args (('x', 'blah'), ('y', 0)) >>> print clean_info_path(`data.info`) File "tests/samplepackage/configure.zcml", line 12.2-12.29 >>> print clean_info_path(str(data.info)) File "tests/samplepackage/configure.zcml", line 12.2-12.29 >>> data.package >>> print clean_path(data.basepath) tests/samplepackage """ def test_include_by_package(): """ >>> context = config.ConfigurationMachine() >>> xmlconfig.registerCommonDirectives(context) >>> import zope.configuration.tests.samplepackage as package >>> xmlconfig.include(context, 'configure.zcml', package) >>> context.execute_actions() >>> data = foo.data.pop() >>> data.args (('x', 'blah'), ('y', 0)) >>> print clean_info_path(`data.info`) File "tests/samplepackage/configure.zcml", line 12.2-12.29 >>> print clean_info_path(str(data.info)) File "tests/samplepackage/configure.zcml", line 12.2-12.29 >>> data.package is package 1 >>> data.basepath[-13:] 'samplepackage' >>> [clean_path(p) for p in data.includepath] ['tests/samplepackage/configure.zcml'] """ # Not any more ## Including the same file more than once produces an error: ## >>> try: ## ... xmlconfig.include(context, 'configure.zcml', package) ## ... except xmlconfig.ConfigurationError, e: ## ... 'OK' ## ... ## 'OK' def test_include_by_file(): """ >>> context = config.ConfigurationMachine() >>> xmlconfig.registerCommonDirectives(context) >>> here = os.path.dirname(__file__) >>> path = os.path.join(here, "samplepackage", "foo.zcml") >>> xmlconfig.include(context, path) >>> context.execute_actions() >>> data = foo.data.pop() >>> data.args (('x', 'foo'), ('y', 2)) >>> print clean_info_path(`data.info`) File "tests/samplepackage/foo.zcml.in", line 12.2-12.28 >>> print clean_info_path(str(data.info)) File "tests/samplepackage/foo.zcml.in", line 12.2-12.28 >>> data.package >>> data.basepath[-13:] 'samplepackage' >>> [clean_path(p) for p in data.includepath] ['tests/samplepackage/foo.zcml.in'] """ def test_include_by_file_glob(): """ >>> context = config.ConfigurationMachine() >>> xmlconfig.registerCommonDirectives(context) >>> here = os.path.dirname(__file__) >>> path = os.path.join(here, "samplepackage/baz*.zcml") >>> xmlconfig.include(context, files=path) >>> context.execute_actions() >>> data = foo.data.pop() >>> data.args (('x', 'foo'), ('y', 3)) >>> print clean_info_path(`data.info`) File "tests/samplepackage/baz3.zcml", line 5.2-5.28 >>> print clean_info_path(str(data.info)) File "tests/samplepackage/baz3.zcml", line 5.2-5.28 >>> data.package >>> data.basepath[-13:] 'samplepackage' >>> [clean_path(p) for p in data.includepath] ['tests/samplepackage/baz3.zcml'] >>> data = foo.data.pop() >>> data.args (('x', 'foo'), ('y', 2)) >>> print clean_info_path(`data.info`) File "tests/samplepackage/baz2.zcml", line 5.2-5.28 >>> print clean_info_path(str(data.info)) File "tests/samplepackage/baz2.zcml", line 5.2-5.28 >>> data.package >>> data.basepath[-13:] 'samplepackage' >>> [clean_path(p) for p in data.includepath] ['tests/samplepackage/baz2.zcml'] """ def clean_actions(actions): return [ {'discriminator': discriminator, 'info': clean_info_path(`info`), 'includepath': [clean_path(p) for p in includepath], } for (discriminator, callable, args, kw, includepath, info, order) in [config.expand_action(*action) for action in actions] ] def clean_text_w_paths(error): r = [] for line in unicode(error).split("\n"): line = line.rstrip() if not line: continue l = line.find('File "') if l >= 0: line = line[:l] + clean_info_path(line[l:]) r.append(line) return '\n'.join(r) def test_includeOverrides(): """ When we have conflicting directives, we can resolve them if one of the conflicting directives was from a file that included all of the others. The problem with this is that this requires that all of the overriding directives be in one file, typically the top-most including file. This isn't very convenient. Fortunately, we can overcome this with the includeOverrides directive. Let's look at an example to see how this works. Look at the file bar.zcml. It includes bar1.zcml and bar2.zcml. bar2.zcml includes configure.zcml and has a foo directive. bar2.zcml includes bar21.zcml. bar2.zcml has a foo directive that conflicts with one in bar1.zcml. bar2.zcml also overrides a foo directive in bar21.zcml. bar21.zcml has a foo directive that conflicts with one in in configure.zcml. Whew! Let's see what happens when we try to process bar.zcml. >>> context = config.ConfigurationMachine() >>> xmlconfig.registerCommonDirectives(context) >>> here = os.path.dirname(__file__) >>> path = os.path.join(here, "samplepackage", "bar.zcml") >>> xmlconfig.include(context, path) So far so good, let's look at the configuration actions: >>> pprint=PrettyPrinter(width=70).pprint >>> pprint(clean_actions(context.actions)) [{'discriminator': (('x', 'blah'), ('y', 0)), 'includepath': ['tests/samplepackage/bar.zcml', 'tests/samplepackage/bar1.zcml', 'tests/samplepackage/configure.zcml'], 'info': 'File "tests/samplepackage/configure.zcml", line 12.2-12.29'}, {'discriminator': (('x', 'blah'), ('y', 1)), 'includepath': ['tests/samplepackage/bar.zcml', 'tests/samplepackage/bar1.zcml'], 'info': 'File "tests/samplepackage/bar1.zcml", line 5.2-5.24'}, {'discriminator': (('x', 'blah'), ('y', 0)), 'includepath': ['tests/samplepackage/bar.zcml', 'tests/samplepackage/bar2.zcml', 'tests/samplepackage/bar21.zcml'], 'info': 'File "tests/samplepackage/bar21.zcml", line 3.2-3.24'}, {'discriminator': (('x', 'blah'), ('y', 2)), 'includepath': ['tests/samplepackage/bar.zcml', 'tests/samplepackage/bar2.zcml', 'tests/samplepackage/bar21.zcml'], 'info': 'File "tests/samplepackage/bar21.zcml", line 4.2-4.24'}, {'discriminator': (('x', 'blah'), ('y', 2)), 'includepath': ['tests/samplepackage/bar.zcml', 'tests/samplepackage/bar2.zcml'], 'info': 'File "tests/samplepackage/bar2.zcml", line 5.2-5.24'}, {'discriminator': (('x', 'blah'), ('y', 1)), 'includepath': ['tests/samplepackage/bar.zcml', 'tests/samplepackage/bar2.zcml'], 'info': 'File "tests/samplepackage/bar2.zcml", line 6.2-6.24'}] As you can see, there are a number of conflicts (actions with the same discriminator). Some of these can be resolved, but many can't, as we'll find if we try to execuse the actions: >>> try: ... v = context.execute_actions() ... except config.ConfigurationConflictError, v: ... pass >>> print clean_text_w_paths(str(v)) Conflicting configuration actions For: (('x', 'blah'), ('y', 0)) File "tests/samplepackage/configure.zcml", line 12.2-12.29 File "tests/samplepackage/bar21.zcml", line 3.2-3.24 For: (('x', 'blah'), ('y', 1)) File "tests/samplepackage/bar1.zcml", line 5.2-5.24 File "tests/samplepackage/bar2.zcml", line 6.2-6.24 Note that the conflicts for (('x', 'blah'), ('y', 2)) aren't included in the error because they could be resolved. Let's try this again using includeOverrides. We'll include baro.zcml which includes bar2.zcml as overrides. >>> context = config.ConfigurationMachine() >>> xmlconfig.registerCommonDirectives(context) >>> path = os.path.join(here, "samplepackage", "baro.zcml") >>> xmlconfig.include(context, path) Now, if we look at the actions: >>> pprint(clean_actions(context.actions)) [{'discriminator': (('x', 'blah'), ('y', 0)), 'includepath': ['tests/samplepackage/baro.zcml', 'tests/samplepackage/bar1.zcml', 'tests/samplepackage/configure.zcml'], 'info': 'File "tests/samplepackage/configure.zcml", line 12.2-12.29'}, {'discriminator': (('x', 'blah'), ('y', 1)), 'includepath': ['tests/samplepackage/baro.zcml', 'tests/samplepackage/bar1.zcml'], 'info': 'File "tests/samplepackage/bar1.zcml", line 5.2-5.24'}, {'discriminator': (('x', 'blah'), ('y', 0)), 'includepath': ['tests/samplepackage/baro.zcml'], 'info': 'File "tests/samplepackage/bar21.zcml", line 3.2-3.24'}, {'discriminator': (('x', 'blah'), ('y', 2)), 'includepath': ['tests/samplepackage/baro.zcml'], 'info': 'File "tests/samplepackage/bar2.zcml", line 5.2-5.24'}, {'discriminator': (('x', 'blah'), ('y', 1)), 'includepath': ['tests/samplepackage/baro.zcml'], 'info': 'File "tests/samplepackage/bar2.zcml", line 6.2-6.24'}] We see that: - The conflicting actions between bar2.zcml and bar21.zcml have been resolved, and - The remaining (after conflict resolution) actions from bar2.zcml and bar21.zcml have the includepath that they would have if they were defined in baro.zcml and this override the actions from bar1.zcml and configure.zcml. We can now execute the actions without problem, since the remaining conflicts are resolvable: >>> context.execute_actions() We should now have three entries in foo.data: >>> len(foo.data) 3 >>> data = foo.data.pop(0) >>> data.args (('x', 'blah'), ('y', 0)) >>> print clean_info_path(`data.info`) File "tests/samplepackage/bar21.zcml", line 3.2-3.24 >>> data = foo.data.pop(0) >>> data.args (('x', 'blah'), ('y', 2)) >>> print clean_info_path(`data.info`) File "tests/samplepackage/bar2.zcml", line 5.2-5.24 >>> data = foo.data.pop(0) >>> data.args (('x', 'blah'), ('y', 1)) >>> print clean_info_path(`data.info`) File "tests/samplepackage/bar2.zcml", line 6.2-6.24 We expect the exact same results when using includeOverrides with the ``files`` argument instead of the ``file`` argument. The baro2.zcml file uses the former: >>> context = config.ConfigurationMachine() >>> xmlconfig.registerCommonDirectives(context) >>> path = os.path.join(here, "samplepackage", "baro2.zcml") >>> xmlconfig.include(context, path) Actions look like above: >>> pprint(clean_actions(context.actions)) [{'discriminator': (('x', 'blah'), ('y', 0)), 'includepath': ['tests/samplepackage/baro2.zcml', 'tests/samplepackage/bar1.zcml', 'tests/samplepackage/configure.zcml'], 'info': 'File "tests/samplepackage/configure.zcml", line 12.2-12.29'}, {'discriminator': (('x', 'blah'), ('y', 1)), 'includepath': ['tests/samplepackage/baro2.zcml', 'tests/samplepackage/bar1.zcml'], 'info': 'File "tests/samplepackage/bar1.zcml", line 5.2-5.24'}, {'discriminator': (('x', 'blah'), ('y', 0)), 'includepath': ['tests/samplepackage/baro2.zcml'], 'info': 'File "tests/samplepackage/bar21.zcml", line 3.2-3.24'}, {'discriminator': (('x', 'blah'), ('y', 2)), 'includepath': ['tests/samplepackage/baro2.zcml'], 'info': 'File "tests/samplepackage/bar2.zcml", line 5.2-5.24'}, {'discriminator': (('x', 'blah'), ('y', 1)), 'includepath': ['tests/samplepackage/baro2.zcml'], 'info': 'File "tests/samplepackage/bar2.zcml", line 6.2-6.24'}] >>> context.execute_actions() >>> len(foo.data) 3 >>> del foo.data[:] """ def test_XMLConfig(): """Test processing a configuration file. We'll use the same example from test_includeOverrides: >>> here = os.path.dirname(__file__) >>> path = os.path.join(here, "samplepackage", "baro.zcml") First, process the configuration file: >>> x = xmlconfig.XMLConfig(path) Second, call the resulting object to process the actions: >>> x() And verify the data as above: >>> len(foo.data) 3 >>> data = foo.data.pop(0) >>> data.args (('x', 'blah'), ('y', 0)) >>> print clean_info_path(`data.info`) File "tests/samplepackage/bar21.zcml", line 3.2-3.24 >>> data = foo.data.pop(0) >>> data.args (('x', 'blah'), ('y', 2)) >>> print clean_info_path(`data.info`) File "tests/samplepackage/bar2.zcml", line 5.2-5.24 >>> data = foo.data.pop(0) >>> data.args (('x', 'blah'), ('y', 1)) >>> print clean_info_path(`data.info`) File "tests/samplepackage/bar2.zcml", line 6.2-6.24 Finally, clean up. >>> from zope.testing.cleanup import CleanUp >>> CleanUp().cleanUp() """ def test_XMLConfig_w_module(): """Test processing a configuration file for a module. We'll use the same example from test_includeOverrides: >>> import zope.configuration.tests.samplepackage as module First, process the configuration file: >>> x = xmlconfig.XMLConfig("baro.zcml", module) Second, call the resulting object to process the actions: >>> x() And verify the data as above: >>> len(foo.data) 3 >>> data = foo.data.pop(0) >>> data.args (('x', 'blah'), ('y', 0)) >>> print clean_info_path(`data.info`) File "tests/samplepackage/bar21.zcml", line 3.2-3.24 >>> data = foo.data.pop(0) >>> data.args (('x', 'blah'), ('y', 2)) >>> print clean_info_path(`data.info`) File "tests/samplepackage/bar2.zcml", line 5.2-5.24 >>> data = foo.data.pop(0) >>> data.args (('x', 'blah'), ('y', 1)) >>> print clean_info_path(`data.info`) File "tests/samplepackage/bar2.zcml", line 6.2-6.24 Finally, clean up. >>> from zope.testing.cleanup import CleanUp >>> CleanUp().cleanUp() """ def test_suite(): return unittest.TestSuite(( DocTestSuite('zope.configuration.xmlconfig'), DocTestSuite(), DocFileSuite('../exclude.txt', checker=renormalizing.RENormalizing([ (re.compile('include [^\n]+zope.configuration[\S+]'), 'include /zope.configuration\2'), (re.compile(r'\\'), '/'), ])) )) if __name__ == '__main__': unittest.main(defaultTest='test_suite') zope.configuration-3.7.4/src/zope/configuration/tests/victim.py0000644000000000000000000000001310050206152023101 0ustar 00000000000000import bad zope.configuration-3.7.4/src/zope/configuration/tests/excludedemo/__init__.py0000644000000000000000000000000211151505046025650 0ustar 00000000000000# zope.configuration-3.7.4/src/zope/configuration/tests/excludedemo/configure.zcml0000644000000000000000000000012311151505046026413 0ustar 00000000000000 zope.configuration-3.7.4/src/zope/configuration/tests/excludedemo/spam.zcml0000644000000000000000000000001611151505046025373 0ustar 00000000000000 zope.configuration-3.7.4/src/zope/configuration/tests/excludedemo/sub/__init__.py0000644000000000000000000000000211151505046026441 0ustar 00000000000000# zope.configuration-3.7.4/src/zope/configuration/tests/excludedemo/sub/configure.zcml0000644000000000000000000000001611151505046027205 0ustar 00000000000000 zope.configuration-3.7.4/src/zope/configuration/tests/samplepackage/__init__.py0000644000000000000000000000007510050206152026152 0ustar 00000000000000# # This file is necessary to make this directory a package. zope.configuration-3.7.4/src/zope/configuration/tests/samplepackage/bar.zcml0000644000000000000000000000013410050206152025470 0ustar 00000000000000 zope.configuration-3.7.4/src/zope/configuration/tests/samplepackage/bar1.zcml0000644000000000000000000000020410050206152025547 0ustar 00000000000000 zope.configuration-3.7.4/src/zope/configuration/tests/samplepackage/bar2.zcml0000644000000000000000000000023110050206152025550 0ustar 00000000000000 zope.configuration-3.7.4/src/zope/configuration/tests/samplepackage/bar21.zcml0000644000000000000000000000017010050206152025633 0ustar 00000000000000 zope.configuration-3.7.4/src/zope/configuration/tests/samplepackage/baro.zcml0000644000000000000000000000014510050206152025651 0ustar 00000000000000 zope.configuration-3.7.4/src/zope/configuration/tests/samplepackage/baro2.zcml0000644000000000000000000000014410270541644025746 0ustar 00000000000000 zope.configuration-3.7.4/src/zope/configuration/tests/samplepackage/baz1.zcml0000644000000000000000000000063410050206152025566 0ustar 00000000000000 zope.configuration-3.7.4/src/zope/configuration/tests/samplepackage/baz2.zcml0000644000000000000000000000023710050206152025566 0ustar 00000000000000 zope.configuration-3.7.4/src/zope/configuration/tests/samplepackage/baz3.zcml0000644000000000000000000000023710050206152025567 0ustar 00000000000000 zope.configuration-3.7.4/src/zope/configuration/tests/samplepackage/configure.zcml0000644000000000000000000000067310050206152026715 0ustar 00000000000000 zope.configuration-3.7.4/src/zope/configuration/tests/samplepackage/configure.zcml.in0000644000000000000000000000010110050206152027304 0ustar 00000000000000 zope.configuration-3.7.4/src/zope/configuration/tests/samplepackage/foo.py0000644000000000000000000000246011401620130025173 0ustar 00000000000000############################################################################## # # Copyright (c) 2003 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """Sample module used for testing """ from zope.interface import Interface from zope import schema data = [] class S1(Interface): x = schema.BytesLine() y = schema.Int() class stuff(object): def __init__(self, args, info, basepath, package, includepath): (self.args, self.info, self.basepath, self.package, self.includepath ) = args, info, basepath, package, includepath def handler(_context, **kw): args = kw.items() args.sort() args = tuple(args) discriminator = args args = (stuff(args, _context.info, _context.basepath, _context.package, _context.includepath), ) _context.action(discriminator, data.append, args) zope.configuration-3.7.4/src/zope/configuration/tests/samplepackage/foo.zcml.in0000644000000000000000000000067210050206152026123 0ustar 00000000000000 zope.configuration-3.7.4/src/zope.configuration.egg-info/dependency_links.txt0000644000000000000000000000000111546070066025655 0ustar 00000000000000 zope.configuration-3.7.4/src/zope.configuration.egg-info/namespace_packages.txt0000644000000000000000000000000511546070066026135 0ustar 00000000000000zope zope.configuration-3.7.4/src/zope.configuration.egg-info/not-zip-safe0000644000000000000000000000000111546070064024033 0ustar 00000000000000 zope.configuration-3.7.4/src/zope.configuration.egg-info/PKG-INFO0000644000000000000000000002022411546070066022704 0ustar 00000000000000Metadata-Version: 1.0 Name: zope.configuration Version: 3.7.4 Summary: Zope Configuration Markup Language (ZCML) Home-page: http://pypi.python.org/pypi/zope.configuration Author: Zope Foundation and Contributors Author-email: zope-dev@zope.org License: ZPL 2.1 Description: zope.configuration ================== Overview -------- The zope configuration system provides an extensible system for supporting various kinds of configurations. It is based on the idea of configuration directives. Users of the configuration system provide configuration directives in some language that express configuration choices. The intent is that the language be pluggable. An XML language is provided by default. Detailed Documentation ---------------------- ========================== Zope configuration system ========================== The zope configuration system provides an extensible system for supporting variouse kinds of configurations. It is based on the idea of configuration directives. Users of the configuration system provide configuration directives in some language that express configuration choices. The intent is that the language be pluggable. An XML language is provided by default. Configuration is performed in three stages. In the first stage, directives are processed to compute configuration actions. Configuration actions consist of: - A discriminator - A callable - Positional arguments - Keyword arguments The actions are essentially delayed function calls. Two or more actions conflict if they have the same discriminator. The configuration system has rules for resolving conflicts. If conflicts cannot be resolved, an error will result. Conflict resolution typically discards all but one of the conflicting actions, so that the remaining action of the originally-conflicting actions no longer conflicts. Non-conflicting actions are executed in the order that they were created by passing the positional and non-positional arguments to the action callable. The system is extensible. There is a meta-configuration language for defining configuration directives. A directive is defined by providing meta data about the directive and handler code to process the directive. There are four kinds of directives: - Simple directives compute configuration actions. Their handlers are typically functions that take a context and zero or more keyword arguments and return a sequence of configuration actions. To learn how to create simple directives, see `tests/test_simple.py`. - Grouping directives collect information to be used by nested directives. They are called with a context object which they adapt to some interface that extends IConfigurationContext. To learn how to create grouping directives, look at the documentation in zopeconfigure.py, which provides the implementation of the zope `configure` directive. Other directives can be nested in grouping directives. To learn how to implement nested directives, look at the documentation in `tests/test_nested.py`. - Complex directives are directives that have subdirectives. Subdirectives have handlers that are simply methods of complex directives. Complex diretives are handled by factories, typically classes, that create objects that have methods for handling subdirectives. These objects also have __call__ methods that are called when processing of subdirectives is finished. Complex directives only exist to support old directive handlers. They will probably be deprecated in the future. - Subdirectives are nested in complex directives. They are like simple directives except that they hane handlers that are complex directive methods. Subdirectives, like complex directives only exist to support old directive handlers. They will probably be deprecated in the future. ======= Changes ======= 3.7.4 (2011-04-03) ------------------ - Test fixes for Windows. 3.7.3 (2011-03-11) ------------------ - Correctly locate packages with a __path__ attribute but no __file__ attribute (such as namespace packages installed with setup.py install --single-version-externally-managed). - Allow "info" and "includepath" to be passed optionally to context.action. 3.7.2 (2010-04-30) ------------------ - Prefer the standard libraries doctest module over zope.testing.doctest. 3.7.1 (2010-01-05) ------------------ - Jython support: use ``__builtin__`` module import rather than assuming ``__builtins__`` is available. - Jython support: deal with the fact that the Jython SAX parser returns attribute sets that have an empty string indicating no namespace instead of ``None``. - Allow ``setup.py test`` to run at least a subset of the tests that would be run when using the zope testrunner: ``setup.py test`` runs 53 tests, while ``bin/test`` runs 156. 3.7.0 (2009-12-22) ------------------ - Adjust testing output to newer zope.schema. - Prefer zope.testing.doctest over doctestunit. 3.6.0 (2009-04-01) ------------------ - Removed dependency of `zope.deprecation` package. - Don't suppress deprecation warnings any more in 'zope.configuration' package level. This makes it more likely other packages will generate deprecation warnings now, which will allow us to remove more outdated ones. - Don't fail when zope.testing is not installed. - Added missing ``processFile`` method to ``IConfigurationContext``. It is already implemented in the mix-in class, ``zope.configuration.config.ConfigurationContext``, and used by implementations of ``include`` and ``exclude`` directives. 3.5.0 (2009-02-26) ------------------ - Added the ``exclude`` directive to standard directives. It was previously available via ``zc.configuration`` package and now it's merged into ``zope.configuration``. - Changed package's mailing list address to zope-dev at zope.org, change "cheeseshop" to "pypi" in the package's url. 3.4.1 (2008-12-11) ------------------ - Use built-in 'set' type, rather than importin the 'sets' module, which is deprecated in Python 2.6. - Added support to bootstrap on Jython. 3.4.0 (2007-10-02) ------------------ - Initial release as a standalone package. Before 3.4.0 ------------ This package was part of the Zope 3 distribution and did not have its own CHANGES.txt. For earlier changes please refer to either our subversion log or the CHANGES.txt of earlier Zope 3 releases. Keywords: zope configuration zcml Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable Classifier: Environment :: Web Environment Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: Zope Public License Classifier: Programming Language :: Python Classifier: Natural Language :: English Classifier: Operating System :: OS Independent Classifier: Topic :: Internet :: WWW/HTTP Classifier: Framework :: Zope3 zope.configuration-3.7.4/src/zope.configuration.egg-info/requires.txt0000644000000000000000000000011511546070066024204 0ustar 00000000000000zope.i18nmessageid zope.interface zope.schema setuptools [test] zope.testingzope.configuration-3.7.4/src/zope.configuration.egg-info/SOURCES.txt0000644000000000000000000000470211546070066023476 0ustar 00000000000000.bzrignore CHANGES.txt COPYRIGHT.txt LICENSE.txt README.txt bootstrap.py buildout.cfg setup.py src/zope/__init__.py src/zope.configuration.egg-info/PKG-INFO src/zope.configuration.egg-info/SOURCES.txt src/zope.configuration.egg-info/dependency_links.txt src/zope.configuration.egg-info/namespace_packages.txt src/zope.configuration.egg-info/not-zip-safe src/zope.configuration.egg-info/requires.txt src/zope.configuration.egg-info/top_level.txt src/zope/configuration/README.txt src/zope/configuration/__init__.py src/zope/configuration/config.py src/zope/configuration/docutils.py src/zope/configuration/exceptions.py src/zope/configuration/exclude.txt src/zope/configuration/fields.py src/zope/configuration/interfaces.py src/zope/configuration/name.py src/zope/configuration/stxdocs.py src/zope/configuration/xmlconfig.py src/zope/configuration/zopeconfigure.py src/zope/configuration/tests/__init__.py src/zope/configuration/tests/bad.py src/zope/configuration/tests/conditions.zcml src/zope/configuration/tests/directives.py src/zope/configuration/tests/sample.zcml src/zope/configuration/tests/schema.zcml src/zope/configuration/tests/simple.zcml src/zope/configuration/tests/test_conditions.py src/zope/configuration/tests/test_config.py src/zope/configuration/tests/test_docutils.py src/zope/configuration/tests/test_nested.py src/zope/configuration/tests/test_simple.py src/zope/configuration/tests/test_xmlconfig.py src/zope/configuration/tests/victim.py src/zope/configuration/tests/excludedemo/__init__.py src/zope/configuration/tests/excludedemo/configure.zcml src/zope/configuration/tests/excludedemo/spam.zcml src/zope/configuration/tests/excludedemo/sub/__init__.py src/zope/configuration/tests/excludedemo/sub/configure.zcml src/zope/configuration/tests/samplepackage/__init__.py src/zope/configuration/tests/samplepackage/bar.zcml src/zope/configuration/tests/samplepackage/bar1.zcml src/zope/configuration/tests/samplepackage/bar2.zcml src/zope/configuration/tests/samplepackage/bar21.zcml src/zope/configuration/tests/samplepackage/baro.zcml src/zope/configuration/tests/samplepackage/baro2.zcml src/zope/configuration/tests/samplepackage/baz1.zcml src/zope/configuration/tests/samplepackage/baz2.zcml src/zope/configuration/tests/samplepackage/baz3.zcml src/zope/configuration/tests/samplepackage/configure.zcml src/zope/configuration/tests/samplepackage/configure.zcml.in src/zope/configuration/tests/samplepackage/foo.py src/zope/configuration/tests/samplepackage/foo.zcml.inzope.configuration-3.7.4/src/zope.configuration.egg-info/top_level.txt0000644000000000000000000000000511546070066024334 0ustar 00000000000000zope