zope.configuration-4.3.1/0000755000076500000240000000000013430472371015231 5ustar macstaff00000000000000zope.configuration-4.3.1/bootstrap.py0000644000076500000240000001644213430472367017634 0ustar macstaff00000000000000############################################################################## # # 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. """ import os import shutil import sys import tempfile from optparse import OptionParser __version__ = '2015-07-01' # See zc.buildout's changelog if this version is up to date. tmpeggs = tempfile.mkdtemp(prefix='bootstrap-') usage = '''\ [DESIRED PYTHON FOR BUILDOUT] bootstrap.py [options] Bootstraps a buildout-based project. Simply run this script in a directory containing a buildout.cfg, using the Python that you want bin/buildout to use. Note that by using --find-links to point to local resources, you can keep this script from going over the network. ''' parser = OptionParser(usage=usage) parser.add_option("--version", action="store_true", default=False, help=("Return bootstrap.py version.")) parser.add_option("-t", "--accept-buildout-test-releases", dest='accept_buildout_test_releases', action="store_true", default=False, help=("Normally, if you do not specify a --version, the " "bootstrap script and buildout gets the newest " "*final* versions of zc.buildout and its recipes and " "extensions for you. If you use this flag, " "bootstrap and buildout will get the newest releases " "even if they are alphas or betas.")) parser.add_option("-c", "--config-file", help=("Specify the path to the buildout configuration " "file to be used.")) parser.add_option("-f", "--find-links", help=("Specify a URL to search for buildout releases")) parser.add_option("--allow-site-packages", action="store_true", default=False, help=("Let bootstrap.py use existing site packages")) parser.add_option("--buildout-version", help="Use a specific zc.buildout version") parser.add_option("--setuptools-version", help="Use a specific setuptools version") parser.add_option("--setuptools-to-dir", help=("Allow for re-use of existing directory of " "setuptools versions")) options, args = parser.parse_args() if options.version: print("bootstrap.py version %s" % __version__) sys.exit(0) ###################################################################### # load/install setuptools try: from urllib.request import urlopen except ImportError: from urllib2 import urlopen ez = {} if os.path.exists('ez_setup.py'): exec(open('ez_setup.py').read(), ez) else: exec(urlopen('https://bootstrap.pypa.io/ez_setup.py').read(), ez) if not options.allow_site_packages: # ez_setup imports site, which adds site packages # this will remove them from the path to ensure that incompatible versions # of setuptools are not in the path import site # inside a virtualenv, there is no 'getsitepackages'. # We can't remove these reliably if hasattr(site, 'getsitepackages'): for sitepackage_path in site.getsitepackages(): # Strip all site-packages directories from sys.path that # are not sys.prefix; this is because on Windows # sys.prefix is a site-package directory. if sitepackage_path != sys.prefix: sys.path[:] = [x for x in sys.path if sitepackage_path not in x] setup_args = dict(to_dir=tmpeggs, download_delay=0) if options.setuptools_version is not None: setup_args['version'] = options.setuptools_version if options.setuptools_to_dir is not None: setup_args['to_dir'] = options.setuptools_to_dir ez['use_setuptools'](**setup_args) import setuptools import pkg_resources # This does not (always?) update the default working set. We will # do it. for path in sys.path: if path not in pkg_resources.working_set.entries: pkg_resources.working_set.add_entry(path) ###################################################################### # Install buildout ws = pkg_resources.working_set setuptools_path = ws.find( pkg_resources.Requirement.parse('setuptools')).location # Fix sys.path here as easy_install.pth added before PYTHONPATH cmd = [sys.executable, '-c', 'import sys; sys.path[0:0] = [%r]; ' % setuptools_path + 'from setuptools.command.easy_install import main; main()', '-mZqNxd', tmpeggs] find_links = os.environ.get( 'bootstrap-testing-find-links', options.find_links or ('http://downloads.buildout.org/' if options.accept_buildout_test_releases else None) ) if find_links: cmd.extend(['-f', find_links]) requirement = 'zc.buildout' version = options.buildout_version if version is None and not options.accept_buildout_test_releases: # Figure out the most recent final version of zc.buildout. import setuptools.package_index _final_parts = '*final-', '*final' def _final_version(parsed_version): try: return not parsed_version.is_prerelease except AttributeError: # Older setuptools for part in parsed_version: if (part[:1] == '*') and (part not in _final_parts): return False return True index = setuptools.package_index.PackageIndex( search_path=[setuptools_path]) if find_links: index.add_find_links((find_links,)) req = pkg_resources.Requirement.parse(requirement) if index.obtain(req) is not None: best = [] bestv = None for dist in index[req.project_name]: distv = dist.parsed_version if _final_version(distv): if bestv is None or distv > bestv: best = [dist] bestv = distv elif distv == bestv: best.append(dist) if best: best.sort() version = best[-1].version if version: requirement = '=='.join((requirement, version)) cmd.append(requirement) import subprocess if subprocess.call(cmd) != 0: raise Exception( "Failed to execute command:\n%s" % repr(cmd)[1:-1]) ###################################################################### # Import and run buildout ws.add_entry(tmpeggs) ws.require(requirement) import zc.buildout.buildout if not [a for a in args if '=' not in a]: args.append('bootstrap') # if -c was provided, we push it back into args for buildout' main function if options.config_file is not None: args[0:0] = ['-c', options.config_file] zc.buildout.buildout.main(args) shutil.rmtree(tmpeggs) zope.configuration-4.3.1/PKG-INFO0000644000076500000240000003005013430472371016324 0ustar macstaff00000000000000Metadata-Version: 2.1 Name: zope.configuration Version: 4.3.1 Summary: Zope Configuration Markup Language (ZCML) Home-page: https://github.com/zopefoundation/zope.configuration Author: Zope Foundation and Contributors Author-email: zope-dev@zope.org License: ZPL 2.1 Description: ``zope.configuration`` ====================== .. image:: https://img.shields.io/pypi/v/zope.configuration.svg :target: https://pypi.python.org/pypi/zope.configuration/ :alt: Latest Version .. image:: https://travis-ci.org/zopefoundation/zope.configuration.svg?branch=master :target: https://travis-ci.org/zopefoundation/zope.configuration .. image:: https://readthedocs.org/projects/zopeconfiguration/badge/?version=latest :target: http://zopeconfiguration.readthedocs.io/en/latest/ :alt: Documentation Status 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. Please see http://zopeconfiguration.readthedocs.io/en/latest/ for the documentation. Changes ======= 4.3.1 (2019-02-12) ------------------ - Do not break when running the tests from a wheel. 4.3.0 (2018-10-01) ------------------ - Simplify exception chaining and nested exception error messages. See `issue 43 `_. 4.2.2 (2018-09-27) ------------------ - Fix ``GlobalObject`` (and ``GlobalInterface``) no longer allowing multiple leading dots. See `issue 41 `_. - Add ``__all__`` to all modules listing the documented members of the module. Note that this is currently a broad list and may be reduced in the future. 4.2.1 (2018-09-26) ------------------ - Fix ``GlobalObject`` (and ``GlobalInterface``) no longer allowing just a single '.'. See `issue 35 `_. 4.2.0 (2018-09-26) ------------------ - Reach 100% automated test coverage. - Add support for Python 3.7. - Drop support for Python 3.3 and remove internal compatibility functions needed to support it. See `issue 20 `_ and `issue 26 `_. - Drop support for ``python setup.py test``. - Make ``zope.configuration.fields.Path`` and ``zope.configuration.config.ConfigurationContext`` expand environment variables and expand user home directories in paths. See `issue 3 `_. - Fix resolving names from a Python 2 package whose ``__init__.py`` has unicode elements in ``__all__``. - Make ``GroupingContextDecorator`` stop shadowing builtins in its ``__getattr__``. These were not intended as arguments to be used by subclasses, and the signature caused confusion. - Fix the doctests with zope.schema 4.7 and above, and run the doctests on both Python 2 and Python 3. See `issue 21 `_. - Fix ``GlobalObject`` and ``GlobalInterface`` fields to only accept dotted names instead of names with ``/``. Previously, slash delimited names could result in incorrect imports. See `issue 6 `_. - Fix the schema fields to include the ``value`` and ``field`` values on exceptions they raise. - Make ``zope.configuration.fields.PythonIdentifier`` subclass ``PythonIdentifier`` from ``zope.schema``. It now implements ``fromBytes``, always produces a native string, and validates the value in ``fromUnicode``. See `issue 28 `_. - Add ``ConfigurationMachine.pass_through_exceptions`` to allow customizing the exceptions that ``ConfigurationMachine.execute_actions`` wraps in a ``ConfigurationExecutionError``. See `issue 10 `_. - Stop catching ``BaseException`` and wrapping it in either ``ConfigurationExecutionError`` or ``ZopeXMLConfigurationError``. ``SystemExit`` and ``KeyboardInterrupt`` were always allowed to propagate; now ``GeneratorExit`` and custom subclasses of ``BaseException`` are also allowed te propagate. 4.1.0 (2017-04-26) ------------------ - Drop support for Python 2.6 and 3.2. - Add support for Python 3.5 and 3.6. - Fix the ``domain`` of MessageID fields to be a native string. Previously on Python 3 they were bytes, which meant that they couldn't be used to find translation utilities registered by zope.i18n. See `issue 17 `_. 4.0.3 (2014-03-19) ------------------ - Add explicit support for Python 3.4. 4.0.2 (2012-12-31) ------------------ - Flesh out PyPI Trove classifiers. - Remove spurious declaration of 'test' dependency on ``zope.testing``. 4.0.1 (2012-11-21) ------------------ - Add support for Python 3.3. - Remove the deprecated 'zope.configuration.stxdocs' script. and made the 'zope.configuration.tests.conditions' helper module (used in running Sphinx doctest snippets) Py3k compatible. https://bugs.launchpad.net/zope.configuration/+bug/1025390 4.0.0 (2012-05-16) ------------------ - Bring unit test coverage to 100%. - Automate build of Sphinx HTML docs and running doctest snippets via tox. - Drop hard testing dependency on ``zope.testing``. - Add explicit support for PyPy. - Add explicit support for Python 3.2. - Drop explicit support for Python 2.4 / 2.5. - Add support for continuous integration using ``tox`` and ``jenkins``. - Add ``Sphinx`` documentation. - Add ``setup.py docs`` alias (installs ``Sphinx`` and dependencies). - Add ``setup.py dev`` alias (runs ``setup.py develop`` plus installs ``nose`` and ``coverage``). 3.8.1 (2012-05-05) ------------------ - Fix Python 2.4 backwards incompat (itemgetter used with multiple args); Python 2.4 now works (at least if you use zope.schema == 3.8.1). This is the last release which will support Python 2.4 or 2.5. 3.8.0 (2011-12-06) ------------------ - Change action structures from tuples to dictionaries to allow for action structure extensibility (merged chrism-dictactions branch). 3.7.4 (2011-04-03) ------------------ - Apply 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: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.4 Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 Classifier: Programming Language :: Python :: Implementation :: CPython Classifier: Programming Language :: Python :: Implementation :: PyPy Classifier: Natural Language :: English Classifier: Operating System :: OS Independent Classifier: Topic :: Internet :: WWW/HTTP Classifier: Framework :: Zope :: 3 Provides-Extra: test Provides-Extra: docs zope.configuration-4.3.1/rtd.txt0000644000076500000240000000003413430472367016565 0ustar macstaff00000000000000repoze.sphinx.autointerface zope.configuration-4.3.1/COPYRIGHT.txt0000644000076500000240000000004013430472367017341 0ustar macstaff00000000000000Zope Foundation and Contributorszope.configuration-4.3.1/buildout.cfg0000644000076500000240000000030213430472367017541 0ustar macstaff00000000000000[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-4.3.1/MANIFEST.in0000644000076500000240000000031413430472367016772 0ustar macstaff00000000000000include *.rst include *.txt include *.py include buildout.cfg include tox.ini include .coveragerc recursive-include docs *.rst *.bat *.py Makefile recursive-include src *.zcml *.in global-exclude *.pyc zope.configuration-4.3.1/.coveragerc0000644000076500000240000000017113430472367017356 0ustar macstaff00000000000000[run] source = src [report] exclude_lines = pragma: no cover raise NotImplementedError raise AssertionError zope.configuration-4.3.1/docs/0000755000076500000240000000000013430472371016161 5ustar macstaff00000000000000zope.configuration-4.3.1/docs/index.rst0000644000076500000240000000036213430472367020030 0ustar macstaff00000000000000:mod:`zope.configuration` Documentation ======================================= Contents: .. toctree:: :maxdepth: 2 narr api hacking Indices and tables ================== * :ref:`genindex` * :ref:`modindex` * :ref:`search` zope.configuration-4.3.1/docs/Makefile0000644000076500000240000001275013430472367017633 0ustar macstaff00000000000000# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = BUILDDIR = _build # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . # the i18n builder cannot share the environment and doctrees with the others I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " singlehtml to make a single large HTML file" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " devhelp to make HTML files and a Devhelp project" @echo " epub to make an epub" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " latexpdf to make LaTeX files and run them through pdflatex" @echo " text to make text files" @echo " man to make manual pages" @echo " texinfo to make Texinfo files" @echo " info to make Texinfo files and run them through makeinfo" @echo " gettext to make PO message catalogs" @echo " changes to make an overview of all changed/added/deprecated items" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" clean: -rm -rf $(BUILDDIR)/* html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." singlehtml: $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml @echo @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/zopeconfiguration.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/zopeconfiguration.qhc" devhelp: $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @echo "To view the help file:" @echo "# mkdir -p $$HOME/.local/share/devhelp/zopeconfiguration" @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/zopeconfiguration" @echo "# devhelp" epub: $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make' in that directory to run these through (pdf)latex" \ "(use \`make latexpdf' here to do that automatically)." latexpdf: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through pdflatex..." $(MAKE) -C $(BUILDDIR)/latex all-pdf @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." text: $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text @echo @echo "Build finished. The text files are in $(BUILDDIR)/text." man: $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man @echo @echo "Build finished. The manual pages are in $(BUILDDIR)/man." texinfo: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." @echo "Run \`make' in that directory to run these through makeinfo" \ "(use \`make info' here to do that automatically)." info: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo "Running Texinfo files through makeinfo..." make -C $(BUILDDIR)/texinfo info @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." gettext: $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale @echo @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." zope.configuration-4.3.1/docs/conf.py0000644000076500000240000002035513430472367017472 0ustar macstaff00000000000000# -*- coding: utf-8 -*- # # zope.configuration documentation build configuration file, created by # sphinx-quickstart on Sat May 5 13:59:34 2012. # # This file is execfile()d with the current directory set to its containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. import sys import os import pkg_resources sys.path.append(os.path.abspath('../src')) rqmt = pkg_resources.require('zope.configuration')[0] # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. #sys.path.insert(0, os.path.abspath('.')) # -- General configuration ----------------------------------------------------- # If your documentation needs a minimal Sphinx version, state it here. #needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = [ 'sphinx.ext.autodoc', 'sphinx.ext.doctest', 'sphinx.ext.intersphinx', 'sphinx.ext.todo', 'sphinx.ext.viewcode', 'repoze.sphinx.autointerface', ] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The encoding of source files. #source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' # General information about the project. project = u'zope.configuration' copyright = u'2012, Zope Foundation Contributors' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. version = '%s.%s' % tuple(map(int, rqmt.version.split('.')[:2])) # The full version, including alpha/beta/rc tags. release = rqmt.version # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. #language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. #today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = ['_build'] # The reST default role (used for this markup: `text`) to use for all documents. default_role = 'obj' # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). #add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. #show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. #modindex_common_prefix = [] # -- Options for HTML output --------------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. html_theme = 'default' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. #html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. #html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". #html_title = None # A shorter title for the navigation bar. Default is the same as html_title. #html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. #html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. #html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. #html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. #html_use_smartypants = True # Custom sidebar templates, maps document names to template names. #html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} # If false, no module index is generated. #html_domain_indices = True # If false, no index is generated. #html_use_index = True # If true, the index is split into individual pages for each letter. #html_split_index = False # If true, links to the reST sources are added to the pages. #html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. #html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. #html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. #html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). #html_file_suffix = None # Output file base name for HTML help builder. htmlhelp_basename = 'zopeconfigurationdoc' # -- Options for LaTeX output -------------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). #'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). #'pointsize': '10pt', # Additional stuff for the LaTeX preamble. #'preamble': '', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ ('index', 'zopeconfiguration.tex', u'zope.configuration Documentation', u'Zope Foundation Contributors', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. #latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. #latex_use_parts = False # If true, show page references after internal links. #latex_show_pagerefs = False # If true, show URL addresses after external links. #latex_show_urls = False # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. #latex_domain_indices = True # -- Options for manual page output -------------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ ('index', 'zopeconfiguration', u'zope.configuration Documentation', [u'Zope Foundation Contributors'], 1) ] # If true, show URL addresses after external links. #man_show_urls = False # -- Options for Texinfo output ------------------------------------------------ # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ ('index', 'zopeconfiguration', u'zope.configuration Documentation', u'Zope Foundation Contributors', 'zopeconfiguration', 'One line description of project.', 'Miscellaneous'), ] # Documents to append as an appendix to all manuals. #texinfo_appendices = [] # If false, no module index is generated. #texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. #texinfo_show_urls = 'footnote' intersphinx_mapping = { 'https://docs.python.org/': None, 'https://zopeschema.readthedocs.io/en/latest': None, } autodoc_default_flags = [ 'members', 'show-inheritance', ] zope.configuration-4.3.1/docs/hacking.rst0000644000076500000240000002207113430472367020326 0ustar macstaff00000000000000Hacking on :mod:`zope.configuration` ==================================== Getting the Code ################ The main repository for :mod:`zope.configuration` is in the Zope Foundation Github repository: https://github.com/zopefoundation/zope.configuration You can get a read-only checkout from there: .. code-block:: sh $ git clone https://github.com/zopefoundation/zope.configuration.git or fork it and get a writeable checkout of your fork: .. code-block:: sh $ git clone git@github.com/jrandom/zope.configuration.git The project also mirrors the trunk from the Github repository as a Bazaar branch on Launchpad: https://code.launchpad.net/zope.configuration You can branch the trunk from there using Bazaar: .. code-block:: sh $ bzr branch lp:zope.configuration Working in a ``virtualenv`` ########################### Installing ---------- If you use the ``virtualenv`` package to create lightweight Python development environments, you can run the tests using nothing more than the ``python`` binary in a virtualenv. First, create a scratch environment: .. code-block:: sh $ /path/to/virtualenv --no-site-packages /tmp/hack-zope.configuration Next, get this package registered as a "development egg" in the environment: .. code-block:: sh $ /tmp/hack-zope.configuration/bin/python setup.py develop Running the tests ----------------- Run the tests using the build-in ``setuptools`` testrunner: .. code-block:: sh $ /tmp/hack-zope.configuration/bin/python setup.py test running test ........ ---------------------------------------------------------------------- Ran 249 tests in 0.366s OK If you have the :mod:`nose` package installed in the virtualenv, you can use its testrunner too: .. code-block:: sh $ /tmp/hack-zope.configuration/bin/easy_install nose ... $ /tmp/hack-zope.configuration/bin/python setup.py nosetests running nosetests ....... ---------------------------------------------------------------------- Ran 249 tests in 0.366s OK or: .. code-block:: sh $ /tmp/hack-zope.configuration/bin/nosetests ....... ---------------------------------------------------------------------- Ran 249 tests in 0.366s OK If you have the :mod:`coverage` pacakge installed in the virtualenv, you can see how well the tests cover the code: .. code-block:: sh $ /tmp/hack-zope.configuration/bin/easy_install nose coverage ... $ /tmp/hack-zope.configuration/bin/python setup.py nosetests \ --with coverage --cover-package=zope.configuration running nosetests ... Name Stmts Miss Cover Missing ---------------------------------------------------------------- zope.configuration 3 0 100% zope.configuration._compat 2 0 100% zope.configuration.config 439 0 100% zope.configuration.docutils 34 0 100% zope.configuration.exceptions 2 0 100% zope.configuration.fields 111 0 100% zope.configuration.interfaces 18 0 100% zope.configuration.name 54 0 100% zope.configuration.xmlconfig 269 0 100% zope.configuration.zopeconfigure 17 0 100% ---------------------------------------------------------------- TOTAL 955 0 100% ---------------------------------------------------------------------- Ran 256 tests in 1.063s OK Building the documentation -------------------------- :mod:`zope.configuration` uses the nifty :mod:`Sphinx` documentation system for building its docs. Using the same virtualenv you set up to run the tests, you can build the docs: .. code-block:: sh $ /tmp/hack-zope.configuration/bin/easy_install Sphinx ... $ cd docs $ PATH=/tmp/hack-zope.configuration/bin:$PATH make html sphinx-build -b html -d _build/doctrees . _build/html ... build succeeded. Build finished. The HTML pages are in _build/html. You can also test the code snippets in the documentation: .. code-block:: sh $ PATH=/tmp/hack-zope.configuration/bin:$PATH make doctest sphinx-build -b doctest -d _build/doctrees . _build/doctest ... Doctest summary =============== 554 tests 0 failures in tests 0 failures in setup code build succeeded. Testing of doctests in the sources finished, look at the \ results in _build/doctest/output.txt. Using :mod:`zc.buildout` ######################## Setting up the buildout ----------------------- :mod:`zope.configuration` ships with its own :file:`buildout.cfg` file and :file:`bootstrap.py` for setting up a development buildout: .. code-block:: sh $ /path/to/python2.6 bootstrap.py ... Generated script '.../bin/buildout' $ bin/buildout Develop: '/home/jrandom/projects/Zope/BTK/configuration/.' ... Generated script '.../bin/sphinx-quickstart'. Generated script '.../bin/sphinx-build'. Running the tests ----------------- Run the tests: .. code-block:: sh $ bin/test --all Running zope.testing.testrunner.layer.UnitTests tests: Set up zope.testing.testrunner.layer.UnitTests in 0.000 seconds. Ran 249 tests with 0 failures and 0 errors in 0.366 seconds. Tearing down left over layers: Tear down zope.testing.testrunner.layer.UnitTests in 0.000 seconds. Using :mod:`tox` ################ Running Tests on Multiple Python Versions ----------------------------------------- `tox `_ is a Python-based test automation tool designed to run tests against multiple Python versions. It creates a ``virtualenv`` for each configured version, installs the current package and configured dependencies into each ``virtualenv``, and then runs the configured commands. :mod:`zope.configuration` configures the following :mod:`tox` environments via its ``tox.ini`` file: - The ``py26``, ``py27``, ``py33``, ``py34``, and ``pypy`` environments builds a ``virtualenv`` with ``pypy``, installs :mod:`zope.configuration` and dependencies, and runs the tests via ``python setup.py test -q``. - The ``coverage`` environment builds a ``virtualenv`` with ``python2.6``, installs :mod:`zope.configuration`, installs :mod:`nose` and :mod:`coverage`, and runs ``nosetests`` with statement coverage. - The ``docs`` environment builds a virtualenv with ``python2.6``, installs :mod:`zope.configuration`, installs ``Sphinx`` and dependencies, and then builds the docs and exercises the doctest snippets. This example requires that you have a working ``python2.6`` on your path, as well as installing ``tox``: .. code-block:: sh $ tox -e py26 GLOB sdist-make: .../zope.interface/setup.py py26 sdist-reinst: .../zope.interface/.tox/dist/zope.interface-4.0.2dev.zip py26 runtests: commands[0] .......... ---------------------------------------------------------------------- Ran 249 tests in 0.366s OK ___________________________________ summary ____________________________________ py26: commands succeeded congratulations :) Running ``tox`` with no arguments runs all the configured environments, including building the docs and testing their snippets: .. code-block:: sh $ tox GLOB sdist-make: .../zope.interface/setup.py py26 sdist-reinst: .../zope.interface/.tox/dist/zope.interface-4.0.2dev.zip py26 runtests: commands[0] ... Doctest summary =============== 544 tests 0 failures in tests 0 failures in setup code 0 failures in cleanup code build succeeded. ___________________________________ summary ____________________________________ py26: commands succeeded py27: commands succeeded py32: commands succeeded pypy: commands succeeded coverage: commands succeeded docs: commands succeeded congratulations :) Contributing to :mod:`zope.configuration` ######################################### Submitting a Bug Report ----------------------- :mod:`zope.configuration` tracks its bugs on Github: https://github.com/zopefoundation/zope.configuration/issues Please submit bug reports and feature requests there. Sharing Your Changes -------------------- .. note:: Please ensure that all tests are passing before you submit your code. If possible, your submission should include new tests for new features or bug fixes, although it is possible that you may have tested your new code by updating existing tests. If have made a change you would like to share, the best route is to fork the Githb repository, check out your fork, make your changes on a branch in your fork, and push it. You can then submit a pull request from your branch: https://github.com/zopefoundation/zope.configuration/pulls If you branched the code from Launchpad using Bazaar, you have another option: you can "push" your branch to Launchpad: .. code-block:: sh $ bzr push lp:~jrandom/zope.configuration/cool_feature After pushing your branch, you can link it to a bug report on Launchpad, or request that the maintainers merge your branch using the Launchpad "merge request" feature. zope.configuration-4.3.1/docs/_static/0000755000076500000240000000000013430472371017607 5ustar macstaff00000000000000zope.configuration-4.3.1/docs/_static/.gitignore0000644000076500000240000000000013430472367021572 0ustar macstaff00000000000000zope.configuration-4.3.1/docs/make.bat0000644000076500000240000001177613430472367017607 0ustar macstaff00000000000000@ECHO OFF REM Command file for Sphinx documentation if "%SPHINXBUILD%" == "" ( set SPHINXBUILD=sphinx-build ) set BUILDDIR=_build set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . set I18NSPHINXOPTS=%SPHINXOPTS% . if NOT "%PAPER%" == "" ( set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% ) if "%1" == "" goto help if "%1" == "help" ( :help echo.Please use `make ^` where ^ is one of echo. html to make standalone HTML files echo. dirhtml to make HTML files named index.html in directories echo. singlehtml to make a single large HTML file echo. pickle to make pickle files echo. json to make JSON files echo. htmlhelp to make HTML files and a HTML help project echo. qthelp to make HTML files and a qthelp project echo. devhelp to make HTML files and a Devhelp project echo. epub to make an epub echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter echo. text to make text files echo. man to make manual pages echo. texinfo to make Texinfo files echo. gettext to make PO message catalogs echo. changes to make an overview over all changed/added/deprecated items echo. linkcheck to check all external links for integrity echo. doctest to run all doctests embedded in the documentation if enabled goto end ) if "%1" == "clean" ( for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i del /q /s %BUILDDIR%\* goto end ) if "%1" == "html" ( %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/html. goto end ) if "%1" == "dirhtml" ( %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. goto end ) if "%1" == "singlehtml" ( %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. goto end ) if "%1" == "pickle" ( %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the pickle files. goto end ) if "%1" == "json" ( %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the JSON files. goto end ) if "%1" == "htmlhelp" ( %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run HTML Help Workshop with the ^ .hhp project file in %BUILDDIR%/htmlhelp. goto end ) if "%1" == "qthelp" ( %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run "qcollectiongenerator" with the ^ .qhcp project file in %BUILDDIR%/qthelp, like this: echo.^> qcollectiongenerator %BUILDDIR%\qthelp\zopeconfiguration.qhcp echo.To view the help file: echo.^> assistant -collectionFile %BUILDDIR%\qthelp\zopeconfiguration.ghc goto end ) if "%1" == "devhelp" ( %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp if errorlevel 1 exit /b 1 echo. echo.Build finished. goto end ) if "%1" == "epub" ( %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub if errorlevel 1 exit /b 1 echo. echo.Build finished. The epub file is in %BUILDDIR%/epub. goto end ) if "%1" == "latex" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex if errorlevel 1 exit /b 1 echo. echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. goto end ) if "%1" == "text" ( %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text if errorlevel 1 exit /b 1 echo. echo.Build finished. The text files are in %BUILDDIR%/text. goto end ) if "%1" == "man" ( %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man if errorlevel 1 exit /b 1 echo. echo.Build finished. The manual pages are in %BUILDDIR%/man. goto end ) if "%1" == "texinfo" ( %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo if errorlevel 1 exit /b 1 echo. echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. goto end ) if "%1" == "gettext" ( %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale if errorlevel 1 exit /b 1 echo. echo.Build finished. The message catalogs are in %BUILDDIR%/locale. goto end ) if "%1" == "changes" ( %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes if errorlevel 1 exit /b 1 echo. echo.The overview file is in %BUILDDIR%/changes. goto end ) if "%1" == "linkcheck" ( %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck if errorlevel 1 exit /b 1 echo. echo.Link check complete; look for any errors in the above output ^ or in %BUILDDIR%/linkcheck/output.txt. goto end ) if "%1" == "doctest" ( %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest if errorlevel 1 exit /b 1 echo. echo.Testing of doctests in the sources finished, look at the ^ results in %BUILDDIR%/doctest/output.txt. goto end ) :end zope.configuration-4.3.1/docs/narr.rst0000644000076500000240000011530613430472367017670 0ustar macstaff00000000000000=========================== 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/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 for :mod:`zope.configuration.zopeconfigure`, 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 the "Creating Nested Directives" section below. - 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. .. todo:: Flesh out narrative docs. Using the configuration machinery programatically ================================================= An extended example: .. doctest:: >>> from zope.configuration.config import ConfigurationMachine >>> from zope.configuration.config import metans >>> machine = ConfigurationMachine() >>> ns = "http://www.zope.org/testing" Register some test directives: Start with a grouping directive that sets a package: .. doctest:: >>> 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: .. doctest:: >>> machine.begin((ns, "package"), ... package="zope.configuration.tests.directives", ... ) Which makes it easier to define the other directives: First, define some simple directives: .. doctest:: >>> 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: .. doctest:: >>> 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=48).pprint >>> pprint(machine.actions) [{'args': ('aa', 'xxx', 'cc'), 'callable': f, 'discriminator': ('simple', 'aa', 'xxx', 'cc'), 'includepath': (), 'info': 'first', 'kw': {}, 'order': 0}, {'args': ('naa', 'nbb', 'ncc'), 'callable': f, 'discriminator': ('newsimple', 'naa', 'nbb', 'ncc'), 'includepath': (), 'info': 'second', 'kw': {}, 'order': 0}] Define and try a simple directive that uses a component: .. doctest:: >>> machine((metans, "directive"), ... namespace=ns, name="factory", ... schema=".IFactory", handler=".factory") >>> machine((ns, "factory"), factory=u".f") >>> pprint(machine.actions[-1:]) [{'args': (), 'callable': f, 'discriminator': ('factory', 1, 2), 'includepath': (), 'info': None, 'kw': {}, 'order': 0}] Define and try a complex directive: .. doctest:: >>> 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: .. doctest:: >>> machine((ns, "factory2"), factory=".f") Traceback (most recent call last): ... ConfigurationError: ('Invalid directive', 'factory2') >>> machine.end() >>> pprint(machine.actions) [{'args': ('aa', 'xxx', 'cc'), 'callable': f, 'discriminator': ('simple', 'aa', 'xxx', 'cc'), 'includepath': (), 'info': 'first', 'kw': {}, 'order': 0}, {'args': ('naa', 'nbb', 'ncc'), 'callable': f, 'discriminator': ('newsimple', 'naa', 'nbb', 'ncc'), 'includepath': (), 'info': 'second', 'kw': {}, 'order': 0}, {'args': (), 'callable': f, 'discriminator': ('factory', 1, 2), 'includepath': (), 'info': None, 'kw': {}, 'order': 0}, {'args': (), 'callable': None, 'discriminator': 'Complex.__init__', 'includepath': (), 'info': 'third', 'kw': {}, 'order': 0}, {'args': ('ca',), 'callable': f, 'discriminator': ('Complex.factory', 1, 2), 'includepath': (), 'info': 'fourth', 'kw': {}, 'order': 0}, {'args': ('xxx', 'cc'), 'callable': f, 'discriminator': ('Complex', 1, 2), 'includepath': (), 'info': 'third', 'kw': {}, 'order': 0}] Done with the package .. doctest:: >>> machine.end() Verify that we can use a simple directive outside of the package: .. doctest:: >>> 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: .. doctest:: >>> 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) [{'args': ('aa', 'xxx', 'cc'), 'callable': f, 'discriminator': ('simple', 'aa', 'xxx', 'cc'), 'includepath': (), 'info': 'first', 'kw': {}, 'order': 0}, {'args': ('naa', 'nbb', 'ncc'), 'callable': f, 'discriminator': ('newsimple', 'naa', 'nbb', 'ncc'), 'includepath': (), 'info': 'second', 'kw': {}, 'order': 0}, {'args': (), 'callable': f, 'discriminator': ('factory', 1, 2), 'includepath': (), 'info': None, 'kw': {}, 'order': 0}, {'args': (), 'callable': None, 'discriminator': 'Complex.__init__', 'includepath': (), 'info': 'third', 'kw': {}, 'order': 0}, {'args': ('ca',), 'callable': f, 'discriminator': ('Complex.factory', 1, 2), 'includepath': (), 'info': 'fourth', 'kw': {}, 'order': 0}, {'args': ('xxx', 'cc'), 'callable': f, 'discriminator': ('Complex', 1, 2), 'includepath': (), 'info': 'third', 'kw': {}, 'order': 0}, {'args': ('oaa', 'obb', 'occ'), 'callable': f, 'discriminator': ('simple', 'oaa', 'obb', 'occ'), 'includepath': (), 'info': None, 'kw': {}, 'order': 0}] Overriding Included Configuration ================================= 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`` (in ``zope/configuration/tests/samplepackage``): - It includes ``bar1.zcml`` and ``bar2.zcml``. - ``bar1.zcml`` includes ``configure.zcml`` and has a ``foo`` directive. - ``bar2.zcml`` includes ``bar21.zcml``, and 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``. .. doctest:: >>> import os >>> from zope.configuration.config import ConfigurationMachine >>> from zope.configuration.xmlconfig import include >>> from zope.configuration.xmlconfig import registerCommonDirectives >>> context = ConfigurationMachine() >>> registerCommonDirectives(context) >>> from zope.configuration.tests import __file__ >>> here = os.path.dirname(__file__) >>> path = os.path.join(here, "samplepackage", "bar.zcml") >>> include(context, path) So far so good, let's look at the configuration actions: .. doctest:: >>> from zope.configuration.tests.test_xmlconfig import clean_actions >>> pprint = PrettyPrinter(width=73).pprint >>> pprint(clean_actions(context.actions)) [{'discriminator': (('x', b'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', b'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', b'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', b'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', b'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', b'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: .. doctest:: >>> from zope.configuration.config import ConfigurationConflictError >>> from zope.configuration.tests.test_xmlconfig import clean_text_w_paths >>> try: ... v = context.execute_actions() ... except ConfigurationConflictError as e: ... v = e >>> print(clean_text_w_paths(str(v))) Conflicting configuration actions For: (('x', b'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', b'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. .. doctest:: >>> context = ConfigurationMachine() >>> registerCommonDirectives(context) >>> path = os.path.join(here, "samplepackage", "baro.zcml") >>> include(context, path) Now, if we look at the actions: .. doctest:: >>> pprint(clean_actions(context.actions)) [{'discriminator': (('x', b'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', b'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', b'blah'), ('y', 0)), 'includepath': ['tests/samplepackage/baro.zcml'], 'info': 'File "tests/samplepackage/bar21.zcml", line 3.2-3.24'}, {'discriminator': (('x', b'blah'), ('y', 2)), 'includepath': ['tests/samplepackage/baro.zcml'], 'info': 'File "tests/samplepackage/bar2.zcml", line 5.2-5.24'}, {'discriminator': (('x', b'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: .. doctest:: >>> context.execute_actions() We should now have three entries in foo.data: .. doctest:: >>> from zope.configuration.tests.samplepackage import foo >>> from zope.configuration.tests.test_xmlconfig import clean_info_path >>> len(foo.data) 3 >>> data = foo.data.pop(0) >>> data.args (('x', b'blah'), ('y', 0)) >>> print(clean_info_path(repr(data.info))) File "tests/samplepackage/bar21.zcml", line 3.2-3.24 >>> data = foo.data.pop(0) >>> data.args (('x', b'blah'), ('y', 2)) >>> print(clean_info_path(repr(data.info))) File "tests/samplepackage/bar2.zcml", line 5.2-5.24 >>> data = foo.data.pop(0) >>> data.args (('x', b'blah'), ('y', 1)) >>> print(clean_info_path(repr(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: .. doctest:: >>> context = ConfigurationMachine() >>> registerCommonDirectives(context) >>> path = os.path.join(here, "samplepackage", "baro2.zcml") >>> include(context, path) Actions look like above: .. doctest:: >>> pprint(clean_actions(context.actions)) [{'discriminator': (('x', b'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', b'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', b'blah'), ('y', 0)), 'includepath': ['tests/samplepackage/baro2.zcml'], 'info': 'File "tests/samplepackage/bar21.zcml", line 3.2-3.24'}, {'discriminator': (('x', b'blah'), ('y', 2)), 'includepath': ['tests/samplepackage/baro2.zcml'], 'info': 'File "tests/samplepackage/bar2.zcml", line 5.2-5.24'}, {'discriminator': (('x', b'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[:] Making specific directives conditional ====================================== 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 evaluates to true, processing continues normally; otherwise that element and its descendents are ignored. The expression is always of the form "*verb* *arguments*". There are four verbs that are supported: - have - not-have - installed - not-installed Note that logical "if/else" conditions can be achieved by using both the positive verb and its not-prefixed counterpart on sibling elements with the same arguments. Logical "and" conditions can be achieved by nesting elements. To group arbitrary directives under a condition, nest them under a new ```` element. .. seealso:: :meth:`zope.configuration.xmlconfig.ConfigurationHandler.evaluateCondition` For documentation and examples of the XML implementation of conditions. Features -------- The verbs ``have`` and ``not-have`` test for the presence or absence of features in the configuration context. The argument is a single feature name. Features can be established in Python code using :meth:`~zope.configuration.config.ConfigurationContext.provideFeature`, or they can be set during ZCML processing with the ``meta:provides`` directive (e.g., ````.) A popular use of features is to enable certain configuration only during development with a feature called ``devmode``. (This also demonstrates applying a condition to a single directive.) .. code-block:: xml Module Availability ------------------- The verbs ``installed`` and ``not-installed`` test whether a Python module can be imported successfully or not. They can be used to enable optional configuration if a package is present, or enable fallback functionality if it isn't. (This also demonstrates grouping directives under a new ```` element, and establishing a logical "if/else".) .. code-block:: xml Example ------- 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``. .. doctest:: >>> from zope.configuration.tests.conditions import registry >>> registry [] We start by loading the example ZCML file, *conditions.zcml*: .. literalinclude:: ../src/zope/configuration/tests/conditions.zcml :language: xml .. doctest:: >>> import zope.configuration.tests >>> from zope.configuration.xmlconfig import file >>> context = file("conditions.zcml", zope.configuration.tests) To show that our sample directive works, we see that the unqualified registration was successful: .. doctest:: >>> "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: .. doctest:: >>> "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: .. doctest:: >>> "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: .. doctest:: >>> "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: .. doctest:: >>> "true.condition.nested.in.false" in registry False >>> "false.condition.nested.in.false" in registry False .. testcleanup:: del registry[:] Filtering 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: .. doctest:: >>> import logging >>> import logging.handlers >>> import sys >>> logger = logging.getLogger('config') >>> oldlevel = logger.level >>> logger.setLevel(logging.DEBUG) >>> handler = logging.handlers.MemoryHandler(10) >>> logger.addHandler(handler) Now, we'll include the zope.configuration.tests.excludedemo config: .. doctest:: >>> from zope.configuration.xmlconfig import string >>> _ = string('') >>> len(handler.buffer) 3 >>> logged = [x.getMessage() for x in handler.buffer] >>> logged[0].startswith('include ') True >>> logged[0].endswith('zope/configuration/tests/excludedemo/configure.zcml') True >>> logged[1].startswith('include ') True >>> logged[1].endswith('zope/configuration/tests/excludedemo/sub/configure.zcml') True >>> logged[2].startswith('include ') True >>> logged[2].endswith('zope/configuration/tests/excludedemo/spam.zcml') True >>> del handler.buffer[:] Each run of the configuration machinery runs with fresh state, so rerunning gives the same thing: .. doctest:: >>> _ = string('') >>> len(handler.buffer) 3 >>> logged = [x.getMessage() for x in handler.buffer] >>> logged[0].startswith('include ') True >>> logged[0].endswith('zope/configuration/tests/excludedemo/configure.zcml') True >>> logged[1].startswith('include ') True >>> logged[1].endswith('zope/configuration/tests/excludedemo/sub/configure.zcml') True >>> logged[2].startswith('include ') True >>> logged[2].endswith('zope/configuration/tests/excludedemo/spam.zcml') True >>> del handler.buffer[:] Now, we'll use the exclude directive to exclude the two files included by the configuration file in zope.configuration.tests.excludedemo: .. doctest:: >>> _ = string( ... ''' ... ... ... ... ... ... ''') >>> len(handler.buffer) 1 >>> logged = [x.getMessage() for x in handler.buffer] >>> logged[0].startswith('include ') True >>> logged[0].endswith('zope/configuration/tests/excludedemo/configure.zcml') True .. testcleanup:: logger.setLevel(oldlevel) logger.removeHandler(handler) Creating simple directives ========================== A simple directive is a directive that doesn't contain other directives. It can be implemented via a fairly simple function. To implement a simple directive, you need to do 3 things: - You need to create a schema to describe the directive parameters, - You need to write a directive handler, and - You need to register the directive. In this example, we'll implement a contrived example that records information about files in a file registry. The file registry is just the list, ``file_registry``. .. doctest:: >>> from zope.configuration.tests.simple import file_registry Our registry will contain tuples with: - file path - file title - description - Information about where the file was defined Our schema is defined in ``zope.configuration.tests.simple.IRegisterFile`` (q.v). .. doctest:: >>> from zope.configuration.tests.simple import IRegisterFile Our schema lists the ``path`` and ``title`` attributes. We'll get the description and other information for free, as we'll see later. The title is not required, and may be omitted. The job of a configuration handler is to compute one or more configuration actions. Configuration actions are defered function calls. The handler doesn't perform the actions. It just computes actions, which may be performed later if they are not overridden by other directives. Our handler is given in the function, ``zope.configuration.tests.simple.registerFile``. .. doctest:: >>> from zope.configuration.tests.simple import registerFile It takes a context, a path and a title. All directive handlers take the directive context as the first argument. A directive context, at a minimim, implements, ``zope.configuration.IConfigurationContext``. (Specialized contexts can implement more specific interfaces. We'll say more about that when we talk about grouping directives.) The title argument must have a default value, because we indicated that the title was not required in the schema. (Alternatively, we could have made the title required, but provided a default value in the schema. In the first line of function ``registerFile``, we get the context information object. This object contains information about the configuration directive, such as the file and location within the file of the directive. The context information object also has a text attribute that contains the textual data contained by the configuration directive. (This is the concatenation of all of the xml text nodes directly contained by the directive.) We use this for our description in the second line of the handler. The last thing the handler does is to compute an action by calling the action method of the context. It passes the action method 3 keyword arguments: - discriminator The discriminator is used to identify the action to be performed so that duplicate actions can be detected. Two actions are duplicated, and this conflict, if they have the same discriminator values and the values are not ``None``. Conflicting actions can be resolved if one of the conflicting actions is from a configuration file that directly or indirectly includes the files containing the other conflicting actions. In function ``registerFile``, we a tuple with the string ``'RegisterFile'`` and the path to be registered. - callable The callable is the object to be called to perform the action. - args The args argument contains positinal arguments to be passed to the callable. In function ``registerFile``, we pass a tuple containing a ``FileInfo`` object. (Note that there's nothing special about the FileInfo class. It has nothing to do with creating simple directives. It's just used in this example to organize the application data.) The final step in implementing the simple directive is to register it. We do that with the zcml ``meta:directive`` directive. This is given in the file simple.zcml. Here we specify the name, namespace, schema, and handler for the directive. We also provide a documentation for the directive as text between the start and end tags. The file simple.zcml also includes some directives that use the new directive to register some files. Now let's try it all out: .. doctest:: >>> from zope.configuration import tests >>> from zope.configuration.xmlconfig import file >>> context = file("simple.zcml", tests) Now we should see some file information in the registry: .. doctest:: >>> from zope.configuration.tests.test_xmlconfig import clean_text_w_paths >>> from zope.configuration.tests.test_xmlconfig import clean_path >>> print(clean_path(file_registry[0].path)) tests/simple.py >>> print(file_registry[0].title) How to create a simple directive >>> print(file_registry[0].description) Describes how to implement a simple directive >>> print(clean_text_w_paths(file_registry[0].info)) File "tests/simple.zcml", line 19.2-24.2 Describes how to implement a simple directive >>> print(clean_path(file_registry[1].path)) tests/simple.zcml >>> print(file_registry[1].title) >>> desc = file_registry[1].description >>> print('\n'.join([l.rstrip() ... for l in desc.strip().splitlines() ... if l.rstrip()])) Shows the ZCML directives needed to register a simple directive. Also show some usage examples, >>> print(clean_text_w_paths(file_registry[1].info)) File "tests/simple.zcml", line 26.2-30.2 Shows the ZCML directives needed to register a simple directive. Also show some usage examples, >>> print(clean_path(file_registry[2].path)) tests/__init__.py >>> print(file_registry[2].title) Make this a package >>> print(file_registry[2].description) >>> print(clean_text_w_paths(file_registry[2].info)) File "tests/simple.zcml", line 32.2-32.67 Clean up after ourselves: .. doctest:: >>> del file_registry[:] Creating nested directives ========================== When using ZCML, you sometimes nest ZCML directives. This is typically done either to: - Avoid repetative input. Information shared among multiple directives is provided in a surrounding directive. - Put together information that is too complex or structured to express with a single set of directive parameters. Grouping directives are used to handle both of these cases. See the documentation in :mod:`zope.configuration.zopeconfigure`. This file describes the implementation of the zope ``configure`` directive, which groups directives that use a common package or internationalization domain. You should also have read the section on "Creating simple directives." This file shows you how to handle the second case above. In this case, we have grouping directives that are meant to collaborate with specific contained directives. To do this, you have the grouping directives declare a more specific (or alternate) interface to ``IConfigurationContext``. Directives designed to work with those grouping directives are registered for the new interface. Let's look at example. Suppose we wanted to be able to define schema using ZCML. We'd use a grouping directive to specify schemas and contained directives to specify fields within the schema. We'll use a schema registry to hold the defined schemas:: .. doctest:: >>> from zope.configuration.tests.nested import schema_registry A schema has a name, an id, some documentation, and some fields. We'll provide the name and the id as parameters. We'll define fields as subdirectives and documentation as text contained in the schema directive. The schema directive uses the schema, ``ISchemaInfo`` for it's parameters. .. doctest:: >>> from zope.configuration.tests.nested import ISchemaInfo We also define the schema, ISchema, that specifies an attribute that nested field directives will use to store the fields they define. .. doctest:: >>> from zope.configuration.tests.nested import ISchema The class, ``Schema``, provides the handler for the schema directive. (If you haven't read the documentation in ``zopeconfigure.py``, you need to do so now.) The constructor saves its arguments as attributes and initializes its ``fields`` attribute: .. doctest:: >>> from zope.configuration.tests.nested import Schema The ``after`` method of the ``Schema`` class creates a schema and computes an action to register the schema in the schema registry. The discriminator prevents two schema directives from registering the same schema. It's important to note that when we call the ``action`` method on ``self``, rather than on ``self.context``. This is because, in a grouping directive handler, the handler instance is itself a context. When we call the ``action`` method, the method stores additional meta data associated with the context it was called on. This meta data includes an include path, used when resolving conflicting actions, and an object that contains information about the XML source used to invole the directive. If we called the action method on ``self.context``, the wrong meta data would be associated with the configuration action. The file ``schema.zcml`` contains the meta-configuration directive that defines the schema directive. To define fields, we'll create directives to define the fields. Let's start with a ``text`` field. ``ITextField`` defines the schema for text field parameters. It extends ``IFieldInfo``, which defines data common to all fields. We also define a simple handler method, textField, that takes a context and keyword arguments. (For information on writing simple directives, see ``test_simple.py``.) We've abstracted most of the logic into the function ``field``. The ``field`` function computes a field instance using the constructor, and the keyword arguments passed to it. It also uses the context information object to get the text content of the directive, which it uses for the field description. After computing the field instance, it gets the ``Schema`` instance, which is the context of the context passed to the function. The function checks to see if there is already a field with that name. If there is, it raises an error. Otherwise, it saves the field. We also define an ``IIntInfo`` schema and ``intField`` handler function to support defining integer fields. We register the ``text`` and ``int`` directives in ``schema.zcml``. These are like the simple directive definition we saw in ``test_simple.py`` with an important exception. We provide a ``usedIn`` parameter to say that these directives can *only* ne used in a ``ISchema`` context. In other words, these can only be used inside of ``schema`` directives. The ``schema.zcml`` file also contains some sample ``schema`` directives. We can execute the file: .. doctest:: >>> from zope.configuration import tests >>> from zope.configuration.xmlconfig import file >>> context = file("schema.zcml", tests) And verify that the schema registery has the schemas we expect: .. doctest:: >>> pprint(sorted(schema_registry)) ['zope.configuration.tests.nested.I1', 'zope.configuration.tests.nested.I2'] >>> def sorted(x): ... r = list(x) ... r.sort() ... return r >>> i1 = schema_registry['zope.configuration.tests.nested.I1'] >>> sorted(i1) ['a', 'b'] >>> i1['a'].__class__.__name__ 'Text' >>> i1['a'].description.strip() 'A\n\n Blah blah' >>> i1['a'].min_length 1 >>> i1['b'].__class__.__name__ 'Int' >>> i1['b'].description.strip() 'B\n\n Not feeling very creative' >>> i1['b'].min 1 >>> i1['b'].max 10 >>> i2 = schema_registry['zope.configuration.tests.nested.I2'] >>> sorted(i2) ['x', 'y'] Now let's look at some error situations. For example, let's see what happens if we use a field directive outside of a schema dorective. (Note that we used the context we created above, so we don't have to redefine our directives: .. doctest:: >>> from zope.configuration.xmlconfig import string >>> from zope.configuration.exceptions import ConfigurationError >>> v = string( ... '', ... context) Traceback (most recent call last): ... zope.configuration.exceptions.ConfigurationError: The directive ('http://sample.namespaces.zope.org/schema', 'text') cannot be used in this context File "", line 1.0 Let's see what happens if we declare duplicate fields: .. doctest:: >>> try: ... v = string( ... ''' ... ... ... ... ... ''', ... context) ... except ConfigurationError as e: ... v = e >>> print(v) File "", line 5.7-5.24 ValueError: ('Duplicate field', 'x') zope.configuration-4.3.1/docs/api/0000755000076500000240000000000013430472371016732 5ustar macstaff00000000000000zope.configuration-4.3.1/docs/api/fields.rst0000644000076500000240000000017613430472367020743 0ustar macstaff00000000000000=========================== zope.configuration.fields =========================== .. automodule:: zope.configuration.fields zope.configuration-4.3.1/docs/api/config.rst0000644000076500000240000000017613430472367020742 0ustar macstaff00000000000000=========================== zope.configuration.config =========================== .. automodule:: zope.configuration.config zope.configuration-4.3.1/docs/api/xmlconfig.rst0000644000076500000240000000021213430472367021452 0ustar macstaff00000000000000============================== zope.configuration.xmlconfig ============================== .. automodule:: zope.configuration.xmlconfig zope.configuration-4.3.1/docs/api/docutils.rst0000644000076500000240000000020613430472367021315 0ustar macstaff00000000000000============================= zope.configuration.docutils ============================= .. automodule:: zope.configuration.docutils zope.configuration-4.3.1/docs/api/zopeconfigure.rst0000644000076500000240000000023213430472367022345 0ustar macstaff00000000000000================================== zope.configuration.zopeconfigure ================================== .. automodule:: zope.configuration.zopeconfigure zope.configuration-4.3.1/docs/api/exceptions.rst0000644000076500000240000000021613430472367021651 0ustar macstaff00000000000000=============================== zope.configuration.exceptions =============================== .. automodule:: zope.configuration.exceptions zope.configuration-4.3.1/docs/api/interfaces.rst0000644000076500000240000000023413430472367021613 0ustar macstaff00000000000000====================================== zope.configuration.interfaces ====================================== .. automodule:: zope.configuration.interfaces zope.configuration-4.3.1/docs/api/name.rst0000644000076500000240000000016613430472367020414 0ustar macstaff00000000000000========================= zope.configuration.name ========================= .. automodule:: zope.configuration.name zope.configuration-4.3.1/docs/api.rst0000644000076500000240000000036213430472367017472 0ustar macstaff00000000000000:mod:`zope.configuration` API Reference ======================================= .. toctree:: :maxdepth: 2 api/config api/docutils api/exceptions api/fields api/interfaces api/name api/xmlconfig api/zopeconfigure zope.configuration-4.3.1/setup.py0000644000076500000240000000627113430472367016756 0ustar macstaff00000000000000############################################################################## # # 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): with open(os.path.join(os.path.dirname(__file__), *rnames)) as f: return f.read() TESTS_REQUIRE = [ 'manuel', # We test the specific exceptions raised, which # change from version to version, and we need the behaviour # in DottedName that allows underscores. 'zope.schema >= 4.9.0', 'zope.testing', 'zope.testrunner', ] setup(name='zope.configuration', version='4.3.1', author='Zope Foundation and Contributors', author_email='zope-dev@zope.org', description='Zope Configuration Markup Language (ZCML)', long_description=( read('README.rst') + '\n\n' + read('CHANGES.rst') ), 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', "Programming Language :: Python :: 2", 'Programming Language :: Python :: 2.7', "Programming Language :: Python :: 3", 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: Implementation :: CPython', 'Programming Language :: Python :: Implementation :: PyPy', 'Natural Language :: English', 'Operating System :: OS Independent', 'Topic :: Internet :: WWW/HTTP', 'Framework :: Zope :: 3'], url='https://github.com/zopefoundation/zope.configuration', license='ZPL 2.1', packages=find_packages('src'), package_dir={'': 'src'}, namespace_packages=['zope'], extras_require={ 'docs': ['Sphinx', 'repoze.sphinx.autointerface'], 'test': TESTS_REQUIRE, }, install_requires=[ 'setuptools', 'zope.i18nmessageid', 'zope.interface', 'zope.schema >= 4.9', ], include_package_data=True, zip_safe=False, tests_require=TESTS_REQUIRE, ) zope.configuration-4.3.1/.gitignore0000644000076500000240000000030013430472367017217 0ustar macstaff00000000000000*.pyc *.swp __pycache__ ./.installed.cfg ./bin ./develop-eggs ./eggs ./parts *.egg-info docs/_build .tox .coverage nosetests.xml src/coverage.xml .installed.cfg bin develop-eggs eggs htmlcov/ zope.configuration-4.3.1/tox.ini0000644000076500000240000000123513430472367016552 0ustar macstaff00000000000000[tox] envlist = py27,py34,py35,py36,py37,pypy,pypy3,coverage,docs [testenv] deps = .[test] commands = zope-testrunner --test-path=src --all [] [testenv:coverage] usedevelop = true basepython = python3.6 commands = coverage run -m zope.testrunner --test-path=src --all coverage report --show-missing --skip-covered --fail-under=100 deps = {[testenv]deps} coverage [testenv:docs] basepython = python3.6 commands = sphinx-build -b html -d docs/_build/doctrees docs docs/_build/html sphinx-build -b doctest -d docs/_build/doctrees docs docs/_build/doctest deps = {[testenv]deps} Sphinx repoze.sphinx.autointerface zope.configuration-4.3.1/setup.cfg0000644000076500000240000000050313430472371017050 0ustar macstaff00000000000000[nosetests] nocapture = 1 cover-package = zope.configuration cover-erase = 1 with-doctest = 0 where = src ignore-files = (bad|victim|notyet).py [aliases] dev = develop easy_install zope.configuration[testing] docs = easy_install zope.configuration[docs] [bdist_wheel] universal = 1 [egg_info] tag_build = tag_date = 0 zope.configuration-4.3.1/README.rst0000644000076500000240000000175313430472367016733 0ustar macstaff00000000000000``zope.configuration`` ====================== .. image:: https://img.shields.io/pypi/v/zope.configuration.svg :target: https://pypi.python.org/pypi/zope.configuration/ :alt: Latest Version .. image:: https://travis-ci.org/zopefoundation/zope.configuration.svg?branch=master :target: https://travis-ci.org/zopefoundation/zope.configuration .. image:: https://readthedocs.org/projects/zopeconfiguration/badge/?version=latest :target: http://zopeconfiguration.readthedocs.io/en/latest/ :alt: Documentation Status 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. Please see http://zopeconfiguration.readthedocs.io/en/latest/ for the documentation. zope.configuration-4.3.1/LICENSE.txt0000644000076500000240000000402613430472367017063 0ustar macstaff00000000000000Zope 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-4.3.1/CHANGES.rst0000644000076500000240000001723413430472367017047 0ustar macstaff00000000000000Changes ======= 4.3.1 (2019-02-12) ------------------ - Do not break when running the tests from a wheel. 4.3.0 (2018-10-01) ------------------ - Simplify exception chaining and nested exception error messages. See `issue 43 `_. 4.2.2 (2018-09-27) ------------------ - Fix ``GlobalObject`` (and ``GlobalInterface``) no longer allowing multiple leading dots. See `issue 41 `_. - Add ``__all__`` to all modules listing the documented members of the module. Note that this is currently a broad list and may be reduced in the future. 4.2.1 (2018-09-26) ------------------ - Fix ``GlobalObject`` (and ``GlobalInterface``) no longer allowing just a single '.'. See `issue 35 `_. 4.2.0 (2018-09-26) ------------------ - Reach 100% automated test coverage. - Add support for Python 3.7. - Drop support for Python 3.3 and remove internal compatibility functions needed to support it. See `issue 20 `_ and `issue 26 `_. - Drop support for ``python setup.py test``. - Make ``zope.configuration.fields.Path`` and ``zope.configuration.config.ConfigurationContext`` expand environment variables and expand user home directories in paths. See `issue 3 `_. - Fix resolving names from a Python 2 package whose ``__init__.py`` has unicode elements in ``__all__``. - Make ``GroupingContextDecorator`` stop shadowing builtins in its ``__getattr__``. These were not intended as arguments to be used by subclasses, and the signature caused confusion. - Fix the doctests with zope.schema 4.7 and above, and run the doctests on both Python 2 and Python 3. See `issue 21 `_. - Fix ``GlobalObject`` and ``GlobalInterface`` fields to only accept dotted names instead of names with ``/``. Previously, slash delimited names could result in incorrect imports. See `issue 6 `_. - Fix the schema fields to include the ``value`` and ``field`` values on exceptions they raise. - Make ``zope.configuration.fields.PythonIdentifier`` subclass ``PythonIdentifier`` from ``zope.schema``. It now implements ``fromBytes``, always produces a native string, and validates the value in ``fromUnicode``. See `issue 28 `_. - Add ``ConfigurationMachine.pass_through_exceptions`` to allow customizing the exceptions that ``ConfigurationMachine.execute_actions`` wraps in a ``ConfigurationExecutionError``. See `issue 10 `_. - Stop catching ``BaseException`` and wrapping it in either ``ConfigurationExecutionError`` or ``ZopeXMLConfigurationError``. ``SystemExit`` and ``KeyboardInterrupt`` were always allowed to propagate; now ``GeneratorExit`` and custom subclasses of ``BaseException`` are also allowed te propagate. 4.1.0 (2017-04-26) ------------------ - Drop support for Python 2.6 and 3.2. - Add support for Python 3.5 and 3.6. - Fix the ``domain`` of MessageID fields to be a native string. Previously on Python 3 they were bytes, which meant that they couldn't be used to find translation utilities registered by zope.i18n. See `issue 17 `_. 4.0.3 (2014-03-19) ------------------ - Add explicit support for Python 3.4. 4.0.2 (2012-12-31) ------------------ - Flesh out PyPI Trove classifiers. - Remove spurious declaration of 'test' dependency on ``zope.testing``. 4.0.1 (2012-11-21) ------------------ - Add support for Python 3.3. - Remove the deprecated 'zope.configuration.stxdocs' script. and made the 'zope.configuration.tests.conditions' helper module (used in running Sphinx doctest snippets) Py3k compatible. https://bugs.launchpad.net/zope.configuration/+bug/1025390 4.0.0 (2012-05-16) ------------------ - Bring unit test coverage to 100%. - Automate build of Sphinx HTML docs and running doctest snippets via tox. - Drop hard testing dependency on ``zope.testing``. - Add explicit support for PyPy. - Add explicit support for Python 3.2. - Drop explicit support for Python 2.4 / 2.5. - Add support for continuous integration using ``tox`` and ``jenkins``. - Add ``Sphinx`` documentation. - Add ``setup.py docs`` alias (installs ``Sphinx`` and dependencies). - Add ``setup.py dev`` alias (runs ``setup.py develop`` plus installs ``nose`` and ``coverage``). 3.8.1 (2012-05-05) ------------------ - Fix Python 2.4 backwards incompat (itemgetter used with multiple args); Python 2.4 now works (at least if you use zope.schema == 3.8.1). This is the last release which will support Python 2.4 or 2.5. 3.8.0 (2011-12-06) ------------------ - Change action structures from tuples to dictionaries to allow for action structure extensibility (merged chrism-dictactions branch). 3.7.4 (2011-04-03) ------------------ - Apply 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-4.3.1/.travis.yml0000644000076500000240000000076213430472367017354 0ustar macstaff00000000000000language: python sudo: false python: - 2.7 - 3.4 - 3.5 - 3.6 - pypy - pypy3 matrix: include: - python: "3.7" dist: xenial sudo: true script: - coverage run -m zope.testrunner --test-path=src --auto-color --auto-progress after_success: - coveralls notifications: email: false install: - pip install -U pip setuptools - pip install -U coveralls coverage - pip install -U -e ".[test]" cache: pip before_cache: - rm -f $HOME/.cache/pip/log/debug.log zope.configuration-4.3.1/src/0000755000076500000240000000000013430472371016020 5ustar macstaff00000000000000zope.configuration-4.3.1/src/zope.configuration.egg-info/0000755000076500000240000000000013430472371023335 5ustar macstaff00000000000000zope.configuration-4.3.1/src/zope.configuration.egg-info/PKG-INFO0000644000076500000240000003005013430472370024427 0ustar macstaff00000000000000Metadata-Version: 2.1 Name: zope.configuration Version: 4.3.1 Summary: Zope Configuration Markup Language (ZCML) Home-page: https://github.com/zopefoundation/zope.configuration Author: Zope Foundation and Contributors Author-email: zope-dev@zope.org License: ZPL 2.1 Description: ``zope.configuration`` ====================== .. image:: https://img.shields.io/pypi/v/zope.configuration.svg :target: https://pypi.python.org/pypi/zope.configuration/ :alt: Latest Version .. image:: https://travis-ci.org/zopefoundation/zope.configuration.svg?branch=master :target: https://travis-ci.org/zopefoundation/zope.configuration .. image:: https://readthedocs.org/projects/zopeconfiguration/badge/?version=latest :target: http://zopeconfiguration.readthedocs.io/en/latest/ :alt: Documentation Status 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. Please see http://zopeconfiguration.readthedocs.io/en/latest/ for the documentation. Changes ======= 4.3.1 (2019-02-12) ------------------ - Do not break when running the tests from a wheel. 4.3.0 (2018-10-01) ------------------ - Simplify exception chaining and nested exception error messages. See `issue 43 `_. 4.2.2 (2018-09-27) ------------------ - Fix ``GlobalObject`` (and ``GlobalInterface``) no longer allowing multiple leading dots. See `issue 41 `_. - Add ``__all__`` to all modules listing the documented members of the module. Note that this is currently a broad list and may be reduced in the future. 4.2.1 (2018-09-26) ------------------ - Fix ``GlobalObject`` (and ``GlobalInterface``) no longer allowing just a single '.'. See `issue 35 `_. 4.2.0 (2018-09-26) ------------------ - Reach 100% automated test coverage. - Add support for Python 3.7. - Drop support for Python 3.3 and remove internal compatibility functions needed to support it. See `issue 20 `_ and `issue 26 `_. - Drop support for ``python setup.py test``. - Make ``zope.configuration.fields.Path`` and ``zope.configuration.config.ConfigurationContext`` expand environment variables and expand user home directories in paths. See `issue 3 `_. - Fix resolving names from a Python 2 package whose ``__init__.py`` has unicode elements in ``__all__``. - Make ``GroupingContextDecorator`` stop shadowing builtins in its ``__getattr__``. These were not intended as arguments to be used by subclasses, and the signature caused confusion. - Fix the doctests with zope.schema 4.7 and above, and run the doctests on both Python 2 and Python 3. See `issue 21 `_. - Fix ``GlobalObject`` and ``GlobalInterface`` fields to only accept dotted names instead of names with ``/``. Previously, slash delimited names could result in incorrect imports. See `issue 6 `_. - Fix the schema fields to include the ``value`` and ``field`` values on exceptions they raise. - Make ``zope.configuration.fields.PythonIdentifier`` subclass ``PythonIdentifier`` from ``zope.schema``. It now implements ``fromBytes``, always produces a native string, and validates the value in ``fromUnicode``. See `issue 28 `_. - Add ``ConfigurationMachine.pass_through_exceptions`` to allow customizing the exceptions that ``ConfigurationMachine.execute_actions`` wraps in a ``ConfigurationExecutionError``. See `issue 10 `_. - Stop catching ``BaseException`` and wrapping it in either ``ConfigurationExecutionError`` or ``ZopeXMLConfigurationError``. ``SystemExit`` and ``KeyboardInterrupt`` were always allowed to propagate; now ``GeneratorExit`` and custom subclasses of ``BaseException`` are also allowed te propagate. 4.1.0 (2017-04-26) ------------------ - Drop support for Python 2.6 and 3.2. - Add support for Python 3.5 and 3.6. - Fix the ``domain`` of MessageID fields to be a native string. Previously on Python 3 they were bytes, which meant that they couldn't be used to find translation utilities registered by zope.i18n. See `issue 17 `_. 4.0.3 (2014-03-19) ------------------ - Add explicit support for Python 3.4. 4.0.2 (2012-12-31) ------------------ - Flesh out PyPI Trove classifiers. - Remove spurious declaration of 'test' dependency on ``zope.testing``. 4.0.1 (2012-11-21) ------------------ - Add support for Python 3.3. - Remove the deprecated 'zope.configuration.stxdocs' script. and made the 'zope.configuration.tests.conditions' helper module (used in running Sphinx doctest snippets) Py3k compatible. https://bugs.launchpad.net/zope.configuration/+bug/1025390 4.0.0 (2012-05-16) ------------------ - Bring unit test coverage to 100%. - Automate build of Sphinx HTML docs and running doctest snippets via tox. - Drop hard testing dependency on ``zope.testing``. - Add explicit support for PyPy. - Add explicit support for Python 3.2. - Drop explicit support for Python 2.4 / 2.5. - Add support for continuous integration using ``tox`` and ``jenkins``. - Add ``Sphinx`` documentation. - Add ``setup.py docs`` alias (installs ``Sphinx`` and dependencies). - Add ``setup.py dev`` alias (runs ``setup.py develop`` plus installs ``nose`` and ``coverage``). 3.8.1 (2012-05-05) ------------------ - Fix Python 2.4 backwards incompat (itemgetter used with multiple args); Python 2.4 now works (at least if you use zope.schema == 3.8.1). This is the last release which will support Python 2.4 or 2.5. 3.8.0 (2011-12-06) ------------------ - Change action structures from tuples to dictionaries to allow for action structure extensibility (merged chrism-dictactions branch). 3.7.4 (2011-04-03) ------------------ - Apply 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: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.4 Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 Classifier: Programming Language :: Python :: Implementation :: CPython Classifier: Programming Language :: Python :: Implementation :: PyPy Classifier: Natural Language :: English Classifier: Operating System :: OS Independent Classifier: Topic :: Internet :: WWW/HTTP Classifier: Framework :: Zope :: 3 Provides-Extra: test Provides-Extra: docs zope.configuration-4.3.1/src/zope.configuration.egg-info/not-zip-safe0000644000076500000240000000000113430472370025562 0ustar macstaff00000000000000 zope.configuration-4.3.1/src/zope.configuration.egg-info/namespace_packages.txt0000644000076500000240000000000513430472370027662 0ustar macstaff00000000000000zope zope.configuration-4.3.1/src/zope.configuration.egg-info/SOURCES.txt0000644000076500000240000000612313430472370025222 0ustar macstaff00000000000000.coveragerc .gitignore .travis.yml CHANGES.rst COPYRIGHT.txt LICENSE.txt MANIFEST.in README.rst bootstrap.py buildout.cfg rtd.txt setup.cfg setup.py tox.ini docs/Makefile docs/api.rst docs/conf.py docs/hacking.rst docs/index.rst docs/make.bat docs/narr.rst docs/_static/.gitignore docs/api/config.rst docs/api/docutils.rst docs/api/exceptions.rst docs/api/fields.rst docs/api/interfaces.rst docs/api/name.rst docs/api/xmlconfig.rst docs/api/zopeconfigure.rst 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/__init__.py src/zope/configuration/_compat.py src/zope/configuration/config.py src/zope/configuration/docutils.py src/zope/configuration/exceptions.py src/zope/configuration/fields.py src/zope/configuration/interfaces.py src/zope/configuration/name.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.py src/zope/configuration/tests/conditions.zcml src/zope/configuration/tests/directives.py src/zope/configuration/tests/nested.py src/zope/configuration/tests/notyet.py src/zope/configuration/tests/sample.zcml src/zope/configuration/tests/schema.zcml src/zope/configuration/tests/simple.py src/zope/configuration/tests/simple.zcml src/zope/configuration/tests/test___init__.py src/zope/configuration/tests/test_config.py src/zope/configuration/tests/test_docs.py src/zope/configuration/tests/test_docutils.py src/zope/configuration/tests/test_fields.py src/zope/configuration/tests/test_name.py src/zope/configuration/tests/test_xmlconfig.py src/zope/configuration/tests/test_zopeconfigure.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/NamedForClass.py 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.in src/zope/configuration/tests/unicode_all/__init__.pyzope.configuration-4.3.1/src/zope.configuration.egg-info/requires.txt0000644000076500000240000000025013430472370025731 0ustar macstaff00000000000000setuptools zope.i18nmessageid zope.interface zope.schema>=4.9 [docs] Sphinx repoze.sphinx.autointerface [test] manuel zope.schema>=4.9.0 zope.testing zope.testrunner zope.configuration-4.3.1/src/zope.configuration.egg-info/top_level.txt0000644000076500000240000000000513430472370026061 0ustar macstaff00000000000000zope zope.configuration-4.3.1/src/zope.configuration.egg-info/dependency_links.txt0000644000076500000240000000000113430472370027402 0ustar macstaff00000000000000 zope.configuration-4.3.1/src/zope/0000755000076500000240000000000013430472371016775 5ustar macstaff00000000000000zope.configuration-4.3.1/src/zope/configuration/0000755000076500000240000000000013430472371021644 5ustar macstaff00000000000000zope.configuration-4.3.1/src/zope/configuration/interfaces.py0000644000076500000240000001201313430472367024343 0ustar macstaff00000000000000############################################################################## # # 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 __all__ = [ 'InvalidToken', 'IConfigurationContext', 'IGroupingContext', ] 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. Environment variables in the path are expanded, and if the path begins with the username marker (~), that is expanded as well. .. versionchanged:: 4.2.0 Start expanding home directories and environment variables. """ 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(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. :param callable: The object to call to implement the action. :param tuple args: Arguments to pass to *callable* :param dict kw: Keyword arguments to pass to *callable* :param object discriminator: Used to to find actions that conflict. Actions conflict if they have equal discriminators. The exception to this is the special case of the discriminator with the value `None`. An action with a discriminator of `None` never conflicts with other actions. :keyword int order: This is possible to add an order argument to crudely control the order of execution. :keyword str info: Optional source line information :keyword includepath: is None (the default) or a tuple of include paths for this action. """ def provideFeature(name): """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-4.3.1/src/zope/configuration/zopeconfigure.py0000644000076500000240000001415113430472367025104 0ustar macstaff00000000000000############################################################################## # # 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 class `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: .. code-block:: xml 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 from zope.interface import Interface from zope.schema import BytesLine from zope.configuration.config import GroupingContextDecorator from zope.configuration.fields import GlobalObject __all__ = [ 'IZopeConfigure', 'ZopeConfigure', ] 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 wherever it is convenient. """ package = GlobalObject( title=u"Package", description=( u"The package to be used for evaluating relative imports " u"and file names." ), required=False) i18n_domain = 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(GroupingContextDecorator): """ The implementation of `IZopeConfigure`. """ 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-4.3.1/src/zope/configuration/config.py0000644000076500000240000017530713430472367023505 0ustar macstaff00000000000000############################################################################## # # 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 """ from keyword import iskeyword import operator import os.path import sys from zope.interface.adapter import AdapterRegistry from zope.interface import Interface from zope.interface import implementer from zope.interface import providedBy from zope.schema import TextLine from zope.schema import URI from zope.schema import ValidationError from zope.configuration.exceptions import ConfigurationError from zope.configuration.exceptions import ConfigurationWrapperError from zope.configuration.interfaces import IConfigurationContext from zope.configuration.interfaces import IGroupingContext from zope.configuration.fields import GlobalInterface from zope.configuration.fields import GlobalObject from zope.configuration.fields import PathProcessor from zope.configuration._compat import builtins from zope.configuration._compat import reraise from zope.configuration._compat import string_types from zope.configuration._compat import text_type __all__ = [ 'ConfigurationContext', 'ConfigurationAdapterRegistry', 'ConfigurationMachine', 'IStackItem', 'SimpleStackItem', 'RootStackItem', 'GroupingStackItem', 'ComplexStackItem', 'GroupingContextDecorator', 'DirectiveSchema', 'IDirectivesInfo', 'IDirectivesContext', 'DirectivesHandler', 'IDirectiveInfo', 'IFullInfo', 'IStandaloneDirectiveInfo', 'defineSimpleDirective', 'defineGroupingDirective', 'IComplexDirectiveContext', 'ComplexDirectiveDefinition', 'subdirective', 'IProvidesDirectiveInfo', 'provides', 'toargs', 'expand_action', 'resolveConflicts', 'ConfigurationConflictError', ] zopens = 'http://namespaces.zope.org/zope' metans = 'http://namespaces.zope.org/meta' testns = 'http://namespaces.zope.org/test' class ConfigurationContext(object): """ Mix-in for implementing. :class:`zope.configuration.interfaces.IConfigurationContext`. Note that this class itself does not actually declare that it implements that interface; the subclass must do that. In addition, subclasses must 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 dictionaries where each dictionary has the following keys: - ``discriminator``, a value that identifies the action. Two actions that have the same (non None) discriminator conflict. - ``callable``, an object that is called to execute the action, - ``args``, positional arguments for the action - ``kw``, keyword arguments for the action - ``includepath``, a tuple of include file names (defaults to ()) - ``info``, an object that has descriptive information about the action (defaults to '') """ # pylint:disable=no-member 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: >>> from zope.configuration.config import ConfigurationContext >>> from zope.configuration.config import ConfigurationError >>> c = ConfigurationContext() >>> import zope, zope.interface >>> c.resolve('zope') is zope True >>> c.resolve('zope.interface') is zope.interface True >>> c.resolve('zope.configuration.eek') #doctest: +NORMALIZE_WHITESPACE Traceback (most recent call last): ... ConfigurationError: ImportError: Module zope.configuration has no global eek >>> c.resolve('.config.ConfigurationContext') Traceback (most recent call last): ... AttributeError: 'ConfigurationContext' object has no attribute 'package' >>> import zope.configuration >>> c.package = zope.configuration >>> c.resolve('.') is zope.configuration True >>> c.resolve('.config.ConfigurationContext') is ConfigurationContext True >>> c.resolve('..interface') is zope.interface True >>> c.resolve('str') is str True """ name = dottedname.strip() 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(builtins, 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]: names.pop(0) 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: # Without a fromlist, this returns the package rather than the # module if the name contains a dot. Using a fromlist requires # star imports to work, which may not be true if there are # unicode items in __all__ due to unicode_literals on Python 2. # Getting the module from sys.modules instead avoids both # problems. __import__(mname) mod = sys.modules[mname] except ImportError as 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: moname = mname + '.' + oname __import__(moname) return sys.modules[moname] 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): """ Compute package-relative paths. Examples: >>> import os >>> from zope.configuration.config import ConfigurationContext >>> c = ConfigurationContext() >>> c.path("/x/y/z") == os.path.normpath("/x/y/z") True >>> c.path("y/z") Traceback (most recent call last): ... AttributeError: 'ConfigurationContext' object has no attribute 'package' >>> import zope.configuration >>> c.package = zope.configuration >>> import os >>> d = os.path.dirname(zope.configuration.__file__) >>> c.path("y/z") == d + os.path.normpath("/y/z") True >>> c.path("y/./z") == d + os.path.normpath("/y/z") True >>> c.path("y/../z") == d + os.path.normpath("/z") True """ filename, needs_processing = PathProcessor.expand(filename) if not needs_processing: 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(os.path.normpath(basepath)) self.basepath = basepath return os.path.normpath(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. Examples: >>> from zope.configuration.config import ConfigurationContext >>> from zope.configuration.config import ConfigurationError >>> c = ConfigurationContext() >>> c.checkDuplicate('/foo.zcml') >>> try: ... c.checkDuplicate('/foo.zcml') ... except ConfigurationError as e: ... # On Linux the exact msg has /foo, on Windows \\foo. ... str(e).endswith("foo.zcml' included more than once") True You may use different ways to refer to the same file: >>> import zope.configuration >>> c.package = zope.configuration >>> import os >>> d = os.path.dirname(zope.configuration.__file__) >>> c.checkDuplicate('bar.zcml') >>> try: ... c.checkDuplicate(d + os.path.normpath('/bar.zcml')) ... except ConfigurationError as e: ... str(e).endswith("bar.zcml' included more than once") ... True """ 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. Examples: >>> from zope.configuration.config import ConfigurationContext >>> c = ConfigurationContext() >>> c.processFile('/foo.zcml') True >>> c.processFile('/foo.zcml') False You may use different ways to refer to the same file: >>> import zope.configuration >>> c.package = zope.configuration >>> import os >>> d = os.path.dirname(zope.configuration.__file__) >>> c.processFile('bar.zcml') True >>> c.processFile(os.path.join(d, 'bar.zcml')) False """ path = self.path(filename) if path in self._seen_files: return False self._seen_files.add(path) return True def action(self, discriminator, callable=None, args=(), kw=None, order=0, includepath=None, info=None, **extra): """ Add an action with the given discriminator, callable and arguments. For testing purposes, the callable and arguments may be omitted. In that case, a default noop callable is used. The discriminator must be given, but it can be None, to indicate that the action never conflicts. Examples: >>> from zope.configuration.config import ConfigurationContext >>> c = ConfigurationContext() Normally, the context gets actions from subclasses. We'll provide an actions attribute ourselves: >>> c.actions = [] We'll use a test callable that has a convenient string representation >>> from zope.configuration.tests.directives import f >>> c.action(1, f, (1, ), {'x': 1}) >>> from pprint import PrettyPrinter >>> pprint = PrettyPrinter(width=60).pprint >>> pprint(c.actions) [{'args': (1,), 'callable': f, 'discriminator': 1, 'includepath': (), 'info': '', 'kw': {'x': 1}, 'order': 0}] >>> c.action(None) >>> pprint(c.actions) [{'args': (1,), 'callable': f, 'discriminator': 1, 'includepath': (), 'info': '', 'kw': {'x': 1}, 'order': 0}, {'args': (), 'callable': None, 'discriminator': None, 'includepath': (), 'info': '', 'kw': {}, 'order': 0}] Now set the include path and info: >>> c.includepath = ('foo.zcml',) >>> c.info = "?" >>> c.action(None) >>> pprint(c.actions[-1]) {'args': (), 'callable': None, 'discriminator': None, 'includepath': ('foo.zcml',), 'info': '?', 'kw': {}, 'order': 0} We can add an order argument to crudely control the order of execution: >>> c.action(None, order=99999) >>> pprint(c.actions[-1]) {'args': (), 'callable': None, 'discriminator': None, 'includepath': ('foo.zcml',), 'info': '?', 'kw': {}, 'order': 99999} We can also pass an includepath argument, which will be used as the the includepath for the action. (if includepath is None, self.includepath will be used): >>> c.action(None, includepath=('abc',)) >>> pprint(c.actions[-1]) {'args': (), 'callable': None, 'discriminator': None, 'includepath': ('abc',), 'info': '?', 'kw': {}, 'order': 0} We can also pass an info argument, which will be used as the the source line info for the action. (if info is None, self.info will be used): >>> c.action(None, info='abc') >>> pprint(c.actions[-1]) {'args': (), 'callable': None, 'discriminator': None, 'includepath': ('foo.zcml',), 'info': 'abc', 'kw': {}, 'order': 0} """ if kw is None: kw = {} action = extra if info is None: info = getattr(self, 'info', '') if includepath is None: includepath = getattr(self, 'includepath', ()) action.update( dict( discriminator=discriminator, callable=callable, args=args, kw=kw, includepath=includepath, info=info, order=order, ) ) self.actions.append(action) def hasFeature(self, feature): """ Check whether a named feature has been provided. Initially no features are provided. Examples: >>> from zope.configuration.config import ConfigurationContext >>> c = ConfigurationContext() >>> c.hasFeature('onlinehelp') False 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 that a named feature has been provided. See :meth:`hasFeature` for examples. """ self._features.add(feature) class ConfigurationAdapterRegistry(object): """ Simple adapter registry that manages directives as adapters. Examples: >>> from zope.configuration.interfaces import IConfigurationContext >>> from zope.configuration.config import ConfigurationAdapterRegistry >>> from zope.configuration.config import ConfigurationError >>> from zope.configuration.config import ConfigurationMachine >>> r = ConfigurationAdapterRegistry() >>> c = ConfigurationMachine() >>> r.factory(c, ('http://www.zope.com', 'xxx')) Traceback (most recent call last): ... ConfigurationError: ('Unknown directive', 'http://www.zope.com', 'xxx') >>> def f(): ... pass >>> r.register(IConfigurationContext, ('http://www.zope.com', 'xxx'), f) >>> r.factory(c, ('http://www.zope.com', 'xxx')) is f True >>> r.factory(c, ('http://www.zope.com', 'yyy')) is f Traceback (most recent call last): ... ConfigurationError: ('Unknown directive', 'http://www.zope.com', 'yyy') >>> r.register(IConfigurationContext, 'yyy', f) >>> r.factory(c, ('http://www.zope.com', 'yyy')) is f True Test the documentation feature: >>> from zope.configuration.config import IFullInfo >>> r._docRegistry [] >>> r.document(('ns', 'dir'), IFullInfo, IConfigurationContext, None, ... 'inf', None) >>> r._docRegistry[0][0] == ('ns', 'dir') True >>> r._docRegistry[0][1] is IFullInfo True >>> r._docRegistry[0][2] is IConfigurationContext True >>> r._docRegistry[0][3] is None True >>> r._docRegistry[0][4] == 'inf' True >>> r._docRegistry[0][5] is None True >>> r.document('all-dir', None, None, None, None) >>> r._docRegistry[1][0] ('', 'all-dir') """ def __init__(self): super(ConfigurationAdapterRegistry, self).__init__() 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, string_types): 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 @implementer(IConfigurationContext) class ConfigurationMachine(ConfigurationAdapterRegistry, ConfigurationContext): """ Configuration machine. Example: >>> from zope.configuration.config import ConfigurationMachine >>> machine = ConfigurationMachine() >>> ns = "http://www.zope.org/testing" Register a directive: >>> from zope.configuration.config import metans >>> machine((metans, "directive"), ... namespace=ns, name="simple", ... schema="zope.configuration.tests.directives.ISimple", ... handler="zope.configuration.tests.directives.simple") and try it out: >>> machine((ns, "simple"), a=u"aa", c=u"cc") >>> from pprint import PrettyPrinter >>> pprint = PrettyPrinter(width=60).pprint >>> pprint(machine.actions) [{'args': ('aa', 'xxx', 'cc'), 'callable': f, 'discriminator': ('simple', 'aa', 'xxx', 'cc'), 'includepath': (), 'info': None, 'kw': {}, 'order': 0}] """ package = None basepath = None includepath = () info = '' #: These `Exception` subclasses are allowed to be raised from `execute_actions` #: without being re-wrapped into a `~.ConfigurationError`. (`BaseException` #: and other `~.ConfigurationError` instances are never wrapped.) #: #: Users of instances of this class may modify this before calling `execute_actions` #: if they need to propagate specific exceptions. #: #: .. versionadded:: 4.2.0 pass_through_exceptions = () 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: >>> from zope.configuration.config import ConfigurationMachine >>> output = [] >>> def f(*a, **k): ... output.append(('f', a, k)) >>> context = ConfigurationMachine() >>> context.actions = [ ... (1, f, (1,)), ... (1, f, (11,), {}, ('x', )), ... (2, f, (2,)), ... ] >>> context.execute_actions() >>> output [('f', (1,), {}), ('f', (2,), {})] If the action raises an error, we convert it to a `~.ConfigurationError`. >>> output = [] >>> def bad(): ... bad.xxx >>> context.actions = [ ... (1, f, (1,)), ... (1, f, (11,), {}, ('x', )), ... (2, f, (2,)), ... (3, bad, (), {}, (), 'oops') ... ] >>> context.execute_actions() Traceback (most recent call last): ... zope.configuration.config.ConfigurationExecutionError: oops AttributeError: 'function' object has no attribute 'xxx' Note that actions executed before the error still have an effect: >>> output [('f', (1,), {}), ('f', (2,), {})] If the exception was already a `~.ConfigurationError`, it is raised as-is with the action's ``info`` added. >>> def bad(): ... raise ConfigurationError("I'm bad") >>> context.actions = [ ... (1, f, (1,)), ... (1, f, (11,), {}, ('x', )), ... (2, f, (2,)), ... (3, bad, (), {}, (), 'oops') ... ] >>> context.execute_actions() Traceback (most recent call last): ... zope.configuration.exceptions.ConfigurationError: I'm bad oops """ pass_through_exceptions = self.pass_through_exceptions if testing: pass_through_exceptions = BaseException try: for action in resolveConflicts(self.actions): callable = action['callable'] if callable is None: continue args = action['args'] kw = action['kw'] info = action['info'] try: callable(*args, **kw) except ConfigurationError as ex: ex.add_details(info) raise except pass_through_exceptions: raise except Exception: # Wrap it up and raise. reraise(ConfigurationExecutionError(info, sys.exc_info()[1]), None, sys.exc_info()[2]) finally: if clear: del self.actions[:] class ConfigurationExecutionError(ConfigurationWrapperError): """ An error occurred during execution of a configuration action """ ############################################################################## # 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 """ @implementer(IStackItem) 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. """ #XXX why this *argdata hack instead of schema, data? 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: if not isinstance(action, dict): action = expand_action(*action) # b/c context.action(**action) @implementer(IStackItem) class RootStackItem(object): """ A root stack item. """ 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 @implementer(IStackItem) 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 >>> from zope.configuration.config import GroupingStackItem >>> from zope.configuration.config import ConfigurationMachine >>> context = ConfigurationMachine() We need a callable to use in configuration actions. We'll use a convenient one from the tests: >>> from zope.configuration.tests.directives import f We need a handler for the grouping directive. This is a class that implements a context decorator. The decorator must also provide ``before`` and ``after`` methods that are called before and after any contained directives are processed. We'll typically subclass ``GroupingContextDecorator``, which provides context decoration, and default ``before`` and ``after`` methods. >>> from zope.configuration.config import GroupingContextDecorator >>> class SampleGrouping(GroupingContextDecorator): ... def before(self): ... self.action(('before', self.x, self.y), f) ... def after(self): ... self.action(('after'), f) We'll use our decorator to decorate our initial context, providing keyword arguments x and y: >>> dec = SampleGrouping(context, x=1, y=2) Note that the keyword arguments are made attributes of the decorator. Now we'll create the stack item. >>> item = GroupingStackItem(dec) We still haven't called the before action yet, which we can verify by looking at the context actions: >>> context.actions [] Subdirectives will get looked up as adapters of the context. We'll create a simple handler: >>> def simple(context, data, info): ... context.action(("simple", context.x, context.y, data), f) ... return info and register it with the context: >>> from zope.configuration.interfaces import IConfigurationContext >>> from zope.configuration.config import testns >>> context.register(IConfigurationContext, (testns, 'simple'), simple) This handler isn't really a propert handler, because it doesn't return a new context. It will do for this example. Now we'll call the contained method on the stack item: >>> item.contained((testns, 'simple'), {'z': 'zope'}, "someinfo") 'someinfo' We can verify thet the simple method was called by looking at the context actions. Note that the before method was called before handling the contained directive. >>> from pprint import PrettyPrinter >>> pprint = PrettyPrinter(width=60).pprint >>> pprint(context.actions) [{'args': (), 'callable': f, 'discriminator': ('before', 1, 2), 'includepath': (), 'info': '', 'kw': {}, 'order': 0}, {'args': (), 'callable': f, 'discriminator': ('simple', 1, 2, {'z': 'zope'}), 'includepath': (), 'info': '', 'kw': {}, 'order': 0}] Finally, we call finish, which calls the decorator after method: >>> item.finish() >>> pprint(context.actions) [{'args': (), 'callable': f, 'discriminator': ('before', 1, 2), 'includepath': (), 'info': '', 'kw': {}, 'order': 0}, {'args': (), 'callable': f, 'discriminator': ('simple', 1, 2, {'z': 'zope'}), 'includepath': (), 'info': '', 'kw': {}, 'order': 0}, {'args': (), 'callable': f, 'discriminator': 'after', 'includepath': (), 'info': '', 'kw': {}, 'order': 0}] If there were no nested directives: >>> context = ConfigurationMachine() >>> dec = SampleGrouping(context, x=1, y=2) >>> item = GroupingStackItem(dec) >>> item.finish() Then before will be when we call finish: >>> pprint(context.actions) [{'args': (), 'callable': f, 'discriminator': ('before', 1, 2), 'includepath': (), 'info': '', 'kw': {}, 'order': 0}, {'args': (), 'callable': f, 'discriminator': 'after', 'includepath': (), 'info': '', 'kw': {}, 'order': 0}] """ def __init__(self, context): super(GroupingStackItem, self).__init__(context) def __callBefore(self): actions = self.context.before() if actions: for action in actions: if not isinstance(action, dict): action = expand_action(*action) 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: if not isinstance(action, dict): action = expand_action(*action) self.context.action(**action) def noop(): pass @implementer(IStackItem) 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 >>> from zope.configuration.config import ConfigurationMachine >>> context = ConfigurationMachine() We need a callable to use in configuration actions. We'll use a convenient one from the tests: >>> from zope.configuration.tests.directives import f We need a handler for the complex directive. This is a class with a method for each subdirective: >>> class Handler(object): ... def __init__(self, context, x, y): ... self.context, self.x, self.y = context, x, y ... context.action('init', f) ... def sub(self, context, a, b): ... context.action(('sub', a, b), f) ... def __call__(self): ... self.context.action(('call', self.x, self.y), f) We need a complex directive definition: >>> from zope.interface import Interface >>> from zope.schema import TextLine >>> from zope.configuration.config import ComplexDirectiveDefinition >>> class Ixy(Interface): ... x = TextLine() ... y = TextLine() >>> definition = ComplexDirectiveDefinition( ... context, name="test", schema=Ixy, ... handler=Handler) >>> class Iab(Interface): ... a = TextLine() ... b = TextLine() >>> definition['sub'] = Iab, '' OK, now that we have the context, handler and definition, we're ready to use a stack item. >>> from zope.configuration.config import ComplexStackItem >>> item = ComplexStackItem(definition, context, {'x': u'xv', 'y': u'yv'}, ... 'foo') When we created the definition, the handler (factory) was called. >>> from pprint import PrettyPrinter >>> pprint = PrettyPrinter(width=60).pprint >>> pprint(context.actions) [{'args': (), 'callable': f, 'discriminator': 'init', 'includepath': (), 'info': 'foo', 'kw': {}, 'order': 0}] If a subdirective is provided, the ``contained`` method of the stack item is called. It will lookup the subdirective schema and call the corresponding method on the handler instance: >>> simple = item.contained(('somenamespace', 'sub'), ... {'a': u'av', 'b': u'bv'}, 'baz') >>> simple.finish() Note that the name passed to ``contained`` is a 2-part name, consisting of a namespace and a name within the namespace. >>> pprint(context.actions) [{'args': (), 'callable': f, 'discriminator': 'init', 'includepath': (), 'info': 'foo', 'kw': {}, 'order': 0}, {'args': (), 'callable': f, 'discriminator': ('sub', 'av', 'bv'), 'includepath': (), 'info': 'baz', 'kw': {}, 'order': 0}] The new stack item returned by contained is one that doesn't allow any more subdirectives, When all of the subdirectives have been provided, the ``finish`` method is called: >>> item.finish() The stack item will call the handler if it is callable. >>> pprint(context.actions) [{'args': (), 'callable': f, 'discriminator': 'init', 'includepath': (), 'info': 'foo', 'kw': {}, 'order': 0}, {'args': (), 'callable': f, 'discriminator': ('sub', 'av', 'bv'), 'includepath': (), 'info': 'baz', 'kw': {}, 'order': 0}, {'args': (), 'callable': f, 'discriminator': ('call', 'xv', 'yv'), 'includepath': (), 'info': 'foo', 'kw': {}, 'order': 0}] """ def __init__(self, meta, context, data, info): newcontext = GroupingContextDecorator(context) 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 # XXX why not just use callable()? try: actions = self.handler() except AttributeError as v: if v.args[0] == '__call__': return # noncallable raise except TypeError: return # non callable if actions: # we allow the handler to return nothing for action in actions: if not isinstance(action, dict): action = expand_action(*action) self.context.action(**action) ############################################################################## # Helper classes @implementer(IConfigurationContext, IGroupingContext) class GroupingContextDecorator(ConfigurationContext): """Helper mix-in class for building grouping directives See the discussion (and test) in GroupingStackItem. """ def __init__(self, context, **kw): self.context = context for name, v in kw.items(): setattr(self, name, v) def __getattr__(self, name): 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(GlobalInterface): """A field that contains a global variable value that must be a schema """ class IDirectivesInfo(Interface): """Schema for the ``directives`` directive """ namespace = URI( title=u"Namespace", description=( u"The namespace in which directives' names " u"will be defined"), ) class IDirectivesContext(IDirectivesInfo, IConfigurationContext): pass @implementer(IDirectivesContext) class DirectivesHandler(GroupingContextDecorator): """Handler for the directives directive This is just a grouping directive that adds a namespace attribute to the normal directive context. """ class IDirectiveInfo(Interface): """Information common to all directive definitions. """ name = 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 = GlobalObject( title=u"Directive handler", description=u"The dotted name of the directive handler", ) usedIn = 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. Example: >>> from zope.configuration.config import ConfigurationMachine >>> context = ConfigurationMachine() >>> from zope.interface import Interface >>> from zope.schema import TextLine >>> from zope.configuration.tests.directives import f >>> class Ixy(Interface): ... x = TextLine() ... y = TextLine() >>> def s(context, x, y): ... context.action(('s', x, y), f) >>> from zope.configuration.config import defineSimpleDirective >>> defineSimpleDirective(context, 's', Ixy, s, testns) >>> context((testns, "s"), x=u"vx", y=u"vy") >>> from pprint import PrettyPrinter >>> pprint = PrettyPrinter(width=60).pprint >>> pprint(context.actions) [{'args': (), 'callable': f, 'discriminator': ('s', 'vx', 'vy'), 'includepath': (), 'info': None, 'kw': {}, 'order': 0}] >>> context(('http://www.zope.com/t1', "s"), x=u"vx", y=u"vy") Traceback (most recent call last): ... ConfigurationError: ('Unknown directive', 'http://www.zope.com/t1', 's') >>> context = ConfigurationMachine() >>> defineSimpleDirective(context, 's', Ixy, s, "*") >>> context(('http://www.zope.com/t1', "s"), x=u"vx", y=u"vy") >>> pprint(context.actions) [{'args': (), 'callable': f, 'discriminator': ('s', 'vx', 'vy'), 'includepath': (), 'info': None, 'kw': {}, 'order': 0}] """ namespace = namespace or context.namespace if namespace != '*': 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. Example: >>> from zope.configuration.config import ConfigurationMachine >>> context = ConfigurationMachine() >>> from zope.interface import Interface >>> from zope.schema import TextLine >>> from zope.configuration.tests.directives import f >>> class Ixy(Interface): ... x = TextLine() ... y = TextLine() We won't bother creating a special grouping directive class. We'll just use :class:`GroupingContextDecorator`, which simply sets up a grouping context that has extra attributes defined by a schema: >>> from zope.configuration.config import defineGroupingDirective >>> from zope.configuration.config import GroupingContextDecorator >>> defineGroupingDirective(context, 'g', Ixy, ... GroupingContextDecorator, testns) >>> context.begin((testns, "g"), x=u"vx", y=u"vy") >>> context.stack[-1].context.x 'vx' >>> context.stack[-1].context.y 'vy' >>> context(('http://www.zope.com/t1', "g"), x=u"vx", y=u"vy") Traceback (most recent call last): ... ConfigurationError: ('Unknown directive', 'http://www.zope.com/t1', 'g') >>> context = ConfigurationMachine() >>> defineGroupingDirective(context, 'g', Ixy, ... GroupingContextDecorator, "*") >>> context.begin(('http://www.zope.com/t1', "g"), x=u"vx", y=u"vy") >>> context.stack[-1].context.x 'vx' >>> context.stack[-1].context.y 'vy' """ namespace = namespace or context.namespace if namespace != '*': 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 @implementer(IComplexDirectiveContext) class ComplexDirectiveDefinition(GroupingContextDecorator, dict): """Handler for defining complex directives See the description and tests for ComplexStackItem. """ 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 = 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. Example: >>> from zope.configuration.config import ConfigurationContext >>> from zope.configuration.config import provides >>> c = ConfigurationContext() >>> provides(c, 'apidoc') >>> c.hasFeature('apidoc') True Spaces are not allowed in feature names (this is reserved for providing many features with a single directive in the future). >>> provides(c, 'apidoc onlinehelp') Traceback (most recent call last): ... ValueError: Only one feature name allowed >>> c.hasFeature('apidoc onlinehelp') False """ if len(feature.split()) > 1: raise ValueError("Only one feature name allowed") 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'. Example: >>> from zope.configuration.config import toargs >>> from zope.schema import BytesLine >>> from zope.schema import Float >>> from zope.schema import Int >>> from zope.schema import TextLine >>> from zope.schema import URI >>> class schema(Interface): ... in_ = Int(constraint=lambda v: v > 0) ... f = Float() ... n = TextLine(min_length=1, default=u"rob") ... x = BytesLine(required=False) ... u = URI() >>> context = ConfigurationMachine() >>> from pprint import PrettyPrinter >>> pprint = PrettyPrinter(width=50).pprint >>> pprint(toargs(context, schema, ... {'in': u'1', 'f': u'1.2', 'n': u'bob', 'x': u'x.y.z', ... 'u': u'http://www.zope.org' })) {'f': 1.2, 'in_': 1, 'n': 'bob', 'u': 'http://www.zope.org', 'x': b'x.y.z'} If we have extra data, we'll get an error: >>> toargs(context, schema, ... {'in': u'1', 'f': u'1.2', 'n': u'bob', 'x': u'x.y.z', ... 'u': u'http://www.zope.org', 'a': u'1'}) Traceback (most recent call last): ... ConfigurationError: ('Unrecognized parameters:', 'a') Unless we set a tagged value to say that extra arguments are ok: >>> schema.setTaggedValue('keyword_arguments', True) >>> pprint(toargs(context, schema, ... {'in': u'1', 'f': u'1.2', 'n': u'bob', 'x': u'x.y.z', ... 'u': u'http://www.zope.org', 'a': u'1'})) {'a': '1', 'f': 1.2, 'in_': 1, 'n': 'bob', 'u': 'http://www.zope.org', 'x': b'x.y.z'} If we omit required data we get an error telling us what was omitted: >>> pprint(toargs(context, schema, ... {'in': u'1', 'f': u'1.2', 'n': u'bob', 'x': u'x.y.z'})) Traceback (most recent call last): ... ConfigurationError: ('Missing parameter:', 'u') Although we can omit not-required data: >>> pprint(toargs(context, schema, ... {'in': u'1', 'f': u'1.2', 'n': u'bob', ... 'u': u'http://www.zope.org', 'a': u'1'})) {'a': '1', 'f': 1.2, 'in_': 1, 'n': 'bob', 'u': 'http://www.zope.org'} And we can omit required fields if they have valid defaults (defaults that are valid values): >>> pprint(toargs(context, schema, ... {'in': u'1', 'f': u'1.2', ... 'u': u'http://www.zope.org', 'a': u'1'})) {'a': '1', 'f': 1.2, 'in_': 1, 'n': 'rob', 'u': 'http://www.zope.org'} We also get an error if any data was invalid: >>> pprint(toargs(context, schema, ... {'in': u'0', 'f': u'1.2', 'n': u'bob', 'x': u'x.y.z', ... 'u': u'http://www.zope.org', 'a': u'1'})) Traceback (most recent call last): ... ConfigurationError: ('Invalid value for', 'in', '0') """ data = dict(data) args = {} 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 = text_type(s) del data[n] try: args[str(name)] = field.fromUnicode(s) except ValidationError as v: reraise(ConfigurationError("Invalid value for %r" % (n)).add_details(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 ValidationError as v: reraise(ConfigurationError("Missing parameter: %r" % (n,)).add_details(v), None, sys.exc_info()[2]) 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=None, includepath=(), info=None, order=0, **extra): if kw is None: kw = {} action = extra action.update( dict( discriminator=discriminator, callable=callable, args=args, kw=kw, includepath=includepath, info=info, order=order, ) ) return action 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-None 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. """ # organize actions by discriminators unique = {} output = [] for i, action in enumerate(actions): if not isinstance(action, dict): # old-style tuple action action = expand_action(*action) # "order" is an integer grouping. Actions in a lower order will be # executed before actions in a higher order. Within an order, # actions are executed sequentially based on original action ordering # ("i"). order = action['order'] or 0 discriminator = action['discriminator'] # "ainfo" is a tuple of (order, i, action) where "order" is a # user-supplied grouping, "i" is an integer expressing the relative # position of this action in the action list being resolved, and # "action" is an action dictionary. The purpose of an ainfo is to # associate an "order" and an "i" with a particular action; "order" # and "i" exist for sorting purposes after conflict resolution. ainfo = (order, i, action) if discriminator is None: # The discriminator is None, so this action can never conflict. # We can add it directly to the result. output.append(ainfo) continue L = unique.setdefault(discriminator, []) L.append(ainfo) # Check for conflicts conflicts = {} for discriminator, ainfos in unique.items(): # We use (includepath, order, i) as a sort key because we need to # sort the actions by the paths so that the shortest path with a # given prefix comes first. The "first" action is the one with the # shortest include path. We break sorting ties using "order", then # "i". def bypath(ainfo): path, order, i = ainfo[2]['includepath'], ainfo[0], ainfo[1] return path, order, i ainfos.sort(key=bypath) ainfo, rest = ainfos[0], ainfos[1:] output.append(ainfo) _, _, action = ainfo basepath, baseinfo, discriminator = (action['includepath'], action['info'], action['discriminator']) for _, _, action in rest: includepath = action['includepath'] # Test whether path is a prefix of opath if (includepath[:len(basepath)] != basepath # not a prefix or includepath == basepath): L = conflicts.setdefault(discriminator, [baseinfo]) L.append(action['info']) if conflicts: raise ConfigurationConflictError(conflicts) # Sort conflict-resolved actions by (order, i) and return them. return [x[2] for x in sorted(output, key=operator.itemgetter(0, 1))] class ConfigurationConflictError(ConfigurationError): def __init__(self, conflicts): super(ConfigurationConflictError, self).__init__() self._conflicts = conflicts def _with_details(self, opening, detail_formatter): r = ["Conflicting configuration actions"] for discriminator, infos in sorted(self._conflicts.items()): r.append(" For: %s" % (discriminator, )) for info in infos: for line in text_type(info).rstrip().split(u'\n'): r.append(u" " + line) opening = "\n".join(r) return super(ConfigurationConflictError, self)._with_details(opening, detail_formatter) ############################################################################## # 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-4.3.1/src/zope/configuration/fields.py0000644000076500000240000004227313430472367023501 0ustar macstaff00000000000000############################################################################## # # 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 """ import os import sys import warnings from zope.interface import implementer from zope.schema import Bool as schema_Bool from zope.schema import DottedName from zope.schema import Field from zope.schema import InterfaceField from zope.schema import List from zope.schema import PythonIdentifier as schema_PythonIdentifier from zope.schema import Text from zope.schema import ValidationError from zope.schema.interfaces import IFromUnicode from zope.schema.interfaces import InvalidValue from zope.configuration.exceptions import ConfigurationError from zope.configuration.interfaces import InvalidToken __all__ = [ 'Bool', 'GlobalObject', 'GlobalInterface', 'MessageID', 'Path', 'PythonIdentifier', 'Tokens', ] class PythonIdentifier(schema_PythonIdentifier): r""" This class is like `zope.schema.PythonIdentifier`. Let's look at an example: >>> from zope.configuration.fields import PythonIdentifier >>> class FauxContext(object): ... pass >>> context = FauxContext() >>> field = PythonIdentifier().bind(context) Let's test the fromUnicode method: >>> field.fromUnicode(u'foo') 'foo' >>> field.fromUnicode(u'foo3') 'foo3' >>> field.fromUnicode(u'_foo3') '_foo3' Now let's see whether validation works alright >>> for value in (u'foo', u'foo3', u'foo_', u'_foo3', u'foo_3', u'foo3_'): ... _ = field.fromUnicode(value) >>> from zope.schema import ValidationError >>> for value in (u'3foo', u'foo:', u'\\', u''): ... try: ... field.fromUnicode(value) ... except ValidationError: ... print('Validation Error ' + repr(value)) Validation Error '3foo' Validation Error 'foo:' Validation Error '\\' Validation Error '' .. versionchanged:: 4.2.0 Extend `zope.schema.PythonIdentifier`, which implies that `fromUnicode` validates the strings. """ def _validate(self, value): super(PythonIdentifier, self)._validate(value) if not value: raise ValidationError(value).with_field_and_value(self, value) @implementer(IFromUnicode) class GlobalObject(Field): """ An object that can be accessed as a module global. The special value ``*`` indicates a value of `None`; this is not validated against the *value_type*. """ _DOT_VALIDATOR = DottedName() 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, value): r""" Find and return the module global at the path *value*. >>> d = {'x': 1, 'y': 42, 'z': 'zope'} >>> class fakeresolver(dict): ... def resolve(self, n): ... return self[n] >>> fake = fakeresolver(d) >>> from zope.schema import Int >>> from zope.configuration.fields import GlobalObject >>> g = GlobalObject(value_type=Int()) >>> gg = g.bind(fake) >>> gg.fromUnicode("x") 1 >>> gg.fromUnicode(" x \n ") 1 >>> gg.fromUnicode("y") 42 >>> gg.fromUnicode("z") Traceback (most recent call last): ... WrongType: ('zope', (, ), '') >>> g = GlobalObject(constraint=lambda x: x%2 == 0) >>> gg = g.bind(fake) >>> gg.fromUnicode("x") Traceback (most recent call last): ... ConstraintNotSatisfied: 1 >>> gg.fromUnicode("y") 42 >>> g = GlobalObject() >>> gg = g.bind(fake) >>> print(gg.fromUnicode('*')) None """ name = str(value.strip()) # special case, mostly for interfaces if name == '*': return None try: # Leading dots are allowed here to indicate current # package, but not accepted by DottedName. Take care, # though, because a single dot is valid to resolve, but # not valid to pass to DottedName (as an empty string) to_validate = name.lstrip('.') if to_validate: self._DOT_VALIDATOR.validate(to_validate) except ValidationError as v: v.with_field_and_value(self, name) raise try: value = self.context.resolve(name) except ConfigurationError as v: raise ValidationError(v).with_field_and_value(self, name) self.validate(value) return value @implementer(IFromUnicode) class GlobalInterface(GlobalObject): """ An interface that can be accessed from a module. Example: First, we need to set up a stub name resolver: >>> from zope.interface import Interface >>> class IFoo(Interface): ... pass >>> class Foo(object): ... pass >>> d = {'Foo': Foo, 'IFoo': IFoo} >>> class fakeresolver(dict): ... def resolve(self, n): ... return self[n] >>> fake = fakeresolver(d) Now verify constraints are checked correctly: >>> from zope.configuration.fields import GlobalInterface >>> g = GlobalInterface() >>> gg = g.bind(fake) >>> gg.fromUnicode('IFoo') is IFoo True >>> gg.fromUnicode(' IFoo ') is IFoo True >>> gg.fromUnicode('Foo') Traceback (most recent call last): ... NotAnInterface: (, ... """ def __init__(self, **kw): super(GlobalInterface, self).__init__(InterfaceField(), **kw) @implementer(IFromUnicode) class Tokens(List): """ A list that can be read from a space-separated string. """ def fromUnicode(self, value): r""" Split the input string and convert it to *value_type*. Consider GlobalObject tokens: First, we need to set up a stub name resolver: >>> d = {'x': 1, 'y': 42, 'z': 'zope', 'x.y.x': 'foo'} >>> class fakeresolver(dict): ... def resolve(self, n): ... return self[n] >>> fake = fakeresolver(d) >>> from zope.configuration.fields import Tokens >>> from zope.configuration.fields import GlobalObject >>> g = Tokens(value_type=GlobalObject()) >>> gg = g.bind(fake) >>> gg.fromUnicode(" \n x y z \n") [1, 42, 'zope'] >>> from zope.schema import Int >>> g = Tokens(value_type= ... GlobalObject(value_type= ... Int(constraint=lambda x: x%2 == 0))) >>> gg = g.bind(fake) >>> gg.fromUnicode("x y") Traceback (most recent call last): ... InvalidToken: 1 in x y >>> gg.fromUnicode("z y") Traceback (most recent call last): ... InvalidToken: ('zope', (, ), '') in z y >>> gg.fromUnicode("y y") [42, 42] """ value = value.strip() if value: vt = self.value_type.bind(self.context) values = [] for s in value.split(): try: v = vt.fromUnicode(s) except ValidationError as ex: raise InvalidToken("%s in %r" % (ex, value)).with_field_and_value(self, s) else: values.append(v) else: values = [] self.validate(values) return values class PathProcessor(object): # Internal helper for manipulations on paths @classmethod def expand(cls, filename): # Perform the expansions we want to have done. Returns a # tuple: (path, needs_processing) If the second value is true, # further processing should be done (the path isn't fully # resolved); if false, the path should be used as is filename = filename.strip() # expanding a ~ at the front should generally result # in an absolute path. filename = os.path.expanduser(filename) filename = os.path.expandvars(filename) if os.path.isabs(filename): return os.path.normpath(filename), False return filename, True @implementer(IFromUnicode) class Path(Text): """ A file path name, which may be input as a relative path Input paths are converted to absolute paths and normalized. """ def fromUnicode(self, value): r""" Convert the input path to a normalized, absolute path. Let's look at an example: First, we need a "context" for the field that has a path function for converting relative path to an absolute path. We'll be careful to do this in an operating system independent fashion. >>> from zope.configuration.fields import Path >>> class FauxContext(object): ... def path(self, p): ... return os.path.join(os.sep, 'faux', 'context', p) >>> context = FauxContext() >>> field = Path().bind(context) Lets try an absolute path first: >>> import os >>> p = os.path.join(os.sep, u'a', u'b') >>> n = field.fromUnicode(p) >>> n.split(os.sep) ['', 'a', 'b'] This should also work with extra spaces around the path: >>> p = " \n %s \n\n " % p >>> n = field.fromUnicode(p) >>> n.split(os.sep) ['', 'a', 'b'] Environment variables are expanded: >>> os.environ['path-test'] = '42' >>> with_env = os.path.join(os.sep, u'a', u'${path-test}') >>> n = field.fromUnicode(with_env) >>> n.split(os.sep) ['', 'a', '42'] Now try a relative path: >>> p = os.path.join(u'a', u'b') >>> n = field.fromUnicode(p) >>> n.split(os.sep) ['', 'faux', 'context', 'a', 'b'] The current user is expanded (these are implicitly relative paths): >>> old_home = os.environ.get('HOME') >>> os.environ['HOME'] = os.path.join(os.sep, 'HOME') >>> n = field.fromUnicode('~') >>> n.split(os.sep) ['', 'HOME'] >>> if old_home: ... os.environ['HOME'] = old_home ... else: ... del os.environ['HOME'] .. versionchanged:: 4.2.0 Start expanding home directories and environment variables. """ filename, needs_processing = PathProcessor.expand(value) if needs_processing: filename = self.context.path(filename) return filename @implementer(IFromUnicode) 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 / f .. caution:: Do not confuse this with :class:`zope.schema.Bool`. That class will only parse ``"True"`` and ``"true"`` as `True` values. Any other value will silently be accepted as `False`. This class raises a validation error for unrecognized input. """ def fromUnicode(self, value): """ Convert the input string to a boolean. Example: >>> from zope.configuration.fields import Bool >>> Bool().fromUnicode(u"yes") True >>> Bool().fromUnicode(u"y") True >>> Bool().fromUnicode(u"true") True >>> Bool().fromUnicode(u"no") False >>> Bool().fromUnicode(u"surprise") Traceback (most recent call last): ... zope.schema._bootstrapinterfaces.InvalidValue """ value = value.lower() if value in ('1', 'true', 'yes', 't', 'y'): return True if value in ('0', 'false', 'no', 'f', 'n'): return False # Unlike the superclass, anything else is invalid. raise InvalidValue().with_field_and_value(self, value) @implementer(IFromUnicode) class MessageID(Text): """ Text string that should be translated. When a string is converted to a message ID, it is also recorded in the context. """ __factories = {} def fromUnicode(self, u): """ Translate a string to a MessageID. >>> from zope.configuration.fields import MessageID >>> class Info(object): ... file = 'file location' ... line = 8 >>> class FauxContext(object): ... i18n_strings = {} ... info = Info() >>> context = FauxContext() >>> field = MessageID().bind(context) There is a fallback domain when no domain has been specified. Exchange the warn function so we can make test whether the warning has been issued >>> warned = None >>> def fakewarn(*args, **kw): ... global warned ... warned = args >>> import warnings >>> realwarn = warnings.warn >>> warnings.warn = fakewarn >>> i = field.fromUnicode(u"Hello world!") >>> i 'Hello world!' >>> i.domain 'untranslated' >>> warned ("You did not specify an i18n translation domain for the '' field in file location",) >>> warnings.warn = realwarn With the domain specified: >>> context.i18n_strings = {} >>> context.i18n_domain = 'testing' We can get a message id: >>> i = field.fromUnicode(u"Hello world!") >>> i 'Hello world!' >>> i.domain 'testing' In addition, the string has been registered with the context: >>> context.i18n_strings {'testing': {'Hello world!': [('file location', 8)]}} >>> i = field.fromUnicode(u"Foo Bar") >>> i = field.fromUnicode(u"Hello world!") >>> from pprint import PrettyPrinter >>> pprint=PrettyPrinter(width=70).pprint >>> pprint(context.i18n_strings) {'testing': {'Foo Bar': [('file location', 8)], 'Hello world!': [('file location', 8), ('file location', 8)]}} >>> from zope.i18nmessageid import Message >>> isinstance(list(context.i18n_strings['testing'].keys())[0], Message) True Explicit Message IDs >>> i = field.fromUnicode(u'[View-Permission] View') >>> i 'View-Permission' >>> i.default 'View' >>> i = field.fromUnicode(u'[] [Some] text') >>> i '[Some] text' >>> i.default is None True """ context = self.context domain = getattr(context, 'i18n_domain', '') if not domain: domain = 'untranslated' warnings.warn( "You did not specify an i18n translation domain for the "\ "'%s' field in %s" % (self.getName(), context.info.file) ) if not isinstance(domain, str): # IZopeConfigure specifies i18n_domain as a BytesLine, but that's # wrong on Python 3, where the filesystem uses str, and hence # zope.i18n registers ITranslationDomain utilities with str names. # If we keep bytes, we can't find those utilities. enc = sys.getfilesystemencoding() or sys.getdefaultencoding() domain = domain.decode(enc) 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-4.3.1/src/zope/configuration/docutils.py0000644000076500000240000000565613430472367024065 0ustar macstaff00000000000000############################################################################## # # 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 __all__ = [ 'wrap', 'makeDocStructures', ] 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: >>> from zope.configuration.docutils import wrap >>> print(wrap('foo bar')[:-2]) foo bar >>> print(wrap('foo bar', indent=2)[:-2]) foo bar >>> print(wrap('foo bar, more foo bar', 10)[:-2]) foo bar, more foo bar >>> print(wrap('foo bar, more foo bar', 10, 2)[:-2]) foo bar, more foo bar """ paras = para_sep.split(text.strip()) 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): """ makeDocStructures(context) -> namespaces, subdirs Creates two structures that provide a friendly format for documentation. *namespaces* is a dictionary that maps namespaces to a directives dictionary with the key being the name of the directive and the value is a tuple: (schema, handler, info). *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-4.3.1/src/zope/configuration/tests/0000755000076500000240000000000013430472371023006 5ustar macstaff00000000000000zope.configuration-4.3.1/src/zope/configuration/tests/test_docutils.py0000644000076500000240000001156313430472367026260 0ustar macstaff00000000000000############################################################################## # # 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. # ############################################################################## """Tests for for zope.configuration.docutils """ import unittest class Test_wrap(unittest.TestCase): def _callFUT(self, *args, **kw): from zope.configuration.docutils import wrap return wrap(*args, **kw) def test_empty(self): self.assertEqual(self._callFUT(''), '\n\n') def test_only_whitespace(self): self.assertEqual(self._callFUT(' \t\n\r'), '\n\n') def test_single_paragraphs(self): self.assertEqual( self._callFUT('abcde fghij klmno pqrst uvwxy', 10, 3), ' abcde\n fghij\n klmno\n pqrst\n uvwxy\n\n') def test_multiple_paragraphs(self): self.assertEqual( self._callFUT('abcde fghij klmno\n\npqrst uvwxy', 10, 3), ' abcde\n fghij\n klmno\n\n pqrst\n uvwxy\n\n') class Test_makeDocStructures(unittest.TestCase): def _callFUT(self, *args, **kw): from zope.configuration.docutils import makeDocStructures return makeDocStructures(*args, **kw) def _makeContext(self): class _Context(object): def __init__(self): self._docRegistry = [] return _Context() def test_empty(self): context = self._makeContext() namespaces, subdirs = self._callFUT(context) self.assertEqual(len(namespaces), 0) self.assertEqual(len(subdirs), 0) def test_wo_parents(self): from zope.interface import Interface class ISchema(Interface): pass class IUsedIn(Interface): pass NS = 'http://namespace.example.com/main' NS2 = 'http://namespace.example.com/other' def _one(): raise AssertionError("should not be called") def _two(): raise AssertionError("should not be called") def _three(): raise AssertionError("should not be called") context = self._makeContext() context._docRegistry.append( ((NS, 'one'), ISchema, IUsedIn, _one, 'ONE', None)) context._docRegistry.append( ((NS2, 'two'), ISchema, IUsedIn, _two, 'TWO', None)) context._docRegistry.append( ((NS, 'three'), ISchema, IUsedIn, _three, 'THREE', None)) namespaces, subdirs = self._callFUT(context) self.assertEqual(len(namespaces), 2) self.assertEqual(namespaces[NS], {'one': (ISchema, _one, 'ONE'), 'three': (ISchema, _three, 'THREE')}) self.assertEqual(namespaces[NS2], {'two': (ISchema, _two, 'TWO')}) self.assertEqual(len(subdirs), 0) def test_w_parents(self): from zope.interface import Interface class ISchema(Interface): pass class IUsedIn(Interface): pass PNS = 'http://namespace.example.com/parent' NS = 'http://namespace.example.com/main' NS2 = 'http://namespace.example.com/other' def _one(): raise AssertionError("should not be called") def _two(): raise AssertionError("should not be called") def _three(): raise AssertionError("should not be called") class Parent(object): namespace = PNS name = 'parent' parent1 = Parent() parent2 = Parent() parent2.name = 'parent2' context = self._makeContext() context._docRegistry.append( ((NS, 'one'), ISchema, IUsedIn, _one, 'ONE', parent1)) context._docRegistry.append( ((NS2, 'two'), ISchema, IUsedIn, _two, 'TWO', parent2)) context._docRegistry.append( ((NS, 'three'), ISchema, IUsedIn, _three, 'THREE', parent1)) namespaces, subdirs = self._callFUT(context) self.assertEqual(len(namespaces), 0) self.assertEqual(len(subdirs), 2) self.assertEqual(subdirs[(PNS, 'parent')], [(NS, 'one', ISchema, _one, 'ONE'), (NS, 'three', ISchema, _three, 'THREE')]) self.assertEqual(subdirs[(PNS, 'parent2')], [(NS2, 'two', ISchema, _two, 'TWO')]) def test_suite(): return unittest.TestSuite(( unittest.makeSuite(Test_wrap), unittest.makeSuite(Test_makeDocStructures), )) zope.configuration-4.3.1/src/zope/configuration/tests/conditions.zcml0000644000076500000240000000376413430472367026065 0ustar macstaff00000000000000 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-4.3.1/src/zope/configuration/tests/samplepackage/0000755000076500000240000000000013430472371025603 5ustar macstaff00000000000000zope.configuration-4.3.1/src/zope/configuration/tests/samplepackage/baz2.zcml0000644000076500000240000000023713430472367027337 0ustar macstaff00000000000000 zope.configuration-4.3.1/src/zope/configuration/tests/samplepackage/configure.zcml0000644000076500000240000000067313430472367030466 0ustar macstaff00000000000000 zope.configuration-4.3.1/src/zope/configuration/tests/samplepackage/NamedForClass.py0000644000076500000240000000014613430472367030644 0ustar macstaff00000000000000# Test the "repeat" feature of zope.configuration.name.resolve. class NamedForClass(object): pass zope.configuration-4.3.1/src/zope/configuration/tests/samplepackage/bar1.zcml0000644000076500000240000000020413430472367027320 0ustar macstaff00000000000000 zope.configuration-4.3.1/src/zope/configuration/tests/samplepackage/baz3.zcml0000644000076500000240000000023713430472367027340 0ustar macstaff00000000000000 zope.configuration-4.3.1/src/zope/configuration/tests/samplepackage/baro2.zcml0000644000076500000240000000014413430472367027503 0ustar macstaff00000000000000 zope.configuration-4.3.1/src/zope/configuration/tests/samplepackage/__init__.py0000644000076500000240000000007513430472367027723 0ustar macstaff00000000000000# # This file is necessary to make this directory a package. zope.configuration-4.3.1/src/zope/configuration/tests/samplepackage/baro.zcml0000644000076500000240000000014513430472367027422 0ustar macstaff00000000000000 zope.configuration-4.3.1/src/zope/configuration/tests/samplepackage/bar21.zcml0000644000076500000240000000017013430472367027404 0ustar macstaff00000000000000 zope.configuration-4.3.1/src/zope/configuration/tests/samplepackage/bar2.zcml0000644000076500000240000000023113430472367027321 0ustar macstaff00000000000000 zope.configuration-4.3.1/src/zope/configuration/tests/samplepackage/foo.zcml.in0000644000076500000240000000067213430472367027674 0ustar macstaff00000000000000 zope.configuration-4.3.1/src/zope/configuration/tests/samplepackage/bar.zcml0000644000076500000240000000013413430472367027241 0ustar macstaff00000000000000 zope.configuration-4.3.1/src/zope/configuration/tests/samplepackage/foo.py0000644000076500000240000000245013430472367026746 0ustar macstaff00000000000000############################################################################## # # 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 = sorted(kw.items()) args = tuple(args) discriminator = args args = (stuff(args, _context.info, _context.basepath, _context.package, _context.includepath), ) _context.action(discriminator, data.append, args) zope.configuration-4.3.1/src/zope/configuration/tests/samplepackage/baz1.zcml0000644000076500000240000000063413430472367027337 0ustar macstaff00000000000000 zope.configuration-4.3.1/src/zope/configuration/tests/samplepackage/configure.zcml.in0000644000076500000240000000010113430472367031055 0ustar macstaff00000000000000 zope.configuration-4.3.1/src/zope/configuration/tests/test_zopeconfigure.py0000644000076500000240000000264513430472367027312 0ustar macstaff00000000000000############################################################################## # # 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 zope.configuration.xmlconfig. """ import unittest class ZopeConfigureTests(unittest.TestCase): def _getTargetClass(self): from zope.configuration.xmlconfig import ZopeConfigure return ZopeConfigure def _makeOne(self, *args, **kw): return self._getTargetClass()(*args, **kw) def test_ctor_wo_package(self): zc = self._makeOne(Context()) self.assertEqual(zc.basepath, None) def test_ctor_w_package(self): import os import zope.configuration.tests as zct zc = self._makeOne(Context(), package=zct) self.assertEqual(zc.basepath, os.path.dirname(zct.__file__)) class Context(object): basepath = None def test_suite(): return unittest.TestSuite(( unittest.makeSuite(ZopeConfigureTests), )) zope.configuration-4.3.1/src/zope/configuration/tests/test___init__.py0000644000076500000240000000231413430472367026163 0ustar macstaff00000000000000############################################################################## # # Copyright (c) 20!2 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 zope.configuration.__init__ """ import unittest class Test_namespace(unittest.TestCase): def _callFUT(self, *args, **kw): from zope.configuration import namespace return namespace(*args, **kw) def test_empty(self): self.assertEqual(self._callFUT(''), 'http://namespaces.zope.org/') def test_non_empty(self): self.assertEqual(self._callFUT('test'), 'http://namespaces.zope.org/test') def test_suite(): return unittest.TestSuite(( unittest.makeSuite(Test_namespace), )) zope.configuration-4.3.1/src/zope/configuration/tests/simple.zcml0000644000076500000240000000156313430472367025200 0ustar macstaff00000000000000 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-4.3.1/src/zope/configuration/tests/test_docs.py0000644000076500000240000000614613430472367025363 0ustar macstaff00000000000000# -*- coding: utf-8 -*- ############################################################################## # # Copyright (c) 2018 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """ Tests for the documentation. """ from __future__ import absolute_import from __future__ import division from __future__ import print_function import re import os.path import unittest import doctest import manuel.capture import manuel.codeblock import manuel.doctest import manuel.ignore import manuel.testing from zope.testing import renormalizing checker = renormalizing.RENormalizing([ # Python 3 unicode removed the "u". (re.compile("u('.*?')"), r"\1"), (re.compile('u(".*?")'), r"\1"), # Python 3 bytes added the "b". (re.compile("b('.*?')"), r"\1"), (re.compile('b(".*?")'), r"\1"), ]) optionflags = ( doctest.NORMALIZE_WHITESPACE | doctest.ELLIPSIS | doctest.IGNORE_EXCEPTION_DETAIL ) def test_suite(): suite = unittest.TestSuite() here = os.path.dirname(os.path.abspath(__file__)) while not os.path.exists(os.path.join(here, 'setup.py')): prev, here = here, os.path.dirname(here) if here == prev: # Let's avoid infinite loops at root class SkippedDocTests(unittest.TestCase): # pragma: no cover @unittest.skip('Could not find setup.py') def test_docs(self): pass suite.addTest( unittest.makeSuite(SkippedDocTests)) # pragma: no cover return suite # pragma: no cover docs = os.path.join(here, 'docs') api_docs = os.path.join(docs, 'api') doc_files_to_test = ( 'narr.rst', ) api_files_to_test = ( 'config.rst', 'docutils.rst', 'fields.rst', 'xmlconfig.rst', ) # Plain doctest suites api_to_test = ( 'config', 'docutils', 'fields', 'interfaces', 'name', 'xmlconfig', 'zopeconfigure', ) paths = [os.path.join(docs, f) for f in doc_files_to_test] paths += [os.path.join(api_docs, f) for f in api_files_to_test] m = manuel.ignore.Manuel() m += manuel.doctest.Manuel(checker=checker, optionflags=optionflags) m += manuel.codeblock.Manuel() m += manuel.capture.Manuel() suite.addTest( manuel.testing.TestSuite( m, *paths ) ) for mod_name in api_to_test: mod_name = 'zope.configuration.' + mod_name suite.addTest( doctest.DocTestSuite( mod_name, checker=checker, optionflags=optionflags ) ) return suite zope.configuration-4.3.1/src/zope/configuration/tests/notyet.py0000644000076500000240000000004613430472367024707 0ustar macstaff00000000000000# We don't get imported automagically zope.configuration-4.3.1/src/zope/configuration/tests/__init__.py0000644000076500000240000000007513430472367025126 0ustar macstaff00000000000000# # This file is necessary to make this directory a package. zope.configuration-4.3.1/src/zope/configuration/tests/conditions.py0000644000076500000240000000222113430472367025533 0ustar macstaff00000000000000############################################################################## # # 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. # ############################################################################## """ Enable "Making specific directives condition" section of narrative docs. """ from zope.interface import Interface from zope.schema import Id class IRegister(Interface): """Trivial sample registry.""" id = 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,) ) zope.configuration-4.3.1/src/zope/configuration/tests/directives.py0000644000076500000240000000442013430472367025526 0ustar macstaff00000000000000############################################################################## # # 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 from zope.interface import implementer from zope.schema import NativeStringLine from zope.schema import Text 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): raise NotImplementedError f = F() class ISimple(Interface): a = Text() b = Text(required=False) c = NativeStringLine() 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 @implementer(IPackagedContext) class Packaged(GroupingContextDecorator): pass 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 __call__(self): return [(('Complex', 1,2), f, (self.b, self.c))] class Ik(Interface): for_ = NativeStringLine() class_ = NativeStringLine() x = NativeStringLine() def k(context, for_, class_, x): context.action(('k', for_), f, (for_, class_, x)) zope.configuration-4.3.1/src/zope/configuration/tests/test_config.py0000644000076500000240000022324413430472367025700 0ustar macstaff00000000000000############################################################################## # # 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 unittest import sys # pylint:disable=inherit-non-class,protected-access # pylint:disable=attribute-defined-outside-init, arguments-differ class ConfigurationContextTests(unittest.TestCase): def _getTargetClass(self): from zope.configuration.config import ConfigurationContext return ConfigurationContext def _makeOne(self, *args, **kw): return self._getTargetClass()(*args, **kw) def tearDown(self): for name in ('zope.configuration.tests.victim', 'zope.configuration.tests.bad' 'zope.configuration.tests.unicode_all', 'zope.configuration.tests.unicode_all.__future__'): sys.modules.pop(name, None) def test_resolve_empty(self): c = self._makeOne() with self.assertRaises(ValueError): c.resolve('') def test_resolve_whitespace(self): c = self._makeOne() with self.assertRaises(ValueError): c.resolve(' ') def test_resolve_dot(self): c = self._makeOne() package = c.package = object() self.assertTrue(c.resolve('.') is package) def test_resolve_trailing_dot_in_resolve(self): #Dotted names are no longer allowed to end in dots c = self._makeOne() with self.assertRaises(ValueError): c.resolve('zope.') def test_resolve_builtin(self): c = self._makeOne() self.assertTrue(c.resolve('str') is str) def test_resolve_single_non_builtin(self): import os c = self._makeOne() self.assertTrue(c.resolve('os') is os) def test_resolve_relative_miss_no_package(self): from zope.configuration.exceptions import ConfigurationError c = self._makeOne() c.package = None with self.assertRaises(ConfigurationError): c.resolve('.nonesuch') def test_resolve_relative_miss_w_package_too_many_dots(self): class FauxPackage(object): __name__ = None from zope.configuration.exceptions import ConfigurationError c = self._makeOne() package = c.package = FauxPackage() package.__name__ = 'one.dot' with self.assertRaises(ConfigurationError): c.resolve('....nonesuch') def test_resolve_bad_dotted_last_import(self): # Import error caused by a bad last component in the dotted name. from zope.configuration.exceptions import ConfigurationError c = self._makeOne() with self.assertRaises(ConfigurationError) as exc: c.resolve('zope.configuration.tests.nosuch') self.assertIn('ImportError', str(exc.exception)) def test_resolve_bad_dotted_import(self): # Import error caused by a totally wrong dotted name. from zope.configuration.exceptions import ConfigurationError c = self._makeOne() with self.assertRaises(ConfigurationError) as exc: c.resolve('zope.configuration.nosuch.noreally') self.assertIn('ImportError', str(exc.exception)) def test_resolve_bad_sub_last_import(self): #Import error caused by a bad sub import inside the referenced #dotted name. Here we keep the standard traceback. c = self._makeOne() with self.assertRaises(ImportError): c.resolve('zope.configuration.tests.victim') def test_resolve_bad_sub_import(self): #Import error caused by a bad sub import inside part of the referenced #dotted name. Here we keep the standard traceback. c = self._makeOne() with self.assertRaises(ImportError): c.resolve('zope.configuration.tests.victim.nosuch') def test_resolve_unicode_all(self): # If a package's __init__.py is in the process of being ported from # Python 2 to Python 3 using unicode_literals, then it can end up # with unicode items in __all__, which breaks star imports from that # package; but we tolerate this. c = self._makeOne() self.assertEqual( c.resolve('zope.configuration.tests.unicode_all').foo, 'sentinel') self.assertEqual( c.resolve('zope.configuration.tests.unicode_all.foo'), 'sentinel') def test_path_w_absolute_filename(self): import os c = self._makeOne() self.assertEqual(c.path('/path/to/somewhere'), os.path.normpath('/path/to/somewhere')) def test_path_w_relative_filename_w_basepath(self): import os c = self._makeOne() c.basepath = '/path/to' self.assertEqual(c.path('somewhere'), os.path.normpath('/path/to/somewhere')) def test_path_w_relative_filename_wo_basepath_wo_package(self): import os c = self._makeOne() c.package = None self.assertEqual(c.path('somewhere'), os.path.join(os.getcwd(), 'somewhere')) def test_path_wo_basepath_w_package_having_file(self): #Path must always return an absolute path. import os class stub: __file__ = os.path.join('relative', 'path') c = self._makeOne() c.package = stub() self.assertTrue(os.path.isabs(c.path('y/z'))) def test_path_wo_basepath_w_package_having_no_file_but_path(self): #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 = self._makeOne() c.package = stub() self.assertTrue(os.path.isabs(c.path('y/z'))) def test_path_expand_filenames(self): import os c = self._makeOne() os.environ['path-test'] = '42' try: path = c.path(os.path.join(os.sep, '${path-test}')) self.assertEqual(path, os.path.join(os.sep, '42')) finally: del os.environ['path-test'] def test_path_expand_user(self): import os c = self._makeOne() old_home = os.environ.get('HOME') # HOME should be absolute os.environ['HOME'] = os.path.join(os.sep, 'HOME') try: path = c.path('~') self.assertEqual(path, os.path.join(os.sep, 'HOME')) finally: # pragma: no cover if old_home: os.environ['HOME'] = old_home else: del os.environ['HOME'] def test_checkDuplicate_miss(self): import os c = self._makeOne() c.checkDuplicate('/path') # doesn't raise self.assertEqual(list(c._seen_files), [os.path.normpath('/path')]) def test_checkDuplicate_hit(self): import os from zope.configuration.exceptions import ConfigurationError c = self._makeOne() c.checkDuplicate('/path') with self.assertRaises(ConfigurationError): c.checkDuplicate('/path') self.assertEqual(list(c._seen_files), [os.path.normpath('/path')]) def test_processFile_miss(self): import os c = self._makeOne() self.assertEqual(c.processFile('/path'), True) self.assertEqual(list(c._seen_files), [os.path.normpath('/path')]) def test_processFile_hit(self): import os c = self._makeOne() c.processFile('/path') self.assertEqual(c.processFile('/path'), False) self.assertEqual(list(c._seen_files), [os.path.normpath('/path')]) def test_action_defaults_no_info_no_includepath(self): DISCRIMINATOR = ('a', ('b',), 0) c = self._makeOne() c.actions = [] # normally provided by subclass c.action(DISCRIMINATOR) self.assertEqual(len(c.actions), 1) info = c.actions[0] self.assertEqual(info['discriminator'], DISCRIMINATOR) self.assertEqual(info['callable'], None) self.assertEqual(info['args'], ()) self.assertEqual(info['kw'], {}) self.assertEqual(info['includepath'], ()) self.assertEqual(info['info'], '') self.assertEqual(info['order'], 0) def test_action_defaults_w_info_w_includepath(self): DISCRIMINATOR = ('a', ('b',), 0) c = self._makeOne() c.actions = [] # normally provided by subclass c.info = 'INFO' # normally provided by subclass c.includepath = ('a', 'b') # normally provided by subclass c.action(DISCRIMINATOR) self.assertEqual(len(c.actions), 1) info = c.actions[0] self.assertEqual(info['discriminator'], DISCRIMINATOR) self.assertEqual(info['callable'], None) self.assertEqual(info['args'], ()) self.assertEqual(info['kw'], {}) self.assertEqual(info['order'], 0) self.assertEqual(info['includepath'], ('a', 'b')) self.assertEqual(info['info'], 'INFO') def test_action_explicit_no_extra(self): DISCRIMINATOR = ('a', ('b',), 0) ARGS = (12, 'z') KW = {'one': 1} INCLUDE_PATH = ('p', 'q/r') INFO = 'INFO' def _callable(): raise AssertionError("should not be called") c = self._makeOne() c.actions = [] # normally provided by subclass c.action(DISCRIMINATOR, _callable, ARGS, KW, 42, INCLUDE_PATH, INFO, ) self.assertEqual(len(c.actions), 1) info = c.actions[0] self.assertEqual(info['discriminator'], DISCRIMINATOR) self.assertEqual(info['callable'], _callable) self.assertEqual(info['args'], ARGS) self.assertEqual(info['kw'], KW) self.assertEqual(info['order'], 42) self.assertEqual(info['includepath'], INCLUDE_PATH) self.assertEqual(info['info'], INFO) def test_action_explicit_w_extra(self): DISCRIMINATOR = ('a', ('b',), 0) ARGS = (12, 'z') KW = {'one': 1} INCLUDE_PATH = ('p', 'q/r') INFO = 'INFO' def _callable(): raise AssertionError("should not be called") c = self._makeOne() c.actions = [] # normally provided by subclass c.action(DISCRIMINATOR, _callable, ARGS, KW, 42, INCLUDE_PATH, INFO, foo='bar', baz=17, ) self.assertEqual(len(c.actions), 1) info = c.actions[0] self.assertEqual(info['discriminator'], DISCRIMINATOR) self.assertEqual(info['callable'], _callable) self.assertEqual(info['args'], ARGS) self.assertEqual(info['kw'], KW) self.assertEqual(info['order'], 42) self.assertEqual(info['includepath'], INCLUDE_PATH) self.assertEqual(info['info'], INFO) self.assertEqual(info['foo'], 'bar') self.assertEqual(info['baz'], 17) def test_hasFeature_miss(self): c = self._makeOne() self.assertFalse(c.hasFeature('nonesuch')) def test_hasFeature_hit(self): c = self._makeOne() c._features.add('a.feature') self.assertTrue(c.hasFeature('a.feature')) def test_provideFeature(self): c = self._makeOne() c.provideFeature('a.feature') self.assertTrue(c.hasFeature('a.feature')) class ConfigurationAdapterRegistryTests(unittest.TestCase): def _getTargetClass(self): from zope.configuration.config import ConfigurationAdapterRegistry return ConfigurationAdapterRegistry def _makeOne(self, *args, **kw): return self._getTargetClass()(*args, **kw) def test_ctor(self): reg = self._makeOne() self.assertEqual(len(reg._registry), 0) self.assertEqual(len(reg._docRegistry), 0) def test_register(self): from zope.interface import Interface class IFoo(Interface): pass NS = 'http://namespace.example.com/' NAME = 'testing' def _factory(): raise AssertionError("should not be called") reg = self._makeOne() reg.register(IFoo, (NS, NAME), _factory) self.assertEqual(len(reg._registry), 1) areg = reg._registry[(NS, NAME)] self.assertTrue(areg.lookup1(IFoo, Interface) is _factory) self.assertEqual(len(reg._docRegistry), 0) def test_register_replacement(self): from zope.interface import Interface class IFoo(Interface): pass NS = 'http://namespace.example.com/' NAME = 'testing' def _factory(): raise AssertionError("should not be called") def _rival(): raise AssertionError("should not be called") reg = self._makeOne() reg.register(IFoo, (NS, NAME), _factory) reg.register(IFoo, (NS, NAME), _rival) self.assertEqual(len(reg._registry), 1) areg = reg._registry[(NS, NAME)] self.assertTrue(areg.lookup1(IFoo, Interface) is _rival) self.assertEqual(len(reg._docRegistry), 0) def test_register_new_name(self): from zope.interface import Interface class IFoo(Interface): pass NS = 'http://namespace.example.com/' NAME = 'testing' NAME2 = 'other' def _factory(): raise AssertionError("should not be called") def _rival(): raise AssertionError("should not be called") reg = self._makeOne() reg.register(IFoo, (NS, NAME), _factory) reg.register(IFoo, (NS, NAME2), _rival) self.assertEqual(len(reg._registry), 2) areg = reg._registry[(NS, NAME)] self.assertTrue(areg.lookup1(IFoo, Interface) is _factory) areg = reg._registry[(NS, NAME2)] self.assertTrue(areg.lookup1(IFoo, Interface) is _rival) self.assertEqual(len(reg._docRegistry), 0) def test_document_non_string_name(self): from zope.interface import Interface class ISchema(Interface): pass class IUsedIn(Interface): pass NS = 'http://namespace.example.com/' NAME = 'testing' HANDLER = object() INFO = 'INFO' PARENT = object() reg = self._makeOne() reg.document((NS, NAME), ISchema, IUsedIn, HANDLER, INFO, PARENT) self.assertEqual(len(reg._registry), 0) self.assertEqual(len(reg._docRegistry), 1) name, schema, used_in, handler, info, parent = reg._docRegistry[0] self.assertEqual(name, (NS, NAME)) self.assertEqual(schema, ISchema) self.assertEqual(used_in, IUsedIn) self.assertEqual(info, INFO) self.assertEqual(parent, PARENT) def test_document_w_string_name(self): from zope.interface import Interface class ISchema(Interface): pass class IUsedIn(Interface): pass NAME = 'testing' HANDLER = object() INFO = 'INFO' PARENT = object() reg = self._makeOne() reg.document(NAME, ISchema, IUsedIn, HANDLER, INFO, PARENT) self.assertEqual(len(reg._registry), 0) self.assertEqual(len(reg._docRegistry), 1) name, schema, used_in, handler, info, parent = reg._docRegistry[0] self.assertEqual(name, ('', NAME)) self.assertEqual(schema, ISchema) self.assertEqual(used_in, IUsedIn) self.assertEqual(info, INFO) self.assertEqual(parent, PARENT) def test_factory_miss(self): from zope.configuration.exceptions import ConfigurationError NS = 'http://namespace.example.com/' NAME = 'testing' context = object() reg = self._makeOne() with self.assertRaises(ConfigurationError): reg.factory(context, (NS, NAME)) def test_factory_hit_on_fqn(self): from zope.interface import Interface from zope.interface import implementer class IFoo(Interface): pass @implementer(IFoo) class Context(object): pass NS = 'http://namespace.example.com/' NAME = 'testing' context = Context() def _factory(): raise AssertionError("should not be called") reg = self._makeOne() reg.register(IFoo, (NS, NAME), _factory) self.assertEqual(reg.factory(context, (NS, NAME)), _factory) def test_factory_miss_on_fqn_hit_on_bare_name(self): from zope.interface import Interface from zope.interface import implementer class IFoo(Interface): pass @implementer(IFoo) class Context(object): pass NS = 'http://namespace.example.com/' NAME = 'testing' context = Context() def _factory(): raise AssertionError("should not be called") reg = self._makeOne() reg.register(IFoo, NAME, _factory) self.assertEqual(reg.factory(context, (NS, NAME)), _factory) def test_factory_hit_on_fqn_lookup_fails(self): from zope.interface import Interface from zope.configuration.exceptions import ConfigurationError class IFoo(Interface): pass NS = 'http://namespace.example.com/' NAME = 'testing' context = object() # doesn't provide IFoo def _factory(): raise AssertionError("should not be called") reg = self._makeOne() reg.register(IFoo, (NS, NAME), _factory) with self.assertRaises(ConfigurationError): reg.factory(context, (NS, NAME)) class _ConformsToIConfigurationContext(object): def _getTargetClass(self): raise NotImplementedError def _makeOne(self, *args, **kwargs): raise NotImplementedError def test_class_conforms_to_IConfigurationContext(self): from zope.interface.verify import verifyClass from zope.configuration.interfaces import IConfigurationContext verifyClass(IConfigurationContext, self._getTargetClass()) def test_instance_conforms_to_IConfigurationContext(self): from zope.interface.verify import verifyObject from zope.configuration.interfaces import IConfigurationContext verifyObject(IConfigurationContext, self._makeOne()) class ConfigurationMachineTests(_ConformsToIConfigurationContext, unittest.TestCase, ): def _getTargetClass(self): from zope.configuration.config import ConfigurationMachine return ConfigurationMachine def _makeOne(self, *args, **kw): return self._getTargetClass()(*args, **kw) def test_ctor(self): from zope.configuration.config import RootStackItem from zope.configuration.config import metans cm = self._makeOne() self.assertEqual(cm.package, None) self.assertEqual(cm.basepath, None) self.assertEqual(cm.includepath, ()) self.assertEqual(cm.info, '') self.assertEqual(len(cm.actions), 0) self.assertEqual(len(cm.stack), 1) root = cm.stack[0] self.assertIsInstance(root, RootStackItem) self.assertTrue(root.context is cm) self.assertEqual(len(cm.i18n_strings), 0) # Check bootstrapped meta:*. self.assertIn((metans, 'directive'), cm._registry) self.assertIn((metans, 'directives'), cm._registry) self.assertIn((metans, 'complexDirective'), cm._registry) self.assertIn((metans, 'groupingDirective'), cm._registry) self.assertIn((metans, 'provides'), cm._registry) self.assertIn((metans, 'subdirective'), cm._registry) def test_begin_w___data_and_kw(self): from zope.configuration.config import IConfigurationContext NS = 'http://namespace.example.com/' NAME = 'testing' def _factory(context, data, info): raise AssertionError("should not be called") cm = self._makeOne() cm.register(IConfigurationContext, (NS, NAME), _factory) with self.assertRaises(TypeError): cm.begin((NS, NAME), {'foo': 'bar'}, baz='bam') def test_begin_w___data_no_kw(self): from zope.interface import Interface from zope.configuration.config import IConfigurationContext from zope.configuration.config import RootStackItem class ISchema(Interface): pass class IUsedIn(Interface): pass def _handler(*args, **kw): raise AssertionError("should not be called") NS = 'http://namespace.example.com/' NAME = 'testing' _called_with = [] item = object() def _factory(context, data, info): _called_with.append((context, data, info)) return item cm = self._makeOne() cm.register(IConfigurationContext, (NS, NAME), _factory) cm.begin((NS, NAME), { 'name': 'testing', 'schema': ISchema, 'usedIn': IUsedIn, 'handler': _handler, }, 'INFO') self.assertEqual(len(cm.stack), 2) root = cm.stack[0] self.assertIsInstance(root, RootStackItem) nested = cm.stack[1] self.assertTrue(nested is item) self.assertEqual(_called_with, [(cm, { 'name': 'testing', 'schema': ISchema, 'usedIn': IUsedIn, 'handler': _handler, }, 'INFO')]) def test_begin_wo___data_w_kw(self): from zope.interface import Interface from zope.configuration.config import IConfigurationContext from zope.configuration.config import RootStackItem class ISchema(Interface): pass class IUsedIn(Interface): pass def _handler(*args, **kw): raise AssertionError("should not be called") NS = 'http://namespace.example.com/' NAME = 'testing' _called_with = [] item = object() def _factory(context, data, info): _called_with.append((context, data, info)) return item cm = self._makeOne() cm.register(IConfigurationContext, (NS, NAME), _factory) cm.begin( (NS, NAME), None, 'INFO', name='testing', schema=ISchema, usedIn=IUsedIn, handler=_handler, ) self.assertEqual(len(cm.stack), 2) root = cm.stack[0] self.assertIsInstance(root, RootStackItem) nested = cm.stack[1] self.assertTrue(nested is item) self.assertEqual(_called_with, [(cm, { 'name': 'testing', 'schema': ISchema, 'usedIn': IUsedIn, 'handler': _handler, }, 'INFO')]) def test_end(self): from zope.configuration.config import RootStackItem class FauxItem(object): _finished = False def finish(self): self._finished = True cm = self._makeOne() item = FauxItem() cm.stack.append(item) cm.end() self.assertEqual(len(cm.stack), 1) root = cm.stack[0] self.assertIsInstance(root, RootStackItem) self.assertTrue(item._finished) def test___call__(self): from zope.interface import Interface from zope.configuration.config import IConfigurationContext from zope.configuration.config import RootStackItem class ISchema(Interface): pass class IUsedIn(Interface): pass class FauxItem(object): _finished = False def finish(self): self._finished = True def _handler(*args, **kw): raise AssertionError("should not be called") NS = 'http://namespace.example.com/' NAME = 'testing' _called_with = [] item = FauxItem() def _factory(context, data, info): _called_with.append((context, data, info)) return item cm = self._makeOne() cm.register(IConfigurationContext, (NS, NAME), _factory) cm( (NS, NAME), 'INFO', name='testing', schema=ISchema, usedIn=IUsedIn, handler=_handler, ) self.assertEqual(len(cm.stack), 1) root = cm.stack[0] self.assertIsInstance(root, RootStackItem) self.assertEqual(_called_with, [(cm, { 'name': 'testing', 'schema': ISchema, 'usedIn': IUsedIn, 'handler': _handler, }, 'INFO')]) self.assertTrue(item._finished) def test_getInfo_only_root_default(self): cm = self._makeOne() self.assertEqual(cm.getInfo(), '') def test_getInfo_only_root(self): cm = self._makeOne() cm.info = 'INFO' self.assertEqual(cm.getInfo(), 'INFO') def test_getInfo_w_item(self): class FauxItem(object): info = 'FAUX' def __init__(self): self.context = self cm = self._makeOne() cm.stack.append(FauxItem()) self.assertEqual(cm.getInfo(), 'FAUX') def test_setInfo_only_root(self): cm = self._makeOne() cm.setInfo('INFO') self.assertEqual(cm.info, 'INFO') def test_setInfo_w_item(self): class FauxItem(object): info = 'FAUX' def __init__(self): self.context = self cm = self._makeOne() item = FauxItem() cm.stack.append(item) cm.setInfo('UPDATED') self.assertEqual(item.info, 'UPDATED') def test_execute_actions_empty(self): cm = self._makeOne() cm.execute_actions() # noop def test_execute_actions_wo_errors(self): _called_with = {} def _a1(*args, **kw): _called_with.setdefault('_a1', []).append((args, kw)) def _a2(*args, **kw): _called_with.setdefault('_a2', []).append((args, kw)) cm = self._makeOne() cm.action(None, None) # will be skipped cm.action(None, _a1, ('a', 0), {'foo': 'bar'}, 4) cm.action(None, _a2, ('a', 1), {'foo': 'baz'}, 3) cm.action(None, _a1, ('b', 2), {'foo': 'qux'}, 2) cm.execute_actions() self.assertEqual(_called_with['_a1'], [(('b', 2), {'foo': 'qux'}), (('a', 0), {'foo': 'bar'}), ]) self.assertEqual(_called_with['_a2'], [(('a', 1), {'foo': 'baz'}), ]) def test_execute_actions_w_errors_w_testing(self): def _err(*args, **kw): raise ValueError('XXX') cm = self._makeOne() cm.action(None, _err) with self.assertRaises(ValueError) as exc: cm.execute_actions(testing=True) self.assertEqual(str(exc.exception), 'XXX') def test_execute_actions_w_errors_wo_testing(self): from zope.configuration.config import ConfigurationExecutionError def _err(*args, **kw): raise ValueError('XXX') cm = self._makeOne() cm.info = 'INFO' cm.action(None, _err) with self.assertRaises(ConfigurationExecutionError) as exc: cm.execute_actions() self.assertIs(exc.exception.etype, ValueError) self.assertEqual(str(exc.exception.evalue), "XXX") self.assertEqual(exc.exception.info, "INFO") def _check_execute_actions_w_errors_wo_testing(self, ex_kind, cm=None): ex = ex_kind('XXX') def _err(*args, **kw): raise ex cm = cm if cm is not None else self._makeOne() cm.info = 'INFO' cm.action(None, _err) with self.assertRaises(ex_kind) as exc: cm.execute_actions() self.assertIs(exc.exception, ex) def test_execute_actions_w_errors_wo_testing_SystemExit(self): # It gets passed through as-is self._check_execute_actions_w_errors_wo_testing(SystemExit) def test_execute_actions_w_errors_wo_testing_KeyboardInterrupt(self): # It gets passed through as-is self._check_execute_actions_w_errors_wo_testing(KeyboardInterrupt) def test_execute_actions_w_errors_wo_testing_BaseException(self): # It gets passed through as-is class Bex(BaseException): pass self._check_execute_actions_w_errors_wo_testing(Bex) def test_execute_actions_w_errors_custom(self): # It gets passed through as-is, if we ask it to class Ex(Exception): pass cm = self._makeOne() cm.pass_through_exceptions += (Ex,) self._check_execute_actions_w_errors_wo_testing(Ex, cm) def test_keyword_handling(self): # This is really an integraiton test. from zope.configuration.config import metans from zope.configuration.tests.directives import f machine = self._makeOne() ns = "http://www.zope.org/testing" #Register some test directives, starting 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", ) # 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"}) self.assertEqual(len(machine.actions), 1) self.assertEqual(machine.actions[0], {'args': ('f', 'c', 'x'), 'callable': f, 'discriminator': ('k', 'f'), 'includepath': (), 'info': 'yee ha', 'kw': {}, 'order': 0, }) class _ConformsToIStackItem(object): def _getTargetClass(self): raise NotImplementedError def _makeOne(self, *args, **kwargs): raise NotImplementedError def test_class_conforms_to_IStackItem(self): from zope.interface.verify import verifyClass from zope.configuration.config import IStackItem verifyClass(IStackItem, self._getTargetClass()) def test_instance_conforms_to_IStackItem(self): from zope.interface.verify import verifyObject from zope.configuration.config import IStackItem verifyObject(IStackItem, self._makeOne()) class SimpleStackItemTests(_ConformsToIStackItem, unittest.TestCase, ): def _getTargetClass(self): from zope.configuration.config import SimpleStackItem return SimpleStackItem def _makeOne(self, context=None, handler=None, info=None, schema=None, data=None): from zope.interface import Interface if context is None: context = FauxContext() if handler is None: def handler(): raise AssertionError("should not be called") if info is None: info = 'INFO' if schema is None: schema = Interface if data is None: data = {} return self._getTargetClass()(context, handler, info, schema, data) def test_ctor(self): from zope.interface import Interface from zope.configuration.config import GroupingContextDecorator class ISchema(Interface): pass context = FauxContext() def _handler(): raise AssertionError("should not be called") _data = {} ssi = self._makeOne(context, _handler, 'INFO', ISchema, _data) self.assertIsInstance(ssi.context, GroupingContextDecorator) self.assertTrue(ssi.context.context is context) self.assertEqual(ssi.context.info, 'INFO') self.assertEqual(ssi.handler, _handler) self.assertEqual(ssi.argdata, (ISchema, _data)) def test_contained_raises(self): from zope.configuration.exceptions import ConfigurationError ssi = self._makeOne() with self.assertRaises(ConfigurationError): ssi.contained(('ns', 'name'), {}, '') def test_finish_handler_returns_no_actions(self): from zope.interface import Interface from zope.schema import Text class ISchema(Interface): name = Text(required=True) context = FauxContext() def _handler(context, **kw): return () _data = {'name': 'NAME'} ssi = self._makeOne(context, _handler, 'INFO', ISchema, _data) ssi.finish() # noraise self.assertEqual(context.actions, []) def test_finish_handler_returns_oldstyle_actions(self): from zope.interface import Interface from zope.schema import Text class ISchema(Interface): name = Text(required=True) context = FauxContext() def _action(context, **kw): raise AssertionError("should not be called") def _handler(context, **kw): return [(None, _action)] _data = {'name': 'NAME'} ssi = self._makeOne(context, _handler, 'INFO', ISchema, _data) ssi.finish() self.assertEqual(context.actions, [{'discriminator': None, 'callable': _action, 'args': (), 'kw': {}, 'includepath': (), 'info': 'INFO', 'order': 0, }]) def test_finish_handler_returns_newstyle_actions(self): from zope.interface import Interface from zope.schema import Text class ISchema(Interface): name = Text(required=True) context = FauxContext() def _action(context, **kw): raise AssertionError("should not be called") def _handler(context, **kw): return [{'discriminator': None, 'callable': _action}] _data = {'name': 'NAME'} ssi = self._makeOne(context, _handler, 'INFO', ISchema, _data) ssi.finish() self.assertEqual(context.actions, [{'discriminator': None, 'callable': _action, 'args': (), 'kw': {}, 'includepath': (), 'info': 'INFO', 'order': 0, }]) class RootStackItemTests(_ConformsToIStackItem, unittest.TestCase, ): def _getTargetClass(self): from zope.configuration.config import RootStackItem return RootStackItem def _makeOne(self, context=None): if context is None: context = object() return self._getTargetClass()(context) def test_contained_context_factory_fails(self): from zope.configuration.exceptions import ConfigurationError class _Context(object): def factory(self, context, name): "does nothing" rsi = self._makeOne(_Context()) with self.assertRaises(ConfigurationError): rsi.contained(('ns', 'name'), {}, '') def test_contained_context_factory_normal(self): _called_with = [] _adapter = object() def _factory(context, data, info): _called_with.append((context, data, info)) return _adapter class _Context(object): def factory(self, context, name): return _factory context = _Context() rsi = self._makeOne(context) adapter = rsi.contained(('ns', 'name'), {'a': 'b'}, 'INFO') self.assertTrue(adapter is _adapter) self.assertEqual(_called_with, [(context, {'a': 'b'}, 'INFO')]) def test_finish(self): rsi = self._makeOne() rsi.finish() #noraise class GroupingStackItemTests(_ConformsToIStackItem, unittest.TestCase, ): def _getTargetClass(self): from zope.configuration.config import GroupingStackItem return GroupingStackItem def _makeOne(self, context=None): if context is None: context = object() return self._getTargetClass()(context) def test_contained_context_before_returns_oldstyle_actions(self): _called_with = [] _adapter = object() def _factory(context, data, info): _called_with.append((context, data, info)) return _adapter def _action(*args, **kw): raise AssertionError("should not be called") class _Context(FauxContext): def factory(self, context, name): return _factory def before(self): return [(None, _action)] def after(self): return () context = _Context() rsi = self._makeOne(context) adapter = rsi.contained(('ns', 'name'), {'a': 'b'}, 'INFO') self.assertTrue(adapter is _adapter) self.assertEqual(_called_with, [(context, {'a': 'b'}, 'INFO')]) self.assertEqual(len(context.actions), 1) self.assertEqual(context.actions[0], {'discriminator': None, 'callable': _action, 'args': (), 'kw': {}, 'includepath': (), 'info': None, 'order': 0, }) rsi.finish() # doesn't re-do the 'before' dance self.assertEqual(len(context.actions), 1) def test_contained_context_before_returns_newstyle_actions(self): _called_with = [] _adapter = object() def _factory(context, data, info): _called_with.append((context, data, info)) return _adapter def _before(*args, **kw): raise AssertionError("should not be called") def _after(*args, **kw): raise AssertionError("should not be called") class _Context(FauxContext): def factory(self, context, name): return _factory def before(self): return [{'discriminator': None, 'callable': _before}] def after(self): return [{'discriminator': None, 'callable': _after}] context = _Context() rsi = self._makeOne(context) adapter = rsi.contained(('ns', 'name'), {'a': 'b'}, 'INFO') self.assertTrue(adapter is _adapter) self.assertEqual(_called_with, [(context, {'a': 'b'}, 'INFO')]) self.assertEqual(len(context.actions), 1) self.assertEqual(context.actions[0], # no GSI to add extras {'discriminator': None, 'callable': _before, }) rsi.finish() # doesn't re-do the 'before' dance self.assertEqual(len(context.actions), 2) self.assertEqual(context.actions[1], {'discriminator': None, 'callable': _after, }) def test_finish_calls_before_if_not_already_called(self): def _before(*args, **kw): raise AssertionError("should not be called") def _after(*args, **kw): raise AssertionError("should not be called") class _Context(FauxContext): def before(self): return [(None, _before)] def after(self): return [(None, _after)] context = _Context() rsi = self._makeOne(context) rsi.finish() self.assertEqual(len(context.actions), 2) self.assertEqual(context.actions[0], # no GSI to add extras {'discriminator': None, 'callable': _before, 'args': (), 'kw': {}, 'includepath': (), 'info': None, 'order': 0, }) self.assertEqual(context.actions[1], {'discriminator': None, 'callable': _after, 'args': (), 'kw': {}, 'includepath': (), 'info': None, 'order': 0, }) class ComplexStackItemTests(_ConformsToIStackItem, unittest.TestCase, ): def _getTargetClass(self): from zope.configuration.config import ComplexStackItem return ComplexStackItem def _makeOne(self, meta=None, context=None, data=None, info=None): if meta is None: meta = self._makeMeta() if context is None: context = object() if data is None: data = {'name': 'NAME'} if info is None: info = 'INFO' return self._getTargetClass()(meta, context, data, info) def _makeMeta(self): from zope.interface import Interface from zope.schema import Text class ISchema(Interface): name = Text() class FauxMeta(dict): schema = ISchema _handler_args = None _handler = object() def handler(self, newcontext, **kw): self._handler_kwargs = kw return self._handler return FauxMeta() def test_ctor(self): from zope.configuration.config import GroupingContextDecorator meta = self._makeMeta() context = FauxContext() _data = {'name': 'NAME'} csi = self._makeOne(meta, context, _data, 'INFO') self.assertIsInstance(csi.context, GroupingContextDecorator) self.assertTrue(csi.context.context is context) self.assertEqual(csi.context.info, 'INFO') self.assertEqual(csi.handler, meta._handler) self.assertEqual(meta._handler_kwargs, _data) def test_contained_miss(self): from zope.configuration.exceptions import ConfigurationError NS = 'http://namespace.example.com/' NAME = 'testing' csi = self._makeOne() with self.assertRaises(ConfigurationError): csi.contained((NS, NAME), {}, 'INFO') def test_contained_hit(self): from zope.interface import Interface from zope.configuration.config import GroupingContextDecorator from zope.configuration.config import SimpleStackItem NS = 'http://namespace.example.com/' NAME = 'testing' class ISubSchema(Interface): pass class WithName(object): def testing(self, *args): raise AssertionError("should not be called") meta = self._makeMeta() wn = meta._handler = WithName() meta[NAME] = (ISubSchema, 'SUBINFO') context = FauxContext() _data = {'name': 'NAME'} csi = self._makeOne(meta, context, _data, 'INFO') ssi = csi.contained((NS, NAME), {}, 'SUBINFO') self.assertIsInstance(ssi, SimpleStackItem) self.assertIsInstance(ssi.context, GroupingContextDecorator) self.assertTrue(ssi.context.context is csi.context) self.assertEqual(ssi.context.info, 'SUBINFO') self.assertEqual(ssi.handler, wn.testing) self.assertEqual(ssi.argdata, (ISubSchema, {})) def test_finish_handler_is_noncallable(self): meta = self._makeMeta() context = FauxContext() _data = {'name': 'NAME'} csi = self._makeOne(meta, context, _data, 'INFO') csi.finish() #noraise self.assertEqual(len(context.actions), 0) def test_finish_handler_raises_AE_for___call__(self): def _handler(): raise AttributeError('__call__') meta = self._makeMeta() meta._handler = _handler context = FauxContext() _data = {'name': 'NAME'} csi = self._makeOne(meta, context, _data, 'INFO') csi.finish() #noraise self.assertEqual(len(context.actions), 0) def test_finish_handler_raises_AE_for_other(self): def _handler(): raise AttributeError('other') meta = self._makeMeta() meta._handler = _handler context = FauxContext() _data = {'name': 'NAME'} csi = self._makeOne(meta, context, _data, 'INFO') with self.assertRaises(AttributeError): csi.finish() def test_finish_handler_returns_oldstyle_actions(self): def _action(): raise AssertionError("should not be called") def _handler(): return [(None, _action)] meta = self._makeMeta() meta._handler = _handler context = FauxContext() _data = {'name': 'NAME'} csi = self._makeOne(meta, context, _data, 'INFO') csi.finish() self.assertEqual(len(context.actions), 1) self.assertEqual(context.actions[0], {'discriminator': None, 'callable': _action, 'args': (), 'kw': {}, 'includepath': (), 'info': 'INFO', 'order': 0, }) def test_finish_handler_returns_newstyle_actions(self): def _action(): raise AssertionError("should not be called") def _handler(): return [{'discriminator': None, 'callable': _action}] meta = self._makeMeta() meta._handler = _handler context = FauxContext() _data = {'name': 'NAME'} csi = self._makeOne(meta, context, _data, 'INFO') csi.finish() self.assertEqual(len(context.actions), 1) self.assertEqual(context.actions[0], {'discriminator': None, 'callable': _action, 'args': (), 'kw': {}, 'includepath': (), 'info': 'INFO', 'order': 0, }) class _ConformsToIGroupingContext(object): def _getTargetClass(self): raise NotImplementedError def _makeOne(self, *args, **kwargs): raise NotImplementedError def test_class_conforms_to_IGroupingContext(self): from zope.interface.verify import verifyClass from zope.configuration.interfaces import IGroupingContext verifyClass(IGroupingContext, self._getTargetClass()) def test_instance_conforms_to_IGroupingContext(self): from zope.interface.verify import verifyObject from zope.configuration.interfaces import IGroupingContext verifyObject(IGroupingContext, self._makeOne()) class GroupingContextDecoratorTests(_ConformsToIConfigurationContext, _ConformsToIGroupingContext, unittest.TestCase, ): def _getTargetClass(self): from zope.configuration.config import GroupingContextDecorator return GroupingContextDecorator def _makeOne(self, context=None, **kw): if context is None: context = FauxContext() context.package = None #appease IConfigurationContext instance = self._getTargetClass()(context, **kw) return instance def test_ctor_no_kwargs(self): context = FauxContext() gcd = self._makeOne(context) self.assertTrue(gcd.context is context) def test_ctor_w_kwargs(self): context = FauxContext() gcd = self._makeOne(context, foo='bar', baz=42) self.assertTrue(gcd.context is context) self.assertEqual(gcd.foo, 'bar') self.assertEqual(gcd.baz, 42) def test_getattr_fetches_from_context_and_caches(self): context = FauxContext() gcd = self._makeOne(context) context.foo = 'bar' self.assertEqual(gcd.foo, 'bar') self.assertIn('foo', gcd.__dict__) def test_before(self): gcd = self._makeOne() gcd.before() #noraise def test_after(self): gcd = self._makeOne() gcd.after() #noraise class _ConformsToIDirectivesContext(object): def _getTargetClass(self): raise NotImplementedError def _makeOne(self, *args, **kwargs): raise NotImplementedError def test_class_conforms_to_IDirectivesContext(self): from zope.interface.verify import verifyClass from zope.configuration.config import IDirectivesContext verifyClass(IDirectivesContext, self._getTargetClass()) def test_instance_conforms_to_IDirectivesContext(self): from zope.interface.verify import verifyObject from zope.configuration.config import IDirectivesContext verifyObject(IDirectivesContext, self._makeOne()) class DirectivesHandlerTests(_ConformsToIDirectivesContext, unittest.TestCase, ): def _getTargetClass(self): from zope.configuration.config import DirectivesHandler return DirectivesHandler def _makeOne(self, context=None): if context is None: context = FauxContext() context.package = None #appease IConfigurationContext context.namespace = None #appease IDirectivesInfo instance = self._getTargetClass()(context) return instance class Test_defineSimpleDirective(unittest.TestCase): def _callFUT(self, *args, **kw): from zope.configuration.config import defineSimpleDirective return defineSimpleDirective(*args, **kw) def _makeContext(self): class _Context(FauxContext): def __init__(self): FauxContext.__init__(self) self._registered = [] self._documented = [] def register(self, usedIn, name, factory): self._registered.append((usedIn, name, factory)) def document(self, name, schema, usedIn, handler, info): self._documented.append((name, schema, usedIn, handler, info)) return _Context() def test_defaults(self): from zope.interface import Interface from zope.configuration.interfaces import IConfigurationContext as ICC class ISchema(Interface): pass NS = 'http://namespace.example.com/' NAME = 'testing' context = self._makeContext() context.namespace = NS context.info = 'INFO' def _handler(): raise AssertionError("should not be called") self._callFUT(context, NAME, ISchema, _handler) self.assertEqual(len(context._registered), 1) usedIn, name, factory = context._registered[0] self.assertEqual(usedIn, ICC) self.assertEqual(name, (NS, NAME)) sub = object() ssi = factory(sub, {'a': 1}, 'SUBINFO') self.assertTrue(ssi.context.context is sub) self.assertEqual(ssi.context.info, 'SUBINFO') self.assertEqual(ssi.handler, _handler) self.assertEqual(len(context._documented), 1) self.assertEqual(context._documented[0], ((NS, NAME), ISchema, ICC, _handler, 'INFO')) def test_explicit_w_star_namespace(self): from zope.interface import Interface class ISchema(Interface): pass class IUsedIn(Interface): pass NS = 'http://namespace.example.com/' NAME = 'testing' context = self._makeContext() context.namespace = NS context.info = 'INFO' def _handler(): raise AssertionError("should not be called") self._callFUT(context, NAME, ISchema, _handler, namespace='*', usedIn=IUsedIn) self.assertEqual(len(context._registered), 1) usedIn, name, factory = context._registered[0] self.assertEqual(usedIn, IUsedIn) self.assertEqual(name, NAME) sub = object() ssi = factory(sub, {'a': 1}, 'SUBINFO') self.assertTrue(ssi.context.context is sub) self.assertEqual(ssi.context.info, 'SUBINFO') self.assertEqual(ssi.handler, _handler) self.assertEqual(len(context._documented), 1) self.assertEqual(context._documented[0], (NAME, ISchema, IUsedIn, _handler, 'INFO')) class Test_defineGroupingDirective(unittest.TestCase): def _callFUT(self, *args, **kw): from zope.configuration.config import defineGroupingDirective return defineGroupingDirective(*args, **kw) def _makeContext(self): class _Context(FauxContext): def __init__(self): FauxContext.__init__(self) self._registered = [] self._documented = [] def register(self, usedIn, name, factory): self._registered.append((usedIn, name, factory)) def document(self, name, schema, usedIn, handler, info): self._documented.append((name, schema, usedIn, handler, info)) return _Context() def test_defaults(self): from zope.interface import Interface from zope.schema import Text from zope.configuration.interfaces import IConfigurationContext as ICC class ISchema(Interface): arg = Text() NS = 'http://namespace.example.com/' NAME = 'testing' context = self._makeContext() context.namespace = NS context.info = 'INFO' newcontext = FauxContext() _called_with = [] def _handler(context, **kw): _called_with.append((context, kw)) return newcontext self._callFUT(context, NAME, ISchema, _handler) self.assertEqual(len(context._registered), 1) usedIn, name, factory = context._registered[0] self.assertEqual(usedIn, ICC) self.assertEqual(name, (NS, NAME)) sub = object() gsi = factory(sub, {'arg': 'val'}, 'SUBINFO') self.assertTrue(gsi.context is newcontext) self.assertEqual(newcontext.info, 'SUBINFO') self.assertEqual(_called_with, [(sub, {'arg': 'val'})]) self.assertEqual(len(context._documented), 1) self.assertEqual(context._documented[0], ((NS, NAME), ISchema, ICC, _handler, 'INFO')) def test_explicit_w_star_namespace(self): from zope.interface import Interface from zope.schema import Text class ISchema(Interface): arg = Text() class IUsedIn(Interface): pass NS = 'http://namespace.example.com/' NAME = 'testing' context = self._makeContext() context.namespace = NS context.info = 'INFO' newcontext = FauxContext() _called_with = [] def _handler(context, **kw): _called_with.append((context, kw)) return newcontext self._callFUT(context, NAME, ISchema, _handler, namespace='*', usedIn=IUsedIn) self.assertEqual(len(context._registered), 1) usedIn, name, factory = context._registered[0] self.assertEqual(usedIn, IUsedIn) self.assertEqual(name, NAME) sub = object() gsi = factory(sub, {'arg': 'val'}, 'SUBINFO') self.assertTrue(gsi.context is newcontext) self.assertEqual(newcontext.info, 'SUBINFO') self.assertEqual(_called_with, [(sub, {'arg': 'val'})]) self.assertEqual(len(context._documented), 1) self.assertEqual(context._documented[0], (NAME, ISchema, IUsedIn, _handler, 'INFO')) class _ConformsToIComplexDirectiveContext(object): def _getTargetClass(self): raise NotImplementedError def _makeOne(self, *args, **kwargs): raise NotImplementedError def test_class_conforms_to_IComplexDirectiveContext(self): from zope.interface.verify import verifyClass from zope.configuration.config import IComplexDirectiveContext verifyClass(IComplexDirectiveContext, self._getTargetClass()) def test_instance_conforms_to_IComplexDirectiveContext(self): from zope.interface.verify import verifyObject from zope.configuration.config import IComplexDirectiveContext verifyObject(IComplexDirectiveContext, self._makeOne()) class ComplexDirectiveDefinitionTests(_ConformsToIComplexDirectiveContext, unittest.TestCase, ): def _getTargetClass(self): from zope.configuration.config import ComplexDirectiveDefinition return ComplexDirectiveDefinition def _makeOne(self, context=None): if context is None: context = self._makeContext() instance = self._getTargetClass()(context) return instance def _makeContext(self, package=None, namespace=None, name=None, schema=None, handler=None, usedIn=None): context = FauxContext() context.package = package context.namespace = namespace context.name = name context.schema = schema context.handler = handler context.usedIn = usedIn return context def test_before(self): from zope.interface import Interface from zope.schema import Text class ISchema(Interface): arg = Text() class IUsedIn(Interface): pass NS = 'http://namespace.example.com/' NAME = 'testing' _handled = [] _csi_handler = object() def _handler(context, **kw): _handled.append((context, kw)) return _csi_handler context = self._makeContext(namespace=NS, name=NAME, schema=ISchema, handler=_handler, usedIn=IUsedIn) context.info = 'INFO' _registered = [] def _register(*args): _registered.append(args) context.register = _register _documented = [] def _document(*args): _documented.append(args) context.document = _document cdd = self._makeOne(context) cdd.before() self.assertEqual(len(_registered), 1) usedIn, fqn, factory = _registered[0] self.assertEqual(usedIn, IUsedIn) self.assertEqual(fqn, (NS, NAME)) sub = FauxContext() csi = factory(sub, {'arg': 'val'}, 'SUBINFO') self.assertEqual(csi.meta, cdd) self.assertEqual(csi.context.context, sub) self.assertEqual(csi.context.info, 'SUBINFO') self.assertEqual(csi.handler, _csi_handler) self.assertEqual(_handled, [(csi.context, {'arg': 'val'})]) self.assertEqual(_documented, [((NS, NAME), ISchema, IUsedIn, _handler, 'INFO')]) class Test_subdirective(unittest.TestCase): def _callFUT(self, *args, **kw): from zope.configuration.config import subdirective return subdirective(*args, **kw) def _makeContext(self, package=None, namespace=None, name=None, schema=None, handler=None, usedIn=None): class _Context(object): def __init__(self): self.context = {} self._documented = [] def document(self, *args): self._documented.append(args) context = _Context() context.package = package context.namespace = namespace context.name = name context.schema = schema context.handler = handler context.usedIn = usedIn return context def test_wo_handler_attribute(self): from zope.interface import Interface from zope.schema import Text class ISubSchema(Interface): arg = Text() class ISchema(Interface): pass class IUsedIn(Interface): pass NS = 'http://namespace.example.com/' NAME = 'testing' SUBNAME = 'sub' _handler = object() context = self._makeContext( None, NS, NAME, ISchema, _handler, IUsedIn) context.info = 'INFO' self._callFUT(context, SUBNAME, ISubSchema) self.assertEqual(len(context._documented), 1) fqn, schema, usedIn, handler, info, ctx = context._documented[0] self.assertEqual(fqn, (NS, SUBNAME)) self.assertEqual(schema, ISubSchema) self.assertEqual(usedIn, IUsedIn) self.assertEqual(handler, _handler) self.assertEqual(info, 'INFO') self.assertEqual(ctx, context.context) self.assertEqual(context.context[SUBNAME], (ISubSchema, 'INFO')) def test_w_handler_attribute(self): from zope.interface import Interface from zope.schema import Text class ISubSchema(Interface): arg = Text() class ISchema(Interface): pass class IUsedIn(Interface): pass class Handler(object): sub = object() NS = 'http://namespace.example.com/' NAME = 'testing' SUBNAME = 'sub' handler = Handler() context = self._makeContext(None, NS, NAME, ISchema, handler, IUsedIn) context.info = 'INFO' self._callFUT(context, SUBNAME, ISubSchema) self.assertEqual(len(context._documented), 1) fqn, schema, usedIn, handler, info, ctx = context._documented[0] self.assertEqual(fqn, (NS, SUBNAME)) self.assertEqual(schema, ISubSchema) self.assertEqual(usedIn, IUsedIn) self.assertEqual(handler, Handler.sub) self.assertEqual(info, 'INFO') self.assertEqual(ctx, context.context) self.assertEqual(context.context[SUBNAME], (ISubSchema, 'INFO')) class Test_provides(unittest.TestCase): def _callFUT(self, *args, **kw): from zope.configuration.config import provides return provides(*args, **kw) def test_w_multiple(self): context = FauxContext() with self.assertRaises(ValueError): self._callFUT(context, 'one two') def test_w_single(self): _provided = [] def _provideFeature(feature): _provided.append(feature) context = FauxContext() context.provideFeature = _provideFeature self._callFUT(context, 'one') self.assertEqual(_provided, ['one']) class Test_toargs(unittest.TestCase): def _callFUT(self, *args, **kw): from zope.configuration.config import toargs return toargs(*args, **kw) def test_w_empty_schema_no_data(self): from zope.interface import Interface class ISchema(Interface): pass context = FauxContext() self.assertEqual(self._callFUT(context, ISchema, {}), {}) def test_w_empty_schema_w_data_no_kwargs_allowed(self): from zope.configuration.exceptions import ConfigurationError from zope.interface import Interface class ISchema(Interface): pass context = FauxContext() with self.assertRaises(ConfigurationError) as exc: self._callFUT(context, ISchema, {'a': 'b'}) self.assertEqual(exc.exception.args, ('Unrecognized parameters:', 'a')) def test_w_empty_schema_w_data_w_kwargs_allowed(self): from zope.interface import Interface class ISchema(Interface): pass ISchema.setTaggedValue('keyword_arguments', True) context = FauxContext() self.assertEqual(self._callFUT(context, ISchema, {'a': 'b'}), {'a': 'b'}) def test_w_keyword_sub(self): from zope.interface import Interface from zope.schema import Text class ISchema(Interface): for_ = Text() context = FauxContext() self.assertEqual(self._callFUT(context, ISchema, {'for': 'foo'}), {'for_': 'foo'}) def test_w_field_missing_no_default(self): from zope.interface import Interface from zope.schema import Text from zope.configuration.exceptions import ConfigurationError class ISchema(Interface): no_default = Text() context = FauxContext() with self.assertRaises(ConfigurationError) as exc: self._callFUT(context, ISchema, {}) self.assertEqual(exc.exception.args, ("Missing parameter: 'no_default'",)) # It includes the details of any validation failure; # The rendering of the nested exception varies by Python version, # sadly. exception_str = str(exc.exception) self.assertTrue(exception_str.startswith( "Missing parameter: 'no_default'\n" ), exception_str) self.assertTrue(exception_str.endswith( "RequiredMissing: no_default" ), exception_str) def test_w_field_missing_but_default(self): from zope.interface import Interface from zope.schema import Text class ISchema(Interface): w_default = Text(default=u'default') context = FauxContext() self.assertEqual(self._callFUT(context, ISchema, {}), {'w_default': 'default'}) def test_w_invalid_value(self): from zope.interface import Interface from zope.schema import Int from zope.configuration.exceptions import ConfigurationError class ISchema(Interface): count = Int(min=0) context = FauxContext() with self.assertRaises(ConfigurationError) as exc: self._callFUT(context, ISchema, {'count': '-1'}) self.assertEqual(exc.exception.args, ("Invalid value for 'count'",)) for meth in str, repr: exception_str = meth(exc.exception) self.assertIn( "Invalid value for", exception_str) self.assertIn( "TooSmall: (-1, 0)", exception_str) class Test_expand_action(unittest.TestCase): def _callFUT(self, *args, **kw): from zope.configuration.config import expand_action return expand_action(*args, **kw) def test_defaults(self): self.assertEqual(self._callFUT(('a', 1, None)), {'discriminator': ('a', 1, None), 'callable': None, 'args': (), 'kw': {}, 'includepath': (), 'info': None, 'order': 0, }) def test_explicit_no_extra(self): def _callable(): raise AssertionError("should not be called") self.assertEqual(self._callFUT(('a', 1, None), _callable, ('b', 2), {'c': None}, ('p', 'q/r'), 'INFO', 42, ), {'discriminator': ('a', 1, None), 'callable': _callable, 'args': ('b', 2), 'kw': {'c': None}, 'includepath': ('p', 'q/r'), 'info': 'INFO', 'order': 42, }) def test_explicit_w_extra(self): def _callable(): raise AssertionError("should not be called") self.assertEqual(self._callFUT(('a', 1, None), _callable, ('b', 2), {'c': None}, ('p', 'q/r'), 'INFO', 42, foo='bar', baz=None, ), {'discriminator': ('a', 1, None), 'callable': _callable, 'args': ('b', 2), 'kw': {'c': None}, 'includepath': ('p', 'q/r'), 'info': 'INFO', 'order': 42, 'foo': 'bar', 'baz': None, }) class Test_resolveConflicts(unittest.TestCase): def _callFUT(self, *args, **kw): from zope.configuration.config import resolveConflicts return resolveConflicts(*args, **kw) def test_empty(self): self.assertEqual(self._callFUT(()), []) def test_expands_oldstyle_actions(self): def _callable(): raise AssertionError("should not be called") self.assertEqual( self._callFUT([(None, _callable)]), [ { 'discriminator': None, 'callable': _callable, 'args': (), 'kw': {}, 'includepath': (), 'info': None, 'order': 0, } ] ) def test_wo_discriminator_clash(self): from zope.configuration.config import expand_action def _a(): raise AssertionError("should not be called") def _b(): raise AssertionError("should not be called") def _c(): raise AssertionError("should not be called") def _d(): raise AssertionError("should not be called") actions = [ expand_action(('a', 1), _a, order=3), expand_action(('b', 2), _b, order=1), expand_action(('c', 3), _c, order=2), expand_action(('d', 4), _d, order=1), ] self.assertEqual([x['callable'] for x in self._callFUT(actions)], [_b, _d, _c, _a]) def test_w_resolvable_discriminator_clash(self): from zope.configuration.config import expand_action def _a(): raise AssertionError("should not be called") def _b(): raise AssertionError("should not be called") actions = [ expand_action(('a', 1), _a, includepath=('a',)), expand_action(('a', 1), _b, includepath=('a', 'b')), ] self.assertEqual([x['callable'] for x in self._callFUT(actions)], [_a]) def test_w_non_resolvable_discriminator_clash_different_paths(self): from zope.configuration.config import ConfigurationConflictError from zope.configuration.config import expand_action def _a(): raise AssertionError("should not be called") def _b(): raise AssertionError("should not be called") actions = [ expand_action(('a', 1), _a, includepath=('b', 'c'), info='X'), expand_action(('a', 1), _b, includepath=('a',), info='Y'), ] with self.assertRaises(ConfigurationConflictError) as exc: self._callFUT(actions) self.assertEqual(exc.exception._conflicts, {('a', 1): ['Y', 'X']}) def test_w_non_resolvable_discriminator_clash_same_path(self): from zope.configuration.config import ConfigurationConflictError from zope.configuration.config import expand_action def _a(): raise AssertionError("should not be called") def _b(): raise AssertionError("should not be called") actions = [ expand_action(('a', 1), _a, includepath=('a',), info='X'), expand_action(('a', 1), _b, includepath=('a',), info='Y'), ] with self.assertRaises(ConfigurationConflictError) as exc: self._callFUT(actions) self.assertEqual(exc.exception._conflicts, {('a', 1): ['X', 'Y']}) def test_configuration_conflict_error_has_readable_exception(self): from zope.configuration.config import ConfigurationConflictError from zope.configuration.config import expand_action def _a(): raise AssertionError("should not be called") def _b(): raise AssertionError("should not be called") actions = [ expand_action(('a', 1), _a, includepath=('a',), info='conflict!'), expand_action( ('a', 1), _b, includepath=('a',), info='conflict2!'), ] with self.assertRaises(ConfigurationConflictError) as exc: self._callFUT(actions) self.assertEqual( "Conflicting configuration actions\n " "For: ('a', 1)\n conflict!\n conflict2!", str(exc.exception)) exc.exception.add_details('a detail') self.assertEqual( "Conflicting configuration actions\n " "For: ('a', 1)\n conflict!\n conflict2!\n" " a detail", str(exc.exception)) def test_wo_discriminators_final_sorting_order(self): from zope.configuration.config import expand_action def _a(): raise AssertionError("should not be called") def _b(): raise AssertionError("should not be called") def _c(): raise AssertionError("should not be called") def _d(): raise AssertionError("should not be called") actions = [ expand_action(None, _a, order=3), expand_action(None, _b, order=1), expand_action(None, _c, order=2), expand_action(None, _d, order=1), ] self.assertEqual([x['callable'] for x in self._callFUT(actions)], [_b, _d, _c, _a]) class FauxContext(object): def __init__(self): self.actions = [] def action(self, **kw): self.actions.append(kw) zope.configuration-4.3.1/src/zope/configuration/tests/test_fields.py0000644000076500000240000002610213430472367025673 0ustar macstaff00000000000000############################################################################## # # Copyright (c) 20!2 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 zope.configuration.fields. """ import unittest # pylint:disable=protected-access class _ConformsToIFromUnicode(object): def _getTargetClass(self): raise NotImplementedError def _makeOne(self, *args, **kw): raise NotImplementedError def test_class_conforms_to_IFromUnicode(self): from zope.interface.verify import verifyClass from zope.schema.interfaces import IFromUnicode verifyClass(IFromUnicode, self._getTargetClass()) def test_instance_conforms_to_IFromUnicode(self): from zope.interface.verify import verifyObject from zope.schema.interfaces import IFromUnicode verifyObject(IFromUnicode, self._makeOne()) class GlobalObjectTests(unittest.TestCase, _ConformsToIFromUnicode): def _getTargetClass(self): from zope.configuration.fields import GlobalObject return GlobalObject def _makeOne(self, *args, **kw): return self._getTargetClass()(*args, **kw) def test__validate_wo_value_type(self): go = self._makeOne(value_type=None) for value in [0, 0.0, (), [], set(), frozenset(), u'', b'']: go._validate(value) #noraise def test__validate_w_value_type(self): from zope.schema import Text from zope.schema.interfaces import WrongType go = self._makeOne(value_type=Text()) go.validate(u'') for value in [0, 0.0, (), [], set(), frozenset(), b'']: with self.assertRaises(WrongType): go._validate(value) def test_fromUnicode_w_star_and_extra_ws(self): go = self._makeOne() self.assertEqual(go.fromUnicode(' * '), None) def test_fromUnicode_w_resolve_fails(self): from zope.schema import ValidationError from zope.configuration.config import ConfigurationError class Context(object): _resolved = None def resolve(self, name): self._resolved = name raise ConfigurationError() go = self._makeOne() context = Context() bound = go.bind(context) with self.assertRaises(ValidationError) as exc: bound.fromUnicode('tried') self.assertEqual(context._resolved, 'tried') ex = exc.exception self.assertIs(ex.field, bound) self.assertEqual(ex.value, 'tried') def test_fromUnicode_w_resolve_success(self): _target = object() class Context(object): _resolved = None def resolve(self, name): self._resolved = name return _target go = self._makeOne() context = Context() bound = go.bind(context) found = bound.fromUnicode('tried') self.assertIs(found, _target) self.assertEqual(context._resolved, 'tried') def test_fromUnicode_w_resolve_dots(self): _target = object() class Context(object): _resolved = None def resolve(self, name): self._resolved = name return _target go = self._makeOne() context = Context() bound = go.bind(context) for name in ( '.', '..', '.foo', '..foo.bar' ): __traceback_info__ = name found = bound.fromUnicode(name) self.assertIs(found, _target) self.assertEqual(context._resolved, name) def test_fromUnicode_w_resolve_but_validation_fails(self): from zope.schema import Text from zope.schema import ValidationError _target = object() class Context(object): _resolved = None def resolve(self, name): self._resolved = name return _target go = self._makeOne(value_type=Text()) context = Context() bound = go.bind(context) with self.assertRaises(ValidationError): bound.fromUnicode('tried') self.assertEqual(context._resolved, 'tried') def test_fromUnicode_rejects_slash(self): from zope.schema import ValidationError _target = object() field = self._makeOne() with self.assertRaises(ValidationError) as exc: field.fromUnicode('foo/bar') ex = exc.exception self.assertIs(ex.field, field) self.assertEqual(ex.value, 'foo/bar') class GlobalInterfaceTests(unittest.TestCase, _ConformsToIFromUnicode): def _getTargetClass(self): from zope.configuration.fields import GlobalInterface return GlobalInterface def _makeOne(self, *args, **kw): return self._getTargetClass()(*args, **kw) def test_ctor(self): from zope.schema import InterfaceField gi = self._makeOne() self.assertIsInstance(gi.value_type, InterfaceField) class TokensTests(unittest.TestCase, _ConformsToIFromUnicode): def _getTargetClass(self): from zope.configuration.fields import Tokens return Tokens def _makeOne(self, *args, **kw): return self._getTargetClass()(*args, **kw) def test_fromUnicode_empty(self): tok = self._makeOne() self.assertEqual(tok.fromUnicode(''), []) def test_fromUnicode_strips_ws(self): from zope.schema import Text tok = self._makeOne(value_type=Text()) self.assertEqual(tok.fromUnicode(u' one two three '), [u'one', u'two', u'three']) def test_fromUnicode_invalid(self): from zope.schema import Int from zope.configuration.interfaces import InvalidToken tok = self._makeOne(value_type=Int(min=0)) with self.assertRaises(InvalidToken) as exc: tok.fromUnicode(u' 1 -1 3 ') ex = exc.exception self.assertIs(ex.field, tok) self.assertEqual(ex.value, '-1') class PathTests(unittest.TestCase, _ConformsToIFromUnicode): def _getTargetClass(self): from zope.configuration.fields import Path return Path def _makeOne(self, *args, **kw): return self._getTargetClass()(*args, **kw) def test_fromUnicode_absolute(self): import os path = self._makeOne() self.assertEqual(path.fromUnicode('/'), os.path.normpath('/')) def test_fromUnicode_relative(self): class Context(object): _pathed = None def path(self, value): self._pathed = value return '/hard/coded' context = Context() path = self._makeOne() bound = path.bind(context) self.assertEqual(bound.fromUnicode('relative/path'), '/hard/coded') self.assertEqual(context._pathed, 'relative/path') class BoolTests(unittest.TestCase, _ConformsToIFromUnicode): def _getTargetClass(self): from zope.configuration.fields import Bool return Bool def _makeOne(self, *args, **kw): return self._getTargetClass()(*args, **kw) def test_fromUnicode_w_true_values(self): values = ['1', 'true', 'yes', 't', 'y'] values += [x.upper() for x in values] bo = self._makeOne() for value in values: self.assertEqual(bo.fromUnicode(value), True) def test_fromUnicode_w_false_values(self): values = ['0', 'false', 'no', 'f', 'n'] values += [x.upper() for x in values] bo = self._makeOne() for value in values: self.assertEqual(bo.fromUnicode(value), False) def test_fromUnicode_w_invalid(self): from zope.schema.interfaces import InvalidValue bo = self._makeOne() with self.assertRaises(InvalidValue) as exc: bo.fromUnicode('notvalid') ex = exc.exception self.assertIs(ex.field, bo) self.assertEqual(ex.value, 'notvalid') class MessageIDTests(unittest.TestCase, _ConformsToIFromUnicode): def _getTargetClass(self): from zope.configuration.fields import MessageID return MessageID def _makeOne(self, *args, **kw): return self._getTargetClass()(*args, **kw) def _makeContext(self, domain='testing_domain'): class Info(object): file = 'test_file' line = 42 class Context(object): i18n_domain = domain def __init__(self): self.i18n_strings = {} self.info = Info() return Context() def test_wo_domain(self): import warnings mid = self._makeOne() context = self._makeContext(None) bound = mid.bind(context) with warnings.catch_warnings(record=True) as log: msgid = bound.fromUnicode(u'testing') self.assertEqual(len(log), 1) self.assertTrue(str(log[0].message).startswith( 'You did not specify an i18n translation domain')) self.assertEqual(msgid, 'testing') self.assertEqual(msgid.default, None) self.assertEqual(msgid.domain, 'untranslated') self.assertEqual(context.i18n_strings, {'untranslated': {'testing': [('test_file', 42)]}}) def test_w_empty_id(self): import warnings mid = self._makeOne() context = self._makeContext() bound = mid.bind(context) with warnings.catch_warnings(record=True) as log: msgid = bound.fromUnicode(u'[] testing') self.assertEqual(len(log), 0) self.assertEqual(msgid, 'testing') self.assertEqual(msgid.default, None) self.assertEqual(msgid.domain, 'testing_domain') self.assertEqual(context.i18n_strings, {'testing_domain': {'testing': [('test_file', 42)]}}) def test_w_id_and_default(self): import warnings mid = self._makeOne() context = self._makeContext() bound = mid.bind(context) with warnings.catch_warnings(record=True) as log: msgid = bound.fromUnicode(u'[testing] default') self.assertEqual(len(log), 0) self.assertEqual(msgid, 'testing') self.assertEqual(msgid.default, 'default') self.assertEqual(msgid.domain, 'testing_domain') self.assertEqual(context.i18n_strings, {'testing_domain': {'testing': [('test_file', 42)]}}) def test_domain_decodes_bytes(self): mid = self._makeOne() context = self._makeContext(domain=b'domain') bound = mid.bind(context) msgid = bound.fromUnicode(u'msgid') self.assertIsInstance(msgid.domain, str) self.assertEqual(msgid.domain, 'domain') def test_suite(): return unittest.defaultTestLoader.loadTestsFromName(__name__) zope.configuration-4.3.1/src/zope/configuration/tests/excludedemo/0000755000076500000240000000000013430472371025304 5ustar macstaff00000000000000zope.configuration-4.3.1/src/zope/configuration/tests/excludedemo/configure.zcml0000644000076500000240000000012313430472367030155 0ustar macstaff00000000000000 zope.configuration-4.3.1/src/zope/configuration/tests/excludedemo/__init__.py0000644000076500000240000000000213430472367027412 0ustar macstaff00000000000000# zope.configuration-4.3.1/src/zope/configuration/tests/excludedemo/sub/0000755000076500000240000000000013430472371026075 5ustar macstaff00000000000000zope.configuration-4.3.1/src/zope/configuration/tests/excludedemo/sub/configure.zcml0000644000076500000240000000001613430472367030747 0ustar macstaff00000000000000 zope.configuration-4.3.1/src/zope/configuration/tests/excludedemo/sub/__init__.py0000644000076500000240000000000213430472367030203 0ustar macstaff00000000000000# zope.configuration-4.3.1/src/zope/configuration/tests/excludedemo/spam.zcml0000644000076500000240000000001613430472367027135 0ustar macstaff00000000000000 zope.configuration-4.3.1/src/zope/configuration/tests/unicode_all/0000755000076500000240000000000013430472371025264 5ustar macstaff00000000000000zope.configuration-4.3.1/src/zope/configuration/tests/unicode_all/__init__.py0000644000076500000240000000011513430472367027377 0ustar macstaff00000000000000from __future__ import unicode_literals __all__ = ['foo'] foo = 'sentinel' zope.configuration-4.3.1/src/zope/configuration/tests/test_xmlconfig.py0000644000076500000240000013530613430472367026422 0ustar macstaff00000000000000############################################################################## # # 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 zope.configuration.xmlconfig. """ import unittest NS = u'ns' FOO = u'foo' XXX = u'xxx' SPLAT = u'splat' SPLATV = u'splatv' A = u'a' AVALUE = u'avalue' B = u'b' BVALUE = u'bvalue' # pylint:disable=protected-access class ZopeXMLConfigurationErrorTests(unittest.TestCase): maxDiff = None def _getTargetClass(self): from zope.configuration.xmlconfig import ZopeXMLConfigurationError return ZopeXMLConfigurationError def _makeOne(self, *args, **kw): return self._getTargetClass()(*args, **kw) def test___str___uses_repr_of_info(self): zxce = self._makeOne('info', Exception('value')) self.assertEqual( str(zxce), "'info'\n Exception: value" ) class ZopeSAXParseExceptionTests(unittest.TestCase): maxDiff = None def _getTargetClass(self): from zope.configuration.xmlconfig import ZopeSAXParseException return ZopeSAXParseException def _makeOne(self, *args, **kw): return self._getTargetClass()(*args, **kw) def test___str___not_a_sax_error(self): zxce = self._makeOne("info", Exception('Not a SAX error')) self.assertEqual( str(zxce), "info\n Exception: Not a SAX error") def test___str___w_a_sax_error(self): zxce = self._makeOne("info", Exception('filename.xml:24:32:WAAA')) self.assertEqual( str(zxce), 'info\n Exception: filename.xml:24:32:WAAA') class ParserInfoTests(unittest.TestCase): def _getTargetClass(self): from zope.configuration.xmlconfig import ParserInfo return ParserInfo def _makeOne(self, *args, **kw): return self._getTargetClass()(*args, **kw) def test___repr___w_eline_ecolumn_match_line_column(self): pi = self._makeOne('filename.xml', 24, 32) pi.end(24, 32) self.assertEqual(repr(pi), 'File "filename.xml", line 24.32') def test___repr___w_eline_ecolumn_dont_match_line_column(self): pi = self._makeOne('filename.xml', 24, 32) pi.end(33, 21) self.assertEqual(repr(pi), 'File "filename.xml", line 24.32-33.21') def test___str___w_eline_ecolumn_match_line_column(self): pi = self._makeOne('filename.xml', 24, 32) pi.end(24, 32) self.assertEqual(str(pi), 'File "filename.xml", line 24.32') def test___str___w_eline_ecolumn_dont_match_line_column_bad_file(self): pi = self._makeOne('/path/to/nonesuch.xml', 24, 32) pi.end(33, 21) self.assertEqual(str(pi), 'File "/path/to/nonesuch.xml", line 24.32-33.21\n' ' Could not read source.') def test___str___w_good_file(self): pi = self._makeOne('tests//sample.zcml', 3, 2) pi.end(3, 57) self.assertEqual( str(pi), 'File "tests//sample.zcml", line 3.2-3.57\n' ' ') class ConfigurationHandlerTests(unittest.TestCase): def _getTargetClass(self): from zope.configuration.xmlconfig import ConfigurationHandler return ConfigurationHandler def _makeOne(self, *args, **kw): return self._getTargetClass()(*args, **kw) def test_ctor_defaults(self): context = FauxContext() handler = self._makeOne(context) self.assertTrue(handler.context is context) self.assertFalse(handler.testing) self.assertEqual(handler.ignore_depth, 0) def test_ctor_explicit(self): context = FauxContext() handler = self._makeOne(context, True) self.assertTrue(handler.context is context) self.assertTrue(handler.testing) self.assertEqual(handler.ignore_depth, 0) self.assertTrue(handler.locator is None) def test_setDocumentLocator(self): context = FauxContext() locator = FauxLocator('tests//sample.zcml', 1, 1) handler = self._makeOne(context, True) handler.setDocumentLocator(locator) self.assertTrue(handler.locator is locator) def test_startElementNS_w_zcml_condition_failing(self): from zope.configuration.xmlconfig import ZCML_CONDITION context = FauxContext() handler = self._makeOne(context, True) # No locator set: we won't need it, due to a failed condition. handler.startElementNS((NS, FOO), FOO, {ZCML_CONDITION: 'have nonesuch', (None, A): AVALUE, (None, B): BVALUE, }) self.assertEqual(handler.ignore_depth, 1) def test_startElementNS_w_ignore_depth_already_set(self): context = FauxContext() handler = self._makeOne(context, True) handler.ignore_depth = 1 # No locator set: we won't need it, as an ancestor had a # failed condition. handler.startElementNS((NS, FOO), FOO, {(XXX, SPLAT): SPLATV, (None, A): AVALUE, (None, B): BVALUE, }) self.assertEqual(handler.ignore_depth, 2) def _check_elementNS_context_raises(self, raises, catches, testing=False, meth='endElementNS', meth_args=((NS, FOO), FOO)): class ErrorContext(FauxContext): def end(self, *args): raise raises("xxx") begin = end class Info(object): _line = _col = None def end(self, line, col): self._line, self._col = line, col context = ErrorContext() info = Info() context.setInfo(info) locator = FauxLocator('tests//sample.zcml', 1, 1) handler = self._makeOne(context, testing) handler.setDocumentLocator(locator) locator.line, locator.column = 7, 16 meth = getattr(handler, meth) with self.assertRaises(catches) as exc: meth(*meth_args) return exc.exception, info def _check_startElementNS_context_begin_raises(self, raises, catches, testing=False): return self._check_elementNS_context_raises( raises, catches, testing, meth='startElementNS', meth_args=((NS, FOO), FOO, {(XXX, SPLAT): SPLATV, (None, A): AVALUE, (None, B): BVALUE, }) ) def test_startElementNS_context_begin_raises_wo_testing(self): from zope.configuration.xmlconfig import ZopeXMLConfigurationError raised, _ = self._check_startElementNS_context_begin_raises(AttributeError, ZopeXMLConfigurationError) info = raised.info self.assertEqual(info.file, 'tests//sample.zcml') self.assertEqual(info.line, 7) self.assertEqual(info.column, 16) def test_startElementNS_context_begin_raises_w_testing(self): self._check_startElementNS_context_begin_raises(AttributeError, AttributeError, True) def test_startElementNS_context_begin_raises_BaseException(self): class Bex(BaseException): pass self._check_startElementNS_context_begin_raises(Bex, Bex) def test_startElementNS_normal(self): # Integration test of startElementNS / endElementNS pair. context = FauxContext() locator = FauxLocator('tests//sample.zcml', 1, 1) handler = self._makeOne(context) handler.setDocumentLocator(locator) handler.startElementNS((NS, FOO), FOO, {(XXX, SPLAT): SPLATV, (None, A): AVALUE, (None, B): BVALUE, }) self.assertEqual(context.info.file, 'tests//sample.zcml') self.assertEqual(context.info.line, 1) self.assertEqual(context.info.column, 1) self.assertEqual(context.begin_args, ((NS, FOO), {'a': AVALUE, 'b': BVALUE})) self.assertFalse(context._end_called) def test_endElementNS_w_ignore_depth_already_set(self): context = FauxContext() handler = self._makeOne(context, True) handler.ignore_depth = 1 # No locator set: we won't need it, as we had a # failed condition. handler.endElementNS((NS, FOO), FOO) self.assertEqual(handler.ignore_depth, 0) def _check_endElementNS_context_end_raises(self, raises, catches, testing=False): return self._check_elementNS_context_raises(raises, catches, testing) def test_endElementNS_context_end_raises_wo_testing(self): from zope.configuration.xmlconfig import ZopeXMLConfigurationError raised, info = self._check_endElementNS_context_end_raises(AttributeError, ZopeXMLConfigurationError) self.assertIs(raised.info, info) self.assertEqual(raised.info._line, 7) self.assertEqual(raised.info._col, 16) def test_endElementNS_context_end_raises_w_testing(self): _, info = self._check_endElementNS_context_end_raises(AttributeError, AttributeError, True) self.assertEqual(info._line, 7) self.assertEqual(info._col, 16) def test_endElementNS_context_end_raises_BaseException(self): class Bex(BaseException): pass self._check_endElementNS_context_end_raises(Bex, Bex) def test_evaluateCondition_w_have_no_args(self): context = FauxContext() handler = self._makeOne(context) with self.assertRaises(ValueError) as exc: handler.evaluateCondition('have') self.assertEqual(str(exc.exception.args[0]), "Feature name missing: 'have'") def test_evaluateCondition_w_not_have_too_many_args(self): context = FauxContext() handler = self._makeOne(context) with self.assertRaises(ValueError) as exc: handler.evaluateCondition('not-have a b') self.assertEqual(str(exc.exception.args[0]), "Only one feature allowed: 'not-have a b'") def test_evaluateCondition_w_have_miss(self): context = FauxContext() handler = self._makeOne(context) self.assertFalse(handler.evaluateCondition('have feature')) def test_evaluateCondition_w_have_hit(self): context = FauxContext() context._features = ('feature',) handler = self._makeOne(context) self.assertTrue(handler.evaluateCondition('have feature')) def test_evaluateCondition_w_not_have_miss(self): context = FauxContext() context._features = ('feature',) handler = self._makeOne(context) self.assertFalse(handler.evaluateCondition('not-have feature')) def test_evaluateCondition_w_not_have_hit(self): context = FauxContext() handler = self._makeOne(context) self.assertTrue(handler.evaluateCondition('not-have feature')) def test_evaluateCondition_w_installed_no_args(self): context = FauxContext() handler = self._makeOne(context) with self.assertRaises(ValueError) as exc: handler.evaluateCondition('installed') self.assertEqual(str(exc.exception.args[0]), "Package name missing: 'installed'") def test_evaluateCondition_w_not_installed_too_many_args(self): context = FauxContext() handler = self._makeOne(context) with self.assertRaises(ValueError) as exc: handler.evaluateCondition('not-installed a b') self.assertEqual(str(exc.exception.args[0]), "Only one package allowed: 'not-installed a b'") def test_evaluateCondition_w_installed_miss(self): context = FauxContext() handler = self._makeOne(context) self.assertFalse( handler.evaluateCondition('installed nonsuch.package')) def test_evaluateCondition_w_installed_hit(self): context = FauxContext() handler = self._makeOne(context) self.assertTrue(handler.evaluateCondition('installed os')) def test_evaluateCondition_w_not_installed_miss(self): context = FauxContext() handler = self._makeOne(context) self.assertFalse(handler.evaluateCondition('not-installed os')) def test_evaluateCondition_w_not_installed_hit(self): context = FauxContext() handler = self._makeOne(context) self.assertTrue( handler.evaluateCondition('not-installed nonsuch.package')) def test_evaluateCondition_w_unknown_verb(self): context = FauxContext() handler = self._makeOne(context) with self.assertRaises(ValueError) as exc: handler.evaluateCondition('nonesuch') self.assertEqual(str(exc.exception.args[0]), "Invalid ZCML condition: 'nonesuch'") def test_endElementNS_normal(self): class Info(object): _line = _col = None def end(self, line, col): self._line, self._col = line, col context = FauxContext() info = Info() context.setInfo(info) locator = FauxLocator('tests//sample.zcml', 7, 16) handler = self._makeOne(context, True) handler.setDocumentLocator(locator) handler.endElementNS((NS, FOO), FOO) self.assertEqual(context.info._line, 7) self.assertEqual(context.info._col, 16) self.assertTrue(context._end_called) class Test_processxmlfile(unittest.TestCase): def _callFUT(self, *args, **kw): from zope.configuration.xmlconfig import processxmlfile return processxmlfile(*args, **kw) def test_w_empty_xml(self): from io import StringIO from zope.configuration.config import ConfigurationMachine from zope.configuration.xmlconfig import registerCommonDirectives from zope.configuration.xmlconfig import ZopeSAXParseException context = ConfigurationMachine() registerCommonDirectives(context) with self.assertRaises(ZopeSAXParseException) as exc: self._callFUT(StringIO(), context) self.assertEqual(str(exc.exception.evalue), ':1:0: no element found') def test_w_valid_xml_fp(self): # Integration test, really from zope.configuration.config import ConfigurationMachine from zope.configuration.xmlconfig import registerCommonDirectives from zope.configuration.tests.samplepackage import foo context = ConfigurationMachine() registerCommonDirectives(context) with open(path("samplepackage", "configure.zcml")) as file: self._callFUT(file, context) self.assertEqual(foo.data, []) context.execute_actions() data = foo.data.pop() self.assertEqual(data.args, (('x', (b'blah')), ('y', 0))) self.assertEqual(clean_info_path(repr(data.info)), 'File "tests/samplepackage/configure.zcml", line 12.2-12.29') self.assertEqual(clean_info_path(str(data.info)), 'File "tests/samplepackage/configure.zcml", line 12.2-12.29\n' + ' ') self.assertEqual(data.package, None) self.assertEqual(data.basepath, None) class Test_openInOrPlain(unittest.TestCase): def _callFUT(self, *args, **kw): from zope.configuration.xmlconfig import openInOrPlain return openInOrPlain(*args, **kw) def _makeFilename(self, fn): import os from zope.configuration.tests.samplepackage import __file__ return os.path.join(os.path.dirname(__file__), fn) def test_file_present(self): import os with self._callFUT(self._makeFilename('configure.zcml')) as fp: self.assertEqual(os.path.basename(fp.name), 'configure.zcml') def test_file_missing_but_dot_in_present(self): import os with self._callFUT(self._makeFilename('foo.zcml')) as fp: self.assertEqual(os.path.basename(fp.name), 'foo.zcml.in') def test_file_missing_and_dot_in_not_present(self): import errno with self.assertRaises(IOError) as exc: self._callFUT(self._makeFilename('nonesuch.zcml')) self.assertEqual(exc.exception.errno, errno.ENOENT) class Test_include(unittest.TestCase): def _callFUT(self, *args, **kw): from zope.configuration.xmlconfig import include return include(*args, **kw) def test_both_file_and_files_passed(self): context = FauxContext() with self.assertRaises(ValueError) as exc: self._callFUT( context, 'tests//sample.zcml', files=['tests/*.zcml']) self.assertEqual(str(exc.exception), "Must specify only one of file or files") def test_neither_file_nor_files_passed_already_seen(self): from zope.configuration import xmlconfig from zope.configuration.config import ConfigurationMachine from zope.configuration.xmlconfig import registerCommonDirectives from zope.configuration.tests import samplepackage context = ConfigurationMachine() registerCommonDirectives(context) context.package = samplepackage fqn = _packageFile(samplepackage, 'configure.zcml') context._seen_files.add(fqn) logger = LoggerStub() with _Monkey(xmlconfig, logger=logger): self._callFUT(context) #skips self.assertEqual(len(logger.debugs), 0) self.assertEqual(len(context.actions), 0) def test_neither_file_nor_files_passed(self): from zope.configuration import xmlconfig from zope.configuration.config import ConfigurationMachine from zope.configuration.xmlconfig import registerCommonDirectives from zope.configuration.tests import samplepackage from zope.configuration.tests.samplepackage import foo context = ConfigurationMachine() registerCommonDirectives(context) before_stack = context.stack[:] context.package = samplepackage fqn = _packageFile(samplepackage, 'configure.zcml') logger = LoggerStub() with _Monkey(xmlconfig, logger=logger): self._callFUT(context) self.assertEqual(len(logger.debugs), 1) self.assertEqual(logger.debugs[0], ('include %s', (fqn,), {})) self.assertEqual(len(context.actions), 1) action = context.actions[0] self.assertEqual(action['callable'], foo.data.append) self.assertEqual(action['includepath'], (fqn,)) self.assertEqual(context.stack, before_stack) self.assertEqual(len(context._seen_files), 1) self.assertIn(fqn, context._seen_files) def test_w_file_passed(self): from zope.configuration import xmlconfig from zope.configuration.config import ConfigurationMachine from zope.configuration.xmlconfig import registerCommonDirectives from zope.configuration import tests from zope.configuration.tests import simple context = ConfigurationMachine() registerCommonDirectives(context) before_stack = context.stack[:] context.package = tests fqn = _packageFile(tests, 'simple.zcml') logger = LoggerStub() with _Monkey(xmlconfig, logger=logger): self._callFUT(context, 'simple.zcml') self.assertEqual(len(logger.debugs), 1) self.assertEqual(logger.debugs[0], ('include %s', (fqn,), {})) self.assertEqual(len(context.actions), 3) action = context.actions[0] self.assertEqual(action['callable'], simple.file_registry.append) self.assertEqual(action['includepath'], (fqn,)) self.assertEqual(action['args'][0].path, _packageFile(tests, 'simple.py')) action = context.actions[1] self.assertEqual(action['callable'], simple.file_registry.append) self.assertEqual(action['includepath'], (fqn,)) self.assertEqual(action['args'][0].path, _packageFile(tests, 'simple.zcml')) action = context.actions[2] self.assertEqual(action['callable'], simple.file_registry.append) self.assertEqual(action['includepath'], (fqn,)) self.assertEqual(action['args'][0].path, _packageFile(tests, '__init__.py')) self.assertEqual(context.stack, before_stack) self.assertEqual(len(context._seen_files), 1) self.assertIn(fqn, context._seen_files) def test_w_files_passed_and_package(self): from zope.configuration import xmlconfig from zope.configuration.config import ConfigurationMachine from zope.configuration.xmlconfig import registerCommonDirectives from zope.configuration.tests import samplepackage from zope.configuration.tests.samplepackage import foo context = ConfigurationMachine() registerCommonDirectives(context) before_stack = context.stack[:] fqn1 = _packageFile(samplepackage, 'baz1.zcml') fqn2 = _packageFile(samplepackage, 'baz2.zcml') fqn3 = _packageFile(samplepackage, 'baz3.zcml') logger = LoggerStub() with _Monkey(xmlconfig, logger=logger): self._callFUT(context, package=samplepackage, files='baz*.zcml') self.assertEqual(len(logger.debugs), 3) self.assertEqual(logger.debugs[0], ('include %s', (fqn1,), {})) self.assertEqual(logger.debugs[1], ('include %s', (fqn2,), {})) self.assertEqual(logger.debugs[2], ('include %s', (fqn3,), {})) self.assertEqual(len(context.actions), 2) action = context.actions[0] self.assertEqual(action['callable'], foo.data.append) self.assertEqual(action['includepath'], (fqn2,)) self.assertIsInstance(action['args'][0], foo.stuff) self.assertEqual(action['args'][0].args, (('x', (b'foo')), ('y', 2))) action = context.actions[1] self.assertEqual(action['callable'], foo.data.append) self.assertEqual(action['includepath'], (fqn3,)) self.assertIsInstance(action['args'][0], foo.stuff) self.assertEqual(action['args'][0].args, (('x', (b'foo')), ('y', 3))) self.assertEqual(context.stack, before_stack) self.assertEqual(len(context._seen_files), 3) self.assertIn(fqn1, context._seen_files) self.assertIn(fqn2, context._seen_files) self.assertIn(fqn3, context._seen_files) class Test_exclude(unittest.TestCase): def _callFUT(self, *args, **kw): from zope.configuration.xmlconfig import exclude return exclude(*args, **kw) def test_both_file_and_files_passed(self): context = FauxContext() with self.assertRaises(ValueError) as exc: self._callFUT( context, 'tests//sample.zcml', files=['tests/*.zcml']) self.assertEqual(str(exc.exception), "Must specify only one of file or files") def test_neither_file_nor_files_passed(self): from zope.configuration.config import ConfigurationMachine from zope.configuration.tests import samplepackage context = ConfigurationMachine() context.package = samplepackage fqn = _packageFile(samplepackage, 'configure.zcml') self._callFUT(context) self.assertEqual(len(context.actions), 0) self.assertEqual(len(context._seen_files), 1) self.assertIn(fqn, context._seen_files) def test_w_file_passed(self): from zope.configuration.config import ConfigurationMachine from zope.configuration import tests context = ConfigurationMachine() context.package = tests fqn = _packageFile(tests, 'simple.zcml') self._callFUT(context, 'simple.zcml') self.assertEqual(len(context.actions), 0) self.assertEqual(len(context._seen_files), 1) self.assertIn(fqn, context._seen_files) def test_w_files_passed_and_package(self): from zope.configuration.config import ConfigurationMachine from zope.configuration.tests import samplepackage context = ConfigurationMachine() fqn1 = _packageFile(samplepackage, 'baz1.zcml') fqn2 = _packageFile(samplepackage, 'baz2.zcml') fqn3 = _packageFile(samplepackage, 'baz3.zcml') self._callFUT(context, package=samplepackage, files='baz*.zcml') self.assertEqual(len(context.actions), 0) self.assertEqual(len(context._seen_files), 3) self.assertIn(fqn1, context._seen_files) self.assertIn(fqn2, context._seen_files) self.assertIn(fqn3, context._seen_files) def test_w_subpackage(self): from zope.configuration.config import ConfigurationMachine from zope.configuration.tests import excludedemo from zope.configuration.tests.excludedemo import sub context = ConfigurationMachine() fqne_spam = _packageFile(excludedemo, 'spam.zcml') fqne_config = _packageFile(excludedemo, 'configure.zcml') fqns_config = _packageFile(sub, 'configure.zcml') self._callFUT(context, package=sub) self.assertEqual(len(context.actions), 0) self.assertEqual(len(context._seen_files), 1) self.assertFalse(fqne_spam in context._seen_files) self.assertFalse(fqne_config in context._seen_files) self.assertIn(fqns_config, context._seen_files) class Test_includeOverrides(unittest.TestCase): def _callFUT(self, *args, **kw): from zope.configuration.xmlconfig import includeOverrides return includeOverrides(*args, **kw) def test_actions_have_parents_includepath(self): from zope.configuration import xmlconfig from zope.configuration.config import ConfigurationMachine from zope.configuration.xmlconfig import registerCommonDirectives from zope.configuration import tests from zope.configuration.tests import simple context = ConfigurationMachine() fqp = _packageFile(tests, 'configure.zcml') registerCommonDirectives(context) before_stack = context.stack[:] context.package = tests # dummy action, path from "previous" include context.includepath = (fqp,) def _callable(): raise AssertionError("should not be called") context.actions.append({'discriminator': None, 'callable': _callable, }) fqn = _packageFile(tests, 'simple.zcml') logger = LoggerStub() with _Monkey(xmlconfig, logger=logger): self._callFUT(context, 'simple.zcml') self.assertEqual(len(logger.debugs), 1) self.assertEqual(logger.debugs[0], ('include %s', (fqn,), {})) self.assertEqual(len(context.actions), 4) action = context.actions[0] self.assertEqual(action['discriminator'], None) self.assertEqual(action['callable'], _callable) action = context.actions[1] self.assertEqual(action['callable'], simple.file_registry.append) self.assertEqual(action['includepath'], (fqp,)) self.assertEqual(action['args'][0].path, _packageFile(tests, 'simple.py')) action = context.actions[2] self.assertEqual(action['callable'], simple.file_registry.append) self.assertEqual(action['includepath'], (fqp,)) self.assertEqual(action['args'][0].path, _packageFile(tests, 'simple.zcml')) action = context.actions[3] self.assertEqual(action['callable'], simple.file_registry.append) self.assertEqual(action['includepath'], (fqp,)) self.assertEqual(action['args'][0].path, _packageFile(tests, '__init__.py')) self.assertEqual(context.stack, before_stack) self.assertEqual(len(context._seen_files), 1) self.assertIn(fqn, context._seen_files) class Test_file(unittest.TestCase): def _callFUT(self, *args, **kw): from zope.configuration.xmlconfig import file return file(*args, **kw) def test_wo_execute_wo_context_wo_package(self): from zope.configuration import xmlconfig from zope.configuration.tests.samplepackage import foo file_name = path("samplepackage", "configure.zcml") logger = LoggerStub() with _Monkey(xmlconfig, logger=logger): context = self._callFUT(file_name, execute=False) self.assertEqual(len(logger.debugs), 1) self.assertEqual(logger.debugs[0], ('include %s', (file_name,), {})) self.assertEqual(len(foo.data), 0) self.assertEqual(len(context.actions), 1) action = context.actions[0] self.assertEqual(action['discriminator'], (('x', (b'blah')), ('y', 0))) self.assertEqual(action['callable'], foo.data.append) def test_wo_execute_wo_context_w_package(self): from zope.configuration import xmlconfig from zope.configuration.tests import samplepackage from zope.configuration.tests.samplepackage import foo file_name = path("samplepackage", "configure.zcml") logger = LoggerStub() with _Monkey(xmlconfig, logger=logger): context = self._callFUT('configure.zcml', package=samplepackage, execute=False) self.assertEqual(len(logger.debugs), 1) self.assertEqual(logger.debugs[0], ('include %s', (file_name,), {})) self.assertEqual(len(foo.data), 0) self.assertTrue(context.package is samplepackage) self.assertEqual(len(context.actions), 1) action = context.actions[0] self.assertEqual(action['discriminator'], (('x', (b'blah')), ('y', 0))) self.assertEqual(action['callable'], foo.data.append) def test_wo_execute_w_context(self): from zope.configuration import xmlconfig from zope.configuration.config import ConfigurationMachine from zope.configuration.xmlconfig import registerCommonDirectives from zope.configuration.tests import samplepackage from zope.configuration.tests.samplepackage import foo context = ConfigurationMachine() context.package = samplepackage registerCommonDirectives(context) file_name = path("samplepackage", "configure.zcml") logger = LoggerStub() with _Monkey(xmlconfig, logger=logger): ret = self._callFUT('configure.zcml', context=context, execute=False) self.assertTrue(ret is context) self.assertEqual(len(logger.debugs), 1) self.assertEqual(logger.debugs[0], ('include %s', (file_name,), {})) self.assertEqual(len(foo.data), 0) self.assertEqual(len(context.actions), 1) action = context.actions[0] self.assertEqual(action['discriminator'], (('x', (b'blah')), ('y', 0))) self.assertEqual(action['callable'], foo.data.append) def test_w_execute(self): import os from zope.configuration import xmlconfig from zope.configuration.tests.samplepackage import foo file_name = path("samplepackage", "configure.zcml") logger = LoggerStub() with _Monkey(xmlconfig, logger=logger): self._callFUT(file_name) self.assertEqual(len(logger.debugs), 1) self.assertEqual(logger.debugs[0], ('include %s', (file_name,), {})) data = foo.data.pop() self.assertEqual(data.args, (('x', (b'blah')), ('y', 0))) self.assertTrue( data.info.file.endswith( os.path.normpath('tests/samplepackage/configure.zcml'))) self.assertEqual(data.info.line, 12) self.assertEqual(data.info.column, 2) self.assertEqual(data.info.eline, 12) self.assertEqual(data.info.ecolumn, 29) self.assertEqual(data.package, None) self.assertTrue(data.basepath.endswith( os.path.normpath('tests/samplepackage'))) class Test_string(unittest.TestCase): def _callFUT(self, *args, **kw): from zope.configuration.xmlconfig import string return string(*args, **kw) def test_wo_execute_wo_context(self): from zope.configuration.tests.samplepackage import foo file_name = path("samplepackage", "configure.zcml") with open(file_name) as f: xml = f.read() context = self._callFUT(xml, execute=False) self.assertEqual(len(foo.data), 0) self.assertEqual(len(context.actions), 1) action = context.actions[0] self.assertEqual(action['discriminator'], (('x', (b'blah')), ('y', 0))) self.assertEqual(action['callable'], foo.data.append) def test_wo_execute_w_context(self): from zope.configuration.config import ConfigurationMachine from zope.configuration.xmlconfig import registerCommonDirectives from zope.configuration.tests.samplepackage import foo context = ConfigurationMachine() registerCommonDirectives(context) file_name = path("samplepackage", "configure.zcml") with open(file_name) as f: xml = f.read() ret = self._callFUT(xml, context=context, execute=False) self.assertTrue(ret is context) self.assertEqual(len(foo.data), 0) self.assertEqual(len(context.actions), 1) action = context.actions[0] self.assertEqual(action['discriminator'], (('x', (b'blah')), ('y', 0))) self.assertEqual(action['callable'], foo.data.append) def test_w_execute(self): from zope.configuration.tests.samplepackage import foo file_name = path("samplepackage", "configure.zcml") with open(file_name) as f: xml = f.read() self._callFUT(xml) data = foo.data.pop() self.assertEqual(data.args, (('x', (b'blah')), ('y', 0))) self.assertTrue(data.info.file, '') self.assertEqual(data.info.line, 12) self.assertEqual(data.info.column, 2) self.assertEqual(data.info.eline, 12) self.assertEqual(data.info.ecolumn, 29) self.assertEqual(data.package, None) self.assertEqual(data.basepath, None) class XMLConfigTests(unittest.TestCase): def setUp(self): from zope.configuration.xmlconfig import _clearContext from zope.configuration.tests.samplepackage.foo import data _clearContext() del data[:] def tearDown(self): from zope.configuration.xmlconfig import _clearContext from zope.configuration.tests.samplepackage.foo import data _clearContext() del data[:] def _getTargetClass(self): from zope.configuration.xmlconfig import XMLConfig return XMLConfig def _makeOne(self, *args, **kw): return self._getTargetClass()(*args, **kw) def test_ctor_w_global_context_missing(self): import os from zope.configuration import xmlconfig from zope.configuration.tests.samplepackage import foo here = os.path.dirname(__file__) path = os.path.join(here, "samplepackage", "configure.zcml") logger = LoggerStub() xmlconfig._context = None with _Monkey(xmlconfig, logger=logger): xc = self._makeOne(path) self.assertEqual(len(logger.debugs), 1) self.assertEqual(logger.debugs[0], ('include %s', (path,), {})) self.assertEqual(len(foo.data), 0) # no execut_actions self.assertEqual(len(xc.context.actions), 1) action = xc.context.actions[0] self.assertEqual(action['discriminator'], (('x', (b'blah')), ('y', 0))) self.assertEqual(action['callable'], foo.data.append) def test_ctor(self): from zope.configuration import xmlconfig from zope.configuration.tests import samplepackage from zope.configuration.tests.samplepackage import foo fqn = _packageFile(samplepackage, 'configure.zcml') logger = LoggerStub() with _Monkey(xmlconfig, logger=logger): xc = self._makeOne(fqn) self.assertEqual(len(logger.debugs), 1) self.assertEqual(logger.debugs[0], ('include %s', (fqn,), {})) self.assertEqual(len(foo.data), 0) # no execut_actions self.assertEqual(len(xc.context.actions), 1) action = xc.context.actions[0] self.assertEqual(action['discriminator'], (('x', (b'blah')), ('y', 0))) self.assertEqual(action['callable'], foo.data.append) def test_ctor_w_module(self): from zope.configuration import xmlconfig from zope.configuration.tests.samplepackage import foo from zope.configuration.tests import samplepackage fqn = _packageFile(samplepackage, 'configure.zcml') logger = LoggerStub() with _Monkey(xmlconfig, logger=logger): xc = self._makeOne("configure.zcml", samplepackage) self.assertEqual(len(logger.debugs), 1) self.assertEqual(logger.debugs[0], ('include %s', (fqn,), {})) self.assertEqual(len(foo.data), 0) # no execut_actions self.assertEqual(len(xc.context.actions), 1) action = xc.context.actions[0] self.assertEqual(action['discriminator'], (('x', (b'blah')), ('y', 0))) self.assertEqual(action['callable'], foo.data.append) def test___call__(self): import os from zope.configuration import xmlconfig from zope.configuration.tests import samplepackage from zope.configuration.tests.samplepackage import foo fqn = _packageFile(samplepackage, 'configure.zcml') logger = LoggerStub() with _Monkey(xmlconfig, logger=logger): xc = self._makeOne(fqn) self.assertEqual(len(logger.debugs), 1) self.assertEqual(logger.debugs[0], ('include %s', (fqn,), {})) self.assertEqual(len(foo.data), 0) xc() # call to process the actions self.assertEqual(len(foo.data), 1) data = foo.data.pop(0) self.assertEqual(data.args, (('x', (b'blah')), ('y', 0))) self.assertTrue( data.info.file.endswith( os.path.normpath('tests/samplepackage/configure.zcml'))) self.assertEqual(data.info.line, 12) self.assertEqual(data.info.column, 2) self.assertEqual(data.info.eline, 12) self.assertEqual(data.info.ecolumn, 29) class Test_xmlconfig(unittest.TestCase): def setUp(self): from zope.configuration.xmlconfig import _clearContext from zope.configuration.tests.samplepackage.foo import data _clearContext() del data[:] def tearDown(self): from zope.configuration.xmlconfig import _clearContext from zope.configuration.tests.samplepackage.foo import data _clearContext() del data[:] def _callFUT(self, *args, **kw): from zope.configuration.xmlconfig import xmlconfig return xmlconfig(*args, **kw) def test_wo_testing_passed(self): import os from zope.configuration import xmlconfig from zope.configuration.tests import samplepackage from zope.configuration.tests.samplepackage import foo def _assertTestingFalse(func): def _wrapper(*args, **kw): assert not kw['testing'] return func(*args, **kw) return _wrapper fqn = _packageFile(samplepackage, 'configure.zcml') context = xmlconfig._getContext() context.execute_actions = _assertTestingFalse(context.execute_actions) with _Monkey(xmlconfig, processxmlfile=_assertTestingFalse( xmlconfig.processxmlfile)): self._callFUT(open(fqn), False) self.assertEqual(len(foo.data), 1) data = foo.data.pop(0) self.assertEqual(data.args, (('x', (b'blah')), ('y', 0))) self.assertTrue( data.info.file.endswith( os.path.normpath('tests/samplepackage/configure.zcml'))) self.assertEqual(data.info.line, 12) self.assertEqual(data.info.column, 2) self.assertEqual(data.info.eline, 12) self.assertEqual(data.info.ecolumn, 29) def test_w_testing_passed(self): import os from zope.configuration import xmlconfig from zope.configuration.tests import samplepackage from zope.configuration.tests.samplepackage import foo def _assertTestingTrue(func): def _wrapper(*args, **kw): assert kw['testing'] return func(*args, **kw) return _wrapper fqn = _packageFile(samplepackage, 'configure.zcml') context = xmlconfig._getContext() context.execute_actions = _assertTestingTrue(context.execute_actions) with _Monkey(xmlconfig, processxmlfile=_assertTestingTrue( xmlconfig.processxmlfile)): self._callFUT(open(fqn), True) self.assertEqual(len(foo.data), 1) data = foo.data.pop(0) self.assertEqual(data.args, (('x', (b'blah')), ('y', 0))) self.assertTrue( data.info.file.endswith( os.path.normpath('tests/samplepackage/configure.zcml'))) self.assertEqual(data.info.line, 12) self.assertEqual(data.info.column, 2) self.assertEqual(data.info.eline, 12) self.assertEqual(data.info.ecolumn, 29) class Test_testxmlconfig(unittest.TestCase): def setUp(self): from zope.configuration.xmlconfig import _clearContext from zope.configuration.tests.samplepackage.foo import data _clearContext() del data[:] def tearDown(self): from zope.configuration.xmlconfig import _clearContext from zope.configuration.tests.samplepackage.foo import data _clearContext() del data[:] def _callFUT(self, *args, **kw): from zope.configuration.xmlconfig import testxmlconfig return testxmlconfig(*args, **kw) def test_w_testing_passed(self): import os from zope.configuration import xmlconfig from zope.configuration.tests import samplepackage from zope.configuration.tests.samplepackage import foo def _assertTestingTrue(func): def _wrapper(*args, **kw): assert kw['testing'] return func(*args, **kw) return _wrapper fqn = _packageFile(samplepackage, 'configure.zcml') context = xmlconfig._getContext() context.execute_actions = _assertTestingTrue(context.execute_actions) with _Monkey(xmlconfig, processxmlfile=_assertTestingTrue( xmlconfig.processxmlfile)): self._callFUT(open(fqn)) self.assertEqual(len(foo.data), 1) data = foo.data.pop(0) self.assertEqual(data.args, (('x', (b'blah')), ('y', 0))) self.assertTrue( data.info.file.endswith( os.path.normpath('tests/samplepackage/configure.zcml'))) self.assertEqual(data.info.line, 12) self.assertEqual(data.info.column, 2) self.assertEqual(data.info.eline, 12) self.assertEqual(data.info.ecolumn, 29) 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): includepath = () _features = () _end_called = False 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 hasFeature(self, feature): return feature in self._features def path(*p): import os return os.path.join(os.path.dirname(__file__), *p) def clean_info_path(s): import os 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): import os s = s[s.rfind("tests"):] s = s.replace(os.sep, '/') return s def clean_actions(actions): return [ { 'discriminator': action['discriminator'], 'info': clean_info_path(repr(action['info'])), 'includepath': [clean_path(p) for p in action['includepath']], } for action in actions ] def clean_text_w_paths(error): r = [] for line in str(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 _packageFile(package, filename): import os return os.path.join(os.path.dirname(package.__file__), filename) class _Monkey(object): def __init__(self, module, **replacements): self.module = module self.orig = {} self.replacements = replacements def __enter__(self): for k, v in self.replacements.items(): orig = getattr(self.module, k, self) if orig is not self: self.orig[k] = orig setattr(self.module, k, v) def __exit__(self, *exc_info): for k in self.replacements: if k in self.orig: setattr(self.module, k, self.orig[k]) else: # pragma: no cover delattr(self.module, k) class LoggerStub(object): debugs = errors = warnings = infos = () def __init__(self): def make_append(lst): return lambda msg, *args, **kwargs: lst.append((msg, args, kwargs)) for name in 'error', 'warning', 'info', 'debug': lst = [] setattr(self, name + 's', lst) func = make_append(lst) setattr(self, name, func) zope.configuration-4.3.1/src/zope/configuration/tests/simple.py0000644000076500000240000000315213430472367024657 0ustar macstaff00000000000000############################################################################## # # Copyright (c) 2003 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """ Utilities for the 'simple directive' section in the narrative docs. """ from zope.interface import Interface from zope.schema import Text from zope.configuration.fields import Path class IRegisterFile(Interface): path = Path( title=u"File path", description=u"This is the path name of the file to be registered.", ) title = Text( title=u"Short summary of the file", description=u"This will be used in file listings", required=False ) class FileInfo(object): def __init__(self, path, title, description, info): (self.path, self.title, self.description, self.info ) = path, title, description, info file_registry = [] def registerFile(context, path, title=u""): info = context.info description = info.text.strip() context.action(discriminator=('RegisterFile', path), callable=file_registry.append, args=(FileInfo(path, title, description, info),) ) zope.configuration-4.3.1/src/zope/configuration/tests/test_name.py0000644000076500000240000001063613430472367025352 0ustar macstaff00000000000000############################################################################## # # Copyright (c) 20!2 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 zope.configuration.name """ import unittest class Test_resolve(unittest.TestCase): def _callFUT(self, *args, **kw): from zope.configuration.name import resolve return resolve(*args, **kw) def test_top_level_module(self): import os self.assertTrue(self._callFUT('os') is os) def test_nested_module(self): import os.path self.assertTrue(self._callFUT('os.path') is os.path) def test_function_in_module(self): import os.path self.assertTrue(self._callFUT('os.path.join') is os.path.join) def test_importable_but_not_attr_of_parent(self): import sys import zope.configuration.tests as zct self.assertFalse('notyet' in zct.__dict__) mod = self._callFUT('zope.configuration.tests.notyet') self.assertTrue(mod is zct.notyet) del zct.notyet del sys.modules['zope.configuration.tests.notyet'] def test_function_in_module_relative(self): import os.path self.assertTrue(self._callFUT('.join', 'os.path') is os.path.join) def test_class_in_module(self): from zope.configuration.tests.directives import Complex self.assertIs( self._callFUT('zope.configuration.tests.directives.Complex'), Complex) def test_class_w_same_name_as_module(self): from zope.configuration.tests.samplepackage.NamedForClass \ import NamedForClass self.assertIs( self._callFUT( 'zope.configuration.tests.samplepackage.NamedForClass+'), NamedForClass) self.assertIs( self._callFUT( 'zope.configuration.tests.samplepackage.NamedForClass.'), NamedForClass) class Test_getNormalizedName(unittest.TestCase): def _callFUT(self, *args, **kw): from zope.configuration.name import getNormalizedName return getNormalizedName(*args, **kw) def test_no_dots(self): self.assertEqual(self._callFUT('os', None), 'os') def test_one_dot(self): self.assertEqual(self._callFUT('os.path', None), 'os.path') def test_two_dots(self): self.assertEqual(self._callFUT('os.path.join', None), 'os.path.join') def test_relative(self): self.assertEqual(self._callFUT('.join', 'os.path'), 'os.path.join') def test_repeat_plus(self): self.assertEqual( self._callFUT('zope.configuration.tests.NamedForClass+', None), 'zope.configuration.tests.NamedForClass+') def test_repeat_dot(self): self.assertEqual( self._callFUT('zope.configuration.tests.NamedForClass.', None), 'zope.configuration.tests.NamedForClass+') def test_repeat_inferred(self): self.assertEqual( self._callFUT( 'zope.configuration.tests.NamedForClass.NamedForClass', None), 'zope.configuration.tests.NamedForClass+') class Test_path(unittest.TestCase): def _callFUT(self, *args, **kw): from zope.configuration.name import path return path(*args, **kw) def test_absolute(self): import os absolute_path = os.path.abspath('/absolute') self.assertEqual(self._callFUT(absolute_path), os.path.normpath(absolute_path)) def test_relative_bogus_package(self): with self.assertRaises(ImportError): self._callFUT('', 'no.such.package.exists') def test_relative_empty(self): import os self.assertEqual(self._callFUT('', 'zope.configuration.tests'), os.path.dirname(__file__)) def test_relative_w_file(self): import os self.assertEqual( self._callFUT('configure.zcml', 'zope.configuration.tests'), os.path.join(os.path.dirname(__file__), 'configure.zcml')) zope.configuration-4.3.1/src/zope/configuration/tests/sample.zcml0000644000076500000240000000044313430472367025164 0ustar macstaff00000000000000 zope.configuration-4.3.1/src/zope/configuration/tests/bad.py0000644000076500000240000000011213430472367024105 0ustar macstaff00000000000000# I'm bad. I want to be bad. Don't try to change me. raise ImportError() zope.configuration-4.3.1/src/zope/configuration/tests/schema.zcml0000644000076500000240000000303013430472367025136 0ustar macstaff00000000000000 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-4.3.1/src/zope/configuration/tests/victim.py0000644000076500000240000000015713430472367024663 0ustar macstaff00000000000000from __future__ import absolute_import from zope.configuration.tests import bad # pylint:disable=unused-import zope.configuration-4.3.1/src/zope/configuration/tests/nested.py0000644000076500000240000001017113430472367024647 0ustar macstaff00000000000000############################################################################## # # Copyright (c) 2003 Zope Foundation and Contributors. # All Rights Reserved. # # This software is subject to the provisions of the Zope Public License, # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS # FOR A PARTICULAR PURPOSE. # ############################################################################## """ Utilities for the 'nested directive' section in the narrative docs. """ from zope.interface import Attribute from zope.interface import Interface from zope.interface import implementer from zope.schema import NativeStringLine from zope.schema import Id from zope.schema import Int from zope.schema import Text from zope.schema import TextLine from zope.configuration.config import GroupingContextDecorator from zope.configuration.config import IConfigurationContext from zope.configuration.fields import Bool schema_registry = {} class ISchemaInfo(Interface): """Parameter schema for the schema directive """ name = TextLine( title=u"The schema name", description=u"This is a descriptive name for the schema.", ) id = Id(title=u"The unique id for the schema") class ISchema(Interface): """Interface that distinguishes the schema directive """ fields = Attribute("Dictionary of field definitions") @implementer(IConfigurationContext, ISchema) class Schema(GroupingContextDecorator): """Handle schema directives """ def __init__(self, context, name, id): self.context, self.name, self.id = context, name, id self.fields = {} def after(self): schema = Interface.__class__( self.name, (Interface, ), self.fields ) schema.__doc__ = self.info.text.strip() self.action( discriminator=('schema', self.id), callable=schema_registry.__setitem__, args=(self.id, schema), ) class IFieldInfo(Interface): name = NativeStringLine( title=u"The field name", ) title = TextLine( title=u"Title", description=u"A short summary or label", default=u"", required=False, ) required = Bool( title=u"Required", description=u"Determines whether a value is required.", default=True) readonly = Bool( title=u"Read Only", description=u"Can the value be modified?", required=False, default=False) class ITextInfo(IFieldInfo): min_length = Int( title=u"Minimum length", description=( u"Value after whitespace processing cannot have less than " 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 = 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, Text, **kw) class IIntInfo(IFieldInfo): min = Int( title=u"Start of the range", required=False, default=None ) max = Int( title=u"End of the range (excluding the value itself)", required=False, default=None ) def intField(context, **kw): field(context, Int, **kw) zope.configuration-4.3.1/src/zope/configuration/xmlconfig.py0000644000076500000240000005502613430472367024221 0ustar macstaff00000000000000############################################################################## # # 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 from glob import glob import logging import io import os import sys 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.interface import Interface from zope.schema import NativeStringLine from zope.configuration.config import ConfigurationMachine from zope.configuration.config import defineGroupingDirective from zope.configuration.config import defineSimpleDirective from zope.configuration.config import GroupingContextDecorator from zope.configuration.config import GroupingStackItem from zope.configuration.config import resolveConflicts from zope.configuration.exceptions import ConfigurationError from zope.configuration.exceptions import ConfigurationWrapperError from zope.configuration.fields import GlobalObject from zope.configuration.zopeconfigure import IZopeConfigure from zope.configuration.zopeconfigure import ZopeConfigure from zope.configuration._compat import reraise __all__ = [ 'ParserInfo', 'ConfigurationHandler', 'processxmlfile', 'openInOrPlain', 'IInclude', 'include', 'exclude', 'includeOverrides', 'registerCommonDirectives', 'file', 'string', 'XMLConfig', 'xmlconfig', 'testxmlconfig', ] logger = logging.getLogger("config") ZCML_NAMESPACE = "http://namespaces.zope.org/zcml" ZCML_CONDITION = (ZCML_NAMESPACE, u"condition") class ZopeXMLConfigurationError(ConfigurationWrapperError): """ Zope XML Configuration error These errors are wrappers for other errors. They include configuration info and the wrapped error type and value. Example >>> from zope.configuration.xmlconfig import ZopeXMLConfigurationError >>> v = ZopeXMLConfigurationError("blah", AttributeError("xxx")) >>> print(v) 'blah' AttributeError: xxx """ USE_INFO_REPR = True class ZopeSAXParseException(ConfigurationWrapperError): """ Sax Parser errors as a ConfigurationError. Example >>> from zope.configuration.xmlconfig import ZopeSAXParseException >>> v = ZopeSAXParseException("info", Exception("foo.xml:12:3:Not well formed")) >>> print(v) info Exception: foo.xml:12:3:Not well formed """ class ParserInfo(object): r""" Information about a directive based on parser data This includes the directive location, as well as text data contained in the directive. Example >>> from zope.configuration.xmlconfig import ParserInfo >>> info = ParserInfo('tests//sample.zcml', 1, 0) >>> info File "tests//sample.zcml", line 1.0 >>> print(info) File "tests//sample.zcml", line 1.0 >>> info.characters("blah\n") >>> info.characters("blah") >>> info.text 'blah\nblah' >>> info.end(7, 0) >>> info File "tests//sample.zcml", line 1.0-7.0 >>> print(info) File "tests//sample.zcml", line 1.0-7.0 """ 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: with open(file) as f: lines = f.readlines()[self.line-1:self.eline] except IOError: src = " Could not read source." else: ecolumn = self.ecolumn if lines[-1][ecolumn:ecolumn+2] == '', ecolumn) if l >= 0: lines[-1] = lines[-1][:l+1] else: # pragma: no cover lines[-1] = lines[-1][:ecolumn+1] column = self.column if lines[0][:column].strip(): # pragma: no cover # Remove text before start if it's noy whitespace lines[0] = lines[0][self.column:] pad = u' ' blank = u'' try: src = blank.join([pad + l for l in lines]) except UnicodeDecodeError: # pragma: no cover # XXX: # I hope so most internation zcml will use UTF-8 as encoding # otherwise this code must be made more clever src = blank.join([pad + 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" % (repr(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. """ locator = None def __init__(self, context, testing=False): 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 _handle_exception(self, ex, info): if self.testing: raise if isinstance(ex, ConfigurationError): ex.add_details(repr(info)) raise exc = ZopeXMLConfigurationError(info, ex) reraise(exc, None, sys.exc_info()[2]) 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 Exception as ex: self._handle_exception(ex, info) 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`` and ``not-have`` verbs each take one argument: the name of a feature: >>> from zope.configuration.config import ConfigurationContext >>> from zope.configuration.xmlconfig import ConfigurationHandler >>> context = ConfigurationContext() >>> context.provideFeature('apidoc') >>> c = ConfigurationHandler(context, testing=True) >>> c.evaluateCondition("have apidoc") True >>> c.evaluateCondition("not-have apidoc") False >>> c.evaluateCondition("have onlinehelp") False >>> c.evaluateCondition("not-have onlinehelp") True Ill-formed expressions raise an error: >>> c.evaluateCondition("want apidoc") Traceback (most recent call last): ... ValueError: Invalid ZCML condition: 'want apidoc' >>> c.evaluateCondition("have x y") Traceback (most recent call last): ... ValueError: Only one feature allowed: 'have x y' >>> c.evaluateCondition("have") Traceback (most recent call last): ... ValueError: Feature name missing: 'have' The ``installed`` and ``not-installed`` verbs each take one argument: the dotted name of a pacakge. If the pacakge is found, in other words, can be imported, then the condition will return true / false: >>> context = ConfigurationContext() >>> c = ConfigurationHandler(context, testing=True) >>> c.evaluateCondition('installed zope.interface') True >>> c.evaluateCondition('not-installed zope.interface') False >>> c.evaluateCondition('installed zope.foo') False >>> c.evaluateCondition('not-installed zope.foo') True Ill-formed expressions raise an error: >>> c.evaluateCondition("installed foo bar") Traceback (most recent call last): ... ValueError: Only one package allowed: 'installed foo bar' >>> c.evaluateCondition("installed") Traceback (most recent call last): ... ValueError: Package name missing: 'installed' """ arguments = expression.split(None) verb = arguments.pop(0) 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 Exception as ex: self._handle_exception(ex, info) def processxmlfile(file, context, testing=False): """Process a configuration file See examples in tests/test_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: reraise(ZopeSAXParseException(file, 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: >>> import os >>> from zope.configuration.xmlconfig import __file__ >>> from zope.configuration.xmlconfig import openInOrPlain >>> here = os.path.dirname(__file__) >>> path = os.path.join(here, 'tests', 'samplepackage', 'configure.zcml') >>> f = openInOrPlain(path) >>> f.name[-14:] 'configure.zcml' >>> f.close() But if we open foo.zcml, we'll get foo.zcml.in, since there isn't a foo.zcml: >>> path = os.path.join(here, 'tests', 'samplepackage', 'foo.zcml') >>> f = openInOrPlain(path) >>> f.name[-11:] 'foo.zcml.in' >>> f.close() Make sure other IOErrors are re-raised. We need to do this in a try-except block because different errors are raised on Windows and on Linux. >>> try: ... f = openInOrPlain('.') ... except IOError: ... print("passed") ... else: ... print("failed") passed """ try: return open(filename) except IOError as e: code, msg = e.args if code == errno.ENOENT: fn = filename + ".in" if os.path.exists(fn): return open(fn) raise 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 = NativeStringLine( title=u"Configuration file name", description=( u"The name of a configuration file to be included/" u"excluded, relative to the directive containing the " u"including configuration file." ), required=False, ) files = NativeStringLine( 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 = 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 """ if files: if file: raise ValueError("Must specify only one of file or files") elif not file: file = 'configure.zcml' # This is a tad tricky. We want to behave as a grouping directive. context = GroupingContextDecorator(_context) if package is not None: context.package = package context.basepath = None if files: paths = glob(context.path(files)) paths = sorted(zip([path.lower() for path in paths], paths)) paths = [path for (l, path) in paths] else: paths = [context.path(file)] for path in paths: if context.processFile(path): with openInOrPlain(path) as f: logger.debug("include %s", f.name) context.basepath = os.path.dirname(path) context.includepath = _context.includepath + (f.name, ) _context.stack.append(GroupingStackItem(context)) processxmlfile(f, context) 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 = GroupingContextDecorator(_context) if package is not None: context.package = package context.basepath = None if files: paths = glob(context.path(files)) paths = sorted(zip([path.lower() for path in paths], paths)) 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. Conflicting actions added by the named *file* or *files* are resolved before this directive completes. .. caution:: If you do not specify a *file*, then the default file of ``configure.zcml`` will be used. A common use is to set *file* to ``overrides.zcml``. """ # 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 resolveConflicts(_context.actions[nactions:]): action['includepath'] = includepath newactions.append(action) _context.actions[nactions:] = newactions def registerCommonDirectives(context): # We have to use the direct definition functions to define # a directive for all namespaces. defineSimpleDirective( context, "include", IInclude, include, namespace="*") defineSimpleDirective( context, "exclude", IInclude, exclude, namespace="*") defineSimpleDirective( context, "includeOverrides", IInclude, includeOverrides, namespace="*") 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 = 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 """ if context is None: context = ConfigurationMachine() registerCommonDirectives(context) f = io.BytesIO(s) if isinstance(s, bytes) else io.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 = ConfigurationMachine() registerCommonDirectives(_context) def _getContext(): global _context if _context is None: _clearContext() try: from zope.testing.cleanup import addCleanUp except ImportError: # pragma: no cover pass else: # pragma: no cover 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): """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-4.3.1/src/zope/configuration/__init__.py0000644000076500000240000000151313430472367023762 0ustar macstaff00000000000000############################################################################## # # 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-4.3.1/src/zope/configuration/exceptions.py0000644000076500000240000000443113430472367024406 0ustar macstaff00000000000000############################################################################## # # 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 """ import traceback __all__ = [ 'ConfigurationError', ] class ConfigurationError(Exception): """There was an error in a configuration """ # A list of strings or things that can be converted to strings, # added by append_details as we walk back up the call/include stack. # We will print them in reverse order so that the most recent detail is # last. _details = () def add_details(self, info): if isinstance(info, BaseException): info = traceback.format_exception_only(type(info), info) # Trim trailing newline info[-1] = info[-1].rstrip() self._details += tuple(info) else: self._details += (info,) return self def _with_details(self, opening, detail_formatter): lines = [' ' + detail_formatter(detail) for detail in self._details] lines.append(opening) lines.reverse() return '\n'.join(lines) def __str__(self): s = super(ConfigurationError, self).__str__() return self._with_details(s, str) def __repr__(self): s = super(ConfigurationError, self).__repr__() return self._with_details(s, repr) class ConfigurationWrapperError(ConfigurationError): USE_INFO_REPR = False def __init__(self, info, exception): super(ConfigurationWrapperError, self).__init__(repr(info) if self.USE_INFO_REPR else info) self.add_details(exception) # This stuff is undocumented and not used but we store # for backwards compatibility self.info = info self.etype = type(exception) self.evalue = exception zope.configuration-4.3.1/src/zope/configuration/_compat.py0000644000076500000240000000230113430472367023641 0ustar macstaff00000000000000############################################################################## # # Copyright (c) 2012 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. # ############################################################################## import sys PY3 = sys.version_info[0] >= 3 if PY3: # pragma: no cover import builtins string_types = (str,) text_type = str # borrowed from 'six' def reraise(tp, value, tb=None): if value is None: value = tp if value.__traceback__ is not tb: raise value.with_traceback(tb) raise value else: # pragma: no cover import __builtin__ as builtins text_type = unicode string_types = (basestring,) # borrowed from 'six' exec("""\ def reraise(tp, value, tb=None): raise tp, value, tb """) zope.configuration-4.3.1/src/zope/configuration/name.py0000644000076500000240000000523513430472367023150 0ustar macstaff00000000000000############################################################################## # # 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. .. note:: This module is no longer used by `zope.configuration` and may be deprecated soon. Its functions are not documented. """ import os from types import ModuleType __all__ = [ 'resolve', 'getNormalizedName', 'path', ] 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 = True else: repeat = False 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 = True else: repeat = False 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={}): # XXX WTF? why not look for abspath before importing? try: package = __import__(package, _globals, _globals, _silly) except ImportError: norm = os.path.normpath(file) if file and os.path.abspath(norm) == norm: # The package didn't matter return norm raise path = os.path.dirname(package.__file__) if file: path = os.path.join(path, file) return path zope.configuration-4.3.1/src/zope/__init__.py0000644000076500000240000000011313430472367021106 0ustar macstaff00000000000000__import__('pkg_resources').declare_namespace(__name__) # pragma: no cover